/// <summary> /// Immediate fill logic, fill the order /// can be reused on other fill models for filling and checking /// </summary> /// <param name="broker"></param> /// <param name="datapoint"></param> /// <param name="pendingorder"></param> /// <param name="highliquidfill"></param> /// <returns></returns> protected Fill ImmediateFill(BrokerModel broker, DataPoint datapoint, PendingOrder pendingorder, bool highliquidfill) { //Get order Order order = pendingorder.Order; Security security = pendingorder.Security; //Check market conditions if (order.State == OrderState.Cancelled || order.State == OrderState.Error) { return(Fill.Cancelled(order.InternalId)); //Order has already been cancelled or has an error } else if (order.State == OrderState.Invalid) { return(Fill.Invalid(order.InternalId, "Invalid Order received")); //Order has already been made invalid } else if (order.State != OrderState.Submitted) { return(Fill.Error(order.InternalId, $"Unexpected order state {order.State}")); //Something else has gone wrong } else if (!IsExchangeOpen(pendingorder.Security, datapoint)) { return(Fill.NoFill()); //Exchange is not opened for trading } else if (IsOrderExpired(order, datapoint)) { return(Fill.Cancelled(order.InternalId, "Order expired")); //Check if order has been expired } //Check latency for processing this order try { if (pendingorder.CreatedUtc.Add(broker.GetLatencyModel(security).GetLatency(order)) > datapoint.Occured) { return(Fill.NoFill()); //We are not suppose to see this order yet, due to added latency } } catch (Exception exc) { _log.Error(exc, $"Could not execute broker latency model function for get latency."); throw; } //Check possible fill price var prices = datapoint.OrderPrice(order.Security, order.Direction); decimal fillprice = prices.Current; //Check order type and logic if (order is LimitOrder limitorder) { //Check if limit order price is triggered if (!limitorder.IsTriggered(prices, out fillprice)) { return(Fill.NoFill()); } } else if (order is StopLimitOrder stoplimitorder) { //Check if stop limit order price is triggered if (!stoplimitorder.IsTriggered(prices, out fillprice)) { return(Fill.NoFill()); } } else if (order is StopMarketOrder stopmarketorder) { //Check if stop order price is triggered if (!stopmarketorder.IsTriggered(prices, out fillprice)) { return(Fill.NoFill()); } } else if (order is MarketOnCloseOrder marketoncloseorder) { //Check if market on close order is triggered if (!marketoncloseorder.IsTriggered()) { return(Fill.NoFill()); } } else if (order is MarketOnOpenOrder marketonopenorder) { //Check if market on open order is triggered if (!marketonopenorder.IsTriggered()) { return(Fill.NoFill()); } } //Check slippage model try { fillprice += broker.GetSlippageModel(security).GetSlippage(order); } catch (Exception exc) { _log.Error(exc, $"Could not execute broker slippage model function for get slippage."); throw; } //Check additional spread try { fillprice += broker.GetSpreadModel(security).GetAdditionalSpread(order); } catch (Exception exc) { _log.Error(exc, $"Could not execute broker spread model function for get additional spread."); throw; } //Check fill amount (based on liquidity) if (!highliquidfill) { //Check fill size possible, according to tick var filled = datapoint.OrderSize(order.Direction); //Needed to fill (in case we partially already filled some of the order) decimal neededfill = order.UnsignedQuantity - pendingorder.OrderFilledQuantity; //Check for partial fills (will not include fees) if (filled < neededfill) { return(Fill.PartialFill(order, pendingorder.Security.Exchange.Name, fillprice, 0m, filled)); } else { //Process needed fill (will include fees) try { return(Fill.FullFill(order, pendingorder.Security.Exchange.Name, fillprice, Math.Abs(broker.GetFeeModel(pendingorder.Security).GetCommissionAndFees(pendingorder)), neededfill)); } catch (Exception exc) { _log.Error(exc, $"Could not process full fill, due to broker feemodel implementation."); throw; } } } else { //Process full fill (will include fees) try { return(Fill.FullFill(order, pendingorder.Security.Exchange.Name, fillprice, Math.Abs(broker.GetFeeModel(pendingorder.Security).GetCommissionAndFees(pendingorder)), order.UnsignedQuantity)); } catch (Exception exc) { _log.Error(exc, $"Could not process full fill, due to broker feemodel implementation."); throw; } } }