/******************************************************** * 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> /// <returns>OrderEvent packet with the full or partial fill information</returns> /// <seealso cref="OrderEvent"/> /// <seealso cref="Order"/> public virtual OrderEvent Fill(Security vehicle, Order order) { 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("Forex.ForexTransactionModel.Fill(): " + err.Message); } return fill; }
/******************************************************** * CLASS PROPERTIES *********************************************************/ /******************************************************** * CLASS METHODS *********************************************************/ /// <summary> /// Process a order fill with the supplied security and order. /// </summary> /// <param name="vehicle">Asset we're working with</param> /// <param name="order">Order class to check if filled.</param> /// <returns>OrderEvent packet with the full or partial fill information</returns> public virtual OrderEvent Fill(Security vehicle, Order order) { var fill = new OrderEvent(order); try { //Based on the order type, select the fill model method. 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("Equity.EquityTransactionModel.Fill(): " + err.Message); } return fill; }
public override void OnOrderEvent(OrderEvent fill) { SymbolData sd; if (_sd.TryGetValue(fill.Symbol, out sd)) { sd.OnOrderEvent(fill); } }
public override void OnOrderEvent(OrderEvent orderEvent) { if (orderEvent.Status == OrderStatus.Submitted) { Console.WriteLine(Time + ": Submitted: " + Transactions.GetOrderById(orderEvent.OrderId)); } if (orderEvent.Status.IsFill()) { Console.WriteLine(Time + ": Filled: " + Transactions.GetOrderById(orderEvent.OrderId)); } }
/// <summary> /// Logs the OrderEvent Transaction /// </summary> /// <param name="orderEvent">the OrderEvent being logged</param> /// <param name="includeHeader">Includes the field names</param> public OrderTransaction Create(OrderEvent orderEvent, OrderTicket ticket, bool includeHeader = true) { var security = _algorithm.Securities[ticket.Symbol]; Order order = _algorithm.Transactions.GetOrderById(orderEvent.OrderId); OrderTransaction t = new OrderTransaction(); // According to Scottrade a Buy is a negative amount (funds flow from my account to the seller's) // However the Quantity filled is a negative number for Sell/Short and a positive for Buy/Long // So multiply by -1 to give order value the correct sign decimal orderValue = -1 * ticket.QuantityFilled * ticket.AverageFillPrice; if (order != null) { var orderDateTime = _algorithm.Time; DateTime settleDate = orderDateTime.AddDays(orderDateTime.DayOfWeek < DayOfWeek.Wednesday ? 3 : 5); // Order Fees are a cost and negative to my account, therefore a negative number var orderFees = security.TransactionModel.GetOrderFee(security, order) * -1; #region "Create OrderTransaction" t.ActionId = orderEvent.Direction.ToString() == "Buy" ? 1 : 13; t.ActionNameUS = orderEvent.Direction.ToString(); t.Amount = orderValue; t.Broker = "IB"; t.CUSIP = "CUSIP"; t.Commission = orderFees; t.Description = string.Format("{0} {1} shares of {2} at ${3}", orderEvent.Direction, ticket.Quantity, orderEvent.Symbol, order.Price); t.Direction = orderEvent.Direction; t.Exchange = ""; t.Fees = 0; // need to calculate based upon difference in Portfolio[symbol].HoldingsValue between buy and sell t.Id = 0; t.Interest = 0; t.Net = orderValue + orderFees; t.OrderId = order.Id; t.OrderType = ticket.OrderType; t.Price = ticket.AverageFillPrice; t.Quantity = ticket.Quantity; t.RecordType = "Trade"; t.SettledDate = settleDate; t.Symbol = ticket.Symbol.Value; t.TaxLotNumber = String.Empty; t.TradeDate = orderDateTime; t.TradeNumber = 0; #endregion } return t; }
public void LastTradeProfit_FlatToShort() { var reference = new DateTime(2016, 02, 16, 11, 53, 30); SecurityPortfolioManager portfolio; var security = InitializeTest(reference, out portfolio); var fillPrice = 100m; var fillQuantity = -100; var orderFee = 1m; var orderDirection = fillQuantity > 0 ? OrderDirection.Buy : OrderDirection.Sell; var fill = new OrderEvent(1, security.Symbol, reference, OrderStatus.Filled, orderDirection, fillPrice, fillQuantity, orderFee); portfolio.ProcessFill(fill); // zero since we're from flat Assert.AreEqual(0, security.Holdings.LastTradeProfit); }
/// <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="StopMarketFill(Security, StopMarketOrder)"/> /// <seealso cref="LimitFill(Security, LimitOrder)"/> public virtual OrderEvent MarketFill(Security asset, MarketOrder order) { //Default order event to return. var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var orderFee = GetOrderFee(asset, order); var fill = new OrderEvent(order, utcTime, orderFee); if (order.Status == OrderStatus.Canceled) return fill; // make sure the exchange is open before filling if (!IsExchangeOpen(asset)) return fill; try { //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 = 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; } catch (Exception err) { Log.Error("SecurityTransactionModel.MarketFill(): " + err.Message); } return fill; }
/// <summary> /// Logs the OrderEvent Transaction /// </summary> /// <param name="orderEvent">the OrderEvent being logged</param> public string ReportTransaction(OrderEvent orderEvent) { #region Print string transmsg = string.Format("Order {0} on not found", orderEvent.OrderId); Order order = _algorithm.Transactions.GetOrderById(orderEvent.OrderId); decimal orderValue = orderEvent.FillQuantity*orderEvent.FillPrice; //if (order != null) //{ //var orderDateTime = order.Time; decimal orderFees = 0; orderFees = _algorithm.Securities[order.Symbol].TransactionModel.GetOrderFee(_algorithm.Securities[order.Symbol], order); int actionid = orderEvent.Direction.ToString() == "Buy" ? 1 : 13; transmsg = string.Format( "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15}", orderEvent.Symbol, orderEvent.FillQuantity, orderEvent.FillPrice, orderEvent.Direction.ToString(), order.Time, order.Time.AddDays(4), orderValue, orderFees, orderValue + orderFees, "", orderEvent.Direction + " share of " + orderEvent.Symbol + "at $" + orderEvent.FillPrice.ToString(), actionid, order.Id, "Trade", "taxlot", "" ); // } _logHandler.Debug(transmsg); return transmsg; #endregion }
/// <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. }
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) { Log.Debug("BrokerageTransactionHandler.HandleOrderEvent(): " + fill); Interlocked.Exchange(ref _lastFillTimeTicks, DateTime.Now.Ticks); // check if the fill currency and the order currency match the symbol currency var security = _algorithm.Securities[fill.Symbol]; 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; _algorithm.TradeBuilder.ProcessFill(fill, conversionRate); } 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; } } }
/// <summary> /// Handle order events /// </summary> /// <param name="orderEvent">the order event</param> public override void OnOrderEvent(OrderEvent orderEvent) { base.OnOrderEvent(orderEvent); ProcessOrderEvent(orderEvent); }
/// <summary> /// Handles error messages from IB /// </summary> private void HandleError(object sender, IB.ErrorEventArgs e) { // https://www.interactivebrokers.com/en/software/api/apiguide/tables/api_message_codes.htm // rewrite these messages to be single lined e.ErrorMsg = e.ErrorMsg.Replace("\r\n", ". ").Replace("\r", ". ").Replace("\n", ". "); Log.Trace(string.Format("InteractiveBrokersBrokerage.HandleError(): Order: {0} ErrorCode: {1} - {2}", e.TickerId, e.ErrorCode, e.ErrorMsg)); // figure out the message type based on our code collections below var brokerageMessageType = BrokerageMessageType.Information; if (ErrorCodes.Contains((int) e.ErrorCode)) { brokerageMessageType = BrokerageMessageType.Error; } else if (WarningCodes.Contains((int) e.ErrorCode)) { brokerageMessageType = BrokerageMessageType.Warning; } // code 1100 is a connection failure, we'll wait a minute before exploding gracefully if ((int) e.ErrorCode == 1100 && !_disconnected1100Fired) { _disconnected1100Fired = true; // begin the try wait logic TryWaitForReconnect(); } else if ((int) e.ErrorCode == 1102) { // we've reconnected _disconnected1100Fired = false; OnMessage(BrokerageMessageEvent.Reconnected(e.ErrorMsg)); } if (InvalidatingCodes.Contains((int)e.ErrorCode)) { Log.Trace(string.Format("InteractiveBrokersBrokerage.HandleError.InvalidateOrder(): Order: {0} ErrorCode: {1} - {2}", e.TickerId, e.ErrorCode, e.ErrorMsg)); // invalidate the order var order = _orderProvider.GetOrderByBrokerageId(e.TickerId); const int orderFee = 0; var orderEvent = new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = OrderStatus.Invalid }; OnOrderEvent(orderEvent); } OnMessage(new BrokerageMessageEvent(brokerageMessageType, (int) e.ErrorCode, e.ErrorMsg)); }
public void OrderEvent(OrderEvent newEvent) { }
/// <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; } 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.TransactionModel; //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.Price).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; } }
/// <summary> /// Updates the order with the same ID /// </summary> /// <param name="order">The new order information</param> /// <returns>True if the request was made for the order to be updated, false otherwise</returns> public override bool UpdateOrder(Order order) { if (true) { Order pending; if (!_pending.TryGetValue(order.Id, out pending)) { // can't update something that isn't there return false; } lock (_needsScanLock) { _needsScan = true; SetPendingOrder(order); } var orderId = order.Id.ToString(); if (!order.BrokerId.Contains(orderId)) order.BrokerId.Add(orderId); // fire off the event that says this order has been updated const int orderFee = 0; var updated = new OrderEvent(order, Algorithm.UtcTime, orderFee) { Status = OrderStatus.Submitted }; OnOrderEvent(updated); return true; } }
/// <summary> /// Places a new order and assigns a new broker ID to the order /// </summary> /// <param name="order">The order to be placed</param> /// <returns>True if the request for a new order has been placed, false otherwise</returns> public override bool PlaceOrder(Order order) { if (order.Status == OrderStatus.New) { lock (_needsScanLock) { _needsScan = true; SetPendingOrder(order); } var orderId = order.Id.ToString(); if (!order.BrokerId.Contains(orderId)) order.BrokerId.Add(orderId); // fire off the event that says this order has been submitted const int orderFee = 0; var submitted = new OrderEvent(order, Algorithm.UtcTime, orderFee) { Status = OrderStatus.Submitted }; OnOrderEvent(submitted); return true; } return false; }
public void SellingShortFromShortAddsToCash() { var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.SetCash(0); securities.Add(Symbols.AAPL, new Security(SecurityExchangeHours, CreateTradeBarDataConfig(SecurityType.Equity, Symbols.AAPL), new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency))); securities[Symbols.AAPL].Holdings.SetHoldings(100, -100); var fill = new OrderEvent(1, Symbols.AAPL, DateTime.MinValue, OrderStatus.Filled, OrderDirection.Sell, 100, -100, 0); Assert.AreEqual(-100, securities[Symbols.AAPL].Holdings.Quantity); portfolio.ProcessFill(fill); Assert.AreEqual(100 * 100, portfolio.Cash); Assert.AreEqual(-200, securities[Symbols.AAPL].Holdings.Quantity); }
/// <summary> /// Cancels the order with the specified ID /// </summary> /// <param name="order">The order to cancel</param> /// <returns>True if the request was made for the order to be canceled, false otherwise</returns> public override bool CancelOrder(Order order) { Order pending; if (!_pending.TryRemove(order.Id, out pending)) { // can't cancel something that isn't there return false; } var orderId = order.Id.ToString(); if (!order.BrokerId.Contains(orderId)) order.BrokerId.Add(order.Id.ToString()); // fire off the event that says this order has been canceled const int orderFee = 0; var canceled = new OrderEvent(order, Algorithm.UtcTime, orderFee) { Status = OrderStatus.Canceled }; OnOrderEvent(canceled); return true; }
public void ForexFillUpdatesCashCorrectly() { var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.SetCash(1000); portfolio.CashBook.Add("EUR", 0, 1.1000m); securities.Add(Symbols.EURUSD, new QuantConnect.Securities.Forex.Forex(SecurityExchangeHours, portfolio.CashBook["USD"], CreateTradeBarDataConfig(SecurityType.Forex, Symbols.EURUSD), SymbolProperties.GetDefault(CashBook.AccountCurrency))); var security = securities[Symbols.EURUSD]; Assert.AreEqual(0, security.Holdings.Quantity); Assert.AreEqual(1000, portfolio.Cash); var orderFee = security.FeeModel.GetOrderFee(security, new MarketOrder(Symbols.EURUSD, 100, DateTime.MinValue)); var fill = new OrderEvent(1, Symbols.EURUSD, DateTime.MinValue, OrderStatus.Filled, OrderDirection.Buy, 1.1000m, 100, orderFee); portfolio.ProcessFill(fill); Assert.AreEqual(100, security.Holdings.Quantity); Assert.AreEqual(998, portfolio.Cash); Assert.AreEqual(100, portfolio.CashBook["EUR"].Amount); Assert.AreEqual(888, portfolio.CashBook["USD"].Amount); }
/// <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) { Log.Trace("ConsoleResultHandler.OrderEvent(): id:" + newEvent.OrderId + " >> Status:" + newEvent.Status + " >> Fill Price: " + newEvent.FillPrice.ToString("C") + " >> Fill Quantity: " + newEvent.FillQuantity); }
public void EquitySellAppliesSettlementCorrectly() { var securityExchangeHours = SecurityExchangeHoursTests.CreateUsEquitySecurityExchangeHours(); var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.SetCash(1000); securities.Add(Symbols.AAPL, new QuantConnect.Securities.Equity.Equity(securityExchangeHours, CreateTradeBarDataConfig(SecurityType.Equity, Symbols.AAPL), new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency))); var security = securities[Symbols.AAPL]; security.SettlementModel = new DelayedSettlementModel(3, TimeSpan.FromHours(8)); Assert.AreEqual(0, security.Holdings.Quantity); Assert.AreEqual(1000, portfolio.Cash); Assert.AreEqual(0, portfolio.UnsettledCash); // Buy on Monday var timeUtc = new DateTime(2015, 10, 26, 15, 30, 0); var orderFee = security.FeeModel.GetOrderFee(security,new MarketOrder(Symbols.AAPL, 10, timeUtc)); var fill = new OrderEvent(1, Symbols.AAPL, timeUtc, OrderStatus.Filled, OrderDirection.Buy, 100, 10, orderFee); portfolio.ProcessFill(fill); Assert.AreEqual(10, security.Holdings.Quantity); Assert.AreEqual(-1, portfolio.Cash); Assert.AreEqual(0, portfolio.UnsettledCash); // Sell on Tuesday, cash unsettled timeUtc = timeUtc.AddDays(1); orderFee = security.FeeModel.GetOrderFee(security, new MarketOrder(Symbols.AAPL, 10, timeUtc)); fill = new OrderEvent(2, Symbols.AAPL, timeUtc, OrderStatus.Filled, OrderDirection.Sell, 100, -10, orderFee); portfolio.ProcessFill(fill); Assert.AreEqual(0, security.Holdings.Quantity); Assert.AreEqual(-2, portfolio.Cash); Assert.AreEqual(1000, portfolio.UnsettledCash); // Thursday, still cash unsettled timeUtc = timeUtc.AddDays(2); portfolio.ScanForCashSettlement(timeUtc); Assert.AreEqual(-2, portfolio.Cash); Assert.AreEqual(1000, portfolio.UnsettledCash); // Friday at open, cash settled var marketOpen = securityExchangeHours.MarketHours[timeUtc.DayOfWeek].GetMarketOpen(TimeSpan.Zero, false); Assert.IsTrue(marketOpen.HasValue); timeUtc = timeUtc.AddDays(1).Date.Add(marketOpen.Value).ConvertToUtc(securityExchangeHours.TimeZone); portfolio.ScanForCashSettlement(timeUtc); Assert.AreEqual(998, portfolio.Cash); Assert.AreEqual(0, portfolio.UnsettledCash); }
public override void OnOrderEvent(OrderEvent orderEvent) { string symbol = orderEvent.Symbol; int portfolioPosition = Portfolio[symbol].Quantity; var actualTicket = Transactions.GetOrderTickets(t => t.OrderId == orderEvent.OrderId).Single(); var actualOrder = Transactions.GetOrderById(orderEvent.OrderId); switch (orderEvent.Status) { case OrderStatus.Submitted: Strategy[symbol].Position = StockState.orderSent; Log("New order submitted: " + actualOrder.ToString()); break; case OrderStatus.PartiallyFilled: Log("Order partially filled: " + actualOrder.ToString()); Log("Canceling order"); actualTicket.Cancel(); //do { } //while (actualTicket.GetMostRecentOrderResponse().IsSuccess); goto case OrderStatus.Filled; case OrderStatus.Filled: if (portfolioPosition > 0) Strategy[symbol].Position = StockState.longPosition; else if (portfolioPosition < 0) Strategy[symbol].Position = StockState.shortPosition; else Strategy[symbol].Position = StockState.noInvested; Strategy[symbol].EntryPrice = actualTicket.AverageFillPrice; Log("Order filled: " + actualOrder.ToString()); break; case OrderStatus.Canceled: Log("Order successfully canceled: " + actualOrder.ToString()); break; default: break; } }
public void ComputeMarginProperlyShortCoverZeroLong() { const decimal leverage = 2m; const int amount = 1000; const int quantity = (int)(amount * 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(amount); var config = CreateTradeBarDataConfig(SecurityType.Equity, Symbols.AAPL); securities.Add(new Security(SecurityExchangeHours, config, new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency))); var security = securities[Symbols.AAPL]; security.SetLeverage(leverage); var time = DateTime.Now; const decimal sellPrice = 1m; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, sellPrice, sellPrice, sellPrice, sellPrice, 1)); var order = new MarketOrder(Symbols.AAPL, -quantity, time) { Price = sellPrice }; var fill = new OrderEvent(order, DateTime.UtcNow, 0) { FillPrice = sellPrice, 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)); portfolio.ProcessFill(fill); // we shouldn't be able to place a new short order var newOrder = new MarketOrder(Symbols.AAPL, -1, time.AddSeconds(1)) { Price = sellPrice }; var sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsFalse(sufficientCapital); // we should be able to place cover to zero newOrder = new MarketOrder(Symbols.AAPL, quantity, time.AddSeconds(1)) { Price = sellPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsTrue(sufficientCapital); // now the stock doubles, so we should have negative margin remaining time = time.AddDays(1); const decimal highPrice = sellPrice * 2; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, highPrice, highPrice, highPrice, highPrice, 1)); // we still shouldn be able to place cover to zero newOrder = new MarketOrder(Symbols.AAPL, quantity, time.AddSeconds(1)) { Price = highPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsTrue(sufficientCapital); // we shouldn't be able to place cover to long newOrder = new MarketOrder(Symbols.AAPL, quantity + 1, time.AddSeconds(1)) { Price = highPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsFalse(sufficientCapital); }
/// <summary> /// Handle order events from IB /// </summary> private void HandleOrderStatusUpdates(object sender, IB.OrderStatusEventArgs update) { try { var order = _orderProvider.GetOrderByBrokerageId(update.OrderId); if (order == null) { Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Unable to locate order with BrokerageID " + update.OrderId); return; } var status = ConvertOrderStatus(update.Status); if (order.Status == OrderStatus.Filled && update.Filled == 0 && update.Remaining == 0) { // we're done with this order, remove from our state int value; _orderFills.TryRemove(order.Id, out value); } var orderFee = 0m; int filledThisTime; lock (_orderFillsLock) { // lock since we're getting and updating in multiple operations var currentFilled = _orderFills.GetOrAdd(order.Id, 0); if (currentFilled == 0) { // apply order fees on the first fill event TODO: What about partial filled orders that get cancelled? var security = _securityProvider.GetSecurity(order.Symbol); orderFee = security.FeeModel.GetOrderFee(security, order); } filledThisTime = update.Filled - currentFilled; _orderFills.AddOrUpdate(order.Id, currentFilled, (sym, filled) => update.Filled); } if (status == OrderStatus.Invalid) { Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): ERROR -- " + update.OrderId); } // set status based on filled this time if (filledThisTime != 0) { status = update.Remaining != 0 ? OrderStatus.PartiallyFilled : OrderStatus.Filled; } // don't send empty fill events else if (status == OrderStatus.PartiallyFilled || status == OrderStatus.Filled) { Log.Trace("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Ignored zero fill event: OrderId: " + update.OrderId + " Remaining: " + update.Remaining); return; } // mark sells as negative quantities var fillQuantity = order.Direction == OrderDirection.Buy ? filledThisTime : -filledThisTime; order.PriceCurrency = _securityProvider.GetSecurity(order.Symbol).SymbolProperties.QuoteCurrency; var orderEvent = new OrderEvent(order, DateTime.UtcNow, orderFee, "Interactive Brokers Fill Event") { Status = status, FillPrice = update.LastFillPrice, FillQuantity = fillQuantity }; if (update.Remaining != 0) { orderEvent.Message += " - " + update.Remaining + " remaining"; } // if we're able to add to our fixed length, unique queue then send the event // otherwise it is a duplicate, so skip it if (_recentOrderEvents.Add(orderEvent.ToString() + update.Remaining)) { OnOrderEvent(orderEvent); } } catch(InvalidOperationException err) { Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Unable to resolve executions for BrokerageID: " + update.OrderId + " - " + err); } catch (Exception err) { Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): " + err); } }
/// <summary> /// Create a new instance of the order event packet /// </summary> public OrderEventPacket(string algorithmId, OrderEvent eventOrder) : base(PacketType.OrderEvent) { AlgorithmId = algorithmId; Event = eventOrder; }
/// <summary> /// Local processing of the order event. It only logs the transaction and orderEvent /// </summary> /// <param name="orderEvent">OrderEvent - the order event</param> private void ProcessOrderEvent(OrderEvent orderEvent) { //add to the list of order events which is saved to a file when running locally // I will use this file to test Stefano Raggi's code var currentSignalInfo = signalInfos.FirstOrDefault(s => s.Symbol == orderEvent.Symbol); orderId = orderEvent.OrderId; if (currentSignalInfo != null) currentSignalInfo.Status = orderEvent.Status; IEnumerable<OrderTicket> tickets = Transactions.GetOrderTickets(t => t.OrderId == orderId); switch (orderEvent.Status) { case OrderStatus.New: case OrderStatus.None: case OrderStatus.Submitted: case OrderStatus.Invalid: break; case OrderStatus.PartiallyFilled: if (currentSignalInfo != null) { nEntryPrice = Portfolio[orderEvent.Symbol].HoldStock ? Portfolio[orderEvent.Symbol].AveragePrice : 0; currentSignalInfo.TradeAttempts++; //Log(string.Format("Trade Attempts: {0} OrderId {1}", currentSignalInfo.TradeAttempts, orderEvent.OrderId)); } break; case OrderStatus.Canceled: if (currentSignalInfo != null) { //Log(string.Format("Order {0} cancelled.", orderEvent.OrderId)); currentSignalInfo.IsActive = true; currentSignalInfo.TradeAttempts = 0; } break; case OrderStatus.Filled: if (currentSignalInfo != null) { currentSignalInfo.IsActive = true; //if (currentSignalInfo.TradeAttempts > 0) // Log(string.Format("Order Filled OrderId {0} on attempt {1}", orderEvent.OrderId, currentSignalInfo.TradeAttempts)); currentSignalInfo.TradeAttempts = 0; } nEntryPrice = Portfolio[orderEvent.Symbol].HoldStock ? Portfolio[orderEvent.Symbol].AveragePrice : 0; if (tickets != null) { foreach (OrderTicket ticket in tickets) { //int infoId = Convert.ToInt32(ticket.Tag); #region "save the ticket as a OrderTransacton" OrderTransactionFactory transactionFactory = new OrderTransactionFactory((QCAlgorithm)this); OrderTransaction t = transactionFactory.Create(orderEvent, ticket, false); _transactions.Add(t); _orderTransactionProcessor.ProcessTransaction(t); _tradecount++; if (_orderTransactionProcessor.TotalProfit != totalProfit) { tradenet = CalculateTradeProfit(t.Symbol); } totalProfit = _orderTransactionProcessor.TotalProfit; #endregion } } break; } }
/// <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> /// ExecutionReport message handler /// </summary> private void OnExecutionReport(ExecutionReport message) { var orderId = message.getOrderID(); var orderStatus = message.getFXCMOrdStatus(); if (orderId != "NONE" && message.getAccount() == _accountId) { if (_openOrders.ContainsKey(orderId) && OrderIsClosed(orderStatus.getCode())) { _openOrders.Remove(orderId); } else { _openOrders[orderId] = message; } Order order; if (_mapFxcmOrderIdsToOrders.TryGetValue(orderId, out order)) { // existing order if (!OrderIsBeingProcessed(orderStatus.getCode())) { order.PriceCurrency = message.getCurrency(); var orderEvent = new OrderEvent(order, DateTime.UtcNow, 0) { Status = ConvertOrderStatus(orderStatus), FillPrice = Convert.ToDecimal(message.getPrice()), FillQuantity = Convert.ToInt32(message.getSide() == SideFactory.BUY ? message.getLastQty() : -message.getLastQty()) }; // we're catching the first fill so we apply the fees only once if ((int)message.getCumQty() == (int)message.getLastQty() && message.getLastQty() > 0) { var security = _securityProvider.GetSecurity(order.Symbol); orderEvent.OrderFee = security.FeeModel.GetOrderFee(security, order); } _orderEventQueue.Enqueue(orderEvent); } } else if (_mapRequestsToOrders.TryGetValue(message.getRequestID(), out order)) { _mapFxcmOrderIdsToOrders[orderId] = order; order.BrokerId.Add(orderId); order.PriceCurrency = message.getCurrency(); // new order var orderEvent = new OrderEvent(order, DateTime.UtcNow, 0) { Status = ConvertOrderStatus(orderStatus) }; _orderEventQueue.Enqueue(orderEvent); } } if (message.getRequestID() == _currentRequest) { if (message.isLastRptRequested()) { if (orderId == "NONE" && orderStatus.getCode() == IFixValueDefs.__Fields.FXCMORDSTATUS_REJECTED) { if (message.getSide() != SideFactory.UNDISCLOSED) { var messageText = message.getFXCMErrorDetails().Replace("\n", ""); Log.Trace("FxcmBrokerage.OnExecutionReport(): " + messageText); OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "OrderSubmitReject", messageText)); } _isOrderSubmitRejected = true; } _mapRequestsToAutoResetEvents[_currentRequest].Set(); _mapRequestsToAutoResetEvents.Remove(_currentRequest); } } }
public override void OnOrderEvent(OrderEvent orderEvent) { Console.WriteLine(Time + " - " + orderEvent); }
public override void OnOrderEvent(OrderEvent orderEvent) { if (orderEvent.Status.IsFill()) { // if we receive a fill cancel the other outstanding order Transactions.CancelOpenOrders(orderEvent.Symbol); } }
public override void OnOrderEvent(Orders.OrderEvent orderEvent) { Log(orderEvent.ToString()); }