17 using System.Collections.Generic;
63 var orderEvents =
new List<OrderEvent>(1);
122 throw new ArgumentOutOfRangeException();
124 return new Fill(orderEvents);
137 foreach (var kvp
in parameters.SecuritiesForOrders.OrderBy(x => x.Key.Id))
139 var targetOrder = kvp.Key;
140 var security = kvp.Value;
141 var fill = InternalMarketFill(security, targetOrder, targetOrder.Quantity);
144 return new List<OrderEvent>();
162 var fillParameters =
new List<ComboLimitOrderLegParameters>(parameters.
SecuritiesForOrders.Count);
163 foreach (var kvp
in parameters.SecuritiesForOrders.OrderBy(x => x.Key.Id))
165 var targetOrder = kvp.Key;
166 var security = kvp.Value;
169 if (prices.EndTime.ConvertToUtc(security.Exchange.TimeZone) < targetOrder.Time)
172 return new List<OrderEvent>();
175 fillParameters.Add(
new ComboLimitOrderLegParameters
183 var currentPrice = fillParameters.Aggregate(0m, (accumulatedPrice, p) => accumulatedPrice + p.Price);
184 var limitPrice = order.GroupOrderManager.LimitPrice;
186 var fills =
new List<OrderEvent>(fillParameters.Count);
188 switch (order.GroupOrderManager.Direction)
192 if (currentPrice < limitPrice)
194 for (var i = 0; i < fillParameters.Count; i++)
196 var targetParameters = fillParameters[i];
197 var utcTime = targetParameters.Security.LocalTime.ConvertToUtc(targetParameters.Security.Exchange.TimeZone);
202 fill.FillPrice = targetParameters.Prices.Low;
204 fill.FillQuantity = targetParameters.Order.Quantity;
213 if (currentPrice > limitPrice)
215 for (var i = 0; i < fillParameters.Count; i++)
217 var targetParameters = fillParameters[i];
218 var utcTime = targetParameters.Security.LocalTime.ConvertToUtc(targetParameters.Security.Exchange.TimeZone);
223 fill.FillPrice = targetParameters.Prices.High;
225 fill.FillQuantity = targetParameters.Order.Quantity;
246 foreach (var kvp
in parameters.SecuritiesForOrders.OrderBy(x => x.Key.Id))
248 var targetOrder = kvp.Key;
249 var security = kvp.Value;
252 targetOrder.Quantity);
256 return new List<OrderEvent>();
273 return InternalMarketFill(asset, order, order.
Quantity);
289 if (!IsExchangeOpen(asset,
false))
return fill;
293 var pricesEndTimeUtc = prices.EndTime.ConvertToUtc(asset.
Exchange.
TimeZone);
302 fill.FillPrice = prices.Current;
306 var slip = asset.
SlippageModel.GetSlippageApproximation(asset, order);
309 switch (orderDirection)
312 fill.FillPrice += slip;
315 fill.FillPrice -= slip;
320 fill.FillQuantity = quantity;
342 if (!IsExchangeOpen(asset,
false))
return fill;
349 if (pricesEndTime <= order.
Time)
return fill;
352 var slip = asset.
SlippageModel.GetSlippageApproximation(asset, order);
363 fill.FillPrice = Math.Min(order.
StopPrice, prices.Current - slip);
375 fill.FillPrice = Math.Max(order.
StopPrice, prices.Current + slip);
401 if (!IsExchangeOpen(asset,
false))
return fill;
408 if (pricesEndTime <= order.
Time)
return fill;
411 var slip = asset.
SlippageModel.GetSlippageApproximation(asset, order);
421 fill.FillPrice = Math.Min(order.
StopPrice, prices.Current - slip);
433 fill.FillPrice = Math.Max(order.
StopPrice, prices.Current + slip);
481 if (!IsExchangeOpen(asset))
491 if (pricesEndTime <= order.
Time)
return fill;
511 fill.FillPrice = Math.Min(prices.High, order.
LimitPrice);
533 fill.FillPrice = Math.Max(prices.Low, order.
LimitPrice);
571 if (!IsExchangeOpen(asset))
579 var pricesEndTime = DateTime.MinValue;
583 if (subscribedTypes.Contains(typeof(
Tick)))
589 tradeHigh = trade.Current;
590 tradeLow = trade.Current;
595 else if (subscribedTypes.Contains(typeof(
TradeBar)))
598 if (tradeBar !=
null)
600 tradeHigh = tradeBar.
High;
601 tradeLow = tradeBar.Low;
607 if (pricesEndTime <= order.
Time)
return fill;
617 if (GetAskPrice(asset, out pricesEndTime) >= order.
LimitPrice)
633 if (GetBidPrice(asset, out pricesEndTime) <= order.
LimitPrice)
673 if (!IsExchangeOpen(asset))
683 if (pricesEndTime <= order.
Time)
return fill;
686 switch (orderDirection)
690 if (prices.Low < limitPrice)
696 fill.FillPrice = Math.Min(prices.High, limitPrice);
698 fill.FillQuantity = quantity;
703 if (prices.High > limitPrice)
708 fill.FillPrice = Math.Max(prices.Low, limitPrice);
710 fill.FillQuantity = quantity;
744 if (currentBar ==
null || localOrderTime >= currentBar.EndTime)
return fill;
754 if (!IsExchangeOpen(asset,
false))
return fill;
759 var slip = asset.
SlippageModel.GetSlippageApproximation(asset, order);
765 fill.FillPrice += slip;
770 fill.FillPrice -= slip;
806 if (!IsExchangeOpen(asset,
false))
return fill;
811 var slip = asset.
SlippageModel.GetSlippageApproximation(asset, order);
817 fill.FillPrice += slip;
822 fill.FillPrice -= slip;
838 private decimal GetAskPrice(
Security asset, out DateTime endTime)
842 List<Tick> ticks =
null;
843 var isTickSubscribed = subscribedTypes.Contains(typeof(
Tick));
845 if (isTickSubscribed)
847 ticks = asset.
Cache.GetAll<
Tick>().ToList();
849 var quote = ticks.LastOrDefault(x => x.TickType ==
TickType.Quote && x.AskPrice > 0);
852 endTime = quote.EndTime;
853 return quote.AskPrice;
857 if (subscribedTypes.Contains(typeof(
QuoteBar)))
860 if (quoteBar !=
null)
863 return quoteBar.Ask?.Close ?? quoteBar.Close;
867 if (isTickSubscribed)
869 var trade = ticks.LastOrDefault(x => x.TickType ==
TickType.Trade && x.Price > 0);
872 endTime = trade.EndTime;
877 if (subscribedTypes.Contains(typeof(
TradeBar)))
880 if (tradeBar !=
null)
883 return tradeBar.Close;
887 throw new InvalidOperationException(Messages.FillModel.NoMarketDataToGetAskPriceForFilling(asset));
897 private decimal GetBidPrice(
Security asset, out DateTime endTime)
901 List<Tick> ticks =
null;
902 var isTickSubscribed = subscribedTypes.Contains(typeof(
Tick));
904 if (isTickSubscribed)
906 ticks = asset.
Cache.GetAll<
Tick>().ToList();
908 var quote = ticks.LastOrDefault(x => x.TickType ==
TickType.Quote && x.BidPrice > 0);
911 endTime = quote.EndTime;
912 return quote.BidPrice;
916 if (subscribedTypes.Contains(typeof(
QuoteBar)))
919 if (quoteBar !=
null)
922 return quoteBar.Bid?.Close ?? quoteBar.Close;
926 if (isTickSubscribed)
928 var trade = ticks.LastOrDefault(x => x.TickType ==
TickType.Trade && x.Price > 0);
931 endTime = trade.EndTime;
936 if (subscribedTypes.Contains(typeof(
TradeBar)))
939 if (tradeBar !=
null)
942 return tradeBar.Close;
946 throw new InvalidOperationException(Messages.FillModel.NoMarketDataToGetBidPriceForFilling(asset));
959 .GetSubscriptionDataConfigs(asset.
Symbol, includeInternalConfigs:
true)
960 .ToHashSet(x => x.Type);
962 if (subscribedTypes.Count == 0)
967 return subscribedTypes;
974 private bool IsExchangeOpen(
Security asset)
979 if (configs.Count == 0)
984 var hasNonInternals =
false;
985 var exchangeOpenNonInternals =
false;
986 var exchangeOpenInternals =
false;
987 for (
int i = 0; i < configs.Count; i++)
989 var config = configs[i];
991 if (config.IsInternalFeed)
993 exchangeOpenInternals |= config.ExtendedMarketHours;
997 hasNonInternals =
true;
998 exchangeOpenNonInternals |= config.ExtendedMarketHours;
1002 if (hasNonInternals)
1005 return IsExchangeOpen(asset, exchangeOpenNonInternals);
1007 return IsExchangeOpen(asset, exchangeOpenInternals);
1030 var low = asset.
Low;
1031 var high = asset.
High;
1032 var open = asset.
Open;
1033 var close = asset.
Close;
1034 var current = asset.
Price;
1035 var endTime = asset.
Cache.
GetData()?.EndTime ?? DateTime.MinValue;
1039 return new Prices(endTime, current, open, high, low, close);
1046 if (tick !=
null && subscriptionTypes.Contains(typeof(
Tick)))
1051 return new Prices(tick.EndTime, price, 0, 0, 0, 0);
1058 return new Prices(tick.EndTime, price, 0, 0, 0, 0);
1064 if (quoteBar !=
null && subscriptionTypes.Contains(typeof(
QuoteBar)))
1069 return new Prices(quoteBar.EndTime, bar);
1075 if (tradeBar !=
null && subscriptionTypes.Contains(typeof(
TradeBar)))
1077 return new Prices(tradeBar);
1080 return new Prices(endTime, current, open, high, low, close);
1092 if (currentBar ==
null)
1097 var barSpan = currentBar.EndTime - currentBar.Time;
1100 ? asset.
LocalTime.Date == currentBar.EndTime.Date
1102 : asset.
LocalTime <= currentBar.EndTime;
1104 return isOnCurrentBar && asset.
Exchange.
IsOpenDuringBar(currentBar.Time, currentBar.EndTime, isExtendedMarketHours);
1110 private class ComboLimitOrderLegParameters
1114 public Prices Prices {
get;
set; }
1119 public decimal Price
1124 var price = Order.GroupOrderManager.Direction ==
OrderDirection.Buy ? Prices.Low : Prices.High;
1127 var quantity = Order.Quantity.GetOrderLegRatio(Order.GroupOrderManager);
1133 return price * quantity;