//using Cex.Models; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using System; using System.Collections.Generic; using System.Text; using WebSocket.Interfaces; using System.Linq; using System.Threading.Tasks; using System.Globalization; using System.Text.RegularExpressions; using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Linq; using System.IO; namespace WebSocket.Clients { public class LowerCaseUnderscoreContractResolver : DefaultContractResolver { private Regex regex = new Regex("(?!(^[A-Z]))([A-Z])"); protected override string ResolvePropertyName(string propertyName) { return regex.Replace(propertyName, "_$2"); //.ToLower(); } } public class MicrosoftSecondsDateTimeConverter : DateTimeConverterBase { private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var token = JToken.Load(reader); if (token.Type == JTokenType.Integer) { var dt = epoch.AddMilliseconds((long)token); return dt; } // Not a Microsoft date. return new JsonSerializer().Deserialize(token.CreateReader(), objectType); } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } public class BinanceStreamModel { [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string e { get; set; } [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string E { get; set; } [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string s { get; set; } [JsonProperty("k", NullValueHandling = NullValueHandling.Ignore)] public BinanceTickerData Data { get; set; } [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string Ok { get; set; } [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string Error { get; set; } } public class BinanceTickerData { [JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)] [JsonConverter(typeof(MicrosoftSecondsDateTimeConverter))] public DateTime StartTime { get; set; } // : 123400000, // Kline start time [JsonProperty("T", NullValueHandling = NullValueHandling.Ignore)] [JsonConverter(typeof(MicrosoftSecondsDateTimeConverter))] public DateTime CloseTime { get; set; } //"T": 123460000, // Kline close time [JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)] public string Symbol { get; set; } // "s": "BNBBTC", // Symbol [JsonProperty("o", NullValueHandling = NullValueHandling.Ignore)] public decimal OpenPrice { get; set; } // "o": "0.0010", // Open price [JsonProperty("c", NullValueHandling = NullValueHandling.Ignore)] public decimal ClosePrice { get; set; } // "c": "0.0020", // Close price [JsonProperty("h", NullValueHandling = NullValueHandling.Ignore)] public decimal HighPrice { get; set; } // "h": "0.0025", // High price [JsonProperty("l", NullValueHandling = NullValueHandling.Ignore)] public decimal LowPrice { get; set; } // "l": "0.0015", // Low price [JsonProperty("v", NullValueHandling = NullValueHandling.Ignore)] public decimal Volume { get; set; } // "v": "1000", // Base asset volume // "i": "1m", // Interval // "f": 100, // First trade ID // "L": 200, // Last trade ID // "n": 100, // Number of trades // "x": false, // Is this kline closed? // "q": "1.0000", // Quote asset volume // "V": "500", // Taker buy base asset volume // "Q": "0.500", // Taker buy quote asset volume // "B": "123456" // Ignore // } } public class BinanceModel: Command { public string stream { get; set; } public BinanceStreamModel data { get; set; } //"e": "kline", // Event type //"E": 123456789, // Event time //"s": "BNBBTC", // Symbol //"k": { // "t": 123400000, // Kline start time // "T": 123460000, // Kline close time // "s": "BNBBTC", // Symbol // "i": "1m", // Interval // "f": 100, // First trade ID // "L": 200, // Last trade ID // "o": "0.0010", // Open price // "c": "0.0020", // Close price // "h": "0.0025", // High price // "l": "0.0015", // Low price // "v": "1000", // Base asset volume // "n": 100, // Number of trades // "x": false, // Is this kline closed? // "q": "1.0000", // Quote asset volume // "V": "500", // Taker buy base asset volume // "Q": "0.500", // Taker buy quote asset volume // "B": "123456" // Ignore // } } public class BinanceClient : IWebClient { #region Members //private Dictionary data = new Dictionary(); private Dictionary> CommandAnswers = new Dictionary>(); private IWebSocketForClient server; #endregion /// /// ctor /// /// public BinanceClient(IWebSocketForClient server) { this.server = server; Register(); } public void Connected() { //SendAuth(); //wait connected message? } private void Update(string name, Command command, string message, Type dataType) { var data = new Dictionary(); data[name]= JsonConvert.DeserializeObject(message, dataType, GetSettings()); this.server.DataChanged("Binance", GetMarketOHLCVData(data)); //this.server.updateData.Invoke(this.server.ExchangeId, "Binance", GetMarketOHLCVData(data)); } private void Register() { On("kline", (c, obj) => Update(((BinanceModel)c).stream, c, obj, typeof(BinanceModel))); //On("connected", (c, obj) => SendAuth()); //On("auth", (c, obj) => { if (c.Ok != null) { SetSubscribes(); } }); On("ping", (c, obj) => SendPong()); } public void Message(string message) { var parse = JsonConvert.DeserializeObject(message, GetSettings()); //var arg = ((BinanceModel)parse); if (parse.data != null && CommandAnswers.ContainsKey(parse.data.e)) { CommandAnswers[parse.data.e](parse, message); } } public void Tick() { //'ticker' or 'get-balance' } public void Data(byte[] data) { } public void Data(Dictionary data) { } public void Closed(bool byUser, string reason) { //subscribed = false; } public void Error(Exception ex) { } public void Error(string reason) { } public void SendFailed(string data, Exception ex) { } private void Send(object obj) { server.Send(JsonConvert.SerializeObject(obj, GetSettings())); } private void SendPong() { Send(new Command() { E = "pong" }); } private void On(string name, Action command) { CommandAnswers[name] = command; } private void Off(string name) { if (CommandAnswers.ContainsKey(name)) { CommandAnswers.Remove(name); } } public Dictionary GetMarketOHLCVData(Dictionary data) { return data.Select(x => new MarketOHLCVData() { PairSymbol = ((BinanceModel)x.Value).data.s, HighPrice = ((BinanceModel)x.Value).data.Data.HighPrice, LowPrice = ((BinanceModel)x.Value).data.Data.LowPrice, OpenPrice = ((BinanceModel)x.Value).data.Data.OpenPrice, ClosePrice = ((BinanceModel)x.Value).data.Data.ClosePrice, Volume = ((BinanceModel)x.Value).data.Data.Volume, TimeStamp = ((BinanceModel)x.Value).data.Data.CloseTime } as IMarketOHLCVData).ToDictionary(x => x.PairSymbol); } private JsonSerializerSettings GetSettings() { var settings = new JsonSerializerSettings() { ContractResolver = new LowerCaseUnderscoreContractResolver() //Formatting = Formatting.Indented }; //settings.Converters.Add(new MicrosoftSecondsDateTimeConverter()); return settings; } } }