1- /*
1+ /*
22MIT LICENSE
33
44Copyright 2017 Digital Ruby, LLC - http://www.digitalruby.com
@@ -30,67 +30,73 @@ The above copyright notice and this permission notice shall be included in all c
3030
3131namespace ExchangeSharp
3232{
33- public partial class ExchangeBittrexAPI
34- {
33+ public partial class ExchangeBittrexAPI
34+ {
3535
3636#if HAS_SIGNALR
3737
38- /// <summary>
39- /// Ideally this would be one instance per exchange instance, but Bittrex simply does not work if you request multiple end points over a single hub connection, sigh...
40- /// </summary>
41- public sealed class BittrexWebSocketManager : SignalrManager
42- {
43- public BittrexWebSocketManager ( ) : base ( "https://socket.bittrex.com/signalr" , "c2" )
44- {
45- FunctionNamesToFullNames [ "uS" ] = "SubscribeToSummaryDeltas" ;
46- FunctionNamesToFullNames [ "uE" ] = "SubscribeToExchangeDeltas" ;
47- }
38+ /// <summary>
39+ /// Ideally this would be one instance per exchange instance, but Bittrex simply does not work if you request multiple end points over a single hub connection, sigh...
40+ /// </summary>
41+ public sealed class BittrexWebSocketManager : SignalrManager
42+ {
43+ public BittrexWebSocketManager ( ) : base ( "https://socket.bittrex.com/signalr" , "c2" )
44+ {
45+ FunctionNamesToFullNames [ "uS" ] = "SubscribeToSummaryDeltas" ;
46+ FunctionNamesToFullNames [ "uE" ] = "SubscribeToExchangeDeltas" ;
47+ }
4848
49- /// <summary>
50- /// Subscribe to all market summaries
51- /// </summary>
52- /// <param name="callback">Callback</param>
53- /// <param name="marketSymbols">Symbols</param>
54- /// <returns>IDisposable to close the socket</returns>
55- public async Task < IWebSocket > SubscribeToSummaryDeltasAsync ( Func < string , Task > callback , params string [ ] marketSymbols )
56- {
57- SignalrManager . SignalrSocketConnection conn = new SignalrManager . SignalrSocketConnection ( this ) ;
58- await conn . OpenAsync ( "uS" , callback ) ;
59- return conn ;
60- }
49+ /// <summary>
50+ /// Subscribe to all market summaries
51+ /// </summary>
52+ /// <param name="callback">Callback</param>
53+ /// <param name="marketSymbols">Symbols</param>
54+ /// <returns>IDisposable to close the socket</returns>
55+ public async Task < IWebSocket > SubscribeToSummaryDeltasAsync ( Func < string , Task > callback , params string [ ] marketSymbols )
56+ {
57+ SignalrManager . SignalrSocketConnection conn = new SignalrManager . SignalrSocketConnection ( this ) ;
58+ await conn . OpenAsync ( "uS" , callback ) ;
59+ return conn ;
60+ }
6161
62- /// <summary>
63- /// Subscribe to order book updates
64- /// </summary>
65- /// <param name="callback">Callback</param>
66- /// <param name="marketSymbols">The market symbols to subscribe to</param>
67- /// <returns>IDisposable to close the socket</returns>
68- public async Task < IWebSocket > SubscribeToExchangeDeltasAsync ( Func < string , Task > callback , params string [ ] marketSymbols )
69- {
70- SignalrManager . SignalrSocketConnection conn = new SignalrManager . SignalrSocketConnection ( this ) ;
71- List < object [ ] > paramList = new List < object [ ] > ( ) ;
72- foreach ( string marketSymbol in marketSymbols )
73- {
74- paramList . Add ( new object [ ] { marketSymbol } ) ;
75- }
76- await conn . OpenAsync ( "uE" , callback , 0 , paramList . ToArray ( ) ) ;
77- return conn ;
78- }
79- }
62+ /// <summary>
63+ /// Subscribe to order book updates
64+ /// </summary>
65+ /// <param name="callback">Callback</param>
66+ /// <param name="marketSymbols">The market symbols to subscribe to</param>
67+ /// <returns>IDisposable to close the socket</returns>
68+ public async Task < IWebSocket > SubscribeToExchangeDeltasAsync ( Func < string , Task > callback , params string [ ] marketSymbols )
69+ {
70+ SignalrManager . SignalrSocketConnection conn = new SignalrManager . SignalrSocketConnection ( this ) ;
71+ List < object [ ] > paramList = new List < object [ ] > ( ) ;
72+ foreach ( string marketSymbol in marketSymbols )
73+ {
74+ paramList . Add ( new object [ ] { ReverseMarketNameForWS ( ( marketSymbol ) . ToStringInvariant ( ) ) } ) ;
75+ }
76+ await conn . OpenAsync ( "uE" , callback , 0 , paramList . ToArray ( ) ) ;
77+ return conn ;
78+ }
79+ }
8080
8181 private BittrexWebSocketManager webSocket ;
8282
83- protected override async Task < IWebSocket > OnGetTickersWebSocketAsync ( Action < IReadOnlyCollection < KeyValuePair < string , ExchangeTicker > > > callback , params string [ ] marketSymbols )
84- {
85- HashSet < string > filter = new HashSet < string > ( ) ;
86- foreach ( string marketSymbol in marketSymbols )
87- {
88- filter . Add ( marketSymbol ) ;
89- }
90- async Task innerCallback ( string json )
91- {
92- #region sample json
93- /*
83+ public static string ReverseMarketNameForWS ( string WebSocketFeedMarketName )
84+ {
85+ var pair = WebSocketFeedMarketName . Split ( '-' ) ;
86+ return ( pair [ 1 ] + '-' + pair [ 0 ] ) . ToUpperInvariant ( ) ;
87+ }
88+
89+ protected override async Task < IWebSocket > OnGetTickersWebSocketAsync ( Action < IReadOnlyCollection < KeyValuePair < string , ExchangeTicker > > > callback , params string [ ] marketSymbols )
90+ {
91+ HashSet < string > filter = new HashSet < string > ( ) ;
92+ foreach ( string marketSymbol in marketSymbols )
93+ {
94+ filter . Add ( marketSymbol ) ;
95+ }
96+ async Task innerCallback ( string json )
97+ {
98+ #region sample json
99+ /*
94100 {
95101 Nonce : int,
96102 Deltas :
@@ -115,60 +121,62 @@ async Task innerCallback(string json)
115121 */
116122 #endregion
117123
118- var freshTickers = new Dictionary < string , ExchangeTicker > ( StringComparer . OrdinalIgnoreCase ) ;
119- JToken token = JToken . Parse ( json ) ;
120- token = token [ "D" ] ;
121- foreach ( JToken ticker in token )
122- {
123- string marketName = ticker [ "M" ] . ToStringInvariant ( ) ;
124- if ( filter . Count != 0 && ! filter . Contains ( marketName ) )
125- {
126- continue ;
127- }
128- var ( baseCurrency , quoteCurrency ) = await ExchangeMarketSymbolToCurrenciesAsync ( marketName ) ;
129- decimal last = ticker [ "l" ] . ConvertInvariant < decimal > ( ) ;
130- decimal ask = ticker [ "A" ] . ConvertInvariant < decimal > ( ) ;
131- decimal bid = ticker [ "B" ] . ConvertInvariant < decimal > ( ) ;
132- decimal baseCurrencyVolume = ticker [ "V" ] . ConvertInvariant < decimal > ( ) ;
133- decimal quoteCurrencyVolume = ticker [ "m" ] . ConvertInvariant < decimal > ( ) ; //NOTE: Bittrex uses the term BaseVolume when referring to QuoteCurrencyVolume
134- DateTime timestamp = CryptoUtility . UnixTimeStampToDateTimeMilliseconds ( ticker [ "T" ] . ConvertInvariant < long > ( ) ) ;
135- var t = new ExchangeTicker
136- {
137- MarketSymbol = marketName ,
138- Ask = ask ,
139- Bid = bid ,
140- Last = last ,
141- Volume = new ExchangeVolume
142- {
143- BaseCurrencyVolume = baseCurrencyVolume ,
144- BaseCurrency = baseCurrency ,
145- QuoteCurrencyVolume = quoteCurrencyVolume ,
146- QuoteCurrency = quoteCurrency ,
147- Timestamp = timestamp
148- }
149- } ;
150- freshTickers [ marketName ] = t ;
151- }
152- callback ( freshTickers ) ;
153- }
154- return await new BittrexWebSocketManager ( ) . SubscribeToSummaryDeltasAsync ( innerCallback , marketSymbols ) ;
155- }
124+ var freshTickers = new Dictionary < string , ExchangeTicker > ( StringComparer . OrdinalIgnoreCase ) ;
125+ JToken token = JToken . Parse ( json ) ;
126+ token = token [ "D" ] ;
127+ foreach ( JToken ticker in token )
128+ {
156129
157- protected override async Task < IWebSocket > OnGetDeltaOrderBookWebSocketAsync
158- (
159- Action < ExchangeOrderBook > callback ,
160- int maxCount = 20 ,
161- params string [ ] marketSymbols
162- )
163- {
164- if ( marketSymbols == null || marketSymbols . Length == 0 )
165- {
166- marketSymbols = ( await GetMarketSymbolsAsync ( ) ) . ToArray ( ) ;
167- }
168- Task innerCallback ( string json )
169- {
170- #region sample json
171- /*
130+
131+ string marketName = ReverseMarketNameForWS ( ticker [ "M" ] . ToStringInvariant ( ) ) ;
132+ if ( filter . Count != 0 && ! filter . Contains ( marketName ) )
133+ {
134+ continue ;
135+ }
136+ var ( baseCurrency , quoteCurrency ) = await ExchangeMarketSymbolToCurrenciesAsync ( marketName ) ;
137+ decimal last = ticker [ "l" ] . ConvertInvariant < decimal > ( ) ;
138+ decimal ask = ticker [ "A" ] . ConvertInvariant < decimal > ( ) ;
139+ decimal bid = ticker [ "B" ] . ConvertInvariant < decimal > ( ) ;
140+ decimal baseCurrencyVolume = ticker [ "V" ] . ConvertInvariant < decimal > ( ) ;
141+ decimal quoteCurrencyVolume = ticker [ "m" ] . ConvertInvariant < decimal > ( ) ; //NOTE: Bittrex uses the term BaseVolume when referring to QuoteCurrencyVolume
142+ DateTime timestamp = CryptoUtility . UnixTimeStampToDateTimeMilliseconds ( ticker [ "T" ] . ConvertInvariant < long > ( ) ) ;
143+ var t = new ExchangeTicker
144+ {
145+ MarketSymbol = marketName ,
146+ Ask = ask ,
147+ Bid = bid ,
148+ Last = last ,
149+ Volume = new ExchangeVolume
150+ {
151+ BaseCurrencyVolume = baseCurrencyVolume ,
152+ BaseCurrency = baseCurrency ,
153+ QuoteCurrencyVolume = quoteCurrencyVolume ,
154+ QuoteCurrency = quoteCurrency ,
155+ Timestamp = timestamp
156+ }
157+ } ;
158+ freshTickers [ marketName ] = t ;
159+ }
160+ callback ( freshTickers ) ;
161+ }
162+ return await new BittrexWebSocketManager ( ) . SubscribeToSummaryDeltasAsync ( innerCallback , marketSymbols ) ;
163+ }
164+
165+ protected override async Task < IWebSocket > OnGetDeltaOrderBookWebSocketAsync
166+ (
167+ Action < ExchangeOrderBook > callback ,
168+ int maxCount = 20 ,
169+ params string [ ] marketSymbols
170+ )
171+ {
172+ if ( marketSymbols == null || marketSymbols . Length == 0 )
173+ {
174+ marketSymbols = ( await GetMarketSymbolsAsync ( ) ) . ToArray ( ) ;
175+ }
176+ Task innerCallback ( string json )
177+ {
178+ #region sample json
179+ /*
172180 {
173181 MarketName : string,
174182 Nonce : int,
@@ -200,27 +208,26 @@ Task innerCallback(string json)
200208 ]
201209 }
202210 */
203- #endregion
204-
205- var ordersUpdates = JsonConvert . DeserializeObject < BittrexStreamUpdateExchangeState > ( json ) ;
206- var book = new ExchangeOrderBook ( ) ;
207- foreach ( BittrexStreamOrderBookUpdateEntry ask in ordersUpdates . Sells )
208- {
209- var depth = new ExchangeOrderPrice { Price = ask . Rate , Amount = ask . Quantity } ;
210- book . Asks [ depth . Price ] = depth ;
211- }
212-
213- foreach ( BittrexStreamOrderBookUpdateEntry bid in ordersUpdates . Buys )
214- {
215- var depth = new ExchangeOrderPrice { Price = bid . Rate , Amount = bid . Quantity } ;
216- book . Bids [ depth . Price ] = depth ;
217- }
211+ #endregion
218212
219- book . MarketSymbol = ordersUpdates . MarketName ;
220- book . SequenceId = ordersUpdates . Nonce ;
221- callback ( book ) ;
222- return Task . CompletedTask ;
223- }
213+ var ordersUpdates = JsonConvert . DeserializeObject < BittrexStreamUpdateExchangeState > ( json ) ;
214+ var book = new ExchangeOrderBook ( ) ;
215+ foreach ( BittrexStreamOrderBookUpdateEntry ask in ordersUpdates . Sells )
216+ {
217+ var depth = new ExchangeOrderPrice { Price = ask . Rate , Amount = ask . Quantity } ;
218+ book . Asks [ depth . Price ] = depth ;
219+ }
220+ foreach ( BittrexStreamOrderBookUpdateEntry bid in ordersUpdates . Buys )
221+ {
222+ var depth = new ExchangeOrderPrice { Price = bid . Rate , Amount = bid . Quantity } ;
223+ book . Bids [ depth . Price ] = depth ;
224+ }
225+ book . MarketSymbol = ReverseMarketNameForWS ( ordersUpdates . MarketName ) . ToUpperInvariant ( ) ;
226+ book . SequenceId = ordersUpdates . Nonce ;
227+ book . LastUpdatedUtc = DateTime . UtcNow ;
228+ callback ( book ) ;
229+ return Task . CompletedTask ;
230+ }
224231
225232 return await new BittrexWebSocketManager ( ) . SubscribeToExchangeDeltasAsync ( innerCallback , marketSymbols ) ;
226233 }
@@ -236,7 +243,7 @@ async Task innerCallback(string json)
236243 var ordersUpdates = JsonConvert . DeserializeObject < BittrexStreamUpdateExchangeState > ( json ) ;
237244 foreach ( var fill in ordersUpdates . Fills )
238245 {
239- await callback ( new KeyValuePair < string , ExchangeTrade > ( ordersUpdates . MarketName , new ExchangeTrade ( )
246+ await callback ( new KeyValuePair < string , ExchangeTrade > ( ReverseMarketNameForWS ( ordersUpdates . MarketName ) , new ExchangeTrade ( )
240247 {
241248 Amount = fill . Quantity ,
242249 // Bittrex doesn't currently send out FillId on socket.bittrex.com, only beta.bittrex.com, but this will be ready when they start
0 commit comments