protected override IWebSocket OnGetTradesWebSocket(Func <KeyValuePair <string, ExchangeTrade>, Task> callback, params string[] marketSymbols) { /* * spot request: * {"op": "subscribe", "args": ["spot/trade:BTC-USD"]} * futures request: * {"op": "subscribe", "args":["futures/trade:BTC-USD-190628"]} * swap request: * {"op": "subscribe", “args":["swap/trade:BTC-USD-SWAP"]} */ /* * response: * { * "table": "swap/trade", * "data": [{ * "instrument_id": "BTC-USD-SWAP", * "price": "3250", * "side": "sell", * "size": "1", * "timestamp": "2018-12-17T09:48:41.903Z", * "trade_id": "126518511769403393" * }] * } */ return(ConnectWebSocketOkex(async(_socket) => { await AddMarketSymbolsToChannel(_socket, "/trade:{0}", marketSymbols); }, async(_socket, symbol, sArray, token) => { ExchangeTrade trade = token.ParseTrade(amountKey: "size", priceKey: "price", typeKey: "side", timestampKey: "timestamp", timestampType: TimestampType.Iso8601, idKey: "trade_id"); await callback(new KeyValuePair <string, ExchangeTrade>(symbol, trade)); })); }
public Task <ExchangeTrade> MarketTrade(MarketTradeRequest request) { if (!Prices.TryGetValue(request.Market, out var price)) { throw new Exception($"Cannot found MOCK price for {request.Market}"); } var oppVol = double.Parse(((decimal)price * (decimal)request.Volume * -1m).ToString(CultureInfo.InvariantCulture)); var trade = new ExchangeTrade() { Id = Guid.NewGuid().ToString("N"), Price = price, Side = request.Side, Volume = request.Volume, Market = request.Market, OppositeVolume = oppVol, Timestamp = DateTime.UtcNow, ReferenceId = request.ReferenceId, Source = Name, }; Trades[Name].Add(trade); return(Task.FromResult(trade)); }
public async Task ExternalTrade_FullHedge_Sell() { _manager.Start(); var trades = new List <WalletTradeMessage>() { new WalletTradeMessage() { BrokerId = "broker", WalletId = "TEST", ClientId = "client", Trade = new WalletTrade("1", "BTCUSD", 10000, -2, 20000, "1", OrderType.Market, 1, DateTime.UtcNow, OrderSide.Sell, 3, string.Empty, 0), } }; var trade2 = new ExchangeTrade() { Id = "11", Price = 11000, Volume = 2, Side = OrderSide.Buy, Market = "BTC/USD", OppositeVolume = -22000, Timestamp = DateTime.UtcNow, ReferenceId = "1", Source = "FTX", AssociateSymbol = "BTCUSD", AssociateBrokerId = "broker", AssociateWalletId = "TEST" }; await _manager.RegisterLocalTradesAsync(trades); await _manager.RegisterHedgeTradeAsync(trade2); _repository.Data.Should().BeEmpty(); _portfolioReport.ClosedPosition.Should().HaveCount(1); var position = _portfolioReport.ClosedPosition.First(); position.Should().BeEquivalentTo(new { IsOpen = false, Side = OrderSide.Sell, BaseAsset = "BTC", QuotesAsset = "USD", BaseVolume = 0, QuoteVolume = -2000, ResultPercentage = -10 }); }
public async Task ExternalTrade_FullHedge() { _repository.Data["1"] = new PositionPortfolio() { Id = "1", WalletId = "TEST", Symbol = "BTCUSD", IsOpen = true, Side = OrderSide.Buy, BaseAsset = "BTC", QuotesAsset = "USD", BaseVolume = 2, QuoteVolume = -20000, OpenTime = DateTime.UtcNow }; _manager.Start(); var trade = new ExchangeTrade() { Id = "11", Price = 11000, Volume = -2, Side = OrderSide.Sell, Market = "BTC/USD", OppositeVolume = 22000, Timestamp = DateTime.UtcNow, ReferenceId = "1", Source = "FTX", AssociateSymbol = "BTCUSD", AssociateBrokerId = "broker", AssociateWalletId = "TEST" }; await _manager.RegisterHedgeTradeAsync(trade); _repository.Data.Should().BeEmpty(); _portfolioReport.ClosedPosition.Should().HaveCount(1); var position = _portfolioReport.ClosedPosition.First(); position.Should().BeEquivalentTo(new { IsOpen = false, Side = OrderSide.Buy, BaseAsset = "BTC", QuotesAsset = "USD", BaseVolume = 0, QuoteVolume = 2000 }); }
private IEnumerable <ExchangeTrade> ParseTradesWebSocket(JToken token) { var exchangeTradeList = new List <ExchangeTrade>(); foreach (var jtoken in token) { var timeSpan = TimeSpan.Parse(jtoken[3].ToStringInvariant()); var dateTime = DateTime.Today; dateTime = dateTime.Add(timeSpan); var universalTime = dateTime.ToUniversalTime(); var exchangeTrade = new ExchangeTrade { Id = jtoken[0].ConvertInvariant(0L), Price = jtoken[1].ConvertInvariant(new decimal()), Amount = jtoken[2].ConvertInvariant(new decimal()), Timestamp = universalTime, IsBuy = jtoken[4].ToStringInvariant().EqualsWithOption("bid", StringComparison.OrdinalIgnoreCase) }; exchangeTradeList.Add(exchangeTrade); } return(exchangeTradeList); }
// Extension Method: Display contents of an ExchangeTrade object public static void Print(this ExchangeTrade t) { Console.WriteLine("{0} {1} {2}", t.Timestamp.ToDisplay(), t.Price, t.Amount); }
public async Task <ExchangeTrade> MarketTrade(MarketTradeRequest request) { try { using var action = MyTelemetry.StartActivity("FTX Market Trade"); request.AddToActivityAsJsonTag("request"); var refId = request.ReferenceId ?? Guid.NewGuid().ToString("N"); refId.AddToActivityAsTag("reference-id"); var size = (decimal)Math.Abs(request.Volume); var resp = await _restApi.PlaceOrderAsync(request.Market, request.Side == OrderSide.Buy?SideType.buy : SideType.sell, 0, OrderType.market, size, refId, true); resp.AddToActivityAsJsonTag("marketOrder-response"); if (!resp.Success && resp.Error != "Duplicate client order ID") { throw new Exception( $"Cannot place marketOrder. Error: {resp.Error}. Request: {JsonConvert.SerializeObject(request)}. Reference: {refId}"); } action?.AddTag("is-duplicate", resp.Error == "Duplicate client order ID"); var tradeData = await _restApi.GetOrderStatusByClientIdAsync(refId); if (!tradeData.Success) { throw new Exception( $"Cannot get order state. Error: {resp.Error}. Request: {JsonConvert.SerializeObject(request)}. Reference: {refId}"); } if (tradeData.Result.Status != "closed") { await _restApi.CancelOrderByClientIdAsync(refId); } tradeData = await _restApi.GetOrderStatusByClientIdAsync(refId); tradeData.AddToActivityAsJsonTag("order-status-response"); if (!tradeData.Success) { throw new Exception( $"Cannot get second order state. Error: {resp.Error}. Request: {JsonConvert.SerializeObject(request)}. Reference: {refId}"); } size = tradeData.Result.FilledSize ?? 0; if (tradeData.Result.Side == "sell") { size = size * -1; } var trade = new ExchangeTrade() { Id = (tradeData.Result.Id ?? 0).ToString(CultureInfo.InvariantCulture), Market = tradeData.Result.Market, Side = tradeData.Result.Side == "buy" ? OrderSide.Buy : OrderSide.Sell, Price = (double)(tradeData.Result.AvgFillPrice ?? 0), ReferenceId = tradeData.Result.ClientId, Source = FtxConst.Name, Volume = (double)size, Timestamp = tradeData.Result.CreatedAt }; trade.AddToActivityAsJsonTag("response"); if (resp.Error == "Duplicate client order ID") { _logger.LogInformation("Ftx trade is Duplicate. Request: {requestJson}. Trade: {tradeJson}", JsonConvert.SerializeObject(request), JsonConvert.SerializeObject(trade)); } else { _logger.LogInformation("Ftx trade is done. Request: {requestJson}. Trade: {tradeJson}", JsonConvert.SerializeObject(request), JsonConvert.SerializeObject(trade)); } return(trade); } catch (Exception ex) { _logger.LogError(ex, "Cannot execute trade. Request: {requestJson}", JsonConvert.SerializeObject(request)); throw; } }