/// <summary> /// Build the description of the orders needed to fulfill an <see cref="InvestorInstruction" /> which /// is aggregated within an <see cref="InstructionExecutionContext" /> instance. /// </summary> /// <param name="instructionExecutionContext">The <see cref="InstructionExecutionContext" /> instance that aggregates the <see cref="InvestorInstruction" />.</param> /// <returns> /// An <see cref="OrderBasket" /> containing all the orders to be routed in order to fulfill the initial <see cref="InvestorInstruction" />. /// </returns> public OrderBasket Solve(InstructionExecutionContext instructionExecutionContext) { var ordersDescription = new List<OrderDescription>(); // Checks liquidities available to weighted average for execution int remainingQuantityToBeExecuted = instructionExecutionContext.Quantity; var requestedPrice = instructionExecutionContext.Price; var validMarkets = this.GetValidMarkets(requestedPrice); var availableQuantityOnMarkets = this.ComputeAvailableQuantityForThisPrice(validMarkets); if (availableQuantityOnMarkets == 0) { return new OrderBasket(new List<OrderDescription>()); } var ratio = remainingQuantityToBeExecuted / (decimal)availableQuantityOnMarkets; // ReSharper disable once LoopCanBeConvertedToQuery foreach (var marketInfo in validMarkets) { var convertedMarketQuantity = Math.Round(marketInfo.Market.SellQuantity * ratio, 2, MidpointRounding.AwayFromZero); var quantityToExecute = Convert.ToInt32(convertedMarketQuantity); if (quantityToExecute > 0) { ordersDescription.Add(new OrderDescription(marketInfo.Market, instructionExecutionContext.Way, quantityToExecute, requestedPrice, instructionExecutionContext.AllowPartialExecution)); } } return new OrderBasket(ordersDescription); }
public void Route(InvestorInstructionDto investorInstructionDto) { // 1. Digest Investment instructions // 2. Prepare order book (solver) // 3. Send and monitor // 4. Feedback investor var investorInstruction = this.CreateInvestorInstruction(investorInstructionDto.UniqueIdentifier, null, investorInstructionDto.Way, investorInstructionDto.Quantity, investorInstructionDto.Price, investorInstructionDto.AllowPartialExecution, investorInstructionDto.GoodTill); var executionState = new InstructionExecutionContext(investorInstruction); this.RouteImpl(investorInstruction, executionState); }
//// TODO: remove investor instruction as arg here? private void RouteImpl(InvestorInstruction investorInstruction, InstructionExecutionContext instructionExecutionContext) { var solver = new MarketSweepSolver(this.marketSnapshotProvider); var orderBasket = solver.Solve(instructionExecutionContext); EventHandler<DealExecutedEventArgs> handler = (executedOrder, args) => instructionExecutionContext.Executed(args.Quantity); EventHandler<OrderFailedEventArgs> failHandler = (s, failure) => this.SendOrderFailed(investorInstruction, failure, instructionExecutionContext); investorInstruction.Executed += (sender, e) => this.investorInstruction_Executed(sender, e); orderBasket.OrderExecuted += handler; orderBasket.OrderFailed += failHandler; orderBasket.Send(); orderBasket.OrderExecuted -= handler; orderBasket.OrderFailed -= failHandler; }
private void SendOrderFailed(InvestorInstruction investorInstruction, OrderFailedEventArgs reason, InstructionExecutionContext instructionExecutionContext) { this.marketSnapshotProvider.MarketFailed(reason.Market); if (investorInstruction.GoodTill != null && investorInstruction.GoodTill > DateTime.Now && instructionExecutionContext.Quantity > 0) { // retries this.RouteImpl(investorInstruction, instructionExecutionContext); } else { Action<string> failureCallback; if (this.failureCallbacks.TryGetValue(investorInstruction.IdentifierDto, out failureCallback)) { failureCallback(reason.Reason); } } }