| Overall Statistics |
|
Total Trades 94 Average Win 1.22% Average Loss -0.80% Compounding Annual Return 14.536% Drawdown 4.500% Expectancy 0.826 Net Profit 35.747% Sharpe Ratio 1.53 Probabilistic Sharpe Ratio 81.161% Loss Rate 28% Win Rate 72% Profit-Loss Ratio 1.52 Alpha 0.094 Beta 0.043 Annual Standard Deviation 0.066 Annual Variance 0.004 Information Ratio -0.222 Tracking Error 0.209 Treynor Ratio 2.322 Total Fees $119.34 Estimated Strategy Capacity $1100000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X |
#region imports
from AlgorithmImports import *
#endregion
class TrailingStopLoss(QCAlgorithm):
def Initialize(self):
self.Transactions.MarketOrderFillTimeout = timedelta(seconds=30)
startingCash = 100000
self.SetCash(startingCash) # Set Strategy Cash
self.SetStartDate(2020,1 , 1)
self.SetEndDate(2022, 4, 1)
symbol = "SPY"
self.symbol = self.AddEquity(symbol, Resolution.Daily).Symbol
self.tli = self.AddData(TLI, "tli", Resolution.Daily).Symbol
self.tli1 = 0 #Variable to track tli1
self.longEntryThreshhold = 0.1
self.shortEntryThreshhold = -0.1
self.longAllocation = 0.9 # 100% long
self.shortAllocation = -0.9 # 100% short
self.entryTicketLong = None
self.stopMarketTicketLong = None
self.stopMarketLongPrice = 0
self.stopMarketShortPrice = 0
self.entryTicketShort = None
self.stopMarketTicketShort = None
self.entryTime = datetime.min
self.stopMarketOrderFillTime = datetime.min
self.highestPrice = 0
self.lowestPrice = 0
self.str1 = "hello"
def OnData(self, data):
price = self.Securities[self.symbol].Price
openOrders = self.Transactions.GetOpenOrders(self.symbol)
if(openOrders):
self.Debug("There are open orders?")
# move long stop limit
if self.entryTicketLong is not None and self.Portfolio.Invested:
# move up trailing stop price
if price > self.highestPrice:
self.highestPrice = price
self.stopMarketLongPrice = 0.98 * price
self.Debug("Adjusting stopMarketTicktLong")
elif price < self.stopMarketLongPrice:
self.Liquidate(self.symbol)
# move short stop limit
if self.entryTicketShort is not None and self.Portfolio.Invested:
# move down trailing stop price
if price < self.lowestPrice:
self.lowestPrice = price
self.stopMarketShortPrice = price * 1.02
elif price > self.stopMarketShortPrice:
self.Liquidate(self.symbol)
#self.Debug("Price : " + str(price))
curDT = self.Time
if self.tli in data:
tlitime = data[self.tli].Time
self.str1 = curDT.strftime("%m/%d/%Y, %H:%M:%S") + ", tliTime " + tlitime.strftime("%m/%d/%Y, %H:%M:%S") + ", tli val "+ str(data[self.tli].Value) + ", price" + str(price)
self.tli1 = data[self.tli].Value
self.Debug(str(data[self.tli].Value))
# Check for long entry
if not self.Portfolio.Invested:
if self.tli1 >= self.longEntryThreshhold:
self.Debug("Entering long")
#str1 = "Entry Order Long, TLI: " + str(data[self.tli].Value) + " TLI_time" + str(data[self.tli].Time)
self.GoLong(self.symbol, 0.9) #(self, symbol, ratio)
self.entryTime = self.Time
if self.tli1 <= self.shortEntryThreshhold:
self.Debug("Entering short")
self.GoShort(self.symbol, 0.9) #(self, symbol, ratio)
self.entryTime = self.Time
# move long limit price if not filled after 1 day
#if (self.Time - self.entryTime).days > 1 and (self.entryTicketLong is not None) and self.entryTicketLong.Status != OrderStatus.Filled:
# self.entryTime = self.Time
# updateFields = UpdateOrderFields()
# updateFields.LimitPrice = price
# self.entryTicketLong.Update(updateFields)
# move Short limit price if not filled after 1 day
#if (self.Time - self.entryTime).days > 1 and (self.entryTicketShort is not None) and self.entryTicketShort.Status != OrderStatus.Filled:
# self.entryTime = self.Time
# updateFields = UpdateOrderFields()
# updateFields.LimitPrice = price
# self.entryTicketShort.Update(updateFields)
# move long stop limit
#if self.Portfolio.Invested:
def CancelAllOrders(self):
openOrders = self.Transactions.GetOpenOrders()
if len(openOrders)> 0:
for x in openOrders:
self.Transactions.CancelOrder(x.Id)
def GoLong(self, symbol, ratio):
# Make sure we can trade this
security = self.Securities[symbol]
if not security.IsTradable:
self.Debug("{} is not tradable.".format(symbol))
return
# Setup vars
orderQuantity = self.CalculateOrderQuantity(symbol, ratio)
limit = 1.001 # +0.1% Limit Order above current price
limitPrice = round( security.Price * limit, 2 )
openOrders = self.Transactions.GetOpenOrders(symbol)
self.Transactions.CancelOpenOrders(symbol)
self.Log('Going long, canceled all open orders, did a Market Order')
self.SetHoldings(symbol, ratio)
self.entryTicketLong = 1
self.stopMarketLongPrice = 0.98 * security.Price
#if(openOrders):
# Cancel all outstanding orders, then do a Market Order
# self.Transactions.CancelOpenOrders(symbol)
# self.Log('Going long, canceled all open orders, did a Market Order')
# self.SetHoldings(symbol, ratio)
#else:
# No open orders, try a limit order then
# self.LimitOrder(symbol, orderQuantity, limitPrice)
# self.Log( str(security.Price) )
# self.Log('Going long. Placed a limit order at ' + str(limitPrice))
# self.Log('Going long, did a Market Order')
# self.SetHoldings(symbol, ratio)
def GoShort(self, symbol, ratio):
# Make sure we can trade this
security = self.Securities[symbol]
if not security.IsTradable:
self.Debug("{} is not tradable.".format(symbol))
return
# Setup vars
orderQuantity = self.CalculateOrderQuantity(symbol, ratio)
limit = 0.999 # +0.1% Limit Order above current price
limitPrice = round( security.Price * limit, 2 )
openOrders = self.Transactions.GetOpenOrders(symbol)
self.Transactions.CancelOpenOrders(symbol)
self.SetHoldings(symbol, -ratio)
self.entryTicketShort = 1
self.stopMarketShortPrice = 1.02 * security.Price
#if(openOrders):
# Cancel all outstanding orders, then do a Market Order
# self.Transactions.CancelOpenOrders(symbol)
# self.SetHoldings(symbol, -ratio)
# self.Log('Going short, canceled all open orders, did a Market Order')
#else:
# No open orders, try a limit order then
# self.LimitOrder(symbol, -orderQuantity, limitPrice)
# self.Log( str(security.Price) )
# self.Log('Going short. Placed a limit order at ' + str(limitPrice))
# self.Log('Going short, did a Market Order')
# self.SetHoldings(symbol, -ratio)
class TLI(PythonData):
def GetSource(self, config, date, isLive):
source = "https://www.dropbox.com/s/q4njfg7ihs2cwb0/TLI_20220415.csv?dl=1"
return SubscriptionDataSource(source, SubscriptionTransportMedium.RemoteFile);
def Reader(self, config, line, date, isLive):
if not (line.strip() and line[0].isdigit()):
return None
data = line.split(',')
tli = TLI()
try:
tli.Symbol = config.Symbol
# make data available Monday morning (Friday 16:00 + 66 hours)
# since we can't trade on weekend anyway
tli.Time = datetime.strptime(data[0], '%Y-%m-%d %H:%M:%S') + timedelta(hours=66)
tli.Value = data[1]
except ValueError:
return None
return tli