Ejemplo n.º 1
0
        /// <summary>
        /// Processes the specified ticket.
        /// </summary>
        /// <param name="ticket">The ticket.</param>
        /// <returns></returns>
        public virtual OrderTicket Process(OrderTicket ticket)
        {
            //Logging
            if (!Portfolio.IsBacktesting)
            {
                _log.Trace($"Processing order ticket: {ticket}");
            }

            //Try and get quant fund, if applicable
            var quantfund = Portfolio.QuantFunds.FirstOrDefault(x => x.FundId == ticket.FundId);

            //Add to queue
            if (ticket.Type == OrderTicketType.Submit && ticket is SubmitOrderTicket submitOrderTicket)
            {
                return(ProcessSubmitTicket(submitOrderTicket, quantfund));
            }
            else if (ticket.Type == OrderTicketType.Update && ticket is UpdateOrderTicket updateOrderTicket)
            {
                return(ProcessUpdateTicket(updateOrderTicket, quantfund));
            }
            else if (ticket.Type == OrderTicketType.Cancel && ticket is CancelOrderTicket cancelOrderTicket)
            {
                return(ProcessCancelTicket(cancelOrderTicket, quantfund));
            }
            else
            {
                ticket.SetResponse(OrderTicketResponse.Error(ticket.OrderId, OrderTicketResponseErrorCode.ProcessingError, "Could not process order ticket"));
                return(ticket);
            }
        }
Ejemplo n.º 2
0
 /// <summary>
 /// Submits the order ticket for processing.
 /// </summary>
 /// <param name="orderticket">The orderticket.</param>
 /// <returns></returns>
 public OrderTicket SubmitOrderTicket(OrderTicket orderticket)
 {
     //Can only send order tickets if this order ticket is for an existing position
     if (Position[orderticket.Security].IsFlat)
     {
         orderticket.SetResponse(OrderTicketResponse.Error(orderticket.OrderId, OrderTicketResponseErrorCode.UnsupportedRequestType, $"Supporting modules cannot open new positions, only signal modules can."));
         return(orderticket);
     }
     else
     {
         return(QuantFund.ProcessTicket(orderticket));
     }
 }
Ejemplo n.º 3
0
        /// <summary>
        /// Processes the submit ticket.
        /// </summary>
        /// <param name="ticket">The ticket.</param>
        /// <param name="quantfund">Associated Quant Fund</param>
        /// <returns></returns>
        private OrderTicket ProcessSubmitTicket(SubmitOrderTicket ticket, IQuantFund quantfund)
        {
            if (quantfund != null && quantfund.IsBackfilling)
            {
                ticket.SetResponse(
                    OrderTicketResponse.Error(ticket.OrderId, OrderTicketResponseErrorCode.QuantFundBackfilling,
                                              $"Quant fund {ticket.FundId} is currently backfilling, cannot process orders."));
            }
            else
            {
                _orderTicketQueue.Add(ticket);
                ticket.SetResponse(OrderTicketResponse.Processed(ticket.OrderId), OrderTicketState.Processing);
            }

            return(ticket);
        }
Ejemplo n.º 4
0
        /// <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));
        }
