| 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")