private BitfinexWebSocketWrapper SubscribeChannel(string channelName, Symbol symbol) { var channel = new Channel(channelName, symbol); var webSocket = GetFreeWebSocket(channel); webSocket.Send(JsonConvert.SerializeObject(new { @event = "subscribe", channel = channelName, pair = _symbolMapper.GetBrokerageSymbol(symbol) })); return(webSocket); }
/// <summary> /// Places a new order and assigns a new broker ID to the order /// </summary> /// <param name="order">The order to be placed</param> /// <returns>True if the request for a new order has been placed, false otherwise</returns> public override bool PlaceOrder(Order order) { var parameters = new JsonObject { { "symbol", _symbolMapper.GetBrokerageSymbol(order.Symbol) }, { "amount", order.Quantity.ToStringInvariant() }, { "type", ConvertOrderType(_algorithm.BrokerageModel.AccountType, order.Type) }, { "price", GetOrderPrice(order).ToStringInvariant() } }; var orderProperties = order.Properties as BitfinexOrderProperties; if (orderProperties != null) { if (order.Type == OrderType.Limit) { var flags = 0; if (orderProperties.Hidden) { flags |= OrderFlags.Hidden; } if (orderProperties.PostOnly) { flags |= OrderFlags.PostOnly; } parameters.Add("flags", flags); } } var clientOrderId = GetNextClientOrderId(); parameters.Add("cid", clientOrderId); _orderMap.TryAdd(clientOrderId, order); var obj = new JsonArray { 0, "on", null, parameters }; var json = JsonConvert.SerializeObject(obj); WebSocket.Send(json); return(true); }
/// <summary> /// Gets the history for the requested security /// </summary> /// <param name="request">The historical data request</param> /// <returns>An enumerable of bars covering the span specified in the request</returns> public override IEnumerable <BaseData> GetHistory(Data.HistoryRequest request) { if (request.Resolution == Resolution.Tick || request.Resolution == Resolution.Second) { OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidResolution", $"{request.Resolution} resolution not supported, no history returned")); yield break; } string resolution = ConvertResolution(request.Resolution); long resolutionInMS = (long)request.Resolution.ToTimeSpan().TotalMilliseconds; string symbol = _symbolMapper.GetBrokerageSymbol(request.Symbol); long startMTS = (long)Time.DateTimeToUnixTimeStamp(request.StartTimeUtc) * 1000; long endMTS = (long)Time.DateTimeToUnixTimeStamp(request.EndTimeUtc) * 1000; string endpoint = $"v2/candles/trade:{resolution}:t{symbol}/hist?limit=1000&sort=1"; while ((endMTS - startMTS) > resolutionInMS) { var timeframe = $"&start={startMTS}&end={endMTS}"; var restRequest = new RestRequest(endpoint + timeframe, Method.GET); var response = ExecuteRestRequest(restRequest); if (response.StatusCode != HttpStatusCode.OK) { throw new Exception($"BitfinexBrokerage.GetHistory: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}"); } var candles = JsonConvert.DeserializeObject <object[][]>(response.Content) .Select(entries => new Messages.Candle(entries)) .ToList(); startMTS = candles.Last().Timestamp + resolutionInMS; var period = request.Resolution.ToTimeSpan(); foreach (var candle in candles) { yield return(new TradeBar() { Time = Time.UnixMillisecondTimeStampToDateTime(candle.Timestamp), Symbol = request.Symbol, Low = candle.Low, High = candle.High, Open = candle.Open, Close = candle.Close, Volume = candle.Volume, Value = candle.Close, DataType = MarketDataType.TradeBar, Period = period, EndTime = Time.UnixMillisecondTimeStampToDateTime(candle.Timestamp + (long)period.TotalMilliseconds) }); } } }
/// <summary> /// Gets the history for the requested security /// </summary> /// <param name="request">The historical data request</param> /// <returns>An enumerable of bars covering the span specified in the request</returns> public override IEnumerable <BaseData> GetHistory(Data.HistoryRequest request) { if (request.Symbol.SecurityType != SecurityType.Crypto) { OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidSecurityType", $"{request.Symbol.SecurityType} security type not supported, no history returned")); yield break; } if (request.Resolution == Resolution.Tick || request.Resolution == Resolution.Second) { OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidResolution", $"{request.Resolution} resolution not supported, no history returned")); yield break; } if (request.StartTimeUtc >= request.EndTimeUtc) { OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidDateRange", "The history request start date must precede the end date, no history returned")); yield break; } // if the end time cannot be rounded to resolution without a remainder if (request.EndTimeUtc.Ticks % request.Resolution.ToTimeSpan().Ticks > 0) { // give a warning and return OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidEndTime", "The history request's end date is not a full multiple of a resolution. " + "Bitfinex API only allows to support trade bar history requests. The start and end dates " + "of a such request are expected to match exactly with the beginning of the first bar and ending of the last")); yield break; } string resolution = ConvertResolution(request.Resolution); long resolutionInMsec = (long)request.Resolution.ToTimeSpan().TotalMilliseconds; string symbol = _symbolMapper.GetBrokerageSymbol(request.Symbol); long startMsec = (long)Time.DateTimeToUnixTimeStamp(request.StartTimeUtc) * 1000; long endMsec = (long)Time.DateTimeToUnixTimeStamp(request.EndTimeUtc) * 1000; string endpoint = $"v2/candles/trade:{resolution}:t{symbol}/hist?limit=1000&sort=1"; var period = request.Resolution.ToTimeSpan(); do { var timeframe = $"&start={startMsec}&end={endMsec}"; var restRequest = new RestRequest(endpoint + timeframe, Method.GET); var response = ExecuteRestRequest(restRequest); if (response.StatusCode != HttpStatusCode.OK) { throw new Exception( $"BitfinexBrokerage.GetHistory: request failed: [{(int) response.StatusCode}] {response.StatusDescription}, " + $"Content: {response.Content}, ErrorMessage: {response.ErrorMessage}"); } // we need to drop the last bar provided by the exchange as its open time is a history request's end time var candles = JsonConvert.DeserializeObject <object[][]>(response.Content) .Select(entries => new Messages.Candle(entries)) .Where(candle => candle.Timestamp != endMsec) .ToList(); // bitfinex exchange may return us an empty result - if we request data for a small time interval // during which no trades occurred - so it's rational to ensure 'candles' list is not empty before // we proceed to avoid an exception to be thrown if (candles.Any()) { startMsec = candles.Last().Timestamp + resolutionInMsec; } else { OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "NoHistoricalData", $"Exchange returned no data for {symbol} on history request " + $"from {request.StartTimeUtc:s} to {request.EndTimeUtc:s}")); yield break; } foreach (var candle in candles) { yield return(new TradeBar() { Time = Time.UnixMillisecondTimeStampToDateTime(candle.Timestamp), Symbol = request.Symbol, Low = candle.Low, High = candle.High, Open = candle.Open, Close = candle.Close, Volume = candle.Volume, Value = candle.Close, DataType = MarketDataType.TradeBar, Period = period, EndTime = Time.UnixMillisecondTimeStampToDateTime(candle.Timestamp + (long)period.TotalMilliseconds) }); } } while (startMsec < endMsec); }