private async Task GetInitialTickPrices() { foreach (var instrument in Instruments) { try { var orderBook = await apiClient.MakeGetRequestAsync <List <OrderBook> >( $"{Config.EndpointUrl}/api/OrderBooks/{instrument.Name}", ctSource.Token); var tickPrices = orderBook.GroupBy(x => x.AssetPair) .Select(g => new TickPrice( new Instrument(Name, g.Key), g.FirstOrDefault()?.Timestamp ?? DateTime.UtcNow, g.FirstOrDefault(ob => !ob.IsBuy)?.Prices.Select(x => x.Price).DefaultIfEmpty(0).Min() ?? 0, g.FirstOrDefault(ob => ob.IsBuy)?.Prices.Select(x => x.Price).DefaultIfEmpty(0).Max() ?? 0) ) .Where(x => x.Ask > 0 && x.Bid > 0); foreach (var tickPrice in tickPrices) { _lastAsks[instrument.Name] = tickPrice.Ask; _lastBids[instrument.Name] = tickPrice.Bid; await _tickPriceHandler.Handle(tickPrice); } } catch (Exception e) { await LykkeLog.WriteErrorAsync(nameof(LykkeExchange), nameof(GetInitialTickPrices), e); } } }
private async Task HandleOrderStatus(LimitOrderMessage message) { foreach (var order in message.Orders.Where(x => x.Order.ClientId == Config.ClientId)) { if (order.Order.Status == OrderStatus.Cancelled) { await LykkeLog.WriteInfoAsync(nameof(LykkeExchange), nameof(HandleOrderStatus), order.ToString(), "Order canceled. Calling ExecutedTradeHandlers"); await _tradeHandler.Handle(new ExecutionReport(new Instrument(Name, order.Order.AssetPairId), DateTime.UtcNow, order.Order.Price ?? 0, Math.Abs(order.Order.Volume), order.Order.Volume < 0 ? TradeType.Sell : TradeType.Buy, order.Order.ExternalId, OrderExecutionStatus.Cancelled)); } else if (order.Order.Status == OrderStatus.Matched && order.Trades.Any()) { await LykkeLog.WriteInfoAsync(nameof(LykkeExchange), nameof(HandleOrderStatus), order.ToString(), "Order executed. Calling ExecutedTradeHandlers"); await _tradeHandler.Handle(new ExecutionReport(new Instrument(Name, order.Order.AssetPairId), order.Trades.Last().Timestamp, order.Order.Price ?? order.Trades.Last().Price ?? 0, Math.Abs(order.Order.Volume - order.Order.RemainingVolume), order.Order.Volume < 0 ? TradeType.Sell : TradeType.Buy, order.Order.ExternalId, OrderExecutionStatus.Fill)); } } }
private async Task LogAsync(Exception ex, [CallerMemberName] string context = null) { if (LykkeLog == null) { return; } await LykkeLog.WriteErrorAsync(_gdaxExchangeTypeName, _gdaxExchangeTypeName, context, ex); }
protected override void StartImpl() { LykkeLog.WriteInfoAsync(nameof(LykkeExchange), nameof(StartImpl), string.Empty, $"Starting {Name} exchange").Wait(); ctSource = new CancellationTokenSource(); //StartWampConnection(); // TODO: wamp sends strange tickprices with ask=bid, temporary switch to direct rabbitmq connection: StartRabbitMqTickPriceSubscription(); StartRabbitMqOrdersSubscription(); OnConnected(); }
private async Task <bool> CheckServerTime(CancellationToken cancellationToken) { var serverTime = await publicData.GetServerTime(cancellationToken); var now = DateTime.UtcNow; long differenceTicks = Math.Abs(serverTime.FromUnixTime.Ticks - now.Ticks); bool differenceInThreshold = differenceTicks <= TimeSpan.FromMinutes(2).Ticks; await LykkeLog.WriteInfoAsync( nameof(Kraken), nameof(KrakenExchange), nameof(pricesJob), $"Server time: {serverTime.FromUnixTime}; now: {now}; difference ticks: {differenceTicks}. In threshold: {differenceInThreshold}"); return(differenceInThreshold); }
private async Task LogAsync(string message, [CallerMemberName] string context = null) { const int maxMessageLength = 32000; if (LykkeLog == null) { return; } if (message.Length >= maxMessageLength) { message = message.Substring(0, maxMessageLength); } await LykkeLog.WriteInfoAsync(_gdaxExchangeTypeName, _gdaxExchangeTypeName, context, message); }
private async Task <TResult> ExecuteApiMethod <TResult>(Func <CancellationToken, Task <TResult> > method, CancellationToken token) { try { var response = await method(token); return(response); } catch (ApiException ex) { await LykkeLog.WriteErrorAsync(nameof(BitfinexExchange), method.Method.Name, ex); throw; } catch (Exception ex) { await LykkeLog.WriteErrorAsync(nameof(BitfinexExchange), method.Method.Name, ex); throw new ApiException(ex.Message, HttpStatusCode.InternalServerError); } }
public override async Task <long> CancelOrder(long orderId, TimeSpan timeout) { using (var cts = new CancellationTokenSource(timeout)) { try { var response = await ExecuteApiMethod(_exchangeApi.CancelOrderAsync, orderId, cts.Token); return(response.Id); } catch (ApiException ex) { if (ex.Message == "Order could not be cancelled.") { LykkeLog.WriteInfo(nameof(BitfinexExchange), null, ex.Message); return(orderId); } throw; } } }
protected override void StartImpl() { ctSource = new CancellationTokenSource(); CheckServerTime(ctSource.Token) .ContinueWith(task => { if (!task.IsFaulted && task.Result) { OnConnected(); } }); if (config.PubQuotesToRabbit) { pricesJob = Task.Run(async() => { var lasts = Instruments.ToDictionary(x => x.Name, x => 0L); while (!ctSource.IsCancellationRequested) { try { foreach (var pair in config.SupportedCurrencySymbols) { SpreadDataResult result; try { result = await publicData.GetSpread(ctSource.Token, pair.ExchangeSymbol, lasts[pair.LykkeSymbol]); } catch (Exception e) { await LykkeLog.WriteErrorAsync( nameof(Kraken), nameof(KrakenExchange), nameof(pricesJob), e); continue; } lasts[pair.LykkeSymbol] = result.Last; var prices = result.Data.Single().Value.Select(x => new TickPrice(Instruments.Single(i => i.Name == pair.LykkeSymbol), x.Time, x.Ask, x.Bid)).ToArray(); if (prices.Any()) { if (prices.Length == 1 && prices[0].Time == DateTimeUtils.FromUnix(lasts[pair.LykkeSymbol])) { // If there is only one price and it has timestamp of last one, ignore it. } else { foreach (var tickPrice in prices) { await _tickPriceHandler.Handle(tickPrice); } } } await Task.Delay(TimeSpan.FromSeconds(10), ctSource.Token); } await CheckExecutedOrders(); } catch (Exception e) { await LykkeLog.WriteErrorAsync( nameof(Kraken), nameof(KrakenExchange), nameof(pricesJob), e); } } OnStopped(); }); } }
protected override void StartImpl() { ctSource = new CancellationTokenSource(); var random = new Random(); var nPoints = 10000000; var gbms = Instruments.ToDictionary(x => x, x => new GeometricalBrownianMotion(1.0, 0.95, 1.0, nPoints, 0, random)); streamJob = Task.Run(async() => { OnConnected(); while (!ctSource.IsCancellationRequested) { foreach (var instrument in Instruments) { try { var currentTickPrice = new TickPrice(instrument, DateTime.UtcNow, Math.Round((decimal)gbms[instrument].GenerateNextValue(), 6)); lock (syncRoot) { var trades = new List <ExecutionReport>(); var executedOrders = new List <TradingSignal>(); foreach (var tradingSignal in ActualSignals[instrument.Name].Where(x => x.Volume > 0)) { if (tradingSignal.TradeType == TradeType.Buy && currentTickPrice.Ask <= tradingSignal.Price) { var trade = new ExecutionReport( instrument, DateTime.UtcNow, tradingSignal.Price.Value, tradingSignal.Volume, TradeType.Buy, tradingSignal.OrderId, OrderExecutionStatus.Fill); trades.Add(trade); executedOrders.Add(tradingSignal); LykkeLog.WriteInfoAsync(nameof(StubExchange), nameof(StartImpl), nameof(streamJob), $"EXECUTION of order {tradingSignal} by price {trade.Price}").Wait(); } else if (tradingSignal.TradeType == TradeType.Sell && currentTickPrice.Bid >= tradingSignal.Price) { var trade = new ExecutionReport(instrument, DateTime.UtcNow, tradingSignal.Price.Value, tradingSignal.Volume, TradeType.Sell, tradingSignal.OrderId, OrderExecutionStatus.Fill); trades.Add(trade); executedOrders.Add(tradingSignal); LykkeLog.WriteInfoAsync(nameof(StubExchange), nameof(streamJob), trade.ToString(), $"EXECUTION of order {tradingSignal} by price {trade.Price}").Wait(); } } foreach (var signal in executedOrders) { ActualSignals[instrument.Name].Remove(signal); LykkeLog.WriteInfoAsync(nameof(StubExchange), nameof(StartImpl), nameof(streamJob), $"Trading order {signal} was removed from actual signals as executed").Wait(); } trades.ForEach(async x => { Positions[instrument.Name].AddTrade(x); try { await _tradeHandler.Handle(x); } catch (Exception e) { Console.WriteLine(e); } }); if (++counter % 100 == 0) { LykkeLog.WriteInfoAsync(nameof(StubExchange), nameof(StartImpl), nameof(streamJob), $"Step {counter}, total PnL: {Positions[instrument.Name].GetPnL(currentTickPrice.Mid)}").Wait(); } } // TODO: deal with awaitable. I don't want to wait here for Azure and Rabbit connections await _tickPriceHandler.Handle(currentTickPrice); } catch (Exception e) { Console.WriteLine(e); } await Task.Delay(config.PricesIntervalInMilliseconds, ctSource.Token); } } OnStopped(); }); }