| Overall Statistics |
|
Total Orders 48 Average Win 17.83% Average Loss -11.16% Compounding Annual Return 181.694% Drawdown 35.600% Expectancy 0.299 Start Equity 100000 End Equity 140946 Net Profit 40.946% Sharpe Ratio 2.169 Sortino Ratio 3.513 Probabilistic Sharpe Ratio 56.416% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 1.60 Alpha 0 Beta 0 Annual Standard Deviation 0.909 Annual Variance 0.827 Information Ratio 2.23 Tracking Error 0.909 Treynor Ratio 0 Total Fees $3666.00 Estimated Strategy Capacity $1000.00 Lowest Capacity Asset SPY 32HU3XGOJQHVQ|SPY R735QTJ8XC9X Portfolio Turnover 15.38% |
# region imports
from AlgorithmImports import *
# endregion
class FatRedOrangeBarracuda(QCAlgorithm):
def initialize(self):
self.set_start_date(2024, 1, 1)
self.set_end_date(2024, 4,30)
self.set_cash(100000)
self.universe_settings.asynchronous = True
option = self.add_option("SPY", Resolution.MINUTE)
option.set_slippage_model(VolumeShareSlippageModel())
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().strangle(30, 5, -10))
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 chain:
self.log(str(self.time) + str(' ') + str('No chanin available'))
return
if (not self.portfolio.invested and received_signal == 1):
# Find options with the farthest expiry
expiry = max([x.expiry for x in chain])
contracts = [contract for contract in chain if contract.expiry == expiry]
# Order the OTM calls by strike to find the nearest to ATM
call_contracts = sorted([contract for contract in contracts
if contract.right == OptionRight.CALL and
contract.strike > chain.underlying.price],
key=lambda x: x.Strike)
if not call_contracts:
return
# Order the OTM puts by strike to find the nearest to ATM
put_contracts = sorted([contract for contract in contracts
if contract.right == OptionRight.PUT and
contract.strike < chain.underlying.price],
key=lambda x: x.Strike, reverse=True)
if not put_contracts:
return
call = call_contracts[0]
put = put_contracts[0]
quantity_call = self.portfolio.total_portfolio_value*0.5 / call.ask_price
quantity_call = int(quantity_call/100)
quantity_put = self.portfolio.total_portfolio_value*0.5 / put.ask_price
quantity_put = int(quantity_put/100)
quantity = min(quantity_call, quantity_put)
long_strangle = OptionStrategies.strangle(self.symbol, call.strike, put.strike, expiry)
self.buy(long_strangle, quantity)
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