Overall Statistics |
Total Trades 388 Average Win 0.34% Average Loss -0.30% Compounding Annual Return 15.598% Drawdown 1.900% Expectancy 0.332 Net Profit 21.286% Sharpe Ratio 2.408 Probabilistic Sharpe Ratio 97.855% Loss Rate 37% Win Rate 63% Profit-Loss Ratio 1.12 Alpha 0 Beta 0 Annual Standard Deviation 0.044 Annual Variance 0.002 Information Ratio 2.408 Tracking Error 0.044 Treynor Ratio 0 Total Fees $717.80 Estimated Strategy Capacity $20000000.00 Lowest Capacity Asset MES XZDYPWUWC7I9 |
# https://www.quantconnect.com/tutorials/introduction-to-options/stochastic-processes-and-monte-carlo-method # *** https://towardsdatascience.com/improving-your-algo-trading-by-using-monte-carlo-simulation-and-probability-cones-abacde033adf *** # https://kjtradingsystems.com/calculators.html # Additional Stats we can use after completing a Monte Carlo: # Risk of Ruin - https://en.wikipedia.org/wiki/Risk_of_ruin # Max and median drawdowns # Annual rates of return # return / drawdown ratios # Next steps: # 1) write out steps to use QC backtests on the MC excel sheet. # 2) create a copy of the excel sheet that includes position sizing and/or max risk per trade # will need to research how to add those """ Disadvantages of using Monte Carlo: 1) he first disadvantage is that the historical results need to be stable. By “stable” I am not referring to the actual trade results, but rather the trading method. A trader who consistently trades the same system or approach has a stable method. 2) if the market changes significantly. When a “step” change in the market occurs, Monte Carlo analysis is no longer valid. - structural changes in the market could invalidate monte carlo results. - do we want/need a different monte carlo for different regimes? (e.g. quantitative tightening vs easing, inflationary vs deflationary, deflation/reflation/inflation/growth) 3) serial correlation of trades: Monte Carlo assumes each trade is independent of the previous trade, but for many systems the current trade is dependent (or at least correlated) to the previous trade. - there is a modified Monte Carlo approach that detect and deal with serial correlation. 4) Monte Carlo does not fix or detect curvefitting, over optimization, hindsight bias, etc. - If the backtest is overfit, the Monte carlo analysis will be as well and give false confidence. """ class GeekyBlackCamel(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 11, 18) # Set Start Date self.SetCash(100000) # Set Strategy Cash self.AddEquity("SPY", Resolution.Minute) self.AddEquity("BND", Resolution.Minute) self.AddEquity("AAPL", Resolution.Minute) def OnData(self, data: Slice): '''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 ''' if not self.Portfolio.Invested: self.SetHoldings("SPY", 0.33) self.SetHoldings("BND", 0.33) self.SetHoldings("AAPL", 0.33)
# https://www.quantconnect.com/tutorials/introduction-to-options/stochastic-processes-and-monte-carlo-method # *** https://towardsdatascience.com/improving-your-algo-trading-by-using-monte-carlo-simulation-and-probability-cones-abacde033adf *** # https://kjtradingsystems.com/calculators.html # Additional Stats we can use after completing a Monte Carlo: # Risk of Ruin - https://en.wikipedia.org/wiki/Risk_of_ruin # Max and median drawdowns # Annual rates of return # return / drawdown ratios # Next steps: # 1) write out steps to use QC backtests on the MC excel sheet. # 2) create a copy of the excel sheet that includes position sizing and/or max risk per trade # will need to research how to add those """ Disadvantages of using Monte Carlo: 1) he first disadvantage is that the historical results need to be stable. By “stable” I am not referring to the actual trade results, but rather the trading method. A trader who consistently trades the same system or approach has a stable method. 2) if the market changes significantly. When a “step” change in the market occurs, Monte Carlo analysis is no longer valid. - structural changes in the market could invalidate monte carlo results. - do we want/need a different monte carlo for different regimes? (e.g. quantitative tightening vs easing, inflationary vs deflationary, deflation/reflation/inflation/growth) 3) serial correlation of trades: Monte Carlo assumes each trade is independent of the previous trade, but for many systems the current trade is dependent (or at least correlated) to the previous trade. - there is a modified Monte Carlo approach that detect and deal with serial correlation. 4) Monte Carlo does not fix or detect curvefitting, over optimization, hindsight bias, etc. - If the backtest is overfit, the Monte carlo analysis will be as well and give false confidence. """ import decimal import pandas as pd from datetime import datetime from io import StringIO from datetime import datetime, timedelta import pickle from math import floor # page w/ all Micro symbols - https://www.quantconnect.com/datasets/algoseek-us-futures # micros started trading 5/6/2019 """ Algo Overview Intraday momentum strategy that uses the momentum gauge with the following chars. - Uses the mg difference - postive or negative to decide whether to short or long - Uses overnight price gap to further qualify that the above is a continuing trend """ # This DEV is to add another TP to lock in higher gains after significant moves in our direction. # Next Steps: # add new logic (done) # qual check new logic (done) # optimize # compare against STG backtests to see if value is added or not (seems to help significantly) # - will need to update to %s once that upgrade is used in production # FUTURES_CONTRACT = Futures.Indices.SP500EMini FUTURES_CONTRACT = Futures.Indices.MicroSP500EMini EQUITY = "SPXL" BENCHMARK = "SPXL" GAP_MIN = .002 START_TAKE_PROFIT_POINTS = 25 PROFIT_POINTS = 10 START_TAKE_PROFIT_POINTS_HIGHER = 50 PROFIT_POINTS_HIGHER = 30 LIVE = False GO_LIVE_CLEAR = False GO_LIVE_CLOSING_OVERWRITE = False GO_LIVE_CLOSING_OVERWRITE_VALUE = 4569.50 GO_LIVE_CLOSING_VIEW = False RISK_PERCENTAGE = .08 MAX_LOSS_POINTS_AS_PERCENTAGE = .0087 MARGIN_REQ_PER_CONTRACT = 1800 # https://www.interactivebrokers.com/en/trading/margin-futures-fops.php PRICE_PER_POINT = 5 class gapMomentum(QCAlgorithm): closeObjectStoreKey = "close_object" def Initialize(self): """ Initialize -- note SP500 MG start date data is 2018-11-28 """ # -- Preset Times -- # # # Opto Set # self.SetStartDate(2021, 1, 1) # self.SetEndDate(2022, 3, 25) # # Long Set self.SetStartDate(2021, 1, 1) self.SetEndDate(2022, 5, 1) # 1 Year # self.SetStartDate(2021, 3, 25) # self.SetEndDate(2022, 3, 25) # Debug margin req # self.SetStartDate(2020, 3, 3) # self.SetEndDate(2020, 4, 25) # Current Set # self.SetStartDate(2022, 1, 26) # self.SetEndDate(2022, 5, 3) # -- Optimizations Used -- # start_take_profit_points = self.GetParameter("start_take_profit_points") self.start_take_profit_points = START_TAKE_PROFIT_POINTS if start_take_profit_points is None else float(start_take_profit_points) profit_points = self.GetParameter("profit_points") self.profit_points = PROFIT_POINTS if profit_points is None else float(profit_points) gap_min = self.GetParameter("gap_min") self.gapMin = GAP_MIN if gap_min is None else float(gap_min) risk_percentage = self.GetParameter("risk_percentage") self.risk_percentage = RISK_PERCENTAGE if risk_percentage is None else float(risk_percentage) max_loss_as_percentage = self.GetParameter("max_loss_as_percentage") self.max_loss_as_percentage = MAX_LOSS_POINTS_AS_PERCENTAGE if max_loss_as_percentage is None else float(max_loss_as_percentage) start_take_profit_points_higher = self.GetParameter("start_take_profit_points_higher") self.start_take_profit_points_higher = START_TAKE_PROFIT_POINTS_HIGHER if start_take_profit_points_higher is None else float(start_take_profit_points_higher) profit_points_higher = self.GetParameter("profit_points_higher") self.profit_points_higher = PROFIT_POINTS_HIGHER if profit_points_higher is None else float(profit_points_higher) # -- Futures & Equity Setup & Go Live Clear -- # if GO_LIVE_CLEAR or GO_LIVE_CLOSING_OVERWRITE or GO_LIVE_CLOSING_VIEW: self.SetStartDate(2022, 2, 14) self.SetEndDate(2022, 2, 16) self.SetCash(30000) # Setup Futures Contract self.futuresContract = self.AddFuture(FUTURES_CONTRACT) self.futuresContract.SetFilter(5, 150) self.contracts = {} self.oi_contract = None # Brokerage Model self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage) # Add SPXL for market hours self.equity = self.AddEquity(EQUITY, Resolution.Minute).Symbol # Initial dwonload for back-test url = self.Download(address="https://www.dropbox.com/s/6w6ip6fvh2q7ciw/SP500_MG_Real_prod.csv?dl=1", headers={"Cache-Control": "no-cache", "Pragma": "no-cache"}) self.MG_df = pd.read_csv(StringIO(url)) self.MG_df['Date'] = self.MG_df['Date'].apply(Utils.dateModFromString) self.MG_df['Date'] = self.MG_df['Date'].apply(Utils.dateModToFormat) # Capture Closing price self.Schedule.On(self.DateRules.EveryDay(EQUITY), self.TimeRules.BeforeMarketClose(self.equity, 0), self.captureClosingPrice) # Scheduled Entry self.Schedule.On(self.DateRules.EveryDay(EQUITY), self.TimeRules.AfterMarketOpen(self.equity, 0), self.openPosition) # Initilization Vars self.closingPrice = None self.priceGap = None self.price = None self.positionType = None self.openingPrice = 0 self.security = None self.gapDown = None self.gapUp = None self.closePrice = None self.positionType = None self.boughtIn = False self.sitOut = False self.textProfit = True def OnData(self, data): """ Sorts Futures Contracts by OpenInterest """ self.data = data if GO_LIVE_CLOSING_VIEW: value = self.ObjectStore.Read(self.closeObjectStoreKey) deserialized_value = bytes(self.ObjectStore.ReadBytes(self.closeObjectStoreKey)) self.closingPrice = pickle.loads(deserialized_value) self.Debug("Current closingPrice stored: " + str(self.closingPrice)) return if GO_LIVE_CLOSING_OVERWRITE: self.ObjectStore.SaveBytes(self.closeObjectStoreKey, pickle.dumps(GO_LIVE_CLOSING_OVERWRITE_VALUE)) self.Debug("Manually stored closingPrice: " + str(GO_LIVE_CLOSING_OVERWRITE_VALUE)) return if GO_LIVE_CLEAR: self.ObjectStore.Delete(self.closeObjectStoreKey) return # Iterates through futures contracts for chain in data.FutureChains: contracts = sorted([k for k in chain.Value if k.OpenInterest > 0], key=lambda k : k.OpenInterest, reverse=True) if not contracts: continue # Chooses Futures Contract with most open interest self.oi_contract = contracts[0] # Uses Futures Contract with most Open Interest self.symbol = self.oi_contract.Symbol if self.symbol not in self.contracts: self.contracts[self.symbol] = SymbolData(self, self.symbol) self.contracts[self.symbol].consolidators['INTRADAY_CONSOLIDATOR'].DataConsolidated += self.IntradayBars # self.Debug(" Debug for chain ") self.security = self.Securities[self.symbol] self.price = self.security.Price self.initialMargin = self.security.BuyingPowerModel.InitialOvernightMarginRequirement # add to duplicate in openPosition def openPosition(self): """ Opens Position Short or Long """ # Return for hardcoding / clearing if GO_LIVE_CLEAR == True or GO_LIVE_CLOSING_OVERWRITE == True or GO_LIVE_CLOSING_VIEW == True: return # Catch if OnData didn't run first, if it hasn't, set security if self.security is None: for chain in self.data.FutureChains: contracts = sorted([k for k in chain.Value if k.OpenInterest > 0], key=lambda k : k.OpenInterest, reverse=True) if not contracts: continue # Chooses Futures Contract with most open interest self.oi_contract = contracts[0] # Uses Futures Contract with most Open Interest if self.symbol not in self.contracts: self.contracts[self.symbol] = SymbolData(self, self.symbol) self.contracts[self.symbol].consolidators['INTRADAY_CONSOLIDATOR'].DataConsolidated += self.IntradayBars self.security = self.Securities[self.symbol] self.price = self.security.Price # Debug log to indicate that OnData isn't running before open position self.Debug(str(self.Time) + " : Could not find existing futures info data from onData") # Read in stored value for closePrice to closingPrice if self.ObjectStore.ContainsKey(self.closeObjectStoreKey): value = self.ObjectStore.Read(self.closeObjectStoreKey) deserialized_value = bytes(self.ObjectStore.ReadBytes(self.closeObjectStoreKey)) self.closingPrice = pickle.loads(deserialized_value) # Calculate price gap for MG if self.price is not None and self.closingPrice is not None and self.priceGap is None: self.priceGap = self.price - self.closingPrice self.gapUp = self.closingPrice * (1+self.gapMin) self.gapDown = self.closingPrice * (1-self.gapMin) # self.Debug(str(self.Time) + " Set at 'Open'- Old Closing Price: " + str(self.closingPrice) + " -- Open Price: " + str(self.price) + " -- Price Gap: " + str(self.priceGap) + " -- gap Up: " + str(self.gapUp) + " -- gap down: " + str(self.gapDown)) self.Notify.Sms("+12035831419", "Set at Open- Old Closing Price: " + str(self.closingPrice) + " -- Open Price: " + str(self.price) + " -- Price Gap: " + str(self.priceGap) + " -- gap Up: " + str(self.gapUp) + " -- gap down: " + str(self.gapDown)) self.Notify.Sms("+15059771023", "Set at Open- Old Closing Price: " + str(self.closingPrice) + "-- Open Price: " + str(self.price) + " -- Price Gap: " + str(self.priceGap) + " -- gap Up: " + str(self.gapUp) + " -- gap down: " + str(self.gapDown)) # Set start position at beginning of day self.startInvestment = self.Portfolio.TotalPortfolioValue self.boughtIn = False self.sitOut = False self.textProfit = True self.keepPrice = self.price self.openingPrice = self.price # Cause a reinitilization of the file download every open position if LIVE if LIVE == True: url = self.Download(address="https://www.dropbox.com/s/6w6ip6fvh2q7ciw/SP500_MG_Real_prod.csv?dl=1", headers={"Cache-Control": "no-cache", "Pragma": "no-cache"}) self.MG_df = pd.read_csv(StringIO(url)) # Grab last known entry, which should be yesterday self.dayUsed = self.MG_df.iloc[-1][0] self.MG_positive = float(self.MG_df.iloc[-1][1]) self.MG_negative = float(self.MG_df.iloc[-1][2]) self.MG_difference = float(self.MG_df.iloc[-1][3]) self.MG_daily_change = float(self.MG_df.iloc[-1][4]) # Send text at 7:30 mountain with MG info # self.Debug(str(self.Time) + " -- Using Day: " + str(self.dayUsed) + " -- MG Difference: " + str(self.MG_difference) + " -- MG Daily Change: " + str(self.MG_daily_change)) self.Notify.Sms("+12035831419", str(self.Time) + " -- Using Day: " + str(self.dayUsed) + " -- MG Difference: " + str(self.MG_difference) + " -- MG Daily Change: " + str(self.MG_daily_change)) self.Notify.Sms("+15059771023", str(self.Time) + " -- Using Day: " + str(self.dayUsed) + " -- MG Difference: " + str(self.MG_difference) + " -- MG Daily Change: " + str(self.MG_daily_change)) # self.Debug(str(self.Time) + " opening Portfolio Margin: " + str(self.Portfolio.MarginRemaining)) def captureClosingPrice(self): """ Capture closing price """ # Closing Price information self.closePrice = self.price self.Notify.Sms("+12035831419", "Closing Price: " + str(self.closePrice)) self.Notify.Sms("+15059771023", "Closing Price: " + str(self.closePrice)) self.priceGap = None if self.closePrice is not None: self.ObjectStore.SaveBytes(self.closeObjectStoreKey, pickle.dumps(self.closePrice)) # self.Debug(str(self.Time) + ' - Use Close price of: ' + str(self.closePrice)) def IntradayBars(self, sender, bar): """ Creates IntraDay Bars """ if not self.oi_contract or self.oi_contract.Symbol != bar.Symbol: return # Check if Today's date in MG File for back-testing try: current_time = f'{self.Time.year}-{self.Time.month}-{self.Time.day}' todayidxList = self.MG_df.index[self.MG_df['Date']==current_time].tolist() todayIDX = todayidxList[0] self.todaysValues = True except: self.todaysValues = False # Use Yesterday's numbers to prevent look-ahead bias if self.todaysValues == True: self.dayUsed = self.MG_df.loc[todayIDX-1][0] self.MG_positive = float(self.MG_df.loc[todayIDX-1][1]) self.MG_negative = float(self.MG_df.loc[todayIDX-1][2]) self.MG_difference = float(self.MG_df.loc[todayIDX-1][3]) self.MG_daily_change = float(self.MG_df.loc[todayIDX-1][4]) # Time Logic current_time = self.Time.strftime("%H:%M") current_time_as_date = datetime.strptime(current_time, '%H:%M').time() # This is a back-up if open position somehow doesn't get the priceGap, an OK use of a `==` if current_time_as_date == datetime.strptime('9:30', '%H:%M').time(): if self.priceGap is None: if self.ObjectStore.ContainsKey(self.closeObjectStoreKey): value = self.ObjectStore.Read(self.closeObjectStoreKey) deserialized_value = bytes(self.ObjectStore.ReadBytes(self.closeObjectStoreKey)) self.closingPrice = pickle.loads(deserialized_value) self.priceGap = self.price - self.closingPrice self.gapUp = self.closingPrice * (1+self.gapMin) self.gapDown = self.closingPrice * (1-self.gapMin) # self.Debug(str(self.Time) + "Set at '==' - Old Closing Price: " + str(self.closingPrice) + " -- Open Price: " + str(self.price) + " -- Price Gap: " + str(self.priceGap) + " -- gap Up: " + str(self.gapUp) + " -- gap down: " + str(self.gapDown)) self.Notify.Sms("+12035831419", "Set at == - Old Closing Price: " + str(self.closingPrice) + " -- Open Price: " + str(self.price) + " -- Price Gap: " + str(self.priceGap) + " -- gap Up: " + str(self.gapUp) + " -- gap down: " + str(self.gapDown)) self.Notify.Sms("+15059771023", "Set at == - Old Closing Price: " + str(self.closingPrice) + "-- Open Price: " + str(self.price) + " -- Price Gap: " + str(self.priceGap) + " -- gap Up: " + str(self.gapUp) + " -- gap down: " + str(self.gapDown)) # Set start position at beginning of day self.startInvestment = self.Portfolio.TotalPortfolioValue self.boughtIn = False self.sitOut = False self.textProfit = True self.keepPrice = self.price self.openingPrice = self.price if LIVE == True: url = self.Download(address="https://www.dropbox.com/s/6w6ip6fvh2q7ciw/SP500_MG_Real_prod.csv?dl=1", headers={"Cache-Control": "no-cache", "Pragma": "no-cache"}) self.MG_df = pd.read_csv(StringIO(url)) # Grab last known entry, which should be yesterday self.dayUsed = self.MG_df.iloc[-1][0] self.MG_positive = float(self.MG_df.iloc[-1][1]) self.MG_negative = float(self.MG_df.iloc[-1][2]) self.MG_difference = float(self.MG_df.iloc[-1][3]) self.MG_daily_change = float(self.MG_df.iloc[-1][4]) # self.Debug(str(self.Time) + " -- Using Day: " + str(self.dayUsed) + " -- MG Difference: " + str(self.MG_difference) + " -- MG Daily Change: " + str(self.MG_daily_change)) self.Notify.Sms("+12035831419", str(self.Time) + " -- Using Day: " + str(self.dayUsed) + " -- MG Difference: " + str(self.MG_difference) + " -- MG Daily Change: " + str(self.MG_daily_change)) self.Notify.Sms("+15059771023", str(self.Time) + " -- Using Day: " + str(self.dayUsed) + " -- MG Difference: " + str(self.MG_difference) + " -- MG Daily Change: " + str(self.MG_daily_change)) # Begin to allow buy in process when price gap is exceeded if current_time_as_date >= datetime.strptime('9:30', '%H:%M').time() and current_time_as_date <= datetime.strptime('16:55', '%H:%M').time() and self.sitOut == False: # For Initial BuyIn if not self.security.Invested and self.boughtIn == False and self.sitOut == False and self.priceGap: """ It's not enough to wish, dream, hope. Even children know this. We must set sail into the sea of uncertainty. We must meet fear face-to-face. We must take our dreams as maps for a greater journey. Dreams, to come true, need a good story. So go live one. — Vironika Tugaleva """ # -- Long -- # if float(self.MG_daily_change) > 0: self.positionType = 'Long' # Calculate cutOffPrice using percentage instead of static number self.max_loss_points = self.price * self.max_loss_as_percentage self.cutOffPrice = self.price - self.max_loss_points if self.price > self.gapUp: self.contractsToTradeMaxByRisk = floor( (self.Portfolio.TotalPortfolioValue * self.risk_percentage) / (self.max_loss_points * PRICE_PER_POINT)) self.contractsToTradeMaxByEquity = floor( self.Portfolio.TotalPortfolioValue / MARGIN_REQ_PER_CONTRACT ) if self.contractsToTradeMaxByEquity < self.contractsToTradeMaxByRisk: self.contractsToTrade = self.contractsToTradeMaxByEquity else: self.contractsToTrade = self.contractsToTradeMaxByRisk self.MarketOrder(self.symbol, 1) # Changed to 1 for MC analysis__________________________________________________ self.Debug(str(self.Time) + " Buy price: " + str(self.price) + " long contractsToTrade: " + str(self.contractsToTrade) + " portfolio value: " + str(self.Portfolio.TotalPortfolioValue) ) # self.Debug(str(self.Time) + " risk%: " + str(self.risk_percentage) + " risk $: " + str(self.contractsToTrade * self.max_loss_points * 5) + " max_loss_pts: " +str(self.max_loss_points)) self.Notify.Sms("+15059771023", "IntraDay Gap Momentum Playing Long Today at algo price: " + str(self.price)) self.Notify.Sms("+12035831419", "IntraDay Gap Momentum Playing Long Today at algo price: " + str(self.price)) self.boughtIn = True self.stopLossTriggered = False self.stopLossTriggeredHigher = False self.keepPrice = self.price ## Global SL self.stopMarketTicket = self.StopMarketOrder(self.symbol, -1, self.cutOffPrice) # Changed to 1 for MC analysis__________________________________________________ # -- Short -- # elif float(self.MG_daily_change) < 0: self.positionType = 'Short' # Calculate cutOffPrice using percentage instead of static number self.max_loss_points = self.price * self.max_loss_as_percentage self.cutOffPrice = self.price + self.max_loss_points if self.price < self.gapDown: self.contractsToTradeMaxByRisk = floor( (self.Portfolio.TotalPortfolioValue * self.risk_percentage) / (self.max_loss_points * PRICE_PER_POINT)) self.contractsToTradeMaxByEquity = floor( self.Portfolio.TotalPortfolioValue / MARGIN_REQ_PER_CONTRACT ) if self.contractsToTradeMaxByEquity < self.contractsToTradeMaxByRisk: self.contractsToTrade = self.contractsToTradeMaxByEquity else: self.contractsToTrade = self.contractsToTradeMaxByRisk self.MarketOrder(self.symbol, -1) # Changed to 1 for MC analysis__________________________________________________ self.Debug(str(self.Time) + " Sell price: " + str(self.price) + " short contractsToTrade: " + str(self.contractsToTrade) + " portfolio value: " + str(self.Portfolio.TotalPortfolioValue)) # self.Debug(str(self.Time) + " risk%: " + str(self.risk_percentage) + " risk $: " + str(self.contractsToTrade * self.max_loss_points * 5) + " max_loss_pts: " +str(self.max_loss_points)) self.Notify.Sms("+15059771023", "IntraDay Gap Momentum Playing Short Today at algo price: " + str(self.price)) self.Notify.Sms("+12035831419", "IntraDay Gap Momentum Playing Short Today at algo price: " + str(self.price)) self.boughtIn = True self.stopLossTriggered = False self.stopLossTriggeredHigher = False self.keepPrice = self.price ## Global SL self.stopMarketTicket = self.StopMarketOrder(self.symbol, 1, self.cutOffPrice) # Changed to 1 for MC analysis_______________________________________________ # -- Dynamic Exit -- # if self.security.Invested and self.stopLossTriggered == False: # For Long, set a take profit after PROFIT_BUFFER_POINTS exceeded if self.positionType == 'Long': if self.price >= (self.keepPrice + self.start_take_profit_points): # Update Global SL self.Notify.Sms("+15059771023", "TP StopLoss Triggered At: " + str(self.price)) self.Notify.Sms("+12035831419", "TP StopLoss Triggered At: " + str(self.price)) self.stopLossTriggered = True self.keepPoints = self.keepPrice + self.profit_points updateSettings = UpdateOrderFields() updateSettings.StopPrice = decimal.Decimal(round(self.keepPoints, 2)) response = self.stopMarketTicket.Update(updateSettings) # For Short, set a take profit after PROFIT_BUFFER_POINTS exceeded if self.positionType == 'Short': if self.price <= (self.keepPrice - self.start_take_profit_points): # Update Global SL self.Notify.Sms("+15059771023", "TP StopLoss Triggered At: " + str(self.price)) self.Notify.Sms("+12035831419", "TP StopLoss Triggered At: " + str(self.price)) self.stopLossTriggered = True self.keepPoints = self.keepPrice - self.profit_points updateSettings = UpdateOrderFields() updateSettings.StopPrice = decimal.Decimal(round(self.keepPoints, 2)) response = self.stopMarketTicket.Update(updateSettings) # Check if higher take profit threshold is met & raise the dynamic Exit if self.security.Invested and self.stopLossTriggeredHigher == False: if self.positionType == 'Long': if self.price >= (self.keepPrice + self.start_take_profit_points_higher): # Update Global SL self.Notify.Sms("+15059771023", "Higher TP StopLoss Triggered At: " + str(self.price)) self.Notify.Sms("+12035831419", "Higher TP StopLoss Triggered At: " + str(self.price)) self.stopLossTriggeredHigher = True self.keepPoints = self.keepPrice + self.profit_points_higher updateSettings = UpdateOrderFields() updateSettings.StopPrice = decimal.Decimal(round(self.keepPoints, 2)) response = self.stopMarketTicket.Update(updateSettings) self.Debug(str(self.Time) + " Higher TP start - short algo price: " + str(self.price) + " keepPoints higher: " + str(self.keepPoints) + " keepPrice: " +str(self.keepPrice)) if self.positionType == 'Short': if self.price <= (self.keepPrice - self.start_take_profit_points_higher): # Update Global SL self.Notify.Sms("+15059771023", "Higher TP StopLoss Triggered At: " + str(self.price)) self.Notify.Sms("+12035831419", "Higher TP StopLoss Triggered At: " + str(self.price)) self.stopLossTriggeredHigher = True self.keepPoints = self.keepPrice - self.profit_points_higher updateSettings = UpdateOrderFields() updateSettings.StopPrice = decimal.Decimal(round(self.keepPoints, 2)) response = self.stopMarketTicket.Update(updateSettings) self.Debug(str(self.Time) + " Higher TP start - short algo price: " + str(self.price) + " keepPoints higher: " + str(self.keepPoints) + " keepPrice: " +str(self.keepPrice)) # Check whether max_loss_stop is exceeded, if so sit out if self.positionType == 'Long': if self.price <= self.cutOffPrice: self.sitOut = True if self.security.Invested: gapMomentum.closePosition(self) if self.positionType == 'Short': if self.price >= self.cutOffPrice: self.sitOut = True if self.security.Invested: gapMomentum.closePosition(self) # Liquadate always before end of day if self.security.Invested and current_time_as_date >= datetime.strptime('16:55', '%H:%M').time(): # self.Debug('Liqudated Close of Day ' + str(self.positionType) + ': ' + str(self.Time)) gapMomentum.closePosition(self) # Bought in, and have closed position if not self.security.Invested and self.boughtIn == True and self.textProfit == True: self.textProfit = False # Get Daily Profit profit = round((self.Portfolio.TotalPortfolioValue - self.startInvestment), 2) # self.Debug('Daily Profit: '+str(profit)) # Msg if self.positionType == 'Short': self.Notify.Sms("+12035831419", "IntraDay Gap Momentum Sold Short Today for profit: " + str(profit)) self.Notify.Sms("+15059771023", "IntraDay Gap Momentum Sold Short Today for profit: " + str(profit)) if self.positionType == 'Long': self.Notify.Sms("+12035831419", "IntraDay Gap Momentum Sold Long Today for profit: " + str(profit)) self.Notify.Sms("+15059771023", "IntraDay Gap Momentum Sold Long Today for profit: " + str(profit)) def closePosition(self): """ close position """ ### Liquadate and Text self.Liquidate() # -- Clean up Stop Losses -- # # Gets Open Tickets related to stop loss open_tickets = self.Transactions.GetOpenOrderTickets() # Parses through open tickets looking for stop loss stop_tickets = [ticket for ticket in open_tickets if ticket.OrderType == OrderType.StopMarket] # If More then one active stop loss ticket, cancel current stop losses, create ticket if len(stop_tickets) > 1 : for ticket in stop_tickets: ticket.Cancel('Canceled stop loss tickets') create_ticket = True def OnSecuritiesChanged(self, changes): """ QC function for when futures contract changes """ for security in changes.RemovedSecurities: self.symbol = security.Symbol symbolData = self.contracts.get(self.symbol, None) class SymbolData: """ For data consolidation with 5 minute bars""" def __init__(self, algorithm, symbol): self.consolidators = { '1D': TradeBarConsolidator(timedelta(1)), 'INTRADAY_CONSOLIDATOR': TradeBarConsolidator(timedelta(minutes=5)) } # Used for Intraday Function self.atr = AverageTrueRange(14, MovingAverageType.Simple) algorithm.RegisterIndicator(symbol, self.atr, self.consolidators['INTRADAY_CONSOLIDATOR']) class Utils: """ Utility Functions """ @staticmethod def dateModFromString(x): return datetime.strptime(x, '%Y-%m-%d') @staticmethod def dateModToFormat(x): return x.strftime("%Y-%-m-%-d")