public override async Task <ExecutionReport> CancelOrderAndWaitExecution( TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { if (!Guid.TryParse(signal.OrderId, out var id)) { throw new ApiException("GDAX order id can be only Guid"); } var cts = CreateCancellationTokenSource(timeout); try { var response = await _restApi.CancelOrder(id, cts.Token, (sender, httpRequest) => OnSentHttpRequest(httpRequest, translatedSignal), (sender, httpResponse) => OnReceivedHttpRequest(httpResponse, translatedSignal)); if (!response) { return(null); } return(null); // TODO: Here we should just return boolean result } catch (StatusCodeException ex) { throw new ApiException(ex.Message); } }
private static void OnSentHttpRequest(SentHttpRequest request, TranslatedSignalTableEntity translatedSignal) { var url = request.Uri.ToString(); translatedSignal?.RequestSent(request.HttpMethod, url, request.Content); }
public Task <TradeBalanceInfo> GetTradeBalance(TranslatedSignalTableEntity translatedSignal, CancellationToken cancellationToken) { var request = new TradeBalanceRequest() { AssetClass = "currency", BaseAsset = "ZUSD" }; return(MakePostRequestAsync <TradeBalanceInfo>("TradeBalance", request, translatedSignal, cancellationToken)); }
public override async Task <ExecutionReport> AddOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { var cts = new CancellationTokenSource(timeout); var orderInfo = await privateData.AddOrder(signal, translatedSignal, cts.Token); string txId = orderInfo.TxId.FirstOrDefault(); translatedSignal.ExternalId = txId; return(new ExecutionReport(signal.Instrument, DateTime.UtcNow, signal.Price ?? 0, signal.Volume, signal.TradeType, signal.OrderId, OrderExecutionStatus.New)); }
public override async Task <ExecutionReport> CancelOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { var cts = new CancellationTokenSource(timeout); string result = await apiClient.MakePostRequestAsync <string>( $"{Config.EndpointUrl}/api/Orders/{signal.OrderId}/Cancel", CreateHttpContent(new object()), translatedSignal.RequestSent, translatedSignal.ResponseReceived, cts.Token); return(new ExecutionReport(signal.Instrument, DateTime.UtcNow, signal.Price ?? 0, signal.Volume, signal.TradeType, signal.OrderId, OrderExecutionStatus.Cancelled)); }
public async Task SaveAsync(TranslatedSignalTableEntity translatedSignal) { try { translatedSignal.PartitionKey = TranslatedSignalTableEntity.GeneratePartitionKey(); translatedSignal.RowKey = keyProvider.GetNextRowKey().ToString(); await _tableStorage.InsertAsync(translatedSignal); // TODO: save by batchs } catch (Exception ex) { logger.LogError(0, ex, "Can't save translated signal into azure storage"); } }
public async Task <ExecutionReport> CancelOrder(string id, [FromQuery, Required] string exchangeName) { try { if (string.IsNullOrEmpty(exchangeName)) { throw new StatusCodeException(HttpStatusCode.InternalServerError, "Exchange has to be specified"); } var instrument = new Instrument(exchangeName, string.Empty); var tradingSignal = new TradingSignal(instrument, id, OrderCommand.Cancel, TradeType.Unknown, 0, 0, DateTime.UtcNow, OrderType.Unknown); var translatedSignal = new TranslatedSignalTableEntity(SignalSource.RestApi, tradingSignal) { ClientIP = HttpContext.Connection.RemoteIpAddress.ToString() }; try { var result = await Application.GetExchange(exchangeName) .CancelOrderAndWaitExecution(tradingSignal, translatedSignal, _timeout); if (result.ExecutionStatus == OrderExecutionStatus.Rejected) { throw new StatusCodeException(HttpStatusCode.BadRequest, $"Exchange return status: {result.ExecutionStatus}", null); } return(result); } catch (Exception e) { translatedSignal.Failure(e); throw; } finally { await _translatedSignalsRepository.SaveAsync(translatedSignal); } } catch (StatusCodeException) { throw; } catch (Exception e) { throw new StatusCodeException(HttpStatusCode.InternalServerError, e.Message, e); } }
private async Task HandleCancellation(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, Exchange exchange) { try { var executedTrade = await exchange.CancelOrderAndWaitExecution(signal, translatedSignal, apiTimeout); if (executedTrade.ExecutionStatus == OrderExecutionStatus.Cancelled) { await _tradeHandler.Handle(executedTrade); } else { var message = $"Executed trade status {executedTrade.ExecutionStatus} after calling 'exchange.CancelOrderAndWaitExecution'"; translatedSignal.Failure(message); await logger.WriteWarningAsync(nameof(TradingSignalsHandler), nameof(HandleCancellation), signal.ToString(), message); } } catch (ApiException e) { translatedSignal.Failure(e); await logger.WriteInfoAsync(nameof(TradingSignalsHandler), nameof(HandleCancellation), signal.ToString(), e.Message); } catch (Exception e) { translatedSignal.Failure(e); await logger.WriteErrorAsync(nameof(TradingSignalsHandler), nameof(HandleCancellation), signal.ToString(), e); } }
public override async Task <ExecutionReport> AddOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { var symbol = _converters.LykkeSymbolToExchangeSymbol(signal.Instrument.Name); var orderType = _converters.OrderTypeToGdaxOrderType(signal.OrderType); var side = _converters.TradeTypeToGdaxOrderSide(signal.TradeType); var volume = signal.Volume; var price = (!signal.Price.HasValue || signal.Price == 0) ? 1 : signal.Price.Value; var cts = CreateCancellationTokenSource(timeout); try { var response = await _restApi.AddOrder(symbol, volume, price, side, orderType, cts.Token, (sender, httpRequest) => OnSentHttpRequest(httpRequest, translatedSignal), (sender, httpResponse) => OnReceivedHttpRequest(httpResponse, translatedSignal)); var trade = _converters.OrderToTrade(response); return(trade); } catch (StatusCodeException ex) { throw new ApiException(ex.Message); } }
private async Task HandleTradingSignals(Exchange exchange, TradingSignal signal) { await logger.WriteInfoAsync(nameof(TradingSignalsHandler), nameof(HandleTradingSignals), signal.ToString(), "New trading signal to be handled."); var translatedSignal = new TranslatedSignalTableEntity(SignalSource.RabbitQueue, signal); try { switch (signal.Command) { case OrderCommand.Create: await HandleCreation(signal, translatedSignal, exchange); break; case OrderCommand.Cancel: await HandleCancellation(signal, translatedSignal, exchange); break; default: throw new ArgumentOutOfRangeException(nameof(signal)); } } catch (Exception e) { translatedSignal.Failure(e); } finally { translatedSignalsRepository.Save(translatedSignal); await logger.WriteInfoAsync(nameof(TradingSignalsHandler), nameof(HandleTradingSignals), signal.ToString(), "Signal handled. Waiting for another one."); } }
private static void OnReceivedHttpRequest(ReceivedHttpResponse response, TranslatedSignalTableEntity translatedSignal) { translatedSignal?.ResponseReceived(response.Content); }
public override async Task <ExecutionReport> CancelOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { var ct = new CancellationTokenSource(timeout); var id = signal.OrderId; var response = await _exchangeApi.OrdercancelAsync(cancellationToken : ct.Token, orderID : id); if (response is Error error) { throw new ApiException(error.ErrorProperty.Message); } var res = EnsureCorrectResponse(id, response); return(BitMexModelConverter.OrderToTrade(res[0])); }
private static ExecutionReport CreateAcknowledgement(IExchange exchange, bool success, TradingSignal arrivedSignal, TranslatedSignalTableEntity translatedSignal, Exception exception = null) { var ack = new ExecutionReport { Success = success, Instrument = arrivedSignal.Instrument, ClientOrderId = arrivedSignal.OrderId, ExchangeOrderId = translatedSignal.ExternalId, Message = translatedSignal.ErrorMessage }; if (exception != null) { switch (exception) { case InsufficientFundsException _: ack.FailureType = OrderStatusUpdateFailureType.InsufficientFunds; break; case ApiException _: ack.FailureType = OrderStatusUpdateFailureType.ExchangeError; break; default: ack.FailureType = OrderStatusUpdateFailureType.ConnectorError; break; } } else { ack.FailureType = OrderStatusUpdateFailureType.None; } return(ack); }
public override async Task <ExecutionReport> AddOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { var newOrderSingle = new NewOrderSingle { HandlInst = new HandlInst(HandlInst.AUTOMATED_EXECUTION_ORDER_PRIVATE), Symbol = _modelConverter.ConvertLykkeSymbol(signal.Instrument.Name), Side = _modelConverter.ConvertSide(signal.TradeType), OrderQty = new OrderQty(signal.Volume), OrdType = _modelConverter.ConverOrderType(signal.OrderType), TimeInForce = new TimeInForce(TimeInForce.IMMEDIATE_OR_CANCEL), TransactTime = new TransactTime(DateTime.UtcNow), Price = new Price(signal.Price ?? 0m) }; var cts = new CancellationTokenSource(timeout); var report = await _connector.AddOrderAsync(newOrderSingle, cts.Token); var trade = _modelConverter.ConvertExecutionReport(report); try { var handlerTrade = _modelConverter.ConvertExecutionReport(report); handlerTrade.ExecType = ExecType.Trade; await _executionHandler.Handle(handlerTrade); } catch (Exception ex) { await _log.WriteErrorAsync(nameof(AddOrderAndWaitExecution), "Posting order to Jfd", ex); } return(trade); }
public override async Task <ExecutionReport> AddOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { var cts = new CancellationTokenSource(timeout); switch (signal.OrderType) { case OrderType.Market: var marketOrderResponse = await apiClient.MakePostRequestAsync <MarketOrderResponse>( $"{Config.EndpointUrl}/api/Orders/market", CreateHttpContent(new MarketOrderRequest() { AssetPairId = signal.Instrument.Name, Asset = signal.Instrument.Base, OrderAction = signal.TradeType, Volume = signal.Volume }), translatedSignal.RequestSent, translatedSignal.ResponseReceived, cts.Token); if (marketOrderResponse != null && marketOrderResponse.Error == null) { return(new ExecutionReport(signal.Instrument, DateTime.UtcNow, marketOrderResponse.Result, signal.Volume, signal.TradeType, signal.OrderId, OrderExecutionStatus.Fill)); } else { throw new ApiException("Unexpected result from exchange"); } case OrderType.Limit: try { var limitOrderResponse = await apiClient.MakePostRequestAsync <string>( $"{Config.EndpointUrl}/api/Orders/limit", CreateHttpContent(new LimitOrderRequest() { AssetPairId = signal.Instrument.Name, OrderAction = signal.TradeType, Volume = signal.Volume, Price = signal.Price ?? 0 }), translatedSignal.RequestSent, translatedSignal.ResponseReceived, cts.Token); var orderPlaced = limitOrderResponse != null && Guid.TryParse(limitOrderResponse, out var orderId); if (orderPlaced) { translatedSignal.ExternalId = orderId.ToString(); return(new ExecutionReport(signal.Instrument, DateTime.UtcNow, signal.Price ?? 0, signal.Volume, signal.TradeType, orderId.ToString(), OrderExecutionStatus.New)); } else { throw new ApiException("Unexpected result from exchange"); } } catch (ApiException e) when(e.Message.Contains("ReservedVolumeHigherThanBalance")) { throw new InsufficientFundsException($"Not enough funds to placing order {signal}", e); } default: throw new ApiException($"Unsupported OrderType {signal.OrderType}"); } }
public override async Task <ExecutionReport> AddOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { var symbol = _modelConverter.LykkeSymbolToExchangeSymbol(signal.Instrument.Name); var volume = signal.Volume; var orderType = _modelConverter.ConvertOrderType(signal.OrderType); var side = _modelConverter.ConvertTradeType(signal.TradeType); var price = signal.Price == 0 ? 1 : signal.Price ?? 1; var cts = new CancellationTokenSource(timeout); var response = await _exchangeApi.AddOrder(symbol, volume, price, side, orderType, cts.Token); if (response is Error error) { throw new ApiException(error.Message); } var trade = OrderToTrade((Order)response); return(trade); }
public override async Task <ExecutionReport> AddOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { var symbol = "XBTUSD"; //HACK Hard code! var volume = BitMexModelConverter.ConvertVolume(signal.Volume); var orderType = BitMexModelConverter.ConvertOrderType(signal.OrderType); var side = BitMexModelConverter.ConvertTradeType(signal.TradeType); var price = (double?)signal.Price; var ct = new CancellationTokenSource(timeout); var response = await _exchangeApi.OrdernewAsync(symbol, orderQty : volume, price : price, clOrdID : signal.OrderId, ordType : orderType, side : side, cancellationToken : ct.Token); if (response is Error error) { throw new ApiException(error.ErrorProperty.Message); } var order = (Order)response; return(BitMexModelConverter.OrderToTrade(order)); }
public async Task <ExecutionReport> Post([FromBody] OrderModel orderModel) { try { if (orderModel == null) { throw new StatusCodeException(HttpStatusCode.BadRequest, "Order has to be specified"); } if (string.IsNullOrEmpty(orderModel.ExchangeName)) { ModelState.AddModelError(nameof(orderModel.ExchangeName), "Exchange cannot be null"); } if (Math.Abs((orderModel.DateTime - DateTime.UtcNow).TotalMilliseconds) >= TimeSpan.FromMinutes(5).TotalMilliseconds) { ModelState.AddModelError(nameof(orderModel.DateTime), "Date and time must be in 5 minutes threshold from UTC now"); } if (orderModel.Price == 0 && orderModel.OrderType != OrderType.Market) { ModelState.AddModelError(nameof(orderModel.Price), "Price have to be declared for non-market orders"); } if (!ModelState.IsValid) { throw new StatusCodeException(HttpStatusCode.BadRequest) { Model = new SerializableError(ModelState) } } ; var instrument = new Instrument(orderModel.ExchangeName, orderModel.Instrument); var tradingSignal = new TradingSignal(instrument, GetUniqueOrderId(orderModel), OrderCommand.Create, orderModel.TradeType, orderModel.Price, orderModel.Volume, DateTime.UtcNow, orderModel.OrderType, orderModel.TimeInForce); var translatedSignal = new TranslatedSignalTableEntity(SignalSource.RestApi, tradingSignal) { ClientIP = HttpContext.Connection.RemoteIpAddress.ToString() }; try { var result = await Application.GetExchange(orderModel.ExchangeName) .AddOrderAndWaitExecution(tradingSignal, translatedSignal, _timeout); translatedSignal.SetExecutionResult(result); return(result); } catch (Exception e) { translatedSignal.Failure(e); throw; } finally { await _translatedSignalsRepository.SaveAsync(translatedSignal); } } catch (StatusCodeException) { throw; } catch (Exception e) { throw new StatusCodeException(HttpStatusCode.InternalServerError, e.Message, e); } }
public Task <Dictionary <string, decimal> > GetAccountBalance(TranslatedSignalTableEntity translatedSignal, CancellationToken cancellationToken) { return(MakePostRequestAsync <Dictionary <string, decimal> >("Balance", new AccountBalanceRequest(), translatedSignal, cancellationToken)); }
public override async Task <ExecutionReport> AddOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { var cts = new CancellationTokenSource(timeout); var request = _converter.CreateNewOrderSingle(signal); var response = await _tradeSessionConnector.AddOrderAsync(request, cts.Token); return(_converter.ConvertExecutionReport(response)); }
public Task <IEnumerable <TranslatedSignalTableEntity> > GetTop(int count) { return(_tableStorage.GetTopRecordsAsync(TranslatedSignalTableEntity.GeneratePartitionKey(), count)); }
public abstract Task <ExecutionReport> CancelOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout);
public Task <AddStandardOrderResponse> AddOrder(TradingSignal tradingSignal, TranslatedSignalTableEntity translatedSignal, CancellationToken cancellationToken) { var request = new AddStandardOrderRequest(tradingSignal, _currencySymbols); return(MakePostRequestAsync <AddStandardOrderResponse>("AddOrder", request, translatedSignal, cancellationToken)); }
public Task <CancelOrderResult> CancelOrder(string txId, TranslatedSignalTableEntity translatedSignal) { var request = new CancelOrderRequest(txId); return(MakePostRequestAsync <CancelOrderResult>("CancelOrder", request, translatedSignal, CancellationToken.None)); }
public override Task <ExecutionReport> CancelOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { throw new NotImplementedException(); }
private async Task <T> MakePostRequestAsync <T>(string url, IKrakenRequest request, TranslatedSignalTableEntity translatedSignal, CancellationToken cancellationToken) { try { var now = DateTime.UtcNow; if ((now - lastRequestTime).TotalSeconds <= 2) { await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); } lastRequestTime = DateTime.UtcNow; var content = CreateHttpContent(request, nonceProvider.GetNonce(), url); var response = await apiClient.MakePostRequestAsync <ResponseBase <T> >($"{endpointUrl}/{url}", content, translatedSignal.RequestSent, translatedSignal.ResponseReceived, cancellationToken); if (response.Error.Any()) { if (response.Error.Any(x => x == "EOrder:Insufficient funds")) { throw new InsufficientFundsException(); } else { throw new ApiException(string.Join("; ", response.Error)); } } return(response.Result); } catch (Exception e) { translatedSignal?.Failure(e); throw; } }
public override async Task <ExecutionReport> CancelOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { var cts = new CancellationTokenSource(timeout); if (!long.TryParse(signal.OrderId, out var id)) { throw new ApiException("Bitfinex order id can be only integer"); } var response = await _exchangeApi.CancelOrder(id, cts.Token); if (response is Error error) { throw new ApiException(error.Message); } var trade = OrderToTrade((Order)response); return(trade); }
private async Task HandleCreation(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, Exchange exchange) { try { if (!signal.IsTimeInThreshold(tradingSignalsThreshold)) { translatedSignal.Failure("The signal is too old"); await logger.WriteInfoAsync(nameof(TradingSignalsHandler), nameof(HandleCreation), signal.ToString(), "Skipping old signal"); return; } var executedTrade = await exchange.AddOrderAndWaitExecution(signal, translatedSignal, apiTimeout); bool orderAdded = executedTrade.ExecutionStatus == OrderExecutionStatus.New || executedTrade.ExecutionStatus == OrderExecutionStatus.Pending; bool orderFilled = executedTrade.ExecutionStatus == OrderExecutionStatus.Fill || executedTrade.ExecutionStatus == OrderExecutionStatus.PartialFill; if (orderAdded || orderFilled) { await logger.WriteInfoAsync(nameof(TradingSignalsHandler), nameof(HandleCreation), signal.ToString(), "Created new order"); } else { await logger.WriteWarningAsync(nameof(TradingSignalsHandler), nameof(HandleCreation), signal.ToString(), $"Added order is in unexpected status: {executedTrade}"); translatedSignal.Failure($"Added order is in unexpected status: {executedTrade}"); } await _acknowledHandler.Handle(CreateAcknowledgement(exchange, orderAdded, signal, translatedSignal)); if (orderFilled) { await _tradeHandler.Handle(executedTrade); } } catch (ApiException e) { await logger.WriteInfoAsync(nameof(TradingSignalsHandler), nameof(HandleCreation), signal.ToString(), e.Message); translatedSignal.Failure(e); await _acknowledHandler.Handle(CreateAcknowledgement(exchange, false, signal, translatedSignal, e)); } catch (Exception e) { await logger.WriteErrorAsync(nameof(TradingSignalsHandler), nameof(HandleCreation), signal.ToString(), e); translatedSignal.Failure(e); await _acknowledHandler.Handle(CreateAcknowledgement(exchange, false, signal, translatedSignal, e)); } }
public override async Task <ExecutionReport> CancelOrderAndWaitExecution(TradingSignal signal, TranslatedSignalTableEntity translatedSignal, TimeSpan timeout) { var result = await privateData.CancelOrder(signal.OrderId, translatedSignal); var executedTrade = new ExecutionReport(signal.Instrument, DateTime.UtcNow, signal.Price ?? 0, signal.Volume, signal.TradeType, signal.OrderId, result.Pending ? OrderExecutionStatus.Pending : OrderExecutionStatus.Cancelled); translatedSignal.SetExecutionResult(executedTrade); return(executedTrade); }
public void Save(TranslatedSignalTableEntity translatedSignal) { SaveAsync(translatedSignal).Wait(); }