public void OldBaseFillModel_MarketOnCloseFill(bool isMarketAlwaysOpen) { var model = new TestFillModelInheritBaseClass(); var security = SecurityTests.GetSecurity(isMarketAlwaysOpen); var reference = new DateTime(2022, 4, 5, 10, 0, 0); var referenceUtc = reference.ConvertToUtc(TimeZones.NewYork); var timeKeeper = new TimeKeeper(referenceUtc); security.SetLocalTimeKeeper(timeKeeper.GetLocalTimeKeeper(TimeZones.NewYork)); var args = new FillModelParameters(security, new MarketOnCloseOrder(security.Symbol, 1, orderDateTime), new MockSubscriptionDataConfigProvider(_config), Time.OneHour); if (isMarketAlwaysOpen) { Assert.Throws <InvalidOperationException>(() => model.Fill(args)); } else { var result = model.Fill(args); Assert.True(model.MarketOnCloseFillWasCalled); Assert.IsNotNull(result); Assert.True(model.GetPricesWasCalled); Assert.AreEqual(12345, result.OrderEvent.FillPrice); } }
/// <summary> /// Return an order event with the fill details /// </summary> /// <param name="parameters">A parameters object containing the security and order</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> public override Fill Fill(FillModelParameters parameters) { Parameters = parameters; using (Py.GIL()) { return((_model.Fill(parameters) as PyObject).GetAndDispose <Fill>()); } }
/// <summary> /// Return an order event with the fill details /// </summary> /// <param name="parameters">A parameters object containing the security and order</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> public override Fill Fill(FillModelParameters parameters) { Parameters = parameters; using (Py.GIL()) { return(_model.Fill(parameters)); } }
public Fill Fill(FillModelParameters parameters) { var order = parameters.Order; var status = OrderStatus.PartiallyFilled; if (FilledOrders.ContainsKey(order.Id)) { status = OrderStatus.Filled; } FilledOrders[order.Id] = order; return(new Fill(new OrderEvent(order, DateTime.UtcNow, OrderFee.Zero) { FillPrice = parameters.Security.Price, FillQuantity = order.Quantity / 2, Status = status })); }
public Fill Fill(FillModelParameters parameters) { var order = parameters.Order; OrderEvent orderEvent; switch (order.Type) { case OrderType.Market: orderEvent = MarketFill(parameters.Security, parameters.Order as MarketOrder); break; case OrderType.Limit: orderEvent = LimitFill(parameters.Security, parameters.Order as LimitOrder); break; case OrderType.StopMarket: orderEvent = StopMarketFill(parameters.Security, parameters.Order as StopMarketOrder); break; case OrderType.StopLimit: orderEvent = StopLimitFill(parameters.Security, parameters.Order as StopLimitOrder); break; case OrderType.MarketOnOpen: orderEvent = MarketOnOpenFill(parameters.Security, parameters.Order as MarketOnOpenOrder); break; case OrderType.MarketOnClose: orderEvent = MarketOnCloseFill(parameters.Security, parameters.Order as MarketOnCloseOrder); break; default: throw new ArgumentOutOfRangeException(); } return(new Fill(orderEvent)); }
public void PerformsMarketFillSell() { var model = new EquityFillModel(); var order = new MarketOrder(Symbols.SPY, -100, Noon); var config = CreateQuoteBarConfig(Symbols.SPY); var security = new Security( SecurityExchangeHoursTests.CreateUsEquitySecurityExchangeHours(), config, new Cash(Currencies.USD, 0, 1m), SymbolProperties.GetDefault(Currencies.USD), ErrorCurrencyConverter.Instance, RegisteredSecurityDataTypesProvider.Null, new SecurityCache() ); security.SetLocalTimeKeeper(TimeKeeper.GetLocalTimeKeeper(TimeZones.NewYork)); security.SetMarketPrice(new IndicatorDataPoint(Symbols.SPY, Noon, 101.123m)); var parameters = new FillModelParameters( security, order, new MockSubscriptionDataConfigProvider(config), Time.OneHour); // IndicatorDataPoint is not market data Assert.Throws <InvalidOperationException>(() => model.Fill(parameters), $"Cannot get bid price to perform fill for {security.Symbol} because no market data subscription were found."); var bidBar = new Bar(101.123m, 101.123m, 101.123m, 101.123m); var askBar = new Bar(101.234m, 101.234m, 101.234m, 101.234m); security.SetMarketPrice(new QuoteBar(Noon, Symbols.SPY, bidBar, 0, askBar, 0)); var fill = model.Fill(parameters).OrderEvent; Assert.AreEqual(order.Quantity, fill.FillQuantity); Assert.AreEqual(bidBar.Close, fill.FillPrice); Assert.AreEqual(OrderStatus.Filled, fill.Status); }
public void SetParameters(FillModelParameters parameters) { Parameters = parameters; }
/// <summary> /// Scans all the outstanding orders and applies the algorithm model fills to generate the order events /// </summary> public virtual void Scan() { lock (_needsScanLock) { // there's usually nothing in here if (!_needsScan) { return; } var stillNeedsScan = false; // process each pending order to produce fills/fire events foreach (var kvp in _pending.OrderBy(x => x.Key)) { var order = kvp.Value; if (order == null) { Log.Error("BacktestingBrokerage.Scan(): Null pending order found: " + kvp.Key); _pending.TryRemove(kvp.Key, out order); continue; } if (order.Status.IsClosed()) { // this should never actually happen as we always remove closed orders as they happen _pending.TryRemove(order.Id, out order); continue; } // all order fills are processed on the next bar (except for market orders) if (order.Time == Algorithm.UtcTime && order.Type != OrderType.Market) { stillNeedsScan = true; continue; } var fills = new[] { new OrderEvent(order, Algorithm.UtcTime, OrderFee.Zero) }; Security security; if (!Algorithm.Securities.TryGetValue(order.Symbol, out security)) { Log.Error("BacktestingBrokerage.Scan(): Unable to process order: " + order.Id + ". The security no longer exists."); // invalidate the order in the algorithm before removing OnOrderEvent(new OrderEvent(order, Algorithm.UtcTime, OrderFee.Zero) { Status = OrderStatus.Invalid }); _pending.TryRemove(order.Id, out order); continue; } // check if the time in force handler allows fills if (order.TimeInForce.IsOrderExpired(security, order)) { OnOrderEvent(new OrderEvent(order, Algorithm.UtcTime, OrderFee.Zero) { Status = OrderStatus.Canceled, Message = "The order has expired." }); _pending.TryRemove(order.Id, out order); continue; } // check if we would actually be able to fill this if (!Algorithm.BrokerageModel.CanExecuteOrder(security, order)) { continue; } // verify sure we have enough cash to perform the fill HasSufficientBuyingPowerForOrderResult hasSufficientBuyingPowerResult; try { hasSufficientBuyingPowerResult = security.BuyingPowerModel.HasSufficientBuyingPowerForOrder(Algorithm.Portfolio, security, order); } catch (Exception err) { // if we threw an error just mark it as invalid and remove the order from our pending list OnOrderEvent(new OrderEvent(order, Algorithm.UtcTime, OrderFee.Zero, err.Message) { Status = OrderStatus.Invalid }); Order pending; _pending.TryRemove(order.Id, out pending); Log.Error(err); Algorithm.Error($"Order Error: id: {order.Id}, Error executing margin models: {err.Message}"); continue; } //Before we check this queued order make sure we have buying power: if (hasSufficientBuyingPowerResult.IsSufficient) { //Model: var model = security.FillModel; //Based on the order type: refresh its model to get fill price and quantity try { if (order.Type == OrderType.OptionExercise) { var option = (Option)security; fills = option.OptionExerciseModel.OptionExercise(option, order as OptionExerciseOrder).ToArray(); } else { var context = new FillModelParameters( security, order, Algorithm.SubscriptionManager.SubscriptionDataConfigService); fills = new[] { model.Fill(context).OrderEvent }; } // invoke fee models for completely filled order events foreach (var fill in fills) { if (fill.Status == OrderStatus.Filled) { // this check is provided for backwards compatibility of older user-defined fill models // that may be performing fee computation inside the fill model w/out invoking the fee model // TODO : This check can be removed in April, 2019 -- a 6-month window to upgrade (also, suspect small % of users, if any are impacted) if (fill.OrderFee.Value.Amount == 0m) { fill.OrderFee = security.FeeModel.GetOrderFee( new OrderFeeParameters(security, order, Algorithm.Portfolio.CashBook.AccountCurrency)); } } } } catch (Exception err) { Log.Error(err); Algorithm.Error($"Order Error: id: {order.Id}, Transaction model failed to fill for order type: {order.Type} with error: {err.Message}"); } } else { // invalidate the order in the algorithm before removing var message = $"Insufficient buying power to complete order (Value:{order.GetValue(security).SmartRounding()}), Reason: {hasSufficientBuyingPowerResult.Reason}."; OnOrderEvent(new OrderEvent(order, Algorithm.UtcTime, OrderFee.Zero, message) { Status = OrderStatus.Invalid }); Order pending; _pending.TryRemove(order.Id, out pending); Algorithm.Error($"Order Error: id: {order.Id}, {message}"); continue; } foreach (var fill in fills) { // check if the fill should be emitted if (!order.TimeInForce.IsFillValid(security, order, fill)) { break; } // change in status or a new fill if (order.Status != fill.Status || fill.FillQuantity != 0) { //If the fill models come back suggesting filled, process the affects on portfolio OnOrderEvent(fill); } if (order.Type == OrderType.OptionExercise) { fill.Message = order.Tag; OnOptionPositionAssigned(fill); } } if (fills.All(x => x.Status.IsClosed())) { _pending.TryRemove(order.Id, out order); } else { stillNeedsScan = true; } } // if we didn't fill then we need to continue to scan _needsScan = stillNeedsScan; } }
public TestableLatestFillModel() { // NOTE. GetPrices will no be called before SubscriptionDataConfigProvider is set by the system Parameters = new FillModelParameters(null, null, new MockSubscriptionDataConfigProvider()); }