@@ -10,33 +10,36 @@ namespace ExchangeSharp
1010
1111 public sealed partial class ExchangeBTSEAPI : ExchangeAPI
1212 {
13- public override string BaseUrl { get ; set ; } = "https://api.btse.com" ;
13+ public override string BaseUrl { get ; set ; } = "https://api.btse.com/spot" ;
14+ public const string TestnetUrl = "https://testapi.btse.io/spot" ;
1415
16+ public ExchangeBTSEAPI ( )
17+ {
18+ NonceStyle = NonceStyle . UnixMillisecondsString ;
19+ }
1520 protected override async Task < IEnumerable < string > > OnGetMarketSymbolsAsync ( )
1621 {
1722 return ( await GetTickersAsync ( ) ) . Select ( pair => pair . Key ) ;
1823 }
1924
2025 protected override async Task < IEnumerable < KeyValuePair < string , ExchangeTicker > > > OnGetTickersAsync ( )
2126 {
22- JToken allPairs = await MakeJsonRequestAsync < JArray > ( "/spot/api/v3/market_summary" ) ;
23- var tasks = allPairs . Select ( async token => await this . ParseTickerAsync ( token ,
24- token [ "symbol" ] . Value < string > ( ) , "lowestAsk" , "highestBid" , "last" , "volume" , null ,
25- null , TimestampType . UnixMilliseconds , "base" , "quote" , "symbol" ) ) ;
27+ JToken allPairs = await MakeJsonRequestAsync < JToken > ( "/api/v3.1/market_summary" , BaseUrl ) ;
28+ var tasks = allPairs . Select ( async token => await ParseBTSETicker ( token ,
29+ token [ "symbol" ] . Value < string > ( ) ) ) ;
2630
2731 return ( await Task . WhenAll ( tasks ) ) . Select ( ticker =>
2832 new KeyValuePair < string , ExchangeTicker > ( ticker . MarketSymbol , ticker ) ) ;
2933 }
3034
3135 protected override async Task < ExchangeTicker > OnGetTickerAsync ( string marketSymbol )
3236 {
33- JToken ticker = await MakeJsonRequestAsync < JObject > ( "/spot/ api/v3/market_summary" , null ,
37+ JToken ticker = await MakeJsonRequestAsync < JToken > ( "/api/v3.1 /market_summary" , BaseUrl ,
3438 new Dictionary < string , object > ( )
3539 {
3640 { "symbol" , marketSymbol }
3741 } ) ;
38- return await this . ParseTickerAsync ( ticker , marketSymbol , "lowestAsk" , "highestBid" , "last" , "volume" , null ,
39- null , TimestampType . UnixMilliseconds , "base" , "quote" , "symbol" ) ;
42+ return await ParseBTSETicker ( ticker , marketSymbol ) ;
4043 }
4144
4245 protected override async Task < IEnumerable < MarketCandle > > OnGetCandlesAsync ( string marketSymbol ,
@@ -59,7 +62,7 @@ protected override async Task<IEnumerable<MarketCandle>> OnGetCandlesAsync(strin
5962 payload . Add ( "end" , startDate . Value . UnixTimestampFromDateTimeMilliseconds ( ) ) ;
6063 }
6164
62- JToken ticker = await MakeJsonRequestAsync < JArray > ( "/spot/ api/v3/ohlcv" , null , payload , "GET" ) ;
65+ JToken ticker = await MakeJsonRequestAsync < JArray > ( "/api/v3.1 /ohlcv" , null , payload , "GET" ) ;
6366 return ticker . Select ( token =>
6467 this . ParseCandle ( token , marketSymbol , periodSeconds , 1 , 2 , 3 , 4 , 0 , TimestampType . UnixMilliseconds , 5 ) ) ;
6568 }
@@ -69,7 +72,7 @@ protected override async Task OnCancelOrderAsync(string orderId, string? marketS
6972 var payload = await GetNoncePayloadAsync ( ) ;
7073
7174 payload [ "order_id" ] = orderId . ConvertInvariant < long > ( ) ;
72- var url = new UriBuilder ( BaseUrl ) { Path = "/spot/ api/v3/order" } ;
75+ var url = new UriBuilder ( BaseUrl ) { Path = "/api/v3.1 /order" } ;
7376 url . AppendPayloadToQuery ( new Dictionary < string , object > ( )
7477 {
7578 { "symbol" , marketSymbol } ,
@@ -80,49 +83,43 @@ await MakeJsonRequestAsync<JToken>(url.ToStringInvariant().Replace(BaseUrl, ""),
8083 requestMethod : "DELETE" , payload : payload ) ;
8184 }
8285
83- protected override async Task < Dictionary < string , decimal > > OnGetAmountsAsync ( )
86+ protected override Task < Dictionary < string , decimal > > OnGetAmountsAsync ( )
8487 {
85- var payload = await GetNoncePayloadAsync ( ) ;
86-
87- var result = await MakeJsonRequestAsync < JToken > ( "/spot/api/v3/user/wallet" ,
88- requestMethod : "GET" , payload : payload ) ;
89- return Extract ( result , token => ( token [ "currency" ] . Value < string > ( ) , token [ "total" ] . Value < decimal > ( ) ) ) ;
88+ return GetBTSEBalance ( false ) ;
9089 }
9190
92- protected override async Task < Dictionary < string , decimal > > OnGetAmountsAvailableToTradeAsync ( )
91+ protected override Task < Dictionary < string , decimal > > OnGetAmountsAvailableToTradeAsync ( )
9392 {
94- var payload = await GetNoncePayloadAsync ( ) ;
95-
96- var result = await MakeJsonRequestAsync < JToken > ( "/spot/api/v3/user/wallet" ,
97- requestMethod : "GET" , payload : payload ) ;
98- return Extract ( result , token => ( token [ "currency" ] . Value < string > ( ) , token [ "available" ] . Value < decimal > ( ) ) ) ;
93+ return GetBTSEBalance ( true ) ;
9994 }
10095
10196 protected override async Task < Dictionary < string , decimal > > OnGetFeesAsync ( )
10297 {
10398 var payload = await GetNoncePayloadAsync ( ) ;
10499
105- var result = await MakeJsonRequestAsync < JToken > ( "/spot/ api/v3/user/fees" ,
100+ var result = await MakeJsonRequestAsync < JToken > ( "/api/v3.1 /user/fees" ,
106101 requestMethod : "GET" , payload : payload ) ;
107102
108- //taker or maker fees in BTSE.. i chose take for here
109- return Extract ( result , token => ( token [ "symbol" ] . Value < string > ( ) , token [ "taker " ] . Value < decimal > ( ) ) ) ;
103+ //taker or maker fees in BTSE.. i chose maker for here
104+ return Extract ( result , token => ( token [ "symbol" ] . Value < string > ( ) , token [ "makerFee " ] . Value < decimal > ( ) ) ) ;
110105 }
111106
112107 protected override async Task < IEnumerable < ExchangeOrderResult > > OnGetOpenOrderDetailsAsync (
113108 string ? marketSymbol = null )
114109 {
115110 if ( marketSymbol == null ) throw new ArgumentNullException ( nameof ( marketSymbol ) ) ;
116111 var payload = await GetNoncePayloadAsync ( ) ;
117- var url = new UriBuilder ( BaseUrl ) { Path = "/spot/api/v3/open_orders" } ;
112+
113+ var url = new UriBuilder ( BaseUrl ) { Path = "/api/v3.1/user/open_orders" } ;
118114 url . AppendPayloadToQuery ( new Dictionary < string , object > ( )
119115 {
120116 { "symbol" , marketSymbol }
121117 } ) ;
118+
119+
122120 var result = await MakeJsonRequestAsync < JToken > ( url . ToStringInvariant ( ) . Replace ( BaseUrl , "" ) ,
123121 requestMethod : "GET" , payload : payload ) ;
124122
125- //taker or maker fees in BTSE.. i chose take for here
126123 return Extract2 ( result , token => new ExchangeOrderResult ( )
127124 {
128125 Amount = token [ "size" ] . Value < decimal > ( ) ,
@@ -135,22 +132,51 @@ protected override async Task<IEnumerable<ExchangeOrderResult>> OnGetOpenOrderDe
135132 } ) ;
136133 }
137134
138- public override async Task < ExchangeOrderResult [ ] > PlaceOrdersAsync ( params ExchangeOrderRequest [ ] orders )
139- {
140- var payload = await GetNoncePayloadAsync ( ) ;
141- payload . Add ( "body" , orders . Select ( request => new
135+ protected override async Task < ExchangeOrderResult > OnPlaceOrderAsync ( ExchangeOrderRequest request )
136+ { var payload = await GetNoncePayloadAsync ( ) ;
137+
138+ var dict = new Dictionary < string , object > ( ) ;
139+
140+ var id = request . OrderId ?? request . ClientOrderId ;
141+ if ( ! string . IsNullOrEmpty ( id ) )
142142 {
143- size = request . Amount ,
144- side = request . IsBuy ? "BUY" : "SELL" ,
145- price = request . Price ,
146- stopPrice = request . StopPrice ,
147- symbol = request . MarketSymbol ,
148- txType = request . OrderType == OrderType . Limit ? "LIMIT" :
149- request . OrderType == OrderType . Stop ? "STOP" : null ,
150- type = request . OrderType == OrderType . Limit ? "LIMIT" :
151- request . OrderType == OrderType . Market ? "MARKET" : null
152- } ) ) ;
153- var result = await MakeJsonRequestAsync < JToken > ( "/spot/api/v3/order" ,
143+ dict . Add ( "clOrderID" , id ) ;
144+
145+ }
146+
147+ dict . Add ( "size" , request . Amount ) ;
148+ dict . Add ( "side" , request . IsBuy ? "BUY" : "SELL" ) ;
149+ dict . Add ( "price" , request . Price ) ;
150+ dict . Add ( "symbol" , request . MarketSymbol ) ;
151+
152+ switch ( request . OrderType )
153+ {
154+ case OrderType . Limit :
155+ dict . Add ( "txType" , "LIMIT" ) ;
156+ dict . Add ( "type" , "LIMIT" ) ;
157+ break ;
158+ case OrderType . Market :
159+
160+ dict . Add ( "type" , "MARKET" ) ;
161+ break ;
162+ case OrderType . Stop :
163+ dict . Add ( "stopPrice" , request . StopPrice ) ;
164+ dict . Add ( "txType" , "STOP" ) ;
165+ break ;
166+ }
167+
168+ foreach ( var extraParameter in request . ExtraParameters )
169+ {
170+ if ( ! dict . ContainsKey ( extraParameter . Key ) )
171+ {
172+ dict . Add ( extraParameter . Key , extraParameter . Value ) ;
173+ }
174+ }
175+
176+
177+ payload . Add ( "body" , dict ) ;
178+
179+ var result = await MakeJsonRequestAsync < JToken > ( "/api/v3.1/order" ,
154180 requestMethod : "POST" , payload : payload ) ;
155181 return Extract2 ( result , token =>
156182 {
@@ -185,42 +211,18 @@ public override async Task<ExchangeOrderResult[]> PlaceOrdersAsync(params Exchan
185211 {
186212 Message = token [ "message" ] . Value < string > ( ) ,
187213 OrderId = token [ "orderID" ] . Value < string > ( ) ,
188- IsBuy = token [ "orderType " ] . Value < string > ( ) . ToLowerInvariant ( ) == "buy" ,
214+ IsBuy = token [ "side " ] . Value < string > ( ) . ToLowerInvariant ( ) == "buy" ,
189215 Price = token [ "price" ] . Value < decimal > ( ) ,
190216 MarketSymbol = token [ "symbol" ] . Value < string > ( ) ,
191217 Result = status ,
192218 Amount = token [ "size" ] . Value < decimal > ( ) ,
193219 OrderDate = token [ "timestamp" ] . ConvertInvariant < long > ( ) . UnixTimeStampToDateTimeMilliseconds ( ) ,
194- } ;
195- } ) . ToArray ( ) ;
196- }
197-
198- private Dictionary < TKey , TValue > Extract < TKey , TValue > ( JToken token , Func < JToken , ( TKey , TValue ) > processor )
199- {
200- if ( token is JArray resultArr )
201- {
202- return resultArr . Select ( processor . Invoke )
203- . ToDictionary ( tuple => tuple . Item1 , tuple => tuple . Item2 ) ;
204- }
205-
206- var resItem = processor . Invoke ( token ) ;
207- return new Dictionary < TKey , TValue > ( )
208- {
209- { resItem . Item1 , resItem . Item2 }
210- } ;
211- }
212-
213- private IEnumerable < TValue > Extract2 < TValue > ( JToken token , Func < JToken , TValue > processor )
214- {
215- if ( token is JArray resultArr )
216- {
217- return resultArr . Select ( processor . Invoke ) ;
218- }
220+ ClientOrderId = token [ "clOrderID" ] . Value < string > ( ) ,
221+ AveragePrice = token [ "averageFillPrice" ] . Value < decimal > ( ) ,
222+ AmountFilled = token [ "fillSize" ] . Value < decimal > ( ) ,
219223
220- return new List < TValue > ( )
221- {
222- processor . Invoke ( token )
223- } ;
224+ } ;
225+ } ) . First ( ) ;
224226 }
225227
226228 protected override Uri ProcessRequestUrl ( UriBuilder url , Dictionary < string , object > payload , string method )
@@ -229,7 +231,6 @@ protected override Uri ProcessRequestUrl(UriBuilder url, Dictionary<string, obje
229231 {
230232 url . AppendPayloadToQuery ( payload ) ;
231233 }
232-
233234 return base . ProcessRequestUrl ( url , payload , method ) ;
234235 }
235236
@@ -252,10 +253,17 @@ protected override async Task ProcessRequestAsync(IHttpWebRequest request, Dicti
252253 json = "" ;
253254 }
254255
256+ var passphrase = Passphrase ? . ToUnsecureString ( ) ;
257+ if ( string . IsNullOrEmpty ( passphrase ) )
258+ {
259+ passphrase = PrivateApiKey ? . ToUnsecureString ( ) ;
260+ }
261+
255262 var hexSha384 = CryptoUtility . SHA384Sign (
256263 $ "{ request . RequestUri . PathAndQuery . Replace ( "/spot" , string . Empty ) } { nonce } { json } ",
257- PrivateApiKey . ToUnsecureString ( ) ) ;
264+ passphrase ) ;
258265 request . AddHeader ( "btse-sign" , hexSha384 ) ;
266+ request . AddHeader ( "btse-nonce" , nonce ) ;
259267 request . AddHeader ( "btse-api" , PublicApiKey . ToUnsecureString ( ) ) ;
260268 await request . WriteToRequestAsync ( json ) ;
261269 }
@@ -273,6 +281,50 @@ protected override async Task<Dictionary<string, object>> GetNoncePayloadAsync()
273281
274282 return result ;
275283 }
284+
285+ private Dictionary < TKey , TValue > Extract < TKey , TValue > ( JToken token , Func < JToken , ( TKey , TValue ) > processor )
286+ {
287+ if ( token is JArray resultArr )
288+ {
289+ return resultArr . Select ( processor . Invoke )
290+ . ToDictionary ( tuple => tuple . Item1 , tuple => tuple . Item2 ) ;
291+ }
292+
293+ var resItem = processor . Invoke ( token ) ;
294+ return new Dictionary < TKey , TValue > ( )
295+ {
296+ { resItem . Item1 , resItem . Item2 }
297+ } ;
298+ }
299+
300+ private IEnumerable < TValue > Extract2 < TValue > ( JToken token , Func < JToken , TValue > processor )
301+ {
302+ if ( token is JArray resultArr )
303+ {
304+ return resultArr . Select ( processor . Invoke ) ;
305+ }
306+
307+ return new List < TValue > ( )
308+ {
309+ processor . Invoke ( token )
310+ } ;
311+ }
312+
313+ private async Task < ExchangeTicker > ParseBTSETicker ( JToken ticker , string marketSymbol )
314+ {
315+ return await this . ParseTickerAsync ( ticker , marketSymbol , "lowestAsk" , "highestBid" , "last" , "volume" , null ,
316+ null , TimestampType . UnixMilliseconds , "base" , "quote" , "symbol" ) ;
317+ }
318+
319+ private async Task < Dictionary < string , decimal > > GetBTSEBalance ( bool availableOnly )
320+ {
321+ var payload = await GetNoncePayloadAsync ( ) ;
322+
323+ var result = await MakeJsonRequestAsync < JToken > ( "/api/v3.1/user/wallet" ,
324+ requestMethod : "GET" , payload : payload ) ;
325+ return Extract ( result , token => ( token [ "currency" ] . Value < string > ( ) , token [ availableOnly ? "available" : "total" ] . Value < decimal > ( ) ) ) ;
326+ }
327+
276328 }
277329
278330 public partial class ExchangeName
0 commit comments