| Overall Statistics |
|
Total Orders 115 Average Win 12.01% Average Loss -9.52% Compounding Annual Return 49.122% Drawdown 64.900% Expectancy 0.180 Start Equity 100000 End Equity 139635.4 Net Profit 39.635% Sharpe Ratio 0.915 Sortino Ratio 0.861 Probabilistic Sharpe Ratio 38.415% Loss Rate 48% Win Rate 52% Profit-Loss Ratio 1.26 Alpha 0.197 Beta 4.152 Annual Standard Deviation 0.763 Annual Variance 0.582 Information Ratio 0.814 Tracking Error 0.71 Treynor Ratio 0.168 Total Fees $3668.60 Estimated Strategy Capacity $3000.00 Lowest Capacity Asset SPY YNBIVSIPHBOM|SPY R735QTJ8XC9X Portfolio Turnover 11.71% |
# region imports
from AlgorithmImports import *
# endregion
class GeekyTanDolphin(QCAlgorithm):
def initialize(self):
self.set_start_date(2024, 1, 1)
self.set_end_date(2024, 10, 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