Adds SimpleCustomFillModel to CustomModelsAlgorithm (#7498)

* Adds `SimpleCustomFillModel` to `CustomModelsAlgorithm`

The simple fill model shows how to implement a simpler version of the most popular order fills: Market, Stop Market and Limit.

This model was tested on QuantConnect Cloud, and will serve as additonal example, since we don't have an example that does not reuse the method of the base class.

* Handles Tick Resolution Case

Tick-resolution data doesn't have TradeBar. We can use the security price, since it represents a trade (`TickTrade`).
This commit is contained in:
Alexandre Catarino
2023-10-04 22:07:32 +01:00
committed by GitHub
parent 78a9b80bbf
commit cbccb6e5cf
2 changed files with 147 additions and 0 deletions
+63
View File
@@ -117,3 +117,66 @@ class CustomBuyingPowerModel(BuyingPowerModel):
hasSufficientBuyingPowerForOrderResult = HasSufficientBuyingPowerForOrderResult(True)
self.algorithm.Log(f"CustomBuyingPowerModel: {hasSufficientBuyingPowerForOrderResult.IsSufficient}")
return hasSufficientBuyingPowerForOrderResult
# The simple fill model shows how to implement a simpler version of
# the most popular order fills: Market, Stop Market and Limit
class SimpleCustomFillModel(FillModel):
def __init__(self):
super().__init__()
def _create_order_event(self, asset, order):
utcTime = Extensions.ConvertToUtc(asset.LocalTime, asset.Exchange.TimeZone)
return OrderEvent(order, utcTime, OrderFee.Zero)
def _set_order_event_to_filled(self, fill, fill_price, fill_quantity):
fill.Status = OrderStatus.Filled
fill.FillQuantity = fill_quantity
fill.FillPrice = fill_price
return fill
def _get_trade_bar(self, asset, orderDirection):
trade_bar = asset.Cache.GetData[TradeBar]()
if trade_bar: return trade_bar
# Tick-resolution data doesn't have TradeBar, use the asset price
price = asset.Price
return TradeBar(asset.LocalTime, asset.Symbol, price, price, price, price, 0)
def MarketFill(self, asset, order):
fill = self._create_order_event(asset, order)
if order.Status == OrderStatus.Canceled: return fill
return self._set_order_event_to_filled(fill,
asset.Cache.AskPrice \
if order.Direction == OrderDirection.Buy else asset.Cache.BidPrice,
order.Quantity)
def StopMarketFill(self, asset, order):
fill = self._create_order_event(asset, order)
if order.Status == OrderStatus.Canceled: return fill
stop_price = order.StopPrice
trade_bar = self._get_trade_bar(asset, order.Direction)
if order.Direction == OrderDirection.Sell and trade_bar.Low < stop_price:
return self._set_order_event_to_filled(fill, stop_price, order.Quantity)
if order.Direction == OrderDirection.Buy and trade_bar.High > stop_price:
return self._set_order_event_to_filled(fill, stop_price, order.Quantity)
return fill
def LimitFill(self, asset, order):
fill = self._create_order_event(asset, order)
if order.Status == OrderStatus.Canceled: return fill
limit_price = order.LimitPrice
trade_bar = self._get_trade_bar(asset, order.Direction)
if order.Direction == OrderDirection.Sell and trade_bar.High > limit_price:
return self._set_order_event_to_filled(fill, limit_price, order.Quantity)
if order.Direction == OrderDirection.Buy and trade_bar.Low < limit_price:
return self._set_order_event_to_filled(fill, limit_price, order.Quantity)
return fill