public void Should_decrease_available_quantity_for_the_market_when_an_order_is_sent() { var marketGateway = new ApiMarketGateway(marketName: "euronext", sellQuantity: 50, sellPrice: 100M); var order = marketGateway.CreateMarketOrder(ApiMarketWay.Buy, quantity: 10); marketGateway.Send(order); Check.That(marketGateway.SellQuantity).IsEqualTo(40); }
public void Should_Notify_LimitOrder_execution() { var marketGateway = new ApiMarketGateway(marketName: "euronext", sellQuantity: 50, sellPrice: 100M); var executed = false; var order = marketGateway.CreateLimitOrder(ApiMarketWay.Buy, price: 100M, quantity: 10, allowPartial: false); marketGateway.OrderExecuted += (s, a) => executed = true; marketGateway.Send(order); Check.That(executed).IsTrue(); Check.That(marketGateway.SellQuantity).IsEqualTo(40); }
public void Should_stop_sending_Orders_to_a_Market_after_3_rejects() { var rejectingMarket = new ApiMarketGateway("LSE (London)", sellQuantity: 100, sellPrice: 100M, orderPredicate: order => false); var investorInstructionAdapter = CompositionRootHelper.ComposeTheHexagon(rejectingMarket); var investorInstructionDto = new InvestorInstructionDto(Way.Buy, quantity: 50, price: 100M, goodTill: DateTime.Now.AddMinutes(5)); investorInstructionAdapter.Route(investorInstructionDto, (args) => { }, (args) => { }); Check.That(rejectingMarket.TimesSent).IsEqualTo(3); Check.That(rejectingMarket.SellQuantity).IsEqualTo(100); }
public void Should_Solve_with_2_markets_when_asked_quantity_is_1() { var marketA = new ApiMarketGateway("NYSE (New York)", sellQuantity: 50, sellPrice: 100M); var rejectingMarket = new ApiMarketGateway("CME (Chicago)", sellQuantity: 50, sellPrice: 100M, orderPredicate: _ => false); var marketsInvolved = new[] { marketA, rejectingMarket }; var marketGatewayAdapter = new MarketsAdapter(marketsInvolved); var investorInstruction = new InvestorInstruction(new InvestorInstructionIdentifierDto().Value, Way.Buy, quantity: 1, price: 100M, goodTill: DateTime.Now.AddMinutes(5)); var instructionExecutionContext = new InstructionExecutionContext(investorInstruction, args => { }, failure => { }); var marketSweepSolver = new MarketSweepSolver(new MarketSnapshotProvider(marketGatewayAdapter.GetAvailableMarketNames(), marketGatewayAdapter)); var orderBasket = marketSweepSolver.Solve(instructionExecutionContext, marketGatewayAdapter); Check.That(orderBasket.OrdersDescriptions.Extracting("Quantity")).ContainsExactly(1); }
public void Should_failed_to_execute_order_when_quantity_is_excessive() { var marketGateway = new ApiMarketGateway(marketName: "euronext", sellQuantity: 50, sellPrice: 100M); var order = marketGateway.CreateMarketOrder(ApiMarketWay.Buy, quantity: 100); bool failed = false; string failureReason = null; marketGateway.OrderFailed += (s, failedEventArgs) => { failed = true; failureReason = failedEventArgs.FailureCause; }; marketGateway.Send(order); Check.That(failed).IsTrue(); Check.That(failureReason).IsEqualTo("Excessive quantity!"); }
public void Should_support_partial_execution_for_LimitOrder() { var marketGateway = new ApiMarketGateway(marketName: "euronext", sellQuantity: 50, sellPrice: 100M); var executed = false; var order = marketGateway.CreateLimitOrder(ApiMarketWay.Buy, price: 100M, quantity: 110, allowPartial: true); var execQuantity = 0; marketGateway.OrderExecuted += (s, a) => { executed = true; execQuantity = a.Quantity; }; marketGateway.Send(order); Check.That(execQuantity).IsEqualTo(50); Check.That(executed).IsTrue(); Check.That(marketGateway.SellQuantity).IsEqualTo(0); }
public void Should_execute_Instruction_when_there_is_enough_liquidity_on_the_Markets() { // Given market A: 100 @ $100, market B: 55 @ $100 // When Investor wants to buy 125 stocks @ $100 Then SOR can execute at the requested MarketPrice var marketA = new ApiMarketGateway("NYSE (New York)", sellQuantity: 100, sellPrice: 100M); var marketB = new ApiMarketGateway("CME (Chicago)", sellQuantity: 55, sellPrice: 100M); var investorInstructionAdapter = CompositionRootHelper.ComposeTheHexagon(marketA, marketB); var investorInstructionDto = new InvestorInstructionDto(Way.Buy, quantity: 125, price: 100M); InstructionExecutedDto instructionExecuted = null; investorInstructionAdapter.Route(investorInstructionDto, (args) => { instructionExecuted = args; }, null); Check.That(instructionExecuted).HasFieldsWithSameValues(new { Way = Way.Buy, Quantity = 125, Price = 100M }); Check.That(marketA.SellQuantity).IsEqualTo(19); Check.That(marketB.SellQuantity).IsEqualTo(11); }
static void Main(string[] args) { var marketA = new ApiMarketGateway("NYSE (New York)", sellQuantity: 150, sellPrice: 100M); var marketB = new ApiMarketGateway("CME (Chicago)", sellQuantity: 55, sellPrice: 101M); var investorAdapter = CompositionRootHelper.ComposeTheHexagon(marketA, marketB); System.Console.WriteLine("SOR connected to markets: {0} and {1}", marketA.MarketName, marketB.MarketName); var investorInstructionDto = new InvestorInstructionDto(Way.Buy, quantity: 125, price: 100M); System.Console.WriteLine(); System.Console.WriteLine("Type 'Enter' to submit the following investor instruction: [{0}]\n\n", investorInstructionDto); System.Console.ReadLine(); investorAdapter.Route(investorInstructionDto, arg => { System.Console.WriteLine("Instruction executed: [{0}]", arg); }, eventArgs => {}); System.Console.WriteLine(); System.Console.WriteLine("Type 'Enter' to exit"); System.Console.ReadLine(); }
public void Should_not_execute_LimitOrder_when_price_is_too_high() { var marketGateway = new ApiMarketGateway(marketName: "euronext", sellQuantity: 50, sellPrice: 100M); var executed = false; bool failed = false; string failureReason = null; var order = marketGateway.CreateLimitOrder(ApiMarketWay.Buy, price: 101M, quantity: 10, allowPartial: false); marketGateway.OrderExecuted += (s, a) => executed = true; marketGateway.OrderFailed += (s, failedEventArgs) => { failed = true; failureReason = failedEventArgs.FailureCause; }; marketGateway.Send(order); Check.That(executed).IsFalse(); Check.That(failed).IsTrue(); Check.That(failureReason).IsEqualTo("Invalid price"); Check.That(marketGateway.SellQuantity).IsEqualTo(50); }
public void Should_execute_Orders_on_weighted_average_of_available_quantities() { // 25 premier ; 75 % sur le second marché // Given market A: 100 @ $100, market B: 50 @ $100 // When Investor wants to buy 75 stocks @ $100 Then SOR can execute at the requested MarketPrice // And execution is: 50 stocks on MarketA and 25 stocks on MarketB var marketA = new ApiMarketGateway("NYSE (New York)", sellQuantity: 100, sellPrice: 100M); var marketB = new ApiMarketGateway("CME (Chicago)", sellQuantity: 50, sellPrice: 100M); var investorInstructionAdapter = CompositionRootHelper.ComposeTheHexagon(marketA, marketB); var investorInstructionDto = new InvestorInstructionDto(Way.Buy, quantity: 75, price: 100M); InstructionExecutedDto instructionExecuted = null; investorInstructionAdapter.Route(investorInstructionDto, (args) => { instructionExecuted = args; }, null); Check.That(instructionExecuted).HasFieldsWithSameValues(new { Way = Way.Buy, Quantity = 75, Price = 100M }); Check.That(marketA.SellQuantity).IsEqualTo(50); Check.That(marketB.SellQuantity).IsEqualTo(25); }
public void Should_succeeded_when_liquidity_is_available_even_if_one_Market_rejects() { // Given market A: 150 @ $100, market B: 55 @ $101 // When Investor wants to buy 125 stocks @ $100 Then SOR can execute at the requested MarketPrice var marketA = new ApiMarketGateway("NYSE (New York)", sellQuantity: 50, sellPrice: 100M); var rejectingMarket = new ApiMarketGateway("LSE (London)", sellQuantity: 50, sellPrice: 100M, orderPredicate: _ => false); var investorInstructionAdapter = CompositionRootHelper.ComposeTheHexagon(marketA, rejectingMarket); var investorInstructionDto = new InvestorInstructionDto(Way.Buy, quantity: 50, price: 100M, goodTill: DateTime.Now.AddMinutes(5)); // Subscribes to the instruction's events InstructionExecutedDto instructionExecuted = null; InstructionFailedDto instructionFailed = null; investorInstructionAdapter.Route(investorInstructionDto, (args) => { instructionExecuted = args; }, (args) => { instructionFailed = args; }); Check.That(instructionExecuted).IsNotNull(); Check.That(instructionFailed).IsNull(); Check.That(marketA.SellQuantity).IsEqualTo(0); Check.That(rejectingMarket.SellQuantity).IsEqualTo(50); }
public void Should_failed_when_Order_exceeds_all_Market_capacity_and_partial_execution_not_allowed() { // Given market A: 150 @ $100, market B: 55 @ $101 // When Investor wants to buy 125 stocks @ $100 Then SOR can execute at the requested MarketPrice var marketA = new ApiMarketGateway("NYSE (New York)", sellQuantity: 15, sellPrice: 100M); var marketB = new ApiMarketGateway("CME (Chicago)", sellQuantity: 55, sellPrice: 101M); var investorInstructionAdapter = CompositionRootHelper.ComposeTheHexagon(marketA, marketB); var investorInstructionDto = new InvestorInstructionDto(Way.Buy, quantity: 125, price: 100M, allowPartialExecution: false); // Subscribes to the instruction's events InstructionExecutedDto instructionExecuted = null; InstructionFailedDto instructionFailed = null; investorInstructionAdapter.Route(investorInstructionDto, (args) => { instructionExecuted = args; }, (args) => { instructionFailed = args; }); // Couldn't execute because order with excessive QuantityOnTheMarket Check.That(instructionFailed.Reason).IsNotNull().And.IsEqualIgnoringCase("Excessive quantity!"); Check.That(instructionExecuted).IsNull(); Check.That(marketA.SellQuantity).IsEqualTo(15); Check.That(marketB.SellQuantity).IsEqualTo(55); }
public LimitOrderAdapter(ApiMarketGateway marketGateway, ApiLimitOrder apiLimitOrder) : base(marketGateway, apiLimitOrder) { this.apiLimitOrder = apiLimitOrder; }
protected OrderAdapter(ApiMarketGateway marketGateway, ApiOrder apiOrder) { this.apiOrder = apiOrder; this.MarketGateway = marketGateway; }