Overall Statistics
Total Trades
162246
Average Win
0.01%
Average Loss
-0.01%
Compounding Annual Return
-39.211%
Drawdown
77.500%
Expectancy
-0.220
Net Profit
-77.465%
Sharpe Ratio
-7.849
Probabilistic Sharpe Ratio
0%
Loss Rate
57%
Win Rate
43%
Profit-Loss Ratio
0.83
Alpha
-0.419
Beta
0.159
Annual Standard Deviation
0.05
Annual Variance
0.002
Information Ratio
-2.871
Tracking Error
0.195
Treynor Ratio
-2.469
Total Fees
$217289.23
Estimated Strategy Capacity
$84000000.00
Lowest Capacity Asset
POOL R735QTJ8XC9X
from System import *
from QuantConnect import *
from QuantConnect import Resolution
from QuantConnect.Algorithm import QCAlgorithm
from QuantConnect.Data.Consolidators import TradeBarConsolidator
from QuantConnect.Data.Market import TradeBar
from QuantConnect.Orders import OrderStatus
from QuantConnect.Orders.Fees import AlphaStreamsFeeModel
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from QuantConnect.Securities import Security, SecurityTransactionManager
from QuantConnect.Orders import *
from QuantConnect.Orders import OrderEvent
from QuantConnect.Orders.Fills import FillModel
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Risk import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Alphas import Insight
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Selection import *
from datetime import datetime, timedelta
import json
from triple_barrier import TripleBarrier
from pm_config import PM_Config
from pattern_match_client_factory import PatternMatchClientFactory

TICKERS = '''DISCK
EA
FB
TWTR
RCL
WYNN
MCD
LB
RL
PVH
AMZN
TSCO
POOL
MGM
MHK
PENN
LKQ
CPB
SJM
CLX
TAP
MNST
TSN
EOG
SLB
VLO
PXD
GS
MTB
CBOE
L
PFG
TROW
CMA
NDAQ
UNM
C
HIG
ICE
VRTX
PRGO
TFX
PKI
PFE
VAR
WST
DHR
ANTM
ABT
TDOC
BAX
ZBH
MCK
CHRW
LUV
NDSN
SWK
FLS
ROP
CMI
NSC
XYL
ALK
TXT
WM
NOC
SNPS
PEGA
FTNT
MSI
XLNX
ADP
GLW
TER
TXN
SHOP
HPE
MPWR
AMD
PTC
XRX
HUBS
FLT
TEAM
CF
SEE
SHW
NUE
MLM
UDR
CCI
DRE
EQIX
VNO
SO
CMS
D
DTE
SRE
TTWO'''.split()

