| Overall Statistics |
|
Total Trades 16 Average Win 0% Average Loss 0.00% Compounding Annual Return -48.810% Drawdown 6.600% Expectancy -1 Net Profit -5.982% Sharpe Ratio -3.334 Probabilistic Sharpe Ratio 4.797% Loss Rate 100% Win Rate 0% Profit-Loss Ratio 0 Alpha -0.453 Beta -0.441 Annual Standard Deviation 0.141 Annual Variance 0.02 Information Ratio -1.812 Tracking Error 0.283 Treynor Ratio 1.07 Total Fees $19.97 Estimated Strategy Capacity $1800000.00 Lowest Capacity Asset TQQQ 31UH85M9QIPGM|TQQQ UK280CGTCB51 |
from typing import Dict
from datetime import timedelta
from AlgorithmImports import *
class Centurion(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2021, 2, 20) # Set Start Date
self.SetEndDate(2021, 3, 25)
# Set Start Date
self.SetCash(100000) # Set Strategy Cash
# self.AddEquity("SPY", Resolution.Minute)
'''
Would it be easier to have a symboldata class?
What would it contain?
- initialization of Data
- dictionaries for stoploss, option types, rebalance weights
'''
# Assets=====================================
self.bndx = self.AddEquity("BNDX", Resolution.Minute).Symbol
self.bnd = self.AddEquity("BND", Resolution.Minute).Symbol
spy = self.AddEquity("SPY", Resolution.Minute)
spy.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.spy = spy.Symbol
self.spycontract: Symbol = None
tqqq = self.AddEquity("TQQQ", Resolution.Minute)
tqqq.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.tqqq = tqqq.Symbol
self.tqqqcontract: Symbol = None
soxl = self.AddEquity("SOXL", Resolution.Minute)
soxl.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.soxl = soxl.Symbol
self.soxlcontract: Symbol = None
tecl = self.AddEquity("TECL", Resolution.Minute)
tecl.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.tecl = tecl.Symbol
self.teclcontract: Symbol = None
#Scheduled Events============================================
self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.AfterMarketOpen("SPY", 1), self.monthlyRebalance)
self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.AfterMarketOpen("SPY", 1), self.captureOpening)
#Variables needed for stoploss
self.stoplosshold = 0
self.dailyOpen: Dict[Symbol,float] = {
self.spy: 0,
self.bndx:0,
self.bnd:0
}
# Rebalancing Weights
self.weights = {
self.spy: .2,
self.bndx: .3,
self.bnd: .4
}
# Underlying & Options (UNOs)
self.uNos = {
self.spy : self.spycontract,
self.tqqq : self.tqqqcontract,
self.tecl : self.teclcontract
#self.soxl : self.soxlcontract
}
def OnData(self, data):
'''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 self.IsWarmingUp:
return
if self.Portfolio.Invested:
self.stoploss(data)
if not self.Portfolio.Invested:
self.monthlyRebalance()
self.setPuts()
self.timeDecayCheck()
self.buyPuts()
self.exerciseLoki()
def setPuts(self):
'''
I want to purchase a long puts for every key in dictionary uNos ((u)underlying (N)and (os)Options)
'''
for underlying in self.uNos.keys():
targetStrike = (self.Securities[underlying].Price * 0.60) - (self.Securities[underlying].Price * 0.60)%5
contracts = self.OptionChainProvider.GetOptionContractList(underlying, self.Time)
puts = [x for x in contracts if x.ID.OptionRight == OptionRight.Put]
puts = sorted( sorted(puts, key = lambda x: x.ID.Date, reverse = True),
key = lambda x: x.ID.StrikePrice)
puts = [x for x in puts if x.ID.StrikePrice == targetStrike]
puts = [x for x in puts if 270 < (x.ID.Date - self.Time).days <= 420]
if len(puts) == 0:
self.Log("No Puts")
continue
self.AddOptionContract(puts[0], Resolution.Minute)
self.uNos[underlying] = puts[0]
def timeDecayCheck(self):
#Don't let an options time to expiration be less than 6 months
for underlying, contract in self.uNos.items():
if contract is None:
continue
if (contract.ID.Date - self.Time).days < 180:
self.Liquidate(contract)
self.RemoveSecurity(contract)
self.uNos[underlying] = None
def buyPuts(self):
for _,contract in self.uNos.items():
if contract is None:
continue
if not self.Portfolio[contract].Invested:
self.SetHoldings(contract, 0.02)
def exerciseLoki(self):
for underlying,contract in self.uNos.items():
if contract is None:
continue
#Liquidate a contract if its increases to 30% OTM
if self.Securities[underlying].Price < contract.ID.StrikePrice * 1:
# self.ExerciseOption(contract, self.Portfolio[contract].Quantity)
self.Liquidate(contract)
self.RemoveSecurity(contract)
self.uNos[underlying] = None
self.Log(f'{contract} has been sold!')
def captureOpening(self):
#Grabs the daily opening price of spy for our stoploss method
if self.IsWarmingUp:
return
for key, value in self.dailyOpen.items():
if self.CurrentSlice.Bars.ContainsKey(key):
self.dailyOpen[key] = self.CurrentSlice[key].Open
self.stoplosshold = 0
def monthlyRebalance(self):
# Rebalance portfolio monthly
if self.IsWarmingUp:
return
for key,value in self.weights.items():
self.SetHoldings(key,value)
def stoploss(self, data):
'''
Stoploss logic:
- If spy drops more than 5% liquidate entire equity portfolio
- Change stoplosshold value to 1, this indicates that the portfolios SL has been hit
and were going to hold until the next trading day
'''
if self.IsWarmingUp:
return
for symbol, weight in self.weights.items():
if self.Securities[symbol].Price == 0:
continue
open = self.dailyOpen[symbol]
#See if any symbol has decreased more than 5% in a given day, liquidate if true and check next one...
if ((self.Securities[symbol].Price-open)/self.Securities[symbol].Price) < -.05:
self.SetHoldings(symbol, 0)
self.stoplosshold = 1
#self.Log('HIT')
self.buyPuts()
self.exerciseLoki()