/// <summary> /// Called when an order ticket event has occurred. /// TODO: not implemented in the flow => so this needs to be implemented in the flow /// </summary> /// <param name="orderticketevent">The order ticket event.</param> public void OnOrderTicketEvent(OrderTicketEvent orderticketevent) { if (IsRunning) { Modules.ForEach(x => x.OnOrderTicketEvent(orderticketevent)); } }
/// <summary> /// Submits the order. /// </summary> /// <param name="pendingorder">The order.</param> /// <returns></returns> public bool SubmitOrder(PendingOrder pendingorder) { //get the underlying order var order = pendingorder.Order; //Check current order state if (order.State == OrderState.New) { //Set order lock (_locker) { _activeOrders[pendingorder.OrderId] = pendingorder; } //Check order id if (order.BrokerId.Contains(order.InternalId.ToString())) { order.BrokerId.Add(order.InternalId.ToString()); } //Order event OrderStateChange?.Invoke(this, OrderTicketEvent.Submitted(order.InternalId)); return(true); } return(false); }
/// <summary> /// Called when [order ticket event]. /// TODO: send to ordertickethandler for processing fills and updating /// TODO: send to quant fund for notification /// </summary> /// <param name="pendingorder"></param> /// <param name="orderticketevent">The orderticketevent.</param> /// <exception cref="NotImplementedException"></exception> public void OnOrderTicketEvent(PendingOrder pendingorder, OrderTicketEvent orderticketevent) { throw new NotImplementedException(); //Send updates (so if the order is completed, the receiving party will know this) //EventRunner.Enqueue(PendingOrderInfoMessage.Generate(pendingorder), true); //EventRunner.Enqueue(OrderInfoMessage.Create(pendingorder), true); }
/// <summary> /// Cancels the order. /// </summary> /// <param name="order">The order.</param> /// <returns></returns> public bool CancelOrder(PendingOrder order) { //Check if we can remove this order if (!_activeOrders.TryRemove(order.OrderId, out PendingOrder active)) { return(false); //Cannot find order } //Send cancelled order notification OrderStateChange?.Invoke(this, OrderTicketEvent.Cancelled(order.OrderId, "Order was cancelled")); return(true); }
/// <summary> /// Updates the order. /// </summary> /// <param name="pendingorder">The order.</param> /// <returns></returns> public bool UpdateOrder(PendingOrder pendingorder) { //Check if we can find this order if (!_activeOrders.TryGetValue(pendingorder.OrderId, out PendingOrder active)) { return(false); //Cannot find order } lock (_locker) { //Update order instance pendingorder.UpdateOrder(active.Order); } //Send updated order notification OrderStateChange?.Invoke(this, OrderTicketEvent.Updated(active.OrderId, "Order was updated")); return(true); }
/// <summary> /// Process submit order /// </summary> /// <param name="ticket"></param> protected virtual OrderTicketResponse CancelOrder(CancelOrderTicket ticket) { //Check if we can get the order if (!OrderTracker.TryGetOrder(ticket.OrderId, out PendingOrder pendingorder)) { _log.Error($"Unable to cancel order with ID {ticket.OrderId}, order id unkown."); return(OrderTicketResponse.Error(ticket.OrderId, OrderTicketResponseErrorCode.UnableToFindOrder)); } //Check if order is closed if (pendingorder.Order.State.IsDone()) { return(OrderTicketResponse.Error(ticket.OrderId, OrderTicketResponseErrorCode.InvalidOrderStatus)); } //try and cancel the order bool ordercancelled = false; try { ordercancelled = BrokerConnection.CancelOrder(pendingorder); } catch (Exception exc) { _log.Error(exc, $"Could not cancel order with id {ticket.OrderId} broker connection refused to do so"); } //If not managed to cancel if (!ordercancelled) { var message = $"Brokerconnection failed to cancel order with id {ticket.OrderId}"; Portfolio.Log(LogLevel.Error, message); HandleOrderTicketEvent(OrderTicketEvent.Error(ticket.OrderId)); return(OrderTicketResponse.Error(ticket.OrderId, OrderTicketResponseErrorCode.BrokerageFailedToCancelOrder)); } //Order was cancelled var order = pendingorder.Order as OrderImpl; order.State = OrderState.Cancelled; pendingorder.UpdateOrder(order); //Return result for order ticket return(OrderTicketResponse.Processed(ticket.OrderId)); }
/// <summary> /// Cancels the order ticket. /// </summary> /// <param name="ticket">The ticket.</param> /// <param name="quantfund">Ticket associated quant fund</param> /// <returns></returns> private OrderTicket ProcessCancelTicket(CancelOrderTicket ticket, IQuantFund quantfund) { //Try and get current order ticket if (!OrderTracker.TryGetOrder(ticket.OrderId, out PendingOrder pendingorder)) { ticket.SetResponse(OrderTicketResponse.Error(ticket.OrderId, OrderTicketResponseErrorCode.UnableToFindOrder)); return(ticket); } try { //Try and process cancel ticket if (pendingorder.OrderState.IsDone()) { _log.Error($"Order is already of state {pendingorder.OrderState} while trying to cancel this order"); ticket.SetResponse(OrderTicketResponse.Error(pendingorder.OrderId, OrderTicketResponseErrorCode.InvalidOrderStatus)); } else if (quantfund != null && quantfund.IsBackfilling) { ticket.SetResponse(OrderTicketResponse.Error(pendingorder.OrderId, OrderTicketResponseErrorCode.QuantFundBackfilling)); } else { // update the order status var order = pendingorder.Order as OrderImpl; order.State = OrderState.CancelPending; pendingorder.UpdateOrder(order); // notify the portfolio with an order event HandleOrderTicketEvent(OrderTicketEvent.Cancelled(pendingorder.OrderId)); // send the request to be processed ticket.SetResponse(OrderTicketResponse.Processed(ticket.OrderId), OrderTicketState.Processing); _orderTicketQueue.Add(ticket); } } catch (Exception exc) { _log.Error(exc); ticket.SetResponse(OrderTicketResponse.Error(pendingorder.OrderId, OrderTicketResponseErrorCode.ProcessingError, exc.Message)); } //return result return(ticket); }
/// <summary> /// Handles the order ticket event. /// </summary> /// <param name="orderticketevent">The orderticketevent.</param> private void HandleOrderTicketEvent(OrderTicketEvent orderticketevent) { //Retrieve the order if (!(GetOrderById(orderticketevent.OrderId) is OrderImpl order)) { _log.Error($"Unable to retrieve order with id {orderticketevent.OrderId} could not proceed"); return; } //Set new order status order.State = orderticketevent.OrderState; //Get pending order instance if (!OrderTracker.TryGetOrder(orderticketevent.OrderId, out PendingOrder pendingorder)) { _log.Error($"Unable to retrieve pending order with id {orderticketevent.OrderId} could not proceed"); return; } //Check if we need to apply fill if ((order.State == OrderState.Filled || order.State == OrderState.PartialFilled)) { pendingorder.AddFill(orderticketevent.Fill); } //Check if we need to fire an event if (orderticketevent.OrderState != OrderState.None) { //Create new event try { Portfolio.OnOrderTicketEvent(pendingorder, orderticketevent); } catch (Exception exc) { Portfolio.Log(LogLevel.Error, $"Could not process orderticket event: {exc.Message}"); _log.Error(exc); } } }
/// <summary> /// Called when an order ticket event has occured. /// </summary> /// <param name="orderticketevent">The orderticketevent.</param> public virtual void OnOrderTicketEvent(OrderTicketEvent orderticketevent) { }
/// <summary> /// Processes the market data. /// </summary> /// <param name="dataupdates">The data updates.</param> public void ProcessMarketData(DataUpdates dataupdates) { //Only accept market data if (_activeOrders.Count == 0) { return; } //Check if we have any data if (!dataupdates.HasUpdates) { return; } lock (_locker) { foreach (var pkv in _activeOrders.OrderBy(x => x.Key)) { //get the order var pendingorder = pkv.Value; var order = pendingorder.Order; //Get datapoint if (!dataupdates[order.Security].HasUpdates) { continue; } var datapoint = dataupdates[order.Security].Ticks.Count > 0 ? dataupdates[order.Security].Ticks.First().Value.First() as DataPoint : dataupdates[order.Security].QuoteBars.Count > 0 ? dataupdates[order.Security].QuoteBars.First().Value as DataPoint : dataupdates[order.Security].TradeBars.Count > 0 ? dataupdates[order.Security].TradeBars.First().Value as DataPoint : null; if (datapoint == null) { continue; } //Check if order is already done if (order.State.IsDone()) { _activeOrders.TryRemove(pkv.Key, out pendingorder); continue; } //Check if we have enough buying power if (!_portfolio.OrderTicketHandler.GetSufficientCapitalForOrder(pendingorder)) { //Remove order from active orders, as it is cancelled by the broker instance _activeOrders.TryRemove(pkv.Key, out pendingorder); _portfolio.Log(LogLevel.Error, $"Insufficient funds to process order by sim broker"); OrderStateChange?.Invoke(this, OrderTicketEvent.Cancelled(pendingorder.OrderId, "Insufficient funds to process order by sim broker")); } //Check if we need to fill this order var fillmodel = BrokerModel.GetFillModel(order); Fill filledresult = Fill.NoFill(); try { filledresult = fillmodel.FillOrder(BrokerModel, datapoint, pendingorder, _usehighlyliquidfills); } catch (Exception exc) { _log.Error(exc); _portfolio.Log(LogLevel.Error, string.Format("Order Error: id: {0}, Transaction model failed to fill for order type: {1} with error: {2}", order.InternalId, order.Type, exc.Message)); OrderStateChange?.Invoke(this, OrderTicketEvent.Cancelled(pendingorder.OrderId, "Exception during processing fill for this order, please check logs")); } //Check for any full or partial fills if (filledresult.FillQuantity > 0) { if (filledresult.Status == OrderState.Filled) { OrderStateChange?.Invoke(this, OrderTicketEvent.Filled(order.InternalId, filledresult)); } else if (filledresult.Status == OrderState.PartialFilled) { OrderStateChange?.Invoke(this, OrderTicketEvent.PartiallyFilled(order.InternalId, filledresult)); } } //Check if order is done if (filledresult.Status.IsDone()) { _activeOrders.TryRemove(pkv.Key, out pendingorder); } } } }
/// <summary> /// Submit update order /// </summary> /// <param name="ticket"></param> protected virtual OrderTicketResponse UpdateOrder(UpdateOrderTicket ticket) { //Get current pending order if (!OrderTracker.TryGetOrder(ticket.OrderId, out PendingOrder pendingorder)) { _log.Error($"Unable to retrieve order with id {ticket.OrderId} could not proceed"); return(OrderTicketResponse.Error(pendingorder.OrderId, OrderTicketResponseErrorCode.UnableToFindOrder, $"Current order with id {pendingorder.OrderId} cannot be found")); } //Get the order var order = pendingorder.Order as OrderImpl; //Check if we can update this order if (!CanUpdateOrder(order)) { return(OrderTicketResponse.Error(order.InternalId, OrderTicketResponseErrorCode.InvalidOrderStatus, $"Unable to update order with id {order.InternalId} and current state {order.State}")); } //Check if we can process this order try { if (!Portfolio.BrokerModel.CanUpdateOrder(order, out string message)) { //Notify we cannot update this order order.State = OrderState.Invalid; var response = OrderTicketResponse.Error(order.InternalId, OrderTicketResponseErrorCode.BrokerageModelRefusedToUpdateOrder, $"Cannot update order {order.InternalId}: {message}"); Portfolio.Log(LogLevel.Error, response.ErrorMessage); HandleOrderTicketEvent(OrderTicketEvent.Error(order.InternalId, response.ErrorMessage)); return(response); } } catch (Exception exc) { _log.Error(exc, $"Could not run CanUpdateOrder on order with id {order.InternalId}, please check the implemented logic"); order.State = OrderState.Invalid; return(OrderTicketResponse.Error(pendingorder.OrderId, OrderTicketResponseErrorCode.ProcessingError, $"Current order with id {pendingorder.OrderId} cannot be processed due to error")); } //Check order quantity and pricing in case this is out of range RoundLotSize(order); RoundOrderPrices(order); //Try and update this order bool orderupdatesucceeded = false; try { orderupdatesucceeded = BrokerConnection.UpdateOrder(pendingorder); } catch (Exception exc) { _log.Error(exc); } //Process a failed order update event if (!orderupdatesucceeded) { var errormessage = $"Broker connection failed to update order with id {order.InternalId} with connection type {BrokerConnection.BrokerType}"; Portfolio.Log(LogLevel.Error, errormessage); HandleOrderTicketEvent(OrderTicketEvent.Error(order.InternalId, "Failed to update order")); return(OrderTicketResponse.Error(order.InternalId, OrderTicketResponseErrorCode.BrokerageFailedToUpdateOrder, errormessage)); } else { //Apply updates to order order.Update(ticket); pendingorder.UpdateOrder(order); } return(OrderTicketResponse.Processed(order.InternalId)); }
/// <summary> /// Process submit order /// </summary> /// <param name="ticket"></param> protected virtual OrderTicketResponse SubmitOrder(SubmitOrderTicket ticket) { //Get order from factory PendingOrder pendingorder = OrderFactory.CreateOrder(ticket); OrderImpl order = pendingorder.Order as OrderImpl; //Round off order quantity for correct amounts RoundLotSize(order); //try and get the order from the order tracker if (!OrderTracker.TryAddOrder(pendingorder)) { _log.Error($"Unable to add new order, order with id {pendingorder.OrderId} was already submitted"); return(OrderTicketResponse.Error(pendingorder.OrderId, OrderTicketResponseErrorCode.OrderAlreadyExists, $"Current order with id {pendingorder.OrderId} was already submitted")); } if (!OrderTracker.TryGetOrder(pendingorder.OrderId, out pendingorder)) { _log.Error($"Unable to retrieve newly added order, order with id {pendingorder.OrderId} was cannot be processed properly"); return(OrderTicketResponse.Error(pendingorder.OrderId, OrderTicketResponseErrorCode.UnableToFindOrder, $"Current order with id {pendingorder.OrderId} cannot be found")); } //Round of prices RoundOrderPrices(order); //Set our new order pendingorder.UpdateOrder(order); //Check for correct size if (order.Quantity == 0) { order.State = OrderState.Invalid; var ticketresponse = OrderTicketResponse.Error(order.InternalId, OrderTicketResponseErrorCode.OrderQuantityZero); Portfolio.Log(LogLevel.Error, ticketresponse.ErrorMessage); return(ticketresponse); } //Check if we have enough capital for an order bool sufficientcapital = GetSufficientCapitalForOrder(pendingorder); if (!sufficientcapital) { //Not enough capital to execute this order order.State = OrderState.Invalid; var response = OrderTicketResponse.Error(order.InternalId, OrderTicketResponseErrorCode.InsufficientBuyingPower, $"Cannot execute order with id {order.InternalId}, insufficient funds to execute order."); Portfolio.Log(LogLevel.Error, response.ErrorMessage); HandleOrderTicketEvent(OrderTicketEvent.Error(order.InternalId, $"Insufficent capital to execute order")); return(response); } //Check if broker accepts order at this moment try { if (!Portfolio.BrokerModel.CanSubmitOrder(order, out var message)) { //Broker model did not accept this order order.State = OrderState.Invalid; var response = OrderTicketResponse.Error(order.InternalId, OrderTicketResponseErrorCode.BrokerageModelRefusedToSubmitOrder, $"Order with id {order.InternalId}: {message}"); Portfolio.Log(LogLevel.Error, ""); HandleOrderTicketEvent(OrderTicketEvent.Error(order.InternalId, $"Broker model of type {Portfolio.BrokerModel.BrokerType} declared order cannot be submitted, message: {message}")); return(response); } } catch (Exception exc) { _log.Error(exc, $"Could not run CanSubmitOrder on order with id {order.InternalId}, please check the implemented logic"); order.State = OrderState.Invalid; return(OrderTicketResponse.Error(pendingorder.OrderId, OrderTicketResponseErrorCode.ProcessingError, $"Current order with id {pendingorder.OrderId} cannot be processed due to error")); } //Try to execute this order to the broker connection attached bool ordersuccess = false; try { ordersuccess = BrokerConnection.SubmitOrder(pendingorder); } catch (Exception exc) { _log.Error(exc); } //Check if placing the order was a success if (!ordersuccess) { order.State = OrderState.Invalid; var submitmessage = "BrokerConnection failed to place order"; var response = OrderTicketResponse.Error(order.InternalId, OrderTicketResponseErrorCode.BrokerageFailedToSubmitOrder, submitmessage); Portfolio.Log(LogLevel.Error, submitmessage); HandleOrderTicketEvent(OrderTicketEvent.Error(order.InternalId, submitmessage)); return(response); } order.State = OrderState.Submitted; return(OrderTicketResponse.Processed(order.InternalId)); }