| Overall Statistics |
|
Total Orders 120 Average Win 11.59% Average Loss -9.42% Compounding Annual Return 35.149% Drawdown 64.900% Expectancy 0.161 Start Equity 100000 End Equity 131518.6 Net Profit 31.519% Sharpe Ratio 0.747 Sortino Ratio 0.69 Probabilistic Sharpe Ratio 33.896% Loss Rate 48% Win Rate 52% Profit-Loss Ratio 1.23 Alpha 0 Beta 0 Annual Standard Deviation 0.733 Annual Variance 0.537 Information Ratio 0.822 Tracking Error 0.733 Treynor Ratio 0 Total Fees $3702.40 Estimated Strategy Capacity $2000.00 Lowest Capacity Asset SPY YO33EEUKQT46|SPY R735QTJ8XC9X Portfolio Turnover 11.26% |
# region imports
from AlgorithmImports import *
# endregion
class GeekyTanDolphin(QCAlgorithm):
def initialize(self):
self.set_start_date(2024, 1, 1)
self.set_end_date(2024, 12, 31)
self.set_cash(100000)
self.universe_settings.asynchronous = True
option = self.add_option("SPY", Resolution.MINUTE)
self.symbol = option.symbol
self.indicator = self.add_data(Signal, "CUSTOM1", Resolution.MINUTE).symbol
#call_spread(min_days_till_expiry: int, higher_strike_from_atm: float, lower_strike_from_atm: float)
#Selects two call contracts to form Bull Call Spread or Bear Call Spread Option strategies.
option.set_filter(lambda universe: universe.include_weeklys().call_spread(30, 5))
self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.CASH)
def on_data(self, data: Slice):
# Get the OptionChain
chain = data.option_chains.get(self.symbol, None)
if self.indicator in data:
received_signal = data[self.indicator].value
if (not self.portfolio.invested and received_signal == 1):
#self.log(str(self.time) + str(' ') + str(received_signal) +str(' ')+ str(len(chain)))
if not chain:
self.log(str(self.time) + str(' ') + str('No chanin available'))
return
# Select the call Option contracts with the furthest expiry
expiry = max([x.expiry for x in chain])
calls = [i for i in chain if i.expiry == expiry and i.right == OptionRight.CALL]
if calls == 0:
self.log(str(self.time) + str(' ') + str('No chanin available'))
return
# Select the ITM and OTM contract strike prices from the remaining contracts
strikes = [x.strike for x in calls]
otm_strike = max(strikes)
itm_strike = min(strikes)
self.log("Buy Order" + str(self.time) + str(' ') + str(received_signal))
itm_call = [x for x in calls if x.strike == itm_strike][0]
otm_call = [x for x in calls if x.strike == otm_strike][0]
quantity_itm = self.portfolio.total_portfolio_value*0.5 / itm_call.ask_price
quantity_itm = int(quantity_itm/100)
quantity_otm = self.portfolio.total_portfolio_value*0.5 / otm_call.ask_price
quantity_otm = int(quantity_otm/100)
quantity = min(quantity_otm, quantity_itm)
option_strategy = OptionStrategies.bull_call_spread(self.symbol, itm_strike, otm_strike, expiry)
self.buy(option_strategy, quantity)
# legs = [
# Leg.create(itm_call.symbol, quantity_itm),
# Leg.create(otm_call.symbol, -quantity_otm)
# ]
# self.combo_market_order(legs, 1)
elif self.portfolio.invested and received_signal == 0:
option_invested = [x.key for x in self.portfolio if x.value.invested and x.value.type==SecurityType.OPTION ]
self.liquidate()
self.log("Sell Order" + str(self.time) + str(' ') + str(received_signal))
else:
# self.set_holdings(self.spy, 0)
self.log("Do Nothing " + str(self.time) + str(' ') + str(received_signal))
class Signal(PythonData):
def get_source(self, config, date, isLive):
source = "https://www.dropbox.com/scl/fi/mynxp0hpeexq2dfjeiset/ml_output_old.csv?rlkey=fd2jghurd7pgzraw8zpnw4fyf&st=yqtpi1fd&dl=1"
return SubscriptionDataSource(source, SubscriptionTransportMedium.REMOTE_FILE)
def reader(self, config, line, date, isLive):
if not (line.strip() and line[0].isdigit()):
return None
csv = line.split(',')
xyz = Signal()
xyz.symbol = config.symbol
xyz.time = datetime.strptime(csv[0], '%Y-%m-%d %H:%M:%S')
xyz.value = int(csv[1])
return xyz