class OAMPatternMatchingAlgorithm(QCAlgorithm):
    VERSION = '3.0.3'

    def Initialize(self: QCAlgorithm):
        qcAlg: QCAlgorithm = self
        tickerFileContents = self.Download(PM_Config.TICKER_CSV_URL)
        self.Log(tickerFileContents)
        self.tickers = TICKERS   # tickerFileContents.split()[0]
        self.Log(self.tickers)

        self.SetStartDate(2018,1,1)   #Set Start Date
        self.SetEndDate(2020,12,28)    #Set End Date
        self.SetCash(2000 * 1000)  # Set Strategy Cash
        self.PROFIT_PCT = 0.01
        self.LOSS_PCT   = 0.01
        
        self.AddEquity("SPY", Resolution.Minute)
        self.SetBenchmark("SPY")
        
        ## Set Universe Selection
        universeSymbols = [Symbol.Create(t, SecurityType.Equity, Market.USA) for t in self.tickers]
        self.UniverseSettings.Resolution = Resolution.Minute
        self.SetUniverseSelection(ManualUniverseSelectionModel(universeSymbols))

        self.pmc = PatternMatchClientFactory.create(qcAlg=self)

        for symbol in universeSymbols:
            equity = qcAlg.AddEquity(symbol, Resolution.Minute)

            ## make 15 min bars from minute bars
            tbc = TradeBarConsolidator(timedelta(minutes=15))
            tbc.DataConsolidated += self.FifteenMinConsolidator
            self.SubscriptionManager.AddConsolidator(symbol, tbc)

            ## hold all pertinent info for each symbol
            NUM_PERIODS_FOR_VWAP = 10
            TB = TripleBarrier(qcAlg, symbol, 0, qcAlg.PROFIT_PCT, qcAlg.LOSS_PCT)
            TB.vwap = self.VWAP(symbol, NUM_PERIODS_FOR_VWAP)
            TB.holdingPeriods = 0
            TripleBarrier.SYMBOL_DICTIONARY[symbol] = TB

        ## Does our broker have any exisiting state (order, positions)
        future = self.Time + timedelta(minutes=2)
        dt     = self.DateRules.On(future)
        tm     = self.TimeRules.At(future.hour, future.minute)
        self.Schedule.On(dt, tm, self.BrokerStateInit)

        self.Debug("[Initialize] Initialize Done()")
        self.Debug("[Initialize] OAMPatternMatchingAlgorithm VERSION: {}".format(OAMPatternMatchingAlgorithm.VERSION))
        self.Debug("[Initialize] Triple Barrier VERSION: {}".format(TripleBarrier.VERSION))
        self.Debug("[Initialize] PM Server: {}".format(PM_Config.PM_SERVER_URL))
        self.Debug("[Initialize] Ticker Urls {}".format(PM_Config.TICKER_CSV_URL))
        self.Debug("[Initialize] Tickers: {}".format(self.tickers))


    def BrokerStateInit(self):
        TripleBarrier.initFromBrokerState(self, self.PROFIT_PCT, self.LOSS_PCT)


    def FifteenMinConsolidator(self: QCAlgorithm, sender, bar: TradeBar):
        self.Debug("Consolidated bar: [{} <--> {}], bar = {}".format(bar.Time, bar.EndTime, bar))

        if not bar.Symbol in TripleBarrier.SYMBOL_DICTIONARY:
            self.Log("15-MIN DATA BUT NO TB -- WAS ALGORITHM REDEPLOYED W/ EXISTING HOLDINGS IN PORTFOLIO")
            return

        security: Security = self.Securities[bar.Symbol]
        TB: TripleBarrier = TripleBarrier.SYMBOL_DICTIONARY[bar.Symbol]

        headers = {
            "TICKER"  : str(bar.Symbol),
            "OPEN"    : str(round(bar.Open, 2)),
            "HIGH"    : str(round(bar.High,2)),
            "LOW"     : str(round(bar.Low,2)),
            "CLOSE"   : str(round(bar.Close,2)),
            "VOLUME"  : str(round(bar.Volume,2)),
            "VOLWAVG" : str(round(TB.vwap.Current.Value,2)),
            "ET"      : bar.Time.strftime("%Y-%m-%dT%H:%M:%S"),
            "ETE"     : bar.EndTime.strftime("%Y-%m-%d %H:%M:%S")
        }

        insight: Insight = self.pmc.getInsight(headers, self)
        self.Log("{} Insight for {} is {}".format(bar.Time, bar.Symbol, insight.Direction))

        # Are we last bar before market close?  If so, see you in the morning
        # 3:30 ...............  3:45 ............... 4:00pm
        #  |-------- BAR --------||-------- BAR -------|
        #
        mktClose: datetime = security.Exchange.Hours.GetNextMarketClose(localDateTime=bar.Time, extendedMarket=False)
        timeDeltaUntilClose = mktClose - self.Time 
        if timeDeltaUntilClose <= timedelta(minutes=10):
            self.Debug("Market is closed for {}.  Close is {}.  It is now {}".format(security, mktClose, self.Time))
            return  
                
        if security.Invested:
            TB.holdingPeriods += 1
            self.Log("15-MIN HOLDING PERIODS FOR {} = {}".format(bar.Symbol, TB.holdingPeriods))
            if TB.holdingPeriods >= 10:
                TB.placeTimeBarrierExitOrder()
                TB.holdingPeriods = 0
        else:
            if insight.Direction == InsightDirection.Up:
                weight = round(1.0 / len(self.tickers), 3)
                quantity = self.CalculateOrderQuantity(str(bar.Symbol), weight)
                TB.placeEntryOrder(quantity)
                
                
    def OnOrderEvent(self: QCAlgorithm, orderEvent: OrderEvent):
        orderId            = orderEvent.OrderId
        order: Order       = self.Transactions.GetOrderById(orderId)
        self.Debug("OnOrderEvent: {}, alg time: {}, brokerId: {}".format(order, self.Time, order.BrokerId))

        if not orderEvent.Symbol in TripleBarrier.SYMBOL_DICTIONARY:
            self.Log("ORDER EVENT BUT NO TB -- WAS ALGORITHM REDEPLOYED W/ EXISTING HOLDINGS IN PORTFOLIO: {}".format(order))
            return

        TB: TripleBarrier = TripleBarrier.SYMBOL_DICTIONARY[orderEvent.Symbol]
        TB.update(order, orderEvent)
