| Overall Statistics |
|
Total Trades 1 Average Win 0% Average Loss 0% Compounding Annual Return 8.739% Drawdown 11.800% Expectancy 0 Net Profit 20.763% Sharpe Ratio 0.748 Probabilistic Sharpe Ratio 31.107% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0.036 Beta 0.183 Annual Standard Deviation 0.085 Annual Variance 0.007 Information Ratio -0.451 Tracking Error 0.185 Treynor Ratio 0.346 Total Fees $1.22 Estimated Strategy Capacity $740000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X |
#region imports
from AlgorithmImports import *
#endregion
class TrailingStopLoss(QCAlgorithm):
def Initialize(self):
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.15
self.shortEntryThreshhold = -0.15
self.longAllocation = 1 # 100% long
self.shortAllocation = -1 # 100% short
self.entryTicketLong = None
self.stopMarketTicketLong = None
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
#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:
quantity = self.CalculateOrderQuantity(self.symbol, 0.9)
#self.entryTicketLong = self.LimitOrder(self.symbol, quantity, price, "Entry Order Long, TLI: " + str(data[self.tli].Value) + " TLI_time" +str(data[self.tli].Time))
self.Debug("Entering long")
self.entryTicketLong = self.MarketOrder(self.symbol, quantity)
self.entryTime = self.Time
#By this time there entryTicketLong should be completed and stopMarketTicketLong should exist
#self.str1 = "Order1, quantity:"+ str(quantity)+ " entryTicketLong status: " + str(self.entryTicketLong.Order) + ", stopMarketTicketLong status: "+ str(self.stopMarketTicketLong.Status)
#self.Debug(self.str1)
if self.tli1 <= self.shortEntryThreshhold:
quantity = self.CalculateOrderQuantity(self.symbol, 0.9)
#self.entryTicketShort = self.LimitOrder(self.symbol, quantity, price, "Entry Order short, TLI: " + str(data[self.tli].Value) + " TLI_time" +str(data[self.tli].Time))
self.Debug("Entering short")
self.entryTicketShort = self.MarketOrder(self.symbol, -quantity)
self.entryTime = self.Time
#self.str1 = "tliTime " + tlitime.strftime("%m/%d/%Y, %H:%M:%S") + ", tli val "+ str(data[self.tli].Value) + ", price " + str(price) + ", Invested " +str(self.Portfolio.Invested) + ", openorders " + str(self.Transactions.GetOpenOrders(self.symbol))
#self.str1 = curDT.strftime("%m/%d/%Y, %H:%M:%S") + ", price" + str(price)
#self.Debug(self.str1)
# 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.stopMarketTicketLong is not None and self.Portfolio.Invested:
# move up trailing stop price
if price > self.highestPrice:
self.highestPrice = price
updateFields = UpdateOrderFields()
updateFields.StopPrice = price * 0.98
self.stopMarketTicketLong.Update(updateFields)
self.Debug(updateFields.StopPrice)
# move short stop limit
if self.stopMarketTicketShort is not None and self.Portfolio.Invested:
# move down trailing stop price
if price < self.lowestPrice:
self.lowestPrice = price
updateFields = UpdateOrderFields()
updateFields.StopPrice = price * 1.02
self.stopMarketTicketShort.Update(updateFields)
self.Debug(updateFields.StopPrice)
def CancelAllOrders(self):
openOrders = self.Transactions.GetOpenOrders()
if len(openOrders)> 0:
for x in openOrders:
self.Transactions.CancelOrder(x.Id)
def OnOrderEvent(self, orderEvent):
self.Debug(str(orderEvent.OrderId) + " " + str(orderEvent.Status))
if (orderEvent.Status == OrderStatus.PartiallyFilled): # or orderEvent.Status == OrderStatus.Invalid):
#self.str1 = "Order1, quantity:"+ str(quantity)+ " entryTicketLong status: " + str(self.entryTicketLong.Status) + ", stopMarketTicketLong status: "+ str(self.stopMarketTicketLong.Status)
self.Debug("partiallyfilled order!!!")
#what was the original order for
#Cancel the rest of the order
return
# send long stop loss order if entry limit order is filled
if self.entryTicketLong is not None:
self.debug(str(entryTicketLong.OrderId))
if self.entryTicketLong is not None and self.entryTicketLong.OrderId == orderEvent.OrderId and orderEvent.Status==OrderStatus.Filled:
self.Debug("entering stopMarketTicketLong")
self.stopMarketTicketLong = self.StopMarketOrder(self.symbol, -self.entryTicketLong.Quantity, 0.98 * self.entryTicketLong.AverageFillPrice)
#self.str1 = "stopMarketTicketLong, quanity " + str(-self.entryTicketLong.Quantity) + ", price " +str(0.98 * self.entryTicketLong.AverageFillPrice)
self.Debug(self.str1)
# send Short stop loss order if entry limit order is filled
if self.entryTicketShort is not None and self.entryTicketShort.OrderId == orderEvent.OrderId and orderEvent.Status==OrderStatus.Filled:
self.Debug("entering stopMarketTicketShort")
self.stopMarketTicketShort = self.StopMarketOrder(self.symbol, self.entryTicketShort.Quantity, 1.02 * self.entryTicketShort.AverageFillPrice)
#self.Debug(self.symbol +", " + str(self.entryTicketShort.Quantity))
# save fill time of Long stop loss order (and reset highestPrice lowestPrice)
if (self.stopMarketTicketLong is not None) and (self.stopMarketTicketLong.OrderId == orderEvent.OrderId):
self.stopMarketOrderFillTime = self.Time
self.highestPrice = 0
self.lowestPrice = 0
# save fill time of short stop loss order (and reset highestPrice lowestPrice)
if (self.stopMarketTicketShort is not None) and (self.stopMarketTicketShort.OrderId == orderEvent.OrderId):
self.stopMarketOrderFillTime = self.Time
self.highestPrice = 0
self.lowestPrice = 0
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