/******************************************************** * CLASS PROPERTIES *********************************************************/ /******************************************************** * CLASS METHODS *********************************************************/ /// <summary> /// Perform neccessary check to see if the model has been filled, appoximate the best we can. /// </summary> /// <param name="vehicle">Asset we're working with</param> /// <param name="order">Order class to check if filled.</param> public virtual OrderEvent Fill(Security vehicle, Order order) { //Default order event to return. var fill = new OrderEvent(order); try { switch (order.Type) { case OrderType.Limit: fill = LimitFill(vehicle, order); break; case OrderType.StopMarket: fill = StopFill(vehicle, order); break; case OrderType.Market: fill = MarketFill(vehicle, order); break; } } catch (Exception err) { Log.Error("SecurityTransactionModel.TransOrderDirection.Fill(): " + err.Message); } return fill; }
/// <summary> /// Default stop fill model implementation in base class security. (Stop Market Order Type) /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> /// <seealso cref="MarketFill(Security, MarketOrder)"/> /// <seealso cref="SecurityTransactionModel.LimitFill"/> public virtual OrderEvent StopMarketFill(Security asset, StopMarketOrder order) { //Default order event to return. var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, 0); // make sure the exchange is open before filling if (!IsExchangeOpen(asset)) return fill; //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) return fill; //Get the range of prices in the last bar: decimal minimumPrice; decimal maximumPrice; DataMinMaxPrices(asset, out minimumPrice, out maximumPrice, order.Direction); //Calculate the model slippage: e.g. 0.01c var slip = asset.SlippageModel.GetSlippageApproximation(asset, order); //Check if the Stop Order was filled: opposite to a limit order switch (order.Direction) { case OrderDirection.Sell: //-> 1.1 Sell Stop: If Price below setpoint, Sell: if (minimumPrice < order.StopPrice) { fill.Status = OrderStatus.Filled; // Assuming worse case scenario fill - fill at lowest of the stop & asset price. fill.FillPrice = Math.Min(order.StopPrice, asset.Price - slip); } break; case OrderDirection.Buy: //-> 1.2 Buy Stop: If Price Above Setpoint, Buy: if (maximumPrice > order.StopPrice) { fill.Status = OrderStatus.Filled; // Assuming worse case scenario fill - fill at highest of the stop & asset price. fill.FillPrice = Math.Max(order.StopPrice, asset.Price + slip); } break; } // assume the order completely filled if (fill.Status == OrderStatus.Filled) { fill.FillQuantity = order.Quantity; fill.OrderFee = asset.FeeModel.GetOrderFee(asset, order); } return fill; }
/// <summary> /// Default market fill model for the base security class. Fills at the last traded price. /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> /// <seealso cref="SecurityTransactionModel.StopMarketFill"/> /// <seealso cref="SecurityTransactionModel.LimitFill"/> public virtual OrderEvent MarketFill(Security asset, MarketOrder order) { //Default order event to return. var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, 0); if (order.Status == OrderStatus.Canceled) return fill; // make sure the exchange is open before filling if (!IsExchangeOpen(asset)) return fill; //Order [fill]price for a market order model is the current security price fill.FillPrice = asset.Price; fill.Status = OrderStatus.Filled; //Calculate the model slippage: e.g. 0.01c var slip = asset.SlippageModel.GetSlippageApproximation(asset, order); //Apply slippage switch (order.Direction) { case OrderDirection.Buy: fill.FillPrice += slip; break; case OrderDirection.Sell: fill.FillPrice -= slip; break; } // assume the order completely filled if (fill.Status == OrderStatus.Filled) { fill.FillQuantity = order.Quantity; fill.OrderFee = asset.FeeModel.GetOrderFee(asset, order); } return fill; }
public override void OnOrderEvent(OrderEvent orderEvent) { if (orderEvent.Status == OrderStatus.Filled && orderEvent.FillQuantity < 0) { SecurityHolding s = Securities [orderEvent.Symbol].Holdings; var profit_pct = s.LastTradeProfit / Portfolio.TotalPortfolioValue; _tradeReturns.Add (profit_pct); if (_tradeReturns.Count > MAXRETURNS) _tradeReturns.RemoveAt (0); } }
private void EmitFillOrder(string[] entries) { try { var brokerId = entries[4]; var order = CachedOrderIDs .FirstOrDefault(o => o.Value.BrokerId.Contains(brokerId)) .Value; if (order == null) { order = _algorithm.Transactions.GetOrderByBrokerageId(brokerId); if (order == null) { // not our order, nothing else to do here return; } } var symbol = _symbolMapper.GetLeanSymbol(entries[2]); var fillPrice = decimal.Parse(entries[6], NumberStyles.Float, CultureInfo.InvariantCulture); var fillQuantity = decimal.Parse(entries[5], NumberStyles.Float, CultureInfo.InvariantCulture); var direction = fillQuantity < 0 ? OrderDirection.Sell : OrderDirection.Buy; var updTime = Time.UnixTimeStampToDateTime(double.Parse(entries[3], NumberStyles.Float, CultureInfo.InvariantCulture)); var orderFee = new OrderFee(new CashAmount( Math.Abs(decimal.Parse(entries[9], NumberStyles.Float, CultureInfo.InvariantCulture)), entries[10] )); var status = OrderStatus.Filled; if (fillQuantity != order.Quantity) { decimal totalFillQuantity; _fills.TryGetValue(order.Id, out totalFillQuantity); totalFillQuantity += fillQuantity; _fills[order.Id] = totalFillQuantity; status = totalFillQuantity == order.Quantity ? OrderStatus.Filled : OrderStatus.PartiallyFilled; } var orderEvent = new OrderEvent ( order.Id, symbol, updTime, status, direction, fillPrice, fillQuantity, orderFee, $"Bitfinex Order Event {direction}" ); // if the order is closed, we no longer need it in the active order list if (status == OrderStatus.Filled) { Order outOrder; CachedOrderIDs.TryRemove(order.Id, out outOrder); decimal ignored; _fills.TryRemove(order.Id, out ignored); } OnOrderEvent(orderEvent); } catch (Exception e) { Log.Error(e); throw; } }
/// <summary> /// Check if the model has stopped out our position yet: /// </summary> /// <param name="security">Asset we're working with</param> /// <param name="order">Stop Order to Check, return filled if true</param> public virtual OrderEvent StopFill(Security security, Order order) { var fill = new OrderEvent(order); try { //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) return fill; //Check if the Stop Order was filled: opposite to a limit order if (order.Direction == OrderDirection.Sell) { //-> 1.1 Sell Stop: If Price below setpoint, Sell: if (security.Price < order.Price) { //Set the order and slippage on the order, update the fill price: order.Status = OrderStatus.Filled; order.Price = security.Price; //Fill at the security price, sometimes gap down skip past stop. } } else if (order.Direction == OrderDirection.Buy) { //-> 1.2 Buy Stop: If Price Above Setpoint, Buy: if (security.Price > order.Price) { order.Status = OrderStatus.Filled; order.Price = security.Price; //Fill at the security price, sometimes gap down skip past stop. } } //Set the fill properties when order filled. if (order.Status == OrderStatus.Filled || order.Status == OrderStatus.PartiallyFilled) { fill.FillQuantity = order.Quantity; fill.FillPrice = security.Price; fill.Status = order.Status; } } catch (Exception err) { Log.Error("ForexTransactionModel.TransOrderDirection.StopFill(): " + err.Message); } return fill; }
public override void OnOrderEvent(OrderEvent orderEvent) { OrderEvents.Add(orderEvent); }
public void OrderEvent(OrderEvent newEvent) { }
public virtual void OnAssignmentOrderEvent(OrderEvent assignmentEvent) { //Algo.OnAssignmentOrderEvent(assignmentEvent); }
private void OnEvent(OrderEvent @event) { TotalEvents++; OrderIds.Add(@event.Id); AllSeenOrderIds.Add(@event.Id); }
/// <summary> /// Default limit if touched fill model implementation in base class security. /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> /// <remarks> /// There is no good way to model limit orders with OHLC because we never know whether the market has /// gapped past our fill price. We have to make the assumption of a fluid, high volume market. /// /// With Limit if Touched orders, whether or not a trigger is surpassed is determined by the high (low) /// of the previous tradebar when making a sell (buy) request. Following the behaviour of /// <see cref="StopLimitFill"/>, current quote information is used when determining fill parameters /// (e.g., price, quantity) as the tradebar containing the incoming data is not yet consolidated. /// This conservative approach, however, can lead to trades not occuring as would be expected when /// compared to future consolidated data. /// </remarks> public virtual OrderEvent LimitIfTouchedFill(Security asset, LimitIfTouchedOrder order) { //Default order event to return. var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, OrderFee.Zero); //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) { return(fill); } // Fill only if open or extended if (!IsExchangeOpen(asset, Parameters.ConfigProvider .GetSubscriptionDataConfigs(asset.Symbol) .IsExtendedMarketHours())) { return(fill); } // Get the range of prices in the last bar: var tradeHigh = 0m; var tradeLow = 0m; var endTimeUtc = DateTime.MinValue; var subscribedTypes = GetSubscribedTypes(asset); if (subscribedTypes.Contains(typeof(Tick))) { var trade = asset.Cache.GetAll <Tick>().LastOrDefault(x => x.TickType == TickType.Trade && x.Price > 0); if (trade != null) { tradeHigh = trade.Price; tradeLow = trade.Price; endTimeUtc = trade.EndTime.ConvertToUtc(asset.Exchange.TimeZone); } } else if (subscribedTypes.Contains(typeof(TradeBar))) { var tradeBar = asset.Cache.GetData <TradeBar>(); if (tradeBar != null) { tradeHigh = tradeBar.High; tradeLow = tradeBar.Low; endTimeUtc = tradeBar.EndTime.ConvertToUtc(asset.Exchange.TimeZone); } } // do not fill on stale data if (endTimeUtc <= order.Time) { return(fill); } //Check if the limit if touched order was filled: switch (order.Direction) { case OrderDirection.Buy: //-> 1.2 Buy: If Price below Trigger, Buy: if (tradeLow <= order.TriggerPrice || order.TriggerTouched) { order.TriggerTouched = true; var askCurrent = GetBestEffortAskPrice(asset, order.Time, out var fillMessage); if (askCurrent <= order.LimitPrice) { fill.Status = OrderStatus.Filled; fill.FillPrice = Math.Min(askCurrent, order.LimitPrice); fill.FillQuantity = order.Quantity; fill.Message = fillMessage; } } break; case OrderDirection.Sell: //-> 1.2 Sell: If Price above Trigger, Sell: if (tradeHigh >= order.TriggerPrice || order.TriggerTouched) { order.TriggerTouched = true; var bidCurrent = GetBestEffortBidPrice(asset, order.Time, out var fillMessage); if (bidCurrent >= order.LimitPrice) { fill.Status = OrderStatus.Filled; fill.FillPrice = Math.Max(bidCurrent, order.LimitPrice); fill.FillQuantity = order.Quantity; fill.Message = fillMessage; } } break; } return(fill); }
/// <summary> /// Default stop fill model implementation in base class security. (Stop Market Order Type) /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> /// <seealso cref="MarketFill(Security, MarketOrder)"/> public virtual OrderEvent StopMarketFill(Security asset, StopMarketOrder order) { //Default order event to return. var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, OrderFee.Zero); //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) { return(fill); } // make sure the exchange is open/normal market hours before filling if (!IsExchangeOpen(asset, false)) { return(fill); } //Get the range of prices in the last bar: var prices = GetPricesCheckingPythonWrapper(asset, order.Direction); var pricesEndTime = prices.EndTime.ConvertToUtc(asset.Exchange.TimeZone); // do not fill on stale data if (pricesEndTime <= order.Time) { return(fill); } //Calculate the model slippage: e.g. 0.01c var slip = asset.SlippageModel.GetSlippageApproximation(asset, order); //Check if the Stop Order was filled: opposite to a limit order switch (order.Direction) { case OrderDirection.Sell: //-> 1.1 Sell Stop: If Price below setpoint, Sell: if (prices.Low < order.StopPrice) { fill.Status = OrderStatus.Filled; // Assuming worse case scenario fill - fill at lowest of the stop & asset price. fill.FillPrice = Math.Min(order.StopPrice, prices.Current - slip); // assume the order completely filled fill.FillQuantity = order.Quantity; } break; case OrderDirection.Buy: //-> 1.2 Buy Stop: If Price Above Setpoint, Buy: if (prices.High > order.StopPrice) { fill.Status = OrderStatus.Filled; // Assuming worse case scenario fill - fill at highest of the stop & asset price. fill.FillPrice = Math.Max(order.StopPrice, prices.Current + slip); // assume the order completely filled fill.FillQuantity = order.Quantity; } break; } return(fill); }
public override void OnOrderEvent(OrderEvent orderEvent) { if (orderEvent.Status == OrderStatus.Filled) { Log($"OnOrderEvent(): New filled order event: {orderEvent}"); // leave 1 unit as error in expected value if (Math.Abs(orderEvent.FillQuantity - _expectedOrderQuantity) > 1) { throw new Exception($"Unexpected order event fill quantity: {orderEvent.FillQuantity}. " + $"Expected {_expectedOrderQuantity}"); } var orderFeeInAccountCurrency = Portfolio.CashBook.ConvertToAccountCurrency(orderEvent.OrderFee.Value).Amount; var expectedOrderFee = _btcUsd.Holdings.TotalFees - _previousHoldingsFees; // just to verify let calculate the order fee using taker fee var calculatedOrderFee = Portfolio.CashBook.ConvertToAccountCurrency( orderEvent.AbsoluteFillQuantity * 0.003m * orderEvent.FillPrice, orderEvent.OrderFee.Value.Currency); if (orderEvent.OrderFee.Value.Currency == AccountCurrency // leave 0.00001m as error in expected fee value || Math.Abs(expectedOrderFee - orderFeeInAccountCurrency) > 0.00001m || Math.Abs(expectedOrderFee - calculatedOrderFee) > 0.00001m) { throw new Exception($"Unexpected order fee: {orderFeeInAccountCurrency}. " + $"Expected {expectedOrderFee}. Calculated Order Fee {calculatedOrderFee}"); } if (!TradeBuilder.HasOpenPosition(_btcUsd.Symbol)) { var lastTrade = TradeBuilder.ClosedTrades.Last(); var expectedProfitLoss = (lastTrade.ExitPrice - lastTrade.EntryPrice) * lastTrade.Quantity * _btcUsd.QuoteCurrency.ConversionRate * (lastTrade.Direction == TradeDirection.Long ? 1 : -1); if (Math.Abs(expectedProfitLoss - lastTrade.ProfitLoss) > 1) { throw new Exception($"Unexpected last trade ProfitLoss: {lastTrade.ProfitLoss}. " + $"Expected {expectedProfitLoss}"); } // There is a difference in what does Holdings and TradeBuilder consider LastTrade if (TradeBuilder.ClosedTrades.Count - _previousClosedTradesCount > 1) { var trade = TradeBuilder.ClosedTrades[_previousClosedTradesCount]; expectedProfitLoss += trade.ProfitLoss; } if (Math.Abs(_btcUsd.Holdings.LastTradeProfit - expectedProfitLoss) > 1) { throw new Exception($"Unexpected Holdings.NetProfit: {_btcUsd.Holdings.LastTradeProfit}. " + $"Expected {expectedProfitLoss}"); } } _previousHoldingsFees = _btcUsd.Holdings.TotalFees; _previousClosedTradesCount = TradeBuilder.ClosedTrades.Count; } }
public override void OnOrderEvent(OrderEvent orderEvent) { Debug(string.Format("OnOrderEvent(OrderEvent): {0}: {1}", Time, orderEvent)); }
public override void OnOrderEvent(OrderEvent orderEvent) { Debug($"{orderEvent}"); }
public override void OnOrderEvent(OrderEvent fill) { Log($"OnOrderEvent({UtcTime:o}):: {fill}"); }
/// <summary> /// Market on Open Fill Model. Return an order event with the fill details /// </summary> /// <param name="asset">Asset we're trading with this order</param> /// <param name="order">Order to be filled</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> public OrderEvent MarketOnOpenFill(Security asset, MarketOnOpenOrder order) { var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, 0); if (order.Status == OrderStatus.Canceled) { return(fill); } // MOO should never fill on the same bar or on stale data // Imagine the case where we have a thinly traded equity, ASUR, and another liquid // equity, say SPY, SPY gets data every minute but ASUR, if not on fill forward, maybe // have large gaps, in which case the currentBar.EndTime will be in the past // ASUR | | | [order] | | | | | | | // SPY | | | | | | | | | | | | | | | | | | | | var currentBar = asset.GetLastData(); var localOrderTime = order.Time.ConvertFromUtc(asset.Exchange.TimeZone); if (currentBar == null || localOrderTime >= currentBar.EndTime) { return(fill); } // if the MOO was submitted during market the previous day, wait for a day to turn over if (asset.Exchange.DateTimeIsOpen(localOrderTime) && localOrderTime.Date == asset.LocalTime.Date) { return(fill); } // wait until market open // make sure the exchange is open before filling if (!IsExchangeOpen(asset)) { return(fill); } fill.FillPrice = GetPrices(asset, order.Direction).Open; fill.Status = OrderStatus.Filled; //Calculate the model slippage: e.g. 0.01c var slip = asset.SlippageModel.GetSlippageApproximation(asset, order); //Apply slippage switch (order.Direction) { case OrderDirection.Buy: fill.FillPrice += slip; break; case OrderDirection.Sell: fill.FillPrice -= slip; break; } // assume the order completely filled if (fill.Status == OrderStatus.Filled) { fill.FillQuantity = order.Quantity; fill.OrderFee = asset.FeeModel.GetOrderFee(asset, order); } return(fill); }
/// <summary> /// Default limit order fill model in the base security class. /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> /// <seealso cref="StopMarketFill(Security, StopMarketOrder)"/> /// <seealso cref="MarketFill(Security, MarketOrder)"/> public virtual OrderEvent LimitFill(Security asset, LimitOrder order) { //Initialise; var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, 0); //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) return fill; //Get the range of prices in the last bar: decimal minimumPrice; decimal maximumPrice; DataMinMaxPrices(asset, out minimumPrice, out maximumPrice, order.Direction); //-> Valid Live/Model Order: switch (order.Direction) { case OrderDirection.Buy: //Buy limit seeks lowest price if (minimumPrice < order.LimitPrice) { //Set order fill: fill.Status = OrderStatus.Filled; // fill at the worse price this bar or the limit price, this allows far out of the money limits // to be executed properly fill.FillPrice = Math.Min(maximumPrice, order.LimitPrice); } break; case OrderDirection.Sell: //Sell limit seeks highest price possible if (maximumPrice > order.LimitPrice) { fill.Status = OrderStatus.Filled; // fill at the worse price this bar or the limit price, this allows far out of the money limits // to be executed properly fill.FillPrice = Math.Max(minimumPrice, order.LimitPrice); } break; } // assume the order completely filled if (fill.Status == OrderStatus.Filled) { fill.FillQuantity = order.Quantity; fill.OrderFee = asset.FeeModel.GetOrderFee(asset, order); } return fill; }
/// <summary> /// Default stop limit fill model implementation in base class security. (Stop Limit Order Type) /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> /// <seealso cref="StopMarketFill(Security, StopMarketOrder)"/> /// <remarks> /// There is no good way to model limit orders with OHLC because we never know whether the market has /// gapped past our fill price. We have to make the assumption of a fluid, high volume market. /// /// Stop limit orders we also can't be sure of the order of the H - L values for the limit fill. The assumption /// was made the limit fill will be done with closing price of the bar after the stop has been triggered.. /// </remarks> public virtual OrderEvent StopLimitFill(Security asset, StopLimitOrder order) { //Default order event to return. var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, OrderFee.Zero); //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) { return(fill); } // make sure the exchange is open before filling -- allow pre/post market fills to occur if (!IsExchangeOpen( asset, Parameters.ConfigProvider .GetSubscriptionDataConfigs(asset.Symbol) .IsExtendedMarketHours())) { return(fill); } //Get the range of prices in the last bar: var prices = GetPricesCheckingPythonWrapper(asset, order.Direction); var pricesEndTime = prices.EndTime.ConvertToUtc(asset.Exchange.TimeZone); // do not fill on stale data if (pricesEndTime <= order.Time) { return(fill); } //Check if the Stop Order was filled: opposite to a limit order switch (order.Direction) { case OrderDirection.Buy: //-> 1.2 Buy Stop: If Price Above Setpoint, Buy: if (prices.High > order.StopPrice || order.StopTriggered) { order.StopTriggered = true; // Fill the limit order, using closing price of bar: // Note > Can't use minimum price, because no way to be sure minimum wasn't before the stop triggered. if (prices.Current < order.LimitPrice) { fill.Status = OrderStatus.Filled; fill.FillPrice = Math.Min(prices.High, order.LimitPrice); // assume the order completely filled fill.FillQuantity = order.Quantity; } } break; case OrderDirection.Sell: //-> 1.1 Sell Stop: If Price below setpoint, Sell: if (prices.Low < order.StopPrice || order.StopTriggered) { order.StopTriggered = true; // Fill the limit order, using minimum price of the bar // Note > Can't use minimum price, because no way to be sure minimum wasn't before the stop triggered. if (prices.Current > order.LimitPrice) { fill.Status = OrderStatus.Filled; fill.FillPrice = Math.Max(prices.Low, order.LimitPrice); // assume the order completely filled fill.FillQuantity = order.Quantity; } } break; } return(fill); }
/// <summary> /// Market on Close Fill Model. Return an order event with the fill details /// </summary> /// <param name="asset">Asset we're trading with this order</param> /// <param name="order">Order to be filled</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> public OrderEvent MarketOnCloseFill(Security asset, MarketOnCloseOrder order) { var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, 0); if (order.Status == OrderStatus.Canceled) return fill; var localOrderTime = order.Time.ConvertFromUtc(asset.Exchange.TimeZone); var nextMarketClose = asset.Exchange.Hours.GetNextMarketClose(localOrderTime, false); // wait until market closes after the order time if (asset.LocalTime < nextMarketClose) { return fill; } fill.FillPrice = asset.Close; fill.Status = OrderStatus.Filled; //Calculate the model slippage: e.g. 0.01c var slip = asset.SlippageModel.GetSlippageApproximation(asset, order); //Apply slippage switch (order.Direction) { case OrderDirection.Buy: fill.FillPrice += slip; break; case OrderDirection.Sell: fill.FillPrice -= slip; break; } // assume the order completely filled if (fill.Status == OrderStatus.Filled) { fill.FillQuantity = order.Quantity; fill.OrderFee = asset.FeeModel.GetOrderFee(asset, order); } return fill; }
/// <summary> /// Default limit order fill model in the base security class. /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> /// <seealso cref="StopMarketFill(Security, StopMarketOrder)"/> /// <seealso cref="MarketFill(Security, MarketOrder)"/> public virtual OrderEvent LimitFill(Security asset, LimitOrder order) { //Initialise; var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, OrderFee.Zero); //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) { return(fill); } // make sure the exchange is open before filling -- allow pre/post market fills to occur if (!IsExchangeOpen(asset, Parameters.ConfigProvider .GetSubscriptionDataConfigs(asset.Symbol) .IsExtendedMarketHours())) { return(fill); } //Get the range of prices in the last bar: var prices = GetPricesCheckingPythonWrapper(asset, order.Direction); var pricesEndTime = prices.EndTime.ConvertToUtc(asset.Exchange.TimeZone); // do not fill on stale data if (pricesEndTime <= order.Time) { return(fill); } //-> Valid Live/Model Order: switch (order.Direction) { case OrderDirection.Buy: //Buy limit seeks lowest price if (prices.Low < order.LimitPrice) { //Set order fill: fill.Status = OrderStatus.Filled; // fill at the worse price this bar or the limit price, this allows far out of the money limits // to be executed properly fill.FillPrice = Math.Min(prices.High, order.LimitPrice); // assume the order completely filled fill.FillQuantity = order.Quantity; } break; case OrderDirection.Sell: //Sell limit seeks highest price possible if (prices.High > order.LimitPrice) { fill.Status = OrderStatus.Filled; // fill at the worse price this bar or the limit price, this allows far out of the money limits // to be executed properly fill.FillPrice = Math.Max(prices.Low, order.LimitPrice); // assume the order completely filled fill.FillQuantity = order.Quantity; } break; } return(fill); }
/// <summary> /// Check if the model has stopped out our position yet: /// </summary> /// <param name="security">Asset we're working with</param> /// <param name="order">Stop Order to Check, return filled if true</param> public virtual OrderEvent StopFill(Security security, Order order) { //Default order event to return. var fill = new OrderEvent(order); try { //If its cancelled don't need anymore checks: if (fill.Status == OrderStatus.Canceled) return fill; //Check if the Stop Order was filled: opposite to a limit order switch (order.Direction) { case OrderDirection.Sell: //-> 1.1 Sell Stop: If Price below setpoint, Sell: if (security.Price < order.Price) { order.Status = OrderStatus.Filled; order.Price = security.Price; } break; case OrderDirection.Buy: //-> 1.2 Buy Stop: If Price Above Setpoint, Buy: if (security.Price > order.Price) { order.Status = OrderStatus.Filled; order.Price = security.Price; } break; } if (order.Status == OrderStatus.Filled || order.Status == OrderStatus.PartiallyFilled) { fill.FillQuantity = order.Quantity; fill.FillPrice = security.Price; //Stop price as security price because can gap past stop price. fill.Status = order.Status; } } catch (Exception err) { Log.Error("SecurityTransactionModel.TransOrderDirection.StopFill(): " + err.Message); } return fill; }
/// <summary> /// Market on Open Fill Model. Return an order event with the fill details /// </summary> /// <param name="asset">Asset we're trading with this order</param> /// <param name="order">Order to be filled</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> public virtual OrderEvent MarketOnOpenFill(Security asset, MarketOnOpenOrder order) { var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, OrderFee.Zero); if (order.Status == OrderStatus.Canceled) { return(fill); } // MOO should never fill on the same bar or on stale data // Imagine the case where we have a thinly traded equity, ASUR, and another liquid // equity, say SPY, SPY gets data every minute but ASUR, if not on fill forward, maybe // have large gaps, in which case the currentBar.EndTime will be in the past // ASUR | | | [order] | | | | | | | // SPY | | | | | | | | | | | | | | | | | | | | var localOrderTime = order.Time.ConvertFromUtc(asset.Exchange.TimeZone); var endTime = DateTime.MinValue; var subscribedTypes = GetSubscribedTypes(asset); if (subscribedTypes.Contains(typeof(Tick))) { var primaryExchangeCode = ((Equity)asset).PrimaryExchange.Code; var officialOpen = (uint)(TradeConditionFlags.Regular | TradeConditionFlags.OfficialOpen); var openingPrints = (uint)(TradeConditionFlags.Regular | TradeConditionFlags.OpeningPrints); var trades = asset.Cache.GetAll <Tick>() .Where(x => x.TickType == TickType.Trade && x.Price > 0 && asset.Exchange.DateTimeIsOpen(x.Time)) .OrderBy(x => x.EndTime).ToList(); // Get the first valid (non-zero) tick of trade type from an open market var tick = trades .Where(x => !string.IsNullOrWhiteSpace(x.SaleCondition)) .FirstOrDefault(x => x.TickType == TickType.Trade && x.Price > 0 && x.ExchangeCode == primaryExchangeCode && (x.ParsedSaleCondition == officialOpen || x.ParsedSaleCondition == openingPrints) && asset.Exchange.DateTimeIsOpen(x.Time)); // If there is no OfficialOpen or OpeningPrints in the current list of trades, // we will wait for the next up to 1 minute before accepting the last trade without flags // We will give priority to trade then use quote to get the timestamp // If there are only quotes, we will need to test for the tick type before we assign the fill price if (tick == null) { var previousOpen = asset.Exchange.Hours .GetMarketHours(asset.LocalTime) .GetMarketOpen(TimeSpan.Zero, false); fill.Message = "No trade with the OfficialOpen or OpeningPrints flag within the 1-minute timeout."; tick = trades.LastOrDefault() ?? asset.Cache.GetAll <Tick>().LastOrDefault(); if ((tick?.EndTime.TimeOfDay - previousOpen)?.TotalMinutes < 1) { return(fill); } fill.Message += $" Fill with last {tick.TickType} data."; } endTime = tick?.EndTime ?? endTime; if (tick?.TickType == TickType.Trade) { fill.FillPrice = tick.Price; } } else if (subscribedTypes.Contains(typeof(TradeBar))) { var tradeBar = asset.Cache.GetData <TradeBar>(); if (tradeBar != null) { // If the order was placed during the bar aggregation, we cannot use its open price if (tradeBar.Time < localOrderTime) { return(fill); } // We need to verify whether the trade data is from the open market. if (tradeBar.Period < Resolution.Hour.ToTimeSpan() && !asset.Exchange.DateTimeIsOpen(tradeBar.Time)) { return(fill); } endTime = tradeBar.EndTime; fill.FillPrice = tradeBar.Open; } } else { fill.Message = $"Warning: No trade information available at {asset.LocalTime.ToStringInvariant()} {asset.Exchange.TimeZone}, order filled using Quote data"; } if (localOrderTime >= endTime) { return(fill); } // if the MOO was submitted during market the previous day, wait for a day to turn over // The date of the order and the trade data end time cannot be the same. // Note that the security local time can be ahead of the data end time. if (asset.Exchange.DateTimeIsOpen(localOrderTime) && localOrderTime.Date == endTime.Date) { return(fill); } // wait until market open // make sure the exchange is open/normal market hours before filling if (!IsExchangeOpen(asset, false)) { return(fill); } // assume the order completely filled fill.FillQuantity = order.Quantity; fill.Status = OrderStatus.Filled; //Calculate the model slippage: e.g. 0.01c var slip = asset.SlippageModel.GetSlippageApproximation(asset, order); var bestEffortMessage = ""; // If there is no trade information, get the bid or ask, then apply the slippage switch (order.Direction) { case OrderDirection.Buy: if (fill.FillPrice == 0) { fill.FillPrice = GetBestEffortAskPrice(asset, order.Time, out bestEffortMessage); fill.Message += bestEffortMessage; } fill.FillPrice += slip; break; case OrderDirection.Sell: if (fill.FillPrice == 0) { fill.FillPrice = GetBestEffortBidPrice(asset, order.Time, out bestEffortMessage); fill.Message += bestEffortMessage; } fill.FillPrice -= slip; break; } return(fill); }
/// <summary> /// Order fill event handler. On an order fill update the resulting information is passed to this method. /// </summary> /// <param name="orderEvent">Order event details containing details of the evemts</param> /// <remarks>This method can be called asynchronously and so should only be used by seasoned C# experts. Ensure you use proper locks on thread-unsafe objects</remarks> public override void OnOrderEvent(OrderEvent orderEvent) { Log(orderEvent.ToString()); }
/// <summary> /// Market on Close Fill Model. Return an order event with the fill details /// </summary> /// <param name="asset">Asset we're trading with this order</param> /// <param name="order">Order to be filled</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> public virtual OrderEvent MarketOnCloseFill(Security asset, MarketOnCloseOrder order) { var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, OrderFee.Zero); if (order.Status == OrderStatus.Canceled) { return(fill); } var localOrderTime = order.Time.ConvertFromUtc(asset.Exchange.TimeZone); var nextMarketClose = asset.Exchange.Hours.GetNextMarketClose(localOrderTime, false); // wait until market closes after the order time if (asset.LocalTime < nextMarketClose) { return(fill); } var subscribedTypes = GetSubscribedTypes(asset); if (subscribedTypes.Contains(typeof(Tick))) { var primaryExchangeCode = ((Equity)asset).PrimaryExchange.Code; var officialClose = (uint)(TradeConditionFlags.Regular | TradeConditionFlags.OfficialClose); var closingPrints = (uint)(TradeConditionFlags.Regular | TradeConditionFlags.ClosingPrints); var trades = asset.Cache.GetAll <Tick>() .Where(x => x.TickType == TickType.Trade && x.Price > 0) .OrderBy(x => x.EndTime).ToList(); // Get the last valid (non-zero) tick of trade type from an close market var tick = trades .Where(x => !string.IsNullOrWhiteSpace(x.SaleCondition)) .LastOrDefault(x => x.ExchangeCode == primaryExchangeCode && (x.ParsedSaleCondition == officialClose || x.ParsedSaleCondition == closingPrints)); // If there is no OfficialClose or ClosingPrints in the current list of trades, // we will wait for the next up to 1 minute before accepting the last tick without flags // We will give priority to trade then use quote to get the timestamp // If there are only quotes, we will need to test for the tick type before we assign the fill price if (tick == null) { tick = trades.LastOrDefault() ?? asset.Cache.GetAll <Tick>().LastOrDefault(); if (Parameters.ConfigProvider.GetSubscriptionDataConfigs(asset.Symbol).IsExtendedMarketHours()) { fill.Message = "No trade with the OfficialClose or ClosingPrints flag within the 1-minute timeout."; if ((tick?.EndTime - nextMarketClose)?.TotalMinutes < 1) { return(fill); } } else { fill.Message = "No trade with the OfficialClose or ClosingPrints flag for data that does not include extended market hours."; } fill.Message += $" Fill with last {tick.TickType} data."; } if (tick?.TickType == TickType.Trade) { fill.FillPrice = tick.Price; } } // make sure the exchange is open/normal market hours before filling // It will return true if the last bar opens before the market closes else if (!IsExchangeOpen(asset, false)) { return(fill); } else if (subscribedTypes.Contains(typeof(TradeBar))) { fill.FillPrice = asset.Cache.GetData <TradeBar>()?.Close ?? 0; } else { fill.Message = $"Warning: No trade information available at {asset.LocalTime.ToStringInvariant()} {asset.Exchange.TimeZone}, order filled using Quote data"; } // Calculate the model slippage: e.g. 0.01c var slip = asset.SlippageModel.GetSlippageApproximation(asset, order); var bestEffortMessage = ""; // If there is no trade information, get the bid or ask, then apply the slippage switch (order.Direction) { case OrderDirection.Buy: if (fill.FillPrice == 0) { fill.FillPrice = GetBestEffortAskPrice(asset, order.Time, out bestEffortMessage); fill.Message += bestEffortMessage; } fill.FillPrice += slip; break; case OrderDirection.Sell: if (fill.FillPrice == 0) { fill.FillPrice = GetBestEffortBidPrice(asset, order.Time, out bestEffortMessage); fill.Message += bestEffortMessage; } fill.FillPrice -= slip; break; } // assume the order completely filled fill.FillQuantity = order.Quantity; fill.Status = OrderStatus.Filled; return(fill); }
public override void OnOrderEvent(OrderEvent orderEvent) { Log($"{Time}: {orderEvent}"); }
public void ComputeMarginProperlyAsSecurityPriceFluctuates() { const decimal leverage = 1m; const int quantity = (int)(1000 * leverage); var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var orderProcessor = new OrderProcessor(); transactions.SetOrderProcessor(orderProcessor); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.CashBook["USD"].SetAmount(quantity); var config = CreateTradeBarDataConfig(SecurityType.Equity, Symbols.AAPL); securities.Add(new Security(SecurityExchangeHours, config)); var security = securities[Symbols.AAPL]; security.SetLeverage(leverage); var time = DateTime.Now; const decimal buyPrice = 1m; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, buyPrice, buyPrice, buyPrice, buyPrice, 1)); var order = new MarketOrder(Symbols.AAPL, quantity, time) { Price = buyPrice }; var fill = new OrderEvent(order, DateTime.UtcNow, 0) { FillPrice = buyPrice, FillQuantity = quantity }; orderProcessor.AddOrder(order); var request = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, order.Quantity, 0, 0, order.Time, null); request.SetOrderId(0); orderProcessor.AddTicket(new OrderTicket(null, request)); Assert.AreEqual(portfolio.CashBook["USD"].Amount, fill.FillPrice * fill.FillQuantity); portfolio.ProcessFill(fill); Assert.AreEqual(0, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity, portfolio.TotalPortfolioValue); // we shouldn't be able to place a trader var newOrder = new MarketOrder(Symbols.AAPL, 1, time.AddSeconds(1)) { Price = buyPrice }; bool sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsFalse(sufficientCapital); // now the stock doubles, so we should have margin remaining time = time.AddDays(1); const decimal highPrice = buyPrice * 2; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, highPrice, highPrice, highPrice, highPrice, 1)); Assert.AreEqual(quantity, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity * 2, portfolio.TotalPortfolioValue); // we shouldn't be able to place a trader var anotherOrder = new MarketOrder(Symbols.AAPL, 1, time.AddSeconds(1)) { Price = highPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, anotherOrder); Assert.IsTrue(sufficientCapital); // now the stock plummets, so we should have negative margin remaining time = time.AddDays(1); const decimal lowPrice = buyPrice / 2; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, lowPrice, lowPrice, lowPrice, lowPrice, 1)); Assert.AreEqual(-quantity / 2m, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity / 2m, portfolio.TotalPortfolioValue); // this would not cause a margin call due to leverage = 1 bool issueMarginCallWarning; var marginCallOrders = portfolio.ScanForMarginCall(out issueMarginCallWarning); Assert.AreEqual(0, marginCallOrders.Count); // now change the leverage and buy more and we'll get a margin call security.SetLeverage(leverage * 2); order = new MarketOrder(Symbols.AAPL, quantity, time) { Price = buyPrice }; fill = new OrderEvent(order, DateTime.UtcNow, 0) { FillPrice = buyPrice, FillQuantity = quantity }; portfolio.ProcessFill(fill); Assert.AreEqual(0, portfolio.TotalPortfolioValue); marginCallOrders = portfolio.ScanForMarginCall(out issueMarginCallWarning); Assert.AreNotEqual(0, marginCallOrders.Count); Assert.AreEqual(-security.Holdings.Quantity, marginCallOrders[0].Quantity); // we bought twice Assert.GreaterOrEqual(-portfolio.MarginRemaining, security.Price * marginCallOrders[0].Quantity); }
/// <summary> /// Handle order event /// </summary> /// <param name="newEvent">Event to process</param> public override void OrderEvent(OrderEvent newEvent) { _capacityEstimate?.OnOrderEvent(newEvent); }
public override void OnOrderEvent(OrderEvent orderEvent) { Console.WriteLine($"{Time}: {orderEvent}"); }
/// <summary> /// Model the slippage on a market order: fixed percentage of order price /// </summary> /// <param name="security">Asset we're working with</param> /// <param name="order">Order to update</param> public virtual OrderEvent MarketFill(Security security, Order order) { var fill = new OrderEvent(order); try { //Calculate the model slippage: e.g. 0.01c decimal slip = GetSlippageApproximation(security, order); switch (order.Direction) { case OrderDirection.Buy: //Set the order and slippage on the order, update the fill price: order.Price = security.Price; order.Price += slip; break; case OrderDirection.Sell: //Set the order and slippage on the order, update the fill price: order.Price = security.Price; order.Price -= slip; break; } //Market orders fill instantly. order.Status = OrderStatus.Filled; //Assume 100% fill for market & modelled orders. fill.FillQuantity = order.Quantity; fill.FillPrice = order.Price; fill.Status = order.Status; } catch (Exception err) { Log.Error("ForexTransactionModel.TransOrderDirection.MarketFill(): " + err.Message); } return fill; }
public override void OnOrderEvent(OrderEvent orderEvent) { Debug($"OnOrderEvent(OrderEvent): {Time}: {orderEvent}"); }
public override void OnOrderEvent(OrderEvent orderEvent) { string symbol = orderEvent.Symbol.Value; Strategy[symbol].Status = orderEvent.Status; switch (orderEvent.Status) { case OrderStatus.New: case OrderStatus.None: case OrderStatus.Submitted: Strategy[orderEvent.Symbol.Value].orderFilled = false; break; case OrderStatus.Invalid: Strategy[symbol].TradeAttempts = 0; Strategy[symbol].orderFilled = false; break; case OrderStatus.PartiallyFilled: if (Strategy[symbol] != null) { Strategy[symbol].orderFilled = true; // Do not unlock the strategy Strategy[symbol].nEntryPrice= Portfolio[orderEvent.Symbol].HoldStock ? Portfolio[orderEvent.Symbol].AveragePrice : 0; Strategy[symbol].orderFilled = true; Strategy[symbol].TradeAttempts++; //Log(string.Format("Trade Attempts: {0} OrderId {1}", currentSignalInfo.TradeAttempts, orderEvent.OrderId)); } break; case OrderStatus.Canceled: if (Strategy[symbol] != null) { //Log(string.Format("Order {0} cancelled.", orderEvent.OrderId)); Strategy[symbol].IsActive = true; // Unlock the strategy for the next bar Strategy[symbol].TradeAttempts = 0; // Reset the number of trade attempts. Strategy[symbol].orderFilled = false; } break; case OrderStatus.Filled: if (Strategy[symbol] != null) { //Log(string.Format("Order Filled OrderId {0} on attempt {1}", orderEvent.OrderId, currentSignalInfo.TradeAttempts)); Strategy[symbol].IsActive = true; Strategy[symbol].TradeAttempts = 0; Strategy[symbol].nEntryPrice = Portfolio[orderEvent.Symbol].HoldStock ? Portfolio[orderEvent.Symbol].AveragePrice : 0; Strategy[symbol].orderFilled = true; } #region "Report Transaction" #endregion break; } }
/// <summary> /// Order fill event handler. On an order fill update the resulting information is passed to this method. /// </summary> /// <param name="orderEvent">Order event details containing details of the evemts</param> /// <remarks>This method can be called asynchronously and so should only be used by seasoned C# experts. Ensure you use proper locks on thread-unsafe objects</remarks> public virtual void OnOrderEvent(OrderEvent orderEvent) { }
/// <summary> /// Default stop limit fill model implementation in base class security. (Stop Limit Order Type) /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> /// <seealso cref="StopMarketFill(Security, StopMarketOrder)"/> /// <seealso cref="SecurityTransactionModel.LimitFill"/> /// <remarks> /// There is no good way to model limit orders with OHLC because we never know whether the market has /// gapped past our fill price. We have to make the assumption of a fluid, high volume market. /// /// Stop limit orders we also can't be sure of the order of the H - L values for the limit fill. The assumption /// was made the limit fill will be done with closing price of the bar after the stop has been triggered.. /// </remarks> public virtual OrderEvent StopLimitFill(Security asset, StopLimitOrder order) { //Default order event to return. var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, 0); //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) return fill; //Get the range of prices in the last bar: decimal minimumPrice; decimal maximumPrice; DataMinMaxPrices(asset, out minimumPrice, out maximumPrice, order.Direction); //Check if the Stop Order was filled: opposite to a limit order switch (order.Direction) { case OrderDirection.Buy: //-> 1.2 Buy Stop: If Price Above Setpoint, Buy: if (maximumPrice > order.StopPrice || order.StopTriggered) { order.StopTriggered = true; // Fill the limit order, using closing price of bar: // Note > Can't use minimum price, because no way to be sure minimum wasn't before the stop triggered. if (asset.Price < order.LimitPrice) { fill.Status = OrderStatus.Filled; fill.FillPrice = order.LimitPrice; } } break; case OrderDirection.Sell: //-> 1.1 Sell Stop: If Price below setpoint, Sell: if (minimumPrice < order.StopPrice || order.StopTriggered) { order.StopTriggered = true; // Fill the limit order, using minimum price of the bar // Note > Can't use minimum price, because no way to be sure minimum wasn't before the stop triggered. if (asset.Price > order.LimitPrice) { fill.Status = OrderStatus.Filled; fill.FillPrice = order.LimitPrice; // Fill at limit price not asset price. } } break; } // assume the order completely filled if (fill.Status == OrderStatus.Filled) { fill.FillQuantity = order.Quantity; fill.OrderFee = asset.FeeModel.GetOrderFee(asset, order); } return fill; }
/// <summary> /// Check if the model has stopped out our position yet: /// </summary> /// <param name="security">Asset we're working with</param> /// <param name="order">Stop Order to Check, return filled if true</param> public virtual OrderEvent StopFill(Security security, Order order) { var fill = new OrderEvent(order); try { //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) return fill; //Calculate the model slippage: e.g. 0.01c decimal slip = GetSlippageApproximation(security, order); //Check if the Stop Order was filled: opposite to a limit order switch (order.Direction) { case OrderDirection.Sell: //-> 1.1 Sell Stop: If Price below setpoint, Sell: if (security.Price < order.Price) { order.Status = OrderStatus.Filled; order.Price = Math.Round(security.Price, 3); order.Price -= slip; } break; case OrderDirection.Buy: //-> 1.2 Buy Stop: If Price Above Setpoint, Buy: if (security.Price > order.Price) { order.Status = OrderStatus.Filled; order.Price = Math.Round(security.Price, 3); order.Price += slip; } break; } if (order.Status == OrderStatus.Filled || order.Status == OrderStatus.PartiallyFilled) { fill.FillQuantity = order.Quantity; fill.FillPrice = order.Price; fill.Status = order.Status; } } catch (Exception err) { Log.Error("Equity.TransOrderDirection.StopFill(): " + err.Message); } return fill; }
/// <summary> /// Market on Open Fill Model. Return an order event with the fill details /// </summary> /// <param name="asset">Asset we're trading with this order</param> /// <param name="order">Order to be filled</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> public OrderEvent MarketOnOpenFill(Security asset, MarketOnOpenOrder order) { var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, 0); if (order.Status == OrderStatus.Canceled) return fill; // MOO should never fill on the same bar or on stale data // Imagine the case where we have a thinly traded equity, ASUR, and another liquid // equity, say SPY, SPY gets data every minute but ASUR, if not on fill forward, maybe // have large gaps, in which case the currentBar.EndTime will be in the past // ASUR | | | [order] | | | | | | | // SPY | | | | | | | | | | | | | | | | | | | | var currentBar = asset.GetLastData(); var localOrderTime = order.Time.ConvertFromUtc(asset.Exchange.TimeZone); if (currentBar == null || localOrderTime >= currentBar.EndTime) return fill; // if the MOO was submitted during market the previous day, wait for a day to turn over if (asset.Exchange.DateTimeIsOpen(localOrderTime) && localOrderTime.Date == asset.LocalTime.Date) { return fill; } // wait until market open // make sure the exchange is open before filling if (!IsExchangeOpen(asset)) return fill; fill.FillPrice = asset.Open; fill.Status = OrderStatus.Filled; //Calculate the model slippage: e.g. 0.01c var slip = asset.SlippageModel.GetSlippageApproximation(asset, order); //Apply slippage switch (order.Direction) { case OrderDirection.Buy: fill.FillPrice += slip; break; case OrderDirection.Sell: fill.FillPrice -= slip; break; } // assume the order completely filled if (fill.Status == OrderStatus.Filled) { fill.FillQuantity = order.Quantity; fill.OrderFee = asset.FeeModel.GetOrderFee(asset, order); } return fill; }
/// <summary> /// Check if the price MarketDataed to our limit price yet: /// </summary> /// <param name="security">Asset we're working with</param> /// <param name="order">Limit order in market</param> public virtual OrderEvent LimitFill(Security security, Order order) { //Initialise; decimal marketDataMinPrice = 0; decimal marketDataMaxPrice = 0; var fill = new OrderEvent(order); try { //If its cancelled don't need anymore checks: if (fill.Status == OrderStatus.Canceled) return fill; //Depending on the resolution, return different data types: BaseData marketData = security.GetLastData(); if (marketData.DataType == MarketDataType.TradeBar) { marketDataMinPrice = ((TradeBar)marketData).Low; marketDataMaxPrice = ((TradeBar)marketData).High; } else { marketDataMinPrice = marketData.Value; marketDataMaxPrice = marketData.Value; } //-> Valid Live/Model Order: switch (order.Direction) { case OrderDirection.Buy: //Buy limit seeks lowest price if (marketDataMinPrice < order.Price) { //Set order fill: order.Status = OrderStatus.Filled; order.Price = security.Price; } break; case OrderDirection.Sell: //Sell limit seeks highest price possible if (marketDataMaxPrice > order.Price) { order.Status = OrderStatus.Filled; order.Price = security.Price; } break; } if (order.Status == OrderStatus.Filled || order.Status == OrderStatus.PartiallyFilled) { fill.FillQuantity = order.Quantity; fill.FillPrice = security.Price; fill.Status = order.Status; } } catch (Exception err) { Log.Error("SecurityTransactionModel.TransOrderDirection.LimitFill(): " + err.Message); } return fill; }
/// <summary> /// Scans all the outstanding orders and applies the algorithm model fills to generate the order events /// </summary> public 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) { var order = kvp.Value; 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 fill = new OrderEvent(order, Algorithm.UtcTime, 0); 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, 0m){Status = OrderStatus.Invalid}); _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 bool sufficientBuyingPower; try { sufficientBuyingPower = Algorithm.Transactions.GetSufficientCapitalForOrder(Algorithm.Portfolio, order); } catch (Exception err) { // if we threw an error just mark it as invalid and remove the order from our pending list Order pending; _pending.TryRemove(order.Id, out pending); order.Status = OrderStatus.Invalid; OnOrderEvent(new OrderEvent(order, Algorithm.UtcTime, 0, "Error in GetSufficientCapitalForOrder")); Log.Error(err); Algorithm.Error(string.Format("Order Error: id: {0}, Error executing margin models: {1}", order.Id, err.Message)); continue; } //Before we check this queued order make sure we have buying power: if (sufficientBuyingPower) { //Model: var model = security.FillModel; //Based on the order type: refresh its model to get fill price and quantity try { switch (order.Type) { case OrderType.Limit: fill = model.LimitFill(security, order as LimitOrder); break; case OrderType.StopMarket: fill = model.StopMarketFill(security, order as StopMarketOrder); break; case OrderType.Market: fill = model.MarketFill(security, order as MarketOrder); break; case OrderType.StopLimit: fill = model.StopLimitFill(security, order as StopLimitOrder); break; case OrderType.MarketOnOpen: fill = model.MarketOnOpenFill(security, order as MarketOnOpenOrder); break; case OrderType.MarketOnClose: fill = model.MarketOnCloseFill(security, order as MarketOnCloseOrder); break; } } catch (Exception err) { Log.Error(err); Algorithm.Error(string.Format("Order Error: id: {0}, Transaction model failed to fill for order type: {1} with error: {2}", order.Id, order.Type, err.Message)); } } else { //Flag order as invalid and push off queue: order.Status = OrderStatus.Invalid; Algorithm.Error(string.Format("Order Error: id: {0}, Insufficient buying power to complete order (Value:{1}).", order.Id, order.GetValue(security).SmartRounding())); } // 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 (fill.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 virtual void OnOrderEvent(OrderEvent newEvent) { //Algo.OnOrderEvent(newEvent); }
/// <summary> /// Model the slippage on a market order: fixed percentage of order price /// </summary> /// <param name="security">Asset we're working with</param> /// <param name="order">Order to update</param> public virtual OrderEvent MarketFill(Security security, Order order) { var fill = new OrderEvent(order); try { //Calculate the model slippage: e.g. 0.01c decimal slip = GetSlippageApproximation(security, order); switch (order.Direction) { case OrderDirection.Buy: order.Price = security.Price; order.Price += slip; break; case OrderDirection.Sell: order.Price = security.Price; order.Price -= slip; break; } //Market orders fill instantly. order.Status = OrderStatus.Filled; order.Price = Math.Round(order.Price, 3); //Fill Order: fill.Status = order.Status; fill.FillQuantity = order.Quantity; fill.FillPrice = order.Price; } catch (Exception err) { Log.Error("Equity.TransOrderDirection.MarketFill(): " + err.Message); } return fill; }
/// <summary> /// Send a new order event to the browser. /// </summary> /// <remarks>In backtesting the order events are not sent because it would generate a high load of messaging.</remarks> /// <param name="newEvent">New order event details</param> public void OrderEvent(OrderEvent newEvent) { // NOP. Don't do any order event processing for results in backtest mode. }
/// <summary> /// /// </summary> /// <param name="orderEvent"></param> public override void OnOrderEvent(OrderEvent orderEvent) { decimal fees = 0m; decimal tradeprofit = 0m; decimal profit = 0m; base.OnOrderEvent(orderEvent); if (orderEvent.Status == OrderStatus.Filled) { var fillprice = orderEvent.FillPrice; var fillquantity = orderEvent.FillQuantity; var amount = orderEvent.FillPrice * orderEvent.FillQuantity; var direction = orderEvent.Direction; var orderid = Portfolio.Transactions.LastOrderId; var fpbuy = ""; var fpsell = ""; if (direction == OrderDirection.Buy) fpbuy = fillprice.ToString(CultureInfo.InvariantCulture); else fpsell = fillprice.ToString(CultureInfo.InvariantCulture); foreach (SecurityHolding holding in Portfolio.Values) { fees = holding.TotalFees - _totalFees; tradeprofit = holding.LastTradeProfit; profit = holding.Profit; _totalFees = holding.TotalFees; holdingcost = holding.HoldingsCost; } var order = Transactions.GetOrderById(orderEvent.OrderId); var dt = order.Time; var quantity = Portfolio[_symbol].Quantity; if (direction.ToString() == "Buy") { amount += fees; } else { amount += fees; } //string transmsg = string.Format( // "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15}", // _symbol, // fillquantity, // fillprice, // direction.ToString(), // dt, // dt.AddDays(4), // 0, // amount, // fees, // 0, // "60505104", // actionNameUs + " share of " + _symbol + "at $" + fillprice.ToString(), // actionid, // orderEvent.OrderId, // "Trade", // "taxlot" // ); //mylog.Debug(transmsg); if (logtrade) { mylog.Debug(tradeheader); string logmsg = string.Format( "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20}", dt, barcount, "", "", "", "", "", "", "", "", "", direction, tradeprofit, fpbuy, fpsell, profit, holdingcost, fillquantity, fees, profit - fees, quantity ); mylog.Debug(logmsg); } } }
/// <summary> /// Send a new order event to the browser. /// </summary> /// <remarks>In backtesting the order events are not sent because it would generate a high load of messaging.</remarks> /// <param name="newEvent">New order event details</param> public void OrderEvent(OrderEvent newEvent) { DebugMessage("DesktopResultHandler.OrderEvent(): id:" + newEvent.OrderId + " >> Status:" + newEvent.Status + " >> Fill Price: " + newEvent.FillPrice.ToString("C") + " >> Fill Quantity: " + newEvent.FillQuantity); }
/// <summary> /// Default market order model. Fill at last price /// </summary> /// <param name="security">Asset we're working with</param> /// <param name="order">Order to update</param> public virtual OrderEvent MarketFill(Security security, Order order) { //Default order event to return. var fill = new OrderEvent(order); try { //Set the order price order.Price = security.Price; order.Status = OrderStatus.Filled; //Set the order event fill: - Assuming 100% fill fill.FillPrice = security.Price; fill.FillQuantity = order.Quantity; fill.Status = order.Status; } catch (Exception err) { Log.Error("SecurityTransactionModel.TransOrderDirection.MarketFill(): " + err.Message); } return fill; }
/// <summary> /// New order event for the algorithm /// </summary> /// <param name="newEvent">New event details</param> public virtual void OrderEvent(OrderEvent newEvent) { }
private void HandleOrderEvent(OrderEvent fill) { // update the order status var order = GetOrderByIdInternal(fill.OrderId); if (order == null) { Log.Error("BrokerageTransactionHandler.HandleOrderEvent(): Unable to locate Order with id " + fill.OrderId); return; } // set the status of our order object based on the fill event order.Status = fill.Status; // save that the order event took place, we're initializing the list with a capacity of 2 to reduce number of mallocs //these hog memory //List<OrderEvent> orderEvents = _orderEvents.GetOrAdd(orderEvent.OrderId, i => new List<OrderEvent>(2)); //orderEvents.Add(orderEvent); //Apply the filled order to our portfolio: if (fill.Status == OrderStatus.Filled || fill.Status == OrderStatus.PartiallyFilled) { Interlocked.Exchange(ref _lastFillTimeTicks, DateTime.UtcNow.Ticks); // check if the fill currency and the order currency match the symbol currency var security = _algorithm.Securities[fill.Symbol]; // Bug in FXCM API flipping the currencies -- disabling for now. 5/17/16 RFB //if (fill.FillPriceCurrency != security.SymbolProperties.QuoteCurrency) //{ // Log.Error(string.Format("Currency mismatch: Fill currency: {0}, Symbol currency: {1}", fill.FillPriceCurrency, security.SymbolProperties.QuoteCurrency)); //} //if (order.PriceCurrency != security.SymbolProperties.QuoteCurrency) //{ // Log.Error(string.Format("Currency mismatch: Order currency: {0}, Symbol currency: {1}", order.PriceCurrency, security.SymbolProperties.QuoteCurrency)); //} try { _algorithm.Portfolio.ProcessFill(fill); var conversionRate = security.QuoteCurrency.ConversionRate; var multiplier = security.SymbolProperties.ContractMultiplier; _algorithm.TradeBuilder.ProcessFill(fill, conversionRate, multiplier); } catch (Exception err) { Log.Error(err); _algorithm.Error(string.Format("Order Error: id: {0}, Error in Portfolio.ProcessFill: {1}", order.Id, err.Message)); } } // update the ticket and order after we've processed the fill, but before the event, this way everything is ready for user code OrderTicket ticket; if (_orderTickets.TryGetValue(fill.OrderId, out ticket)) { ticket.AddOrderEvent(fill); order.Price = ticket.AverageFillPrice; } else { Log.Error("BrokerageTransactionHandler.HandleOrderEvent(): Unable to resolve ticket: " + fill.OrderId); } //We have an event! :) Order filled, send it in to be handled by algorithm portfolio. if (fill.Status != OrderStatus.None) //order.Status != OrderStatus.Submitted { //Create new order event: _resultHandler.OrderEvent(fill); try { //Trigger our order event handler _algorithm.OnOrderEvent(fill); } catch (Exception err) { _algorithm.Error("Order Event Handler Error: " + err.Message); // kill the algorithm _algorithm.RunTimeError = err; } } }
public override void OnOrderEvent(OrderEvent orderEvent) { }