from System import *
from QuantConnect import *
from QuantConnect import Resolution
from QuantConnect.Algorithm import QCAlgorithm
from QuantConnect.Algorithm.Framework.Alphas import InsightDirection
from QuantConnect.Algorithm.Framework.Alphas import Insight
from abc import ABC, abstractmethod


class PatternMatchClient:

    def __init__(self, qcAlg: QCAlgorithm) -> None:
        self.qcAlg = qcAlg

    ## This is the primary abstract method implemented by derived classes
    @abstractmethod
    def getInsight(self, headers, qcAlgorithm: QCAlgorithm) -> Insight:
        pass
from System import *
from QuantConnect import *
from QuantConnect import Resolution
from QuantConnect.Algorithm import QCAlgorithm
from QuantConnect.Algorithm.Framework.Alphas import InsightDirection
from QuantConnect.Algorithm.Framework.Alphas import Insight
from datetime import timedelta
from pm_config import PM_Config
from pattern_match_client import PatternMatchClient
import pandas as pd
import numpy as np
from io import StringIO


## Entry File example naming:
##     - AMZN_combined_trade_entries_2018_2019_2020.csv
## Data format
##     - '2019-01-07 10:15:00'
class PatternMatchClientEntryFile(PatternMatchClient):
    VERSION = '0.0.1'
    BASE_ENTRY_FILE_URL = "https://oamdatastorageacct.blob.core.windows.net/pm-entry/combined_entries"
    ENTRY_LOOKUP_TABLE = {}

    def __init__(self, qcAlg: QCAlgorithm) -> None:
        super().__init__(qcAlg)
        for t in qcAlg.tickers:
            filename = "{}_combined_trade_entries_2018_2019_2020.csv".format(t)
            #url = "{}/{}".format(PatternMatchClientEntryFile.BASE_ENTRY_FILE_URL, filename)
            #csv_str = qcAlg.Download(url)
            #sio = StringIO(csv_str)
            #qcAlg.ObjectStore.Save(filename, csv_str)
            csv_str = qcAlg.ObjectStore.Read(filename)
            sio = StringIO(csv_str)
            df = pd.read_csv(sio)
            df.set_index('trade_entry_index', inplace=True)
            PatternMatchClientEntryFile.ENTRY_LOOKUP_TABLE[t] = df
            qcAlg.Log("READ ENTRY FILE: {}".format(t))


    def getInsight(self, headers, qcAlgorithm: QCAlgorithm):
        try:
            t  = headers["TICKER"]
            dt = headers["ETE"]
            df = PatternMatchClientEntryFile.ENTRY_LOOKUP_TABLE[t]
            bullish_consensus = df.loc[dt]['bull_gt033']
            qcAlgorithm.Log("CONSENSUS:      {}".format(bullish_consensus))
            qcAlgorithm.Log("CONSENSUS TYPE: {}".format(type(bullish_consensus)))
                
            insightDirection = InsightDirection.Flat
            if not type(bullish_consensus) == np.bool_:
                qcAlgorithm.Error("CONSENSUS iS NOT A BOOL")
                exit(-1)
            else:
                if bullish_consensus == True:
                   insightDirection = InsightDirection.Up
    
            return Insight.Price(headers["TICKER"], timedelta(minutes=15), insightDirection)

        except:
            return Insight.Price("DUMMY", timedelta(minutes=15), InsightDirection.Flat)
