Overall Statistics |
Total Trades 150 Average Win 0.07% Average Loss 0.00% Compounding Annual Return -4.358% Drawdown 0.200% Expectancy -0.689 Net Profit -0.169% Sharpe Ratio -6.812 Loss Rate 99% Win Rate 1% Profit-Loss Ratio 22.36 Alpha -0.037 Beta -0.004 Annual Standard Deviation 0.006 Annual Variance 0 Information Ratio -2.674 Tracking Error 0.221 Treynor Ratio 9.764 Total Fees $150.00 |
#Create a function which accepts a consolidated daily OHLC bar and returns a dictionary of pivots: #Including (H5,H4,H3,H2,H1,C,L1,L2,L3,L4,L5) #Decided to use a dictionary instead of a tuple due to code readablity on lookup #Equation from "Secrets of a Pivot Boss" def carmillaPivots(bar): close = bar.Close high = bar.High low = bar.Low r = high - low return { 'H5': (high/low)*close, 'H4': close + r*(1+1/2), 'H3': close + r*(1+1/4), 'H2': close + r*(1+1/6), 'H1': close + r*(1+1/12), 'C' : close, 'L1': close - r*(1+1/12), 'L2': close - r*(1+1/6), 'L3': close - r*(1+1/4), 'L4': close - r*(1+1/2), 'L5': close - ((high/low)*close-close), }
""" This is a docstring! DONE In the algo there is initialize the data DONE There is consolidate the minute data into daily bars (to calculate the pivots) and, when done, DONE there is call the pivot function to return a dictionary of pivots for the day, 5 on each side + one central DONE Store the data, and the prior day's pivot bars DONE Begin trading logic AFTER warm-up period complete TRADE LOGIC: Objective: For this implemtation I'm selling at lvl 3 with a stop at lvl 4. TP'ing at opposite for this simple algorithm. (unlikely to be profitable?) -Positions will be held overnight until they are either stopped out or limit ordered. -If stopped out during day, no more trading for the day -Orders will be replaced each day unless position is still current, at which point, however, opposite stop order has been canceled. When minute bars start flowing in, pivots will have been calculated. Now we place limit and stop orders according to the pivots if there isn't a current position: DONE, for now trade fixed number of shares --Need to have a proper risk management concept: want the algo to trade 1% of account value spread based on the pivot points. However, it's possible that....why not just have it trade a fixed number of shares for the first go? Don't want to overleverage and get errors as a result. Not sure what that does to the algorithm DONE 1) if not self.traded and not self.Portfolio.Invested: #if we haven't traded today and don't have a position from the previous day (or anymore) -cancel all potentially open orders using liquidate #this allows prior trade to complete and still place new trades for the day -place limit orders on H3 and L3 pivots, rounding to nearest cent with GTC expiry -self.tradePivots == self.currentPivots -set self.traded == True 2) SKIP for now because does nothing elif self.Portfolio.Invested: #we've placed orders for the day and are currently in position -do nothing. All orders already in place. return 3) SKIP for now because does nothing elif [there exist 4 open orders]: #in this case we're waiting for a trade return INSTEAD 4) cancelling other side for the day --to check and see if we've filled one set of order we can determine how many open stopmarketorders there are. If there are none means we've lost and should liquidate. else: #in this case we've lost.' liquidate ^INSTEAD 5) if less than 2 open stop market orders: means we've had a losing trade. Cancel the other limit and stop order. ORDER FILL EVENT HANDLER: SKIP due to functionality being implemented on a not-as-ideal basis by #5 in the above If one limit order is filled, immediately cancel the stop order on the opposite limit so the opposite side can be used as a take profit. To make more resilient in the future: -Implement OCO orders via interactive brokers, natively -Check to ensure entire order is filled. If not and has passed center, cancel remaining order and update all other orders appropriately. - Algo weaknesses: -In trending market should keep getting stopped out over and over again. --Use pivot point width to compensate or a momentum indicator such as MACD histogram...slope of moving average. -Unsure if liquidating on the opposite leve is reasonable ## Resources! Order ticket demo algorithm: https://github.com/QuantConnect/Lean/blob/master/Algorithm.Python/OrderTicketDemoAlgorithm.py Plotting indicators on price charts (2015): https://www.quantconnect.com/forum/discussion/859/how-to-plot-indicator-on-price-chart/p1 Order sample Python code: https://github.com/QuantConnect/Lean/blob/master/Algorithm.Python/OrderTicketDemoAlgorithm.py C# OCO Sample: https://www.quantconnect.com/forum/discussion/1700/support-for-one-cancels-the-other-order-oco/p1 Monitor order event handler to see when stops triggered: https://www.quantconnect.com/forum/discussion/2380/know-when-stop-order-or-stop-limit-order-has-triggered/p1 DEBUGGING: Debug python in visual studio: https://www.quantconnect.com/tutorials/open-source/debugging-python-in-visual-studio """
from datetime import datetime, timedelta #from pivots import carmillaPivots <<don't know how to import pivots.py, keep getting an error that it's not defined class CarmillaH3(QCAlgorithm): def __init__(self): self.traded = False def Initialize(self): #main parameters self.SetStartDate(2018, 11, 25) # Set Start Date self.SetEndDate(datetime.now()) # Set End Date self.SetCash(100000) # Set Strategy Cash #pivot storage / trade flags self.currentPivots = {} self.priorPivots = {} self.tradePivots = {} self.traded = False #order tracking self.__openLimitOrders = [] self.__openStopMarketOrders = [] #Symbol Setyo #in the future will use universes. Excited QC supports multiple asset selection like this! symbol = "SPY" equity = self.AddEquity(symbol, Resolution.Minute) self.equity = equity.Symbol #may want to account for dividends based on backtest results! #eventually want to include universe of stocks #set warm-up period of 1 days so can calculate pivots (need 2 days when compare the two) self.SetWarmUp(timedelta(2)) # this is correct #Brokerage model and account type: self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash) ## EXTRANEOUS Set benchmark self.SetBenchmark("SPY") #CONSOLIDATOR as per: https://www.quantconnect.com/tutorials/api-tutorials/consolidating-data-to-build-bars # define our 1 day bar consolidator pivotConsolidator = TradeBarConsolidator(timedelta(days=1)) # attach our event handler. The event handler is a function that will # be called each time we produce a new consolidated piece of data. pivotConsolidator.DataConsolidated += self.PivotBarHandler # this call adds the pivot consolidator to # the manager to receive updates from the engine self.SubscriptionManager.AddConsolidator(symbol, pivotConsolidator) #Create a function which accepts a consolidated daily OHLC bar and returns a dictionary of pivots: #Including (H5,H4,H3,H2,H1,C,L1,L2,L3,L4,L5) #Decided to use a dictionary instead of a tuple due to code readablity on lookup #Equation from "Secrets of a Pivot Boss" def carmillaPivots(self, bar): close = bar.Close high = bar.High low = bar.Low r = high - low return { 'H5': (high/low)*close, 'H4': close + r*(1+1/2), 'H3': close + r*(1+1/4), 'H2': close + r*(1+1/6), 'H1': close + r*(1+1/12), 'C' : close, 'L1': close - r*(1+1/12), 'L2': close - r*(1+1/6), 'L3': close - r*(1+1/4), 'L4': close - r*(1+1/2), 'L5': close - ((high/low)*close-close) } def PivotBarHandler(self, sender, bar): '''This is our event handler for our daily bar defined above in Initialize(). So each time the consolidator produces a new bar, this function will be called automatically. The sender parameter will be the instance of the IDataConsolidator that invoked the event ''' #self.Debug(str(self.Time) + " " + str(bar)) self.priorPivots = self.currentPivots self.currentPivots = self.carmillaPivots(bar) self.traded = False #Main event handler def OnData(self, data): '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. Arguments: data: Slice object keyed by symbol containing the stock data ''' #Only run trade logic if IsWarmingUp is false, which will prevent errors due to unpopulated pivot dictionaries if self.IsWarmingUp: return #If traded is false, create orders if not self.traded and not self.Portfolio.Invested: #if we haven't traded today and don't have a position from the previous day (or anymore) self.Liquidate() #cancel all potentially open orders using liquidate this allows prior trade to complete and still place new trades for the day #place limit orders on H3 and L3 pivots, rounding to nearest cent with GTC expiry #close = self.Securities[self.equity.Value].Close #H3 short newTicket = self.LimitOrder(self.equity, -10, self.currentPivots["H3"]) self.__openLimitOrders.append(newTicket) #L3 long newTicket = self.LimitOrder(self.equity, 10, self.currentPivots["L3"]) self.__openLimitOrders.append(newTicket) #place stop orders on H4 and L4 pivot #H4 stop (market sell) newTicket = self.StopMarketOrder(self.equity, 10, self.currentPivots["H4"]) self.__openStopMarketOrders.append(newTicket) #L4 stop (market buy) newTicket = self.StopMarketOrder(self.equity, -10, self.currentPivots["L4"]) self.__openStopMarketOrders.append(newTicket) #Set flag and pivots self.traded == True self.tradePivots == self.currentPivots else: #check to see how many stop orders remain. If none means we've lost (because we're cancelling the other on trade fill) and need to close the opposite limit and stop order. if len(self.__openStopMarketOrders) <2: self.Liquidate() ''' if not self.Portfolio.Invested: self.SetHoldings(symbol, 1) holdings = self.Portfolio[self.contract.Symbol].Quantity if holdings <= 0: # Go long if self.fast_sma > self.slow_sma: self.Log("BUY >> {}".format(price)) self.MarketOrder(self.contract.Symbol, 1) if holdings > 0 and self.fast_sma < self.slow_sma: self.Log("SELL >> {}".format(price)) self.Liquidate() ''' #Helper order methods: #def ......