Ejemplo n.º 5
0
        /// <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);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Processes the update ticket.
        /// </summary>
        /// <param name="ticket">The ticket.</param>
        /// <param name="quantfund">Ticket associated quant fund</param>
        /// <returns></returns>
        private OrderTicket ProcessUpdateTicket(UpdateOrderTicket 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 update 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
                {
                    // 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 what we have
            return(ticket);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Processes the order according to the order flow for a quant fund.
        /// </summary>
        /// <param name="orderticket">The orderticket.</param>
        /// <param name="useflow"></param>
        public OrderTicket ProcessTicket(OrderTicket orderticket, bool useflow = true)
        {
            //Go trough the modules flow
            var rm     = Modules.FirstOrDefault(x => x is RiskManagementModule) as RiskManagementModule;
            var mm     = Modules.FirstOrDefault(x => x is MoneyManagementModule) as MoneyManagementModule;
            var result = OrderTicketResponse.Success(orderticket.OrderId);

            if (orderticket is SubmitOrderTicket submitorderticket)
            {
                //Get risk management orders associated to this order
                if (rm != null && useflow)
                {
                    //Check risk management module, trading allowed
                    try
                    {
                        if (!rm.IsTradingAllowed(orderticket.Security))
                        {
                            result = OrderTicketResponse.Error(orderticket.OrderId, OrderTicketResponseErrorCode.RiskManagementNotAllowed, $"Risk management module ({rm.Name}) did not allow trading");
                        }
                    }
                    catch (Exception exc)
                    {
                        _log.Error(exc, $"Could not process user risk management method IsTradingAllowed for module with name {rm.Name}");
                        Portfolio.ExceptionHandler.HandleException(exc, FundId);
                    }

                    //Check risk management module, additional orders
                    try
                    {
                        var orders = rm.RiskManagement(submitorderticket, GetState(orderticket.Security),
                                                       Universe.GetWeight(orderticket.Security)).ToArray();
                        if (orders.Length > 0)
                        {
                            //Process additional orders before these orders
                            orders.ForEach(o => ProcessTicket(o, false));
                        }
                    }
                    catch (Exception exc)
                    {
                        _log.Error(exc, $"Could not process user risk management method RiskManagement for module with name {rm.Name}");
                        Portfolio.ExceptionHandler.HandleException(exc, FundId);
                    }
                }

                //Check money management
                if (mm != null && useflow)
                {
                    try
                    {
                        //Get new order quantity
                        submitorderticket.Quantity = mm.OrderQuantity(submitorderticket, GetState(orderticket.Security), Universe.GetWeight(orderticket.Security));
                    }
                    catch (Exception exc)
                    {
                        _log.Error(exc, $"Could not process user money management method OrderQuantity for module with name {mm.Name}");
                        Portfolio.ExceptionHandler.HandleException(exc, FundId);
                    }
                }

                //Check if the exchangeModel is open, else convert to a market on open order
                if (!submitorderticket.Security.Exchange.IsOpen && submitorderticket.OrderType == OrderType.Market)
                {
                    submitorderticket.OrderType = OrderType.MarketOnOpen;
                    Portfolio.Log(LogLevel.Debug, $"Converted order from market to market on open due to closed exchangeModel {orderticket.Security.Exchange.Name}", FundId);
                }
            }

            //General checks
            if (result.IsSuccess)
            {
                result = PreCheck(orderticket);
            }

            //Check result
            if (result.IsError)
            {
                orderticket.SetResponse(result);
                return(orderticket);
            }
            else //process orderticket
            {
                return(Portfolio.OrderTicketHandler.Process(orderticket));
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Perform pre-checks on the order ticket
        /// </summary>
        /// <param name="orderticket">The orderticket.</param>
        /// <returns></returns>
        private OrderTicketResponse PreCheck(OrderTicket orderticket)
        {
            //Check if we know this security
            if (orderticket.Security is UnknownSecurity)
            {
                return(OrderTicketResponse.Error(orderticket.OrderId, OrderTicketResponseErrorCode.MissingSecurity, $"Security with ticker name {orderticket.Security.Ticker} is unknown"));
            }

            //Check if we are running and thus allowed to send this order
            if (!IsRunning)
            {
                return(OrderTicketResponse.Error(orderticket.OrderId, OrderTicketResponseErrorCode.PreOrderChecksError, $"Quant fund is not in a running state, current state is {State} for quant fund {FundId}"));
            }

            //Check asset price
            if (orderticket.Security.Price == 0)
            {
                return(OrderTicketResponse.Error(orderticket.OrderId, OrderTicketResponseErrorCode.SecurityPriceZero, $"No pricing data for security with ticker {orderticket.Security.Ticker}"));
            }

            //Check if trading in this security is possible
            if (orderticket.Security.IsHalted)
            {
                return(OrderTicketResponse.Error(orderticket.OrderId, OrderTicketResponseErrorCode.NonTradableSecurity, $"Security with ticker {orderticket.Security.Ticker} trading is halted"));
            }

            //Check submit orders
            if (orderticket is SubmitOrderTicket submitOrderTicket)
            {
                //Check order size
                if (submitOrderTicket.Quantity == 0)
                {
                    return(OrderTicketResponse.Error(orderticket.OrderId, OrderTicketResponseErrorCode.OrderQuantityZero, "Order quantity of zero is not allowed"));
                }

                //Check if exchangeModel is opened for a market on close order
                if (!submitOrderTicket.Security.Exchange.IsOpen && submitOrderTicket.OrderType == OrderType.MarketOnClose)
                {
                    return(OrderTicketResponse.Error(orderticket.OrderId, OrderTicketResponseErrorCode.ExchangeNotOpen, $"Exchange {submitOrderTicket.Security.Exchange.Name} is not opened on {Portfolio.Clock.CurrentUtc} UTC"));
                }

                //Check market on close order
                if (submitOrderTicket.OrderType == OrderType.MarketOnClose)
                {
                    var nextmarketclose =
                        orderticket.Security.Exchange.NextMarketOpen(orderticket.Security.LocalTime, false);
                    //Must be submitted within a margin of at least 10 minutes
                    var bufferedlastsubmission = nextmarketclose.AddMinutes(-16);
                    if (!submitOrderTicket.Security.Exchange.IsOpen || submitOrderTicket.Security.Exchange.LocalTime > bufferedlastsubmission)
                    {
                        return(OrderTicketResponse.Error(orderticket.OrderId, OrderTicketResponseErrorCode.MarketOnCloseOrderTooLate, "Market on close orders must be placed at least 16 minutes before market close"));
                    }
                }

                //Check if we are backfilling
                if (IsBackfilling)
                {
                    return(OrderTicketResponse.Error(orderticket.OrderId, OrderTicketResponseErrorCode.QuantFundBackfilling, $"Cannot send orders while backfilling {Name}"));
                }
            }

            //Check if conversion price is available
            if (orderticket.Security.ConvertValue(1, CurrencyType.USD) == 0)
            {
                return(OrderTicketResponse.Error(orderticket.OrderId, OrderTicketResponseErrorCode.ConversionRateZero, $"Could not convert value to different currency for security with ticker {orderticket.Security.Ticker}"));
            }

            //Check max orders per day
            if (_maxOrdersDay < orderticket.Security.Exchange.UtcTime.Date)
            {
                _maxOrdersDay     = orderticket.Security.Exchange.UtcTime.Date;
                _currentDayOrders = 1;
            }
            else if (_currentDayOrders >= MaxOrdersPerDay)
            {
                return(OrderTicketResponse.Error(orderticket.OrderId,
                                                 OrderTicketResponseErrorCode.ExceededMaximumOrders,
                                                 $"Maximum amount of orders per day of {MaxOrdersPerDay} has been reached"));
            }
            else
            {
                _currentDayOrders++;
            }

            //Passed all tests
            return(OrderTicketResponse.Success(orderticket.OrderId));
        }
Ejemplo n.º 9
0
        /// <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));
        }
Ejemplo n.º 10
0
        /// <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));
        }