from QuantConnect.Algorithm import QCAlgorithm
from pattern_match_client import PatternMatchClient
from pattern_match_client_pm_server import PatternMatchClientPmServer
from pattern_match_client_entry_file import PatternMatchClientEntryFile


class PatternMatchClientFactory:
    USE_ENTRY_FILES = True

    ## The Factory Create Method
    def create(qcAlg: QCAlgorithm) -> PatternMatchClient:

        ## Entry File Usage (for backtesting)
        if PatternMatchClientFactory.USE_ENTRY_FILES:
            return PatternMatchClientEntryFile(qcAlg)

        ## PM Server Usage (for live and paper trading)
        return PatternMatchClientPmServer(qcAlg)
from System import *
from QuantConnect import *
from QuantConnect import Resolution
from QuantConnect.Algorithm import QCAlgorithm
from QuantConnect.Algorithm.Framework.Alphas import InsightDirection
from QuantConnect.Algorithm.Framework.Alphas import Insight
from datetime import timedelta
from pm_config import PM_Config
from pattern_match_client import PatternMatchClient
import json

class PatternMatchClientPmServer(PatternMatchClient):
    VERSION = '2.0.1'

    def __init__(self, qcAlg: QCAlgorithm) -> None:
        super().__init__(qcAlg)


    def create_url(self, headers: dict):
        ## qs ex: = ?TICKER=AAPL&OPEN=109.2&ET=2021-01-05T10:15:00&HIGH=109.7&LOW=108.2&CLOSE=109.23&VOLUME=20000&VOLWAVG=109.51'
        base = PM_Config.PM_SERVER_URL
        queryString  = "TICKER={}".format(headers["TICKER"])
        queryString += "&OPEN={}".format(headers["OPEN"])
        queryString += "&HIGH={}".format(headers["HIGH"])
        queryString += "&LOW={}".format(headers["LOW"])
        queryString += "&CLOSE={}".format(headers["CLOSE"])
        queryString += "&VOLUME={}".format(headers["VOLUME"])
        queryString += "&VOLWAVG={}".format(headers["VOLWAVG"])
        queryString += "&ET={}".format(headers["ET"])
        url = "{}?{}".format(base, queryString)
        return url 


    def getInsight(self, headers, qcAlgorithm: QCAlgorithm):
        url = self.create_url(headers)
        pattern_match_server_response = qcAlgorithm.Download(url)
        qcAlgorithm.Debug("{} :: Json response from PM Server: {} with url {}".format(qcAlgorithm.Time, pattern_match_server_response, url))

        insightDirection = InsightDirection.Flat
        if len(pattern_match_server_response) > 0:
            decision = json.loads(pattern_match_server_response)["decision"]
            if decision == "BULLISH": insightDirection = InsightDirection.Up
            if decision == "NEUTRAL": insightDirection = InsightDirection.Flat
            if decision == "BEARISH": insightDirection = InsightDirection.Down

        return Insight.Price(headers["TICKER"], timedelta(minutes=15), insightDirection)
from QuantConnect import *
from QuantConnect import Resolution
from QuantConnect.Algorithm import QCAlgorithm
from QuantConnect.Orders import OrderStatus
from QuantConnect.Algorithm import *
from QuantConnect.Securities import Security, SecurityTransactionManager
from QuantConnect.Orders import *
from QuantConnect.Orders import OrderEvent
from QuantConnect.Orders.Fills import FillModel
from datetime import datetime, timedelta


