| Overall Statistics |
|
Total Trades 20 Average Win 7.42% Average Loss -3.20% Compounding Annual Return 24.207% Drawdown 13.800% Expectancy 1.369 Net Profit 31.622% Sharpe Ratio 0.974 Probabilistic Sharpe Ratio 44.428% Loss Rate 29% Win Rate 71% Profit-Loss Ratio 2.32 Alpha 0.032 Beta 1.23 Annual Standard Deviation 0.187 Annual Variance 0.035 Information Ratio 0.549 Tracking Error 0.11 Treynor Ratio 0.148 Total Fees $34.47 Estimated Strategy Capacity $45000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X |
#region imports
from AlgorithmImports import *
#endregion
class WellDressedYellowCat(QCAlgorithm):
def Initialize(self):
# parameters:
startingCash = 100000
self.trailingStop = 0.05
self.takeProfit = 0.05
self.longEntryThreshhold = 0.1
self.shortEntryThreshhold = -0.1
self.longAllocation = 1 # 100% long
self.shortAllocation = -1 # 100% short
symbol = "SPY"
self.SetStartDate(2021, 1, 1) # Set Start Date
self.SetEndDate(2022, 4, 10)
self.SetCash(startingCash) # Set Strategy Cash
self.symbol = self.AddEquity(symbol, Resolution.Minute).Symbol
self.tli = self.AddData(TLI, "tli", Resolution.Minute).Symbol
self.AddRiskManagement(TrailingStopRiskManagementModel(self.trailingStop))
self.entryTicket = None
self.stopMarketTicket = None
self.entryTime = datetime.min
self.stopMarketOrderFillTime = datetime.min
self.highestPrice = 0
self.LowestPrice = 0
#setting a long fill time to ensure marketorder is filled - comment out as not needed in backtesting
#self.Transactions.MarketOrderFillTimeout = timedelta(seconds=10)
def OnData(self, data: Slice):
#20220417 Todo: on resolution of indictor (default daily) check if new trigger has been hit
if not self.Portfolio.Invested:
if self.tli in data:
#TODO 20220416: Note I would like the trailing stop to actually be the low of the last three bars for the buy
# These are not the minute bars, these by default are the daily bars, but can be changed to some other resolution
# The price*volume indicators are calculated on this resolution aswell
# The TLI is the gate and then the trigger has to be hit to enter. Once entered and the trade closes out. if the TLI gate is open
# and a new trigger is hit in the up direction, then re-open.
price = self.Securities[self.symbol].Price
quantity = self.CalculateOrderQuantity(self.symbol, 0.9)
#I think I can get away with putting the limit orders straight behind the market order on the SPY or SPX
#as they are so liquid
if data[self.tli].Value > self.longEntryThreshhold:
self.entryTicket = self.MarketOrder(self.symbol, quantity)
self.Debug("Market Order Fill Price: {0}".format(price))
stopPrice = price * (1-self.trailingStop) # Abs stop loss
limitPrice = price *(1+self.takeProfit) # Sell equal or better than takeporofit
stopLimitTicket = self.StopLimitOrder("SPY", quantity, stopPrice, limitPrice)
elif data[self.tli].Value < self.shortEntryThreshhold:
self.entryTicket = self.MarketOrder(self.symbol,-1*quantity)
self.Debug("Market Sell Order Fill Price: {0}".format(price))
stopPrice = price * (1-self.takeProfit) # Trigger stop limit when price falls 1%.
limitPrice = price * (1+self.trailingStop) # Sell equal or better than 1% > close.
stopLimitTicket = self.StopLimitOrder("SPY", quantity, stopPrice, limitPrice)
def OnOrderEvent(self, orderEvent):
if orderEvent.Status != OrderStatus.Filled:
return
# send stop loss order if entry limit order is filled
#if self.entryTicket is not None and self.entryTicket.OrderId == orderEvent.OrderId:
#self.stopMarketTicket = self.StopMarketOrder(self.qqq, -self.entryTicket.Quantity, 0.95 * self.entryTicket.AverageFillPrice)
# save fill time of stop loss order (and reset highestPrice)
if self.stopMarketTicket is not None and self.stopMarketTicket.OrderId == orderEvent.OrderId:
self.stopMarketOrderFillTime = self.Time
self.highestPrice = 0
class TLI(PythonData):
def GetSource(self, config, date, isLive):
source = "https://www.dropbox.com/s/zlm00njnufrhnko/TLI.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