| Overall Statistics |
|
Total Orders 2 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Start Equity 100000 End Equity 100005.5 Net Profit 0% Sharpe Ratio 0 Sortino Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $2.00 Estimated Strategy Capacity $0 Lowest Capacity Asset AFL YHYJ9E9U7TZA|AFL R735QTJ8XC9X Portfolio Turnover 0.11% |
# region imports
from AlgorithmImports import *
# endregion
class CalculatingFluorescentOrangeSeahorse(QCAlgorithm):
def initialize(self):
self.set_start_date(2024, 4, 11)
self.set_end_date(2024, 4, 12)
underlying = self.add_equity("AFL").symbol
get_symbol = lambda s: Symbol.create_option(underlying, underlying.id.market,
OptionStyle.AMERICAN, OptionRight.CALL, s, datetime(2024,4,26))
self.legs = [ Leg.create(get_symbol(80), 1), Leg.create(get_symbol(81), -1) ]
[self.add_option_contract(leg.symbol).set_fill_model(MidPriceFillModel(self))
for leg in self.legs]
self.schedule.on(self.date_rules.on(self.start_date), self.time_rules.at(15,45), self._trade)
def _trade(self):
# Place one order with a limit of 0.5
# The expected spread is 0.45, so it should fill
self.combo_limit_order(self.legs, 1, .5)
def on_order_event(self, order_event):
self.log(str(order_event))
class MidPriceFillModel(FillModel):
def __init__(self, algorithm):
super().__init__()
self._max_spread = 1
self.algorithm = algorithm
def combo_limit_fill(self, order, parameters):
prices = []
for kvp in parameters.SecuritiesForOrders:
order = kvp.key
asset = kvp.Value
quantity = GroupOrderExtensions.get_order_leg_ratio(order.quantity, order.group_order_manager)
prices.append((asset.bid_price + asset.ask_price) / 2 * quantity)
# The discount logic will create fill prices that observe the maximum spread
# for example
# AFL 240426C00080000 at 1.380
# AFL 240426C00081000 at 0.925
# has a spread of 0.45
# it will become
# AFL 240426C00080000 at 0.920
# AFL 240426C00081000 at 0.620
# for a self._max_spread of 0.3
discount = 1
current_price = sum(prices)
if current_price > self._max_spread:
discount = self._max_spread / current_price
current_price = self._max_spread
self.algorithm.log(f'The spread is {current_price=}')
limit_price = order.group_order_manager.limit_price
fills = []
def create_fill(order, asset):
utc_time = Extensions.convert_to_utc(asset.local_time, asset.exchange.time_zone)
fill = OrderEvent(order, utc_time, OrderFee.ZERO)
fill.status = OrderStatus.FILLED
mid_price = (asset.bid_price + asset.ask_price) / 2
fill.fill_price = mid_price * discount
fill.fill_quantity = order.quantity
return fill
match order.group_order_manager.direction:
case OrderDirection.BUY:
if current_price < limit_price:
for kvp in parameters.SecuritiesForOrders:
fills.append(create_fill(kvp.key, kvp.Value))
case OrderDirection.SELL:
if current_price > limit_price:
for kvp in parameters.SecuritiesForOrders:
fills.append(create_fill(kvp.key, kvp.Value))
return fills