class PM_Config():
    ## Stock Universe
    DEV_TICKER_CSV_URL  = "https://oamdatastorageacct.blob.core.windows.net/pattern-matching/tickers_100.csv"
    PROD_TICKER_CSV_URL = "https://oamdatastorageacct.blob.core.windows.net/pattern-matching/tickers_sp500.csv"

    ## Pattern Matching Server
    RANDOM_PM_SERVER_URL = 'https://oam-pattern-matching-server-webapp.azurewebsites.net/randomBullishBearishNeutral' 
    DEV_PM_SERVER_URL    = 'https://oam-pattern-matching-server-webapp-staging.azurewebsites.net/patternMatch'
    PROD_PM_SERVER_URL   = 'https://oam-pattern-matching-server-webapp.azurewebsites.net/patternMatch'

    ## TODO: Change these when going live to PROD (Production)
    TICKER_CSV_URL = DEV_TICKER_CSV_URL
    PM_SERVER_URL  = RANDOM_PM_SERVER_URL
from QuantConnect import *
from QuantConnect import Resolution
from QuantConnect.Algorithm import QCAlgorithm
from QuantConnect.Orders import OrderStatus
from QuantConnect.Algorithm import *
from QuantConnect.Securities import Security, SecurityTransactionManager
from QuantConnect.Orders import *
from QuantConnect.Orders import OrderEvent
from QuantConnect.Orders import Order
from QuantConnect.Orders.Fills import FillModel
from datetime import datetime, timedelta
import math


