| Overall Statistics |
|
Total Trades 2407 Average Win 0.47% Average Loss -0.51% Compounding Annual Return -4.299% Drawdown 58.400% Expectancy -0.056 Net Profit -12.993% Sharpe Ratio 0.124 Probabilistic Sharpe Ratio 3.878% Loss Rate 51% Win Rate 49% Profit-Loss Ratio 0.92 Alpha 0.011 Beta 0.295 Annual Standard Deviation 0.423 Annual Variance 0.179 Information Ratio -0.198 Tracking Error 0.442 Treynor Ratio 0.177 Total Fees $6706.25 Estimated Strategy Capacity $0 |
class CalculatingYellowGreenAnguilline(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2018, 1, 1)
self.SetEndDate(2021, 3, 1)
self.initialCash = 1000000
self.SetCash(self.initialCash)
# Take profit after our portfolio profit/loss is greater than this percentage
self.takeProfitPercent = 0.05
# Liquidate our portfolio when our profit/loss drops below this percentage
self.stopLossPercent = -0.20
self.lastTraded = datetime(1, 1, 1)
# CL Futures with expiries between 30 and 90 days
cl = self.AddFuture("CL", Resolution.Minute, Market.NYMEX)
cl.SetFilter(30, 90)
self.AddFutureOption(cl.Symbol, self.FilterFuturesOptionsContracts)
# EMA indicators keyed by future option symbol
self.ema = {}
# Used to store Symbols with matching strike prices
self.futureOptionCache = {}
def FilterFuturesOptionsContracts(self, filterUniverse):
return filterUniverse.Strikes(-1, 1)
def OnData(self, data):
# Check if all indicators are warmed up before we continue
if not self.AllIndicatorsReady():
return
# Only trade once a day
if self.Time - self.lastTraded < timedelta(days=1):
return
pnlPercent = self.Portfolio.TotalUnrealizedProfit / self.initialCash
if pnlPercent >= self.takeProfitPercent or pnlPercent <= self.stopLossPercent:
self.Liquidate()
self.lastTraded = self.Time
return
expiring_options = []
for symbol, emas in self.ema.items():
# Loop on call options
if symbol.ID.OptionRight == OptionRight.Put:
continue
# Remove contracts close to expiry from our portfolio
if symbol.ID.Date - self.Time <= timedelta(days=30):
self.Liquidate(symbol)
self.Liquidate(symbol.Underlying)
self.Debug(f'Removing {symbol} due to impending expiry (15 days)')
expiring_options.append(symbol)
continue
# Slow and fast EMA (2 hours/6 hours) indicators for the call option
slow = emas[0]
fast = emas[1]
call = symbol
put = self.InvertOption(symbol)
if fast > slow and self.Portfolio[put].Quantity >= 0:
self.Liquidate(put)
self.MarketOrder(symbol, 1)
self.lastTraded = self.Time
elif fast > slow and self.Portfolio[call].Quantity >= 0:
self.Liquidate(call)
self.MarketOrder(put, 1)
self.lastTraded = self.Time
# Remove all options we won't trade anymore from the
# indicators dictionary we created
for symbol in expiring_options:
del self.ema[symbol]
def AllIndicatorsReady(self):
'''
Checks if all indicators are ready to be used
'''
for symbol in self.ema:
for indicator in self.ema[symbol]:
if not indicator.IsReady:
return False
return True
def OnSecuritiesChanged(self, changes):
'''
When any future or future option is added to the algorithm,
this method will execute.
For added futures options, we setup EMA indicators (slow/fast)
for them.
For removed futures options, we remove them from the dictionary of EMA indicators
'''
for security in changes.AddedSecurities:
symbol = security.Symbol
if symbol.SecurityType != SecurityType.FutureOption:
continue
if symbol not in self.ema:
self.ema[symbol] = []
emas = self.ema[symbol]
if len(emas) == 0:
emas.append(self.EMA(symbol, 120, Resolution.Minute))
emas.append(self.EMA(symbol, 360, Resolution.Minute))
for security in changes.RemovedSecurities:
symbol = security.Symbol
if symbol in self.ema:
del self.ema[symbol]
def InvertOption(self, symbol):
'''
Converts a call option symbol to a put option symbol, and vice versa.
'''
if symbol not in self.futureOptionCache:
self.futureOptionCache[symbol] = {}
cache = self.futureOptionCache[symbol]
strike = symbol.ID.StrikePrice
right = symbol.ID.OptionRight
if strike not in cache:
# Adds both calls and puts to the collection
cache[strike] = [
symbol,
Symbol.CreateOption(
symbol.Underlying,
symbol.ID.Market,
symbol.ID.OptionStyle,
OptionRight.Put if right == OptionRight.Call else OptionRight.Call,
symbol.ID.StrikePrice,
symbol.ID.Date)
]
for option in cache[strike]:
if right != option.ID.OptionRight:
return option