public async Task PlaceMarketOrderTest() { _handler.Expect(HttpMethod.Post, $"{BaseUri}orders/market-order") .WithQueryString(new Dictionary <string, string> { ["figi"] = Figi, ["brokerAccountId"] = BrokerAccountId }) .WithJsonContentFromFile("market-order-request") .RespondJsonFromFile("market-order-response"); var response = await _context.PlaceMarketOrderAsync(new MarketOrder(Figi, 10, OperationType.Buy, BrokerAccountId)); var expected = new PlacedMarketOrder( "40fcdfa2-084b-41bc-8f9d-3414a5eb9e8c", OperationType.Buy, OrderStatus.Fill, "That's Why", 10, 5, new MoneyAmount(Currency.Usd, 1.44m)); response.Should().BeEquivalentTo(expected); }
public PlacedOrder(PlacedMarketOrder order, string name, DateTime time, decimal price) : this(order.OrderId, order.Operation, name, order.ExecutedLots, order.Status, time, price) { }
public async Task Perform(DateTime time, CancellationToken ct) { Console.WriteLine($"PERFORM START {Stock} at {time}"); var candles = await _contextProvider.Do(ctx => ctx.MarketCandlesAsync(Stock.Figi, time.Subtract(TimeSpan.FromMinutes(20)), time, CandleInterval.Minute), ct); ct.ThrowIfCancellationRequested(); CandlePayload?candle = candles.Candles.LastOrDefault(); candle.Dump(); bool IsBuyStop() => candle != null && (Stock.Close - candle.Close >= Stock.Tick * 20); switch (Status) { case Init _: { if (IsBuyStop()) { Status = new BuyStop(); } else if (candle != null) { Status = new WaitingForBuy(); } } break; case WaitingForBuy _: { if (candle == null) { Status = new Closed(); } else if (IsBuyStop()) { Status = new BuyStop(); } } break; case BuyStop _: { if (candle == null) { Status = new Closed(); } else if (candle.Close > Stock.Close) { PlacedMarketOrder order = await _contextProvider.Do(ctx => ctx.PlaceMarketOrderAsync( new MarketOrder(Stock.Figi, 1, OperationType.Buy) ), ct); ct.ThrowIfCancellationRequested(); order.Dump(); _addOrder(new PlacedOrder(order, Stock.Name, time, candle.Close)); if (order.Status == OrderStatus.Rejected) { Console.WriteLine($"Buy order rejected: {order.RejectReason}"); break; } Status = new WaitingForSell { SellPrice = candle.Close - Stock.Half(), TakeProfit = candle.Close + Stock.Part(60M) }; } } break; case WaitingForSell status: { if (candle == null) { break; } var possiblyNewStop = candle.Close - Stock.Half(); if (possiblyNewStop > status.SellPrice) { status.SellPrice = possiblyNewStop; Console.WriteLine("Limit became higher"); } if (candle.Close <= status.SellPrice || candle.Close >= status.TakeProfit && status.TakeProfit != default) { var position = (await _contextProvider.Do(c => c.PortfolioAsync())) .Positions .FirstOrDefault(it => it.Figi == candle.Figi); if (position != null) { var closeOrder = await _contextProvider.Do(ctx => ctx.PlaceMarketOrderAsync( new MarketOrder(Stock.Figi, 1, OperationType.Sell) ), ct); ct.ThrowIfCancellationRequested(); closeOrder.Dump(); _addOrder(new PlacedOrder(closeOrder, Stock.Name, time, candle.Close)); if (closeOrder.Status == OrderStatus.Rejected) { Console.WriteLine($"Sell order rejected: {closeOrder.RejectReason}"); break; } } Status = new Closed(); } } break; } Console.WriteLine($"STATUS: {Status}"); }