class TripleBarrier():
    VERSION = '3.0.1'
    SYMBOL_DICTIONARY = {}

    def initFromBrokerState(qcAlg: QCAlgorithm, profitPct: float, stopLossPct: float, noEntryWithinTimeDeltaToClose=None):
        qcAlg.Log("[TB BROKER STATE INIT] SETTING UP TRIPLE BARRIER SYMBOL_DICTIONARY FROM BROKER STATE")

        for symbol, security in qcAlg.Securities.items():
            if symbol not in TripleBarrier.SYMBOL_DICTIONARY:
                qcAlg.Log("[TB BROKER STATE INIT] ADDING TB FOR security: {0}".format(security))
                holdingQuantity = qcAlg.Portfolio[symbol].Quantity
                TB = TripleBarrier(qcAlg, symbol, holdingQuantity, profitPct, stopLossPct, noEntryWithinTimeDeltaToClose)
                TB.enterTime = qcAlg.Time
                TripleBarrier.SYMBOL_DICTIONARY[symbol] = TB
                
        openOrders = qcAlg.Transactions.GetOpenOrders()
        for oo in openOrders:
            o: Order = oo
            symbol = o.Symbol
            if symbol not in TripleBarrier.SYMBOL_DICTIONARY:
                qcAlg.Log("[TB BROKER STATE INIT] OpenOrder, but no TB?? : {0}".format(symbol))
                continue

            TB: TripleBarrier = TripleBarrier.SYMBOL_DICTIONARY[symbol]
            
            ticket: OrderTicket = qcAlg.Transactions.GetOrderTicket(oo.Id)
            if ticket.OrderType == OrderType.Limit:
                qcAlg.Log("[TB BROKER STATE INIT] FOUND TAKE PROFIT OpenOrderTicket : {0}".format(ticket))
                TB.profitTicket = ticket

            if ticket.OrderType == OrderType.StopMarket:
                qcAlg.Log("[TB BROKER STATE INIT] FOUND STOP LOSS OpenOrderTicket : {0}".format(ticket))
                TB.lossTicket = ticket


    def __init__(self, qcAlg: QCAlgorithm, tickerSymbol: Symbol, quantity: float, profitPct: float, stopLossPct: float, noEntryWithinTimeDeltaToClose=None) -> None:
        self.qcAlg: QCAlgorithm = qcAlg
        
        self.tickerSymbol = tickerSymbol
        self.entryPrice   = 0.0
        self.quantity     = round(quantity, 2)
        self.shorting     = self.quantity < 0
        
        self.profictPct   = profitPct
        self.profitTicket = None

        self.stopLossPct  = stopLossPct
        self.lossTicket   = None

        self.enterTime    = datetime.min
        self.entryTicket  = None

        self.noEntryWithinTimeDeltaToClose = noEntryWithinTimeDeltaToClose

        self.tag  = "DOUBLE-BAR" if (self.stopLossPct == 0 or self.profictPct == 0) else "TRIPLE-BAR"
        self.tag += "-{}".format(TripleBarrier.VERSION)

        self.log("BARRIER INIT")


    def log(self, msg):
        self.qcAlg.Log("[{}] {}, {}".format(self.tag, msg, self))


    def __str__(self) -> str:
        holdingQuantity = self.qcAlg.Portfolio[self.tickerSymbol].Quantity
        rval  = "["
        rval +=   "sym: {}, requested_quant: {}, current_quant: {}, ".format(self.tickerSymbol, self.quantity, holdingQuantity)
        rval +=   "fill_price: {} tp: {}, sl: {}, ".format(self.entryPrice, self.profictPct, self.stopLossPct)
        rval +=   "enterTime: {}".format(self.enterTime)
        rval += "]"
        return rval


    def placeEntryOrder(self, quantity):
        self.quantity = math.floor(quantity)
        security: Security = self.qcAlg.Securities[self.tickerSymbol]

        # is the mkt open
        IS_OPEN = security.Exchange.Hours.IsOpen(self.qcAlg.Time, extendedMarket=False)
        if not IS_OPEN:
            self.log("EXCHANGE NOT OPEN: NOT ENTERING POSITION WITH OPENING MKT ORDER")
            return

        ## Are we too close to close
        if self.noEntryWithinTimeDeltaToClose is not None:
            mktClose: datetime = security.Exchange.Hours.GetNextMarketClose(localDateTime=self.qcAlg.Time, extendedMarket=False)
            timeDeltaTilClosed = mktClose - self.qcAlg.Time
            if timeDeltaTilClosed <= self.noEntryWithinTimeDeltaToClose:
                self.log("NOT ENTERING POSITION THIS CLOSE TO MKT CLOSE: {}".format(timeDeltaTilClosed))
                return

        self.enterTime   = self.qcAlg.Time
        self.entryTicket = self.qcAlg.MarketOrder(self.tickerSymbol, self.quantity, asynchronous=True, tag="MKT ENTRY")
        self.log("PLACED ENTRY ORDER: note: FILL COULD TAKE AWHILE")


    def placeTimeBarrierExitOrder(self):
        self.log("CANCELING OPEN ORDERS AND PLACING TIME BARRIER MKT EXIT ORDER")
        self.qcAlg.Transactions.CancelOpenOrders(self.tickerSymbol, "TIME BARRIER CANCELING ALL OTHER ORDERS")
        self.qcAlg.Liquidate(self.tickerSymbol, tag="TIME BARRIER EXIT ORDER")


    def placeBarriers(self, price, quantity):
        if self.quantity != quantity:
            self.qcAlg.Error("QUANTITY MISMATCH ERROR")

        if self.quantity > 0:
            # LONG - the limit price needs to be above the average purchase price
            tp_price = round(price + (self.profictPct  * price), 2)
            sl_price = round(price - (self.stopLossPct * price), 2)

        if self.quantity < 0:
            # SHORT - the limit price needs to be below the average purchase price
            tp_price = round(price - (self.profictPct  * price), 2)
            sl_price = round(price + (self.stopLossPct * price), 2)

        # submit a take profit limit order - as other order (partial) fills, we need to adjust
        if self.profictPct != 0:
            self.profitTicket = self.qcAlg.LimitOrder(self.tickerSymbol, -self.quantity, tp_price, "TAKE PROFIT {}".format(self.tickerSymbol))
            self.log("TAKE PROFIT ORDER (LMT) SET AT {}".format(tp_price))

        # submit stop loss order - as other order (partial) fills, we need to adjust
        if self.stopLossPct != 0:
            self.lossTicket = self.qcAlg.StopMarketOrder(self.tickerSymbol, -self.quantity, sl_price, "STOP LOSS {}".format(self.tickerSymbol))
            self.log("STOP LOSS ORDER (STP-MKT) SET AT {}".format(sl_price))
        
            
    def update(self, order: Order, orderEvent: OrderEvent):
        if orderEvent.Status == OrderStatus.PartiallyFilled:
            self.updatePartiallyFilled(order, orderEvent)

        if orderEvent.Status == OrderStatus.Filled:
            self.updateFullyFilled(order, orderEvent)


    def updatePartiallyFilled(self, order: Order, orderEvent: OrderEvent):
        holdingQuantity = self.qcAlg.Portfolio[self.tickerSymbol].Quantity
 
        if order.Type == OrderType.Market:
            self.log("PARTIALLY FILLED ENTRY OR EXIT (TIME BARRIER) MKT TICKET")
            ## A mkt order is either the entry or the time-barrier exit - we don't care about partial fills
            return

        if self.lossTicket is None or self.profitTicket is None:
            self.log("NO SIBLING PRICE BARRIER ORDER - PARTIAL FILLS DO NOT REQUIRE UPDATES TO OTHER ORDERS")
            return

        orderResponse = None
        updateOrderFields = UpdateOrderFields() 
        updateOrderFields.Quantity = -holdingQuantity
        if self.lossTicket.OrderId == order.Id and self.profitTicket is not None:
            self.log("PARTIALLY FILLED LOSS TICKET - UPDATE TAKE PROFIT TICKET {}")
            orderResponse = self.profitTicket.Update(updateOrderFields)

        if self.profitTicket.OrderId == order.Id and self.lossTicket is not None:
            self.log("PARTIALLY FILLED PROFIT TICKET - UPDATE STOP LOSS TICKET {}")
            orderResponse = self.lossTicket.Update(updateOrderFields)

        return orderResponse


    def updateFullyFilled(self, order: Order, orderEvent: OrderEvent):
        symbol   = orderEvent.Symbol
        security = self.qcAlg.Securities[symbol]

        MKT_ENTRY_ORDER_FILLED        = order.Type == OrderType.Market and security.Invested
        MKT_TIME_BARRIER_ORDER_FILLED = order.Type == OrderType.Market and not security.Invested
        PRICE_BARRIER_ORDER_FILLED    = order.Type != OrderType.Market

        if MKT_ENTRY_ORDER_FILLED:
            ## If market entry order is now filled place the other orders
            self.entryPrice = order.Price
            quantity        = order.Quantity
            self.log("ENTRY ORDER (MKT) FILLED: {}".format(orderEvent))
            self.placeBarriers(self.entryPrice, quantity)

        if MKT_TIME_BARRIER_ORDER_FILLED:
            self.qcAlg.Transactions.CancelOpenOrders(self.tickerSymbol, "TIME BARRIER CANCELING ALL OTHER ORDERS")
            self.log("EXIT ORDER (MKT) TIME BARRIER FILLED (WE WILL CANCEL THE PRICE BARRIER ORDERS) {}".format(orderEvent))

        if PRICE_BARRIER_ORDER_FILLED:
            self.qcAlg.Transactions.CancelOpenOrders(self.tickerSymbol, "PRICE BARRIER FILL CANCELING ALL OTHER ORDERS")
            self.log("PRICE BARRIER (TP OR SL) ORDER FILLED (WE CANCELED THE OTHER): {}".format(orderEvent))