@@ -10,33 +10,36 @@ namespace ExchangeSharp
10
10
11
11
public sealed partial class ExchangeBTSEAPI : ExchangeAPI
12
12
{
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" ;
14
15
16
+ public ExchangeBTSEAPI ( )
17
+ {
18
+ NonceStyle = NonceStyle . UnixMillisecondsString ;
19
+ }
15
20
protected override async Task < IEnumerable < string > > OnGetMarketSymbolsAsync ( )
16
21
{
17
22
return ( await GetTickersAsync ( ) ) . Select ( pair => pair . Key ) ;
18
23
}
19
24
20
25
protected override async Task < IEnumerable < KeyValuePair < string , ExchangeTicker > > > OnGetTickersAsync ( )
21
26
{
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 > ( ) ) ) ;
26
30
27
31
return ( await Task . WhenAll ( tasks ) ) . Select ( ticker =>
28
32
new KeyValuePair < string , ExchangeTicker > ( ticker . MarketSymbol , ticker ) ) ;
29
33
}
30
34
31
35
protected override async Task < ExchangeTicker > OnGetTickerAsync ( string marketSymbol )
32
36
{
33
- JToken ticker = await MakeJsonRequestAsync < JObject > ( "/spot/ api/v3/market_summary" , null ,
37
+ JToken ticker = await MakeJsonRequestAsync < JToken > ( "/api/v3.1 /market_summary" , BaseUrl ,
34
38
new Dictionary < string , object > ( )
35
39
{
36
40
{ "symbol" , marketSymbol }
37
41
} ) ;
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 ) ;
40
43
}
41
44
42
45
protected override async Task < IEnumerable < MarketCandle > > OnGetCandlesAsync ( string marketSymbol ,
@@ -59,7 +62,7 @@ protected override async Task<IEnumerable<MarketCandle>> OnGetCandlesAsync(strin
59
62
payload . Add ( "end" , startDate . Value . UnixTimestampFromDateTimeMilliseconds ( ) ) ;
60
63
}
61
64
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" ) ;
63
66
return ticker . Select ( token =>
64
67
this . ParseCandle ( token , marketSymbol , periodSeconds , 1 , 2 , 3 , 4 , 0 , TimestampType . UnixMilliseconds , 5 ) ) ;
65
68
}
@@ -69,7 +72,7 @@ protected override async Task OnCancelOrderAsync(string orderId, string? marketS
69
72
var payload = await GetNoncePayloadAsync ( ) ;
70
73
71
74
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" } ;
73
76
url . AppendPayloadToQuery ( new Dictionary < string , object > ( )
74
77
{
75
78
{ "symbol" , marketSymbol } ,
@@ -80,49 +83,43 @@ await MakeJsonRequestAsync<JToken>(url.ToStringInvariant().Replace(BaseUrl, ""),
80
83
requestMethod : "DELETE" , payload : payload ) ;
81
84
}
82
85
83
- protected override async Task < Dictionary < string , decimal > > OnGetAmountsAsync ( )
86
+ protected override Task < Dictionary < string , decimal > > OnGetAmountsAsync ( )
84
87
{
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 ) ;
90
89
}
91
90
92
- protected override async Task < Dictionary < string , decimal > > OnGetAmountsAvailableToTradeAsync ( )
91
+ protected override Task < Dictionary < string , decimal > > OnGetAmountsAvailableToTradeAsync ( )
93
92
{
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 ) ;
99
94
}
100
95
101
96
protected override async Task < Dictionary < string , decimal > > OnGetFeesAsync ( )
102
97
{
103
98
var payload = await GetNoncePayloadAsync ( ) ;
104
99
105
- var result = await MakeJsonRequestAsync < JToken > ( "/spot/ api/v3/user/fees" ,
100
+ var result = await MakeJsonRequestAsync < JToken > ( "/api/v3.1 /user/fees" ,
106
101
requestMethod : "GET" , payload : payload ) ;
107
102
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 > ( ) ) ) ;
110
105
}
111
106
112
107
protected override async Task < IEnumerable < ExchangeOrderResult > > OnGetOpenOrderDetailsAsync (
113
108
string ? marketSymbol = null )
114
109
{
115
110
if ( marketSymbol == null ) throw new ArgumentNullException ( nameof ( marketSymbol ) ) ;
116
111
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" } ;
118
114
url . AppendPayloadToQuery ( new Dictionary < string , object > ( )
119
115
{
120
116
{ "symbol" , marketSymbol }
121
117
} ) ;
118
+
119
+
122
120
var result = await MakeJsonRequestAsync < JToken > ( url . ToStringInvariant ( ) . Replace ( BaseUrl , "" ) ,
123
121
requestMethod : "GET" , payload : payload ) ;
124
122
125
- //taker or maker fees in BTSE.. i chose take for here
126
123
return Extract2 ( result , token => new ExchangeOrderResult ( )
127
124
{
128
125
Amount = token [ "size" ] . Value < decimal > ( ) ,
@@ -135,22 +132,51 @@ protected override async Task<IEnumerable<ExchangeOrderResult>> OnGetOpenOrderDe
135
132
} ) ;
136
133
}
137
134
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 ) )
142
142
{
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" ,
154
180
requestMethod : "POST" , payload : payload ) ;
155
181
return Extract2 ( result , token =>
156
182
{
@@ -185,42 +211,18 @@ public override async Task<ExchangeOrderResult[]> PlaceOrdersAsync(params Exchan
185
211
{
186
212
Message = token [ "message" ] . Value < string > ( ) ,
187
213
OrderId = token [ "orderID" ] . Value < string > ( ) ,
188
- IsBuy = token [ "orderType " ] . Value < string > ( ) . ToLowerInvariant ( ) == "buy" ,
214
+ IsBuy = token [ "side " ] . Value < string > ( ) . ToLowerInvariant ( ) == "buy" ,
189
215
Price = token [ "price" ] . Value < decimal > ( ) ,
190
216
MarketSymbol = token [ "symbol" ] . Value < string > ( ) ,
191
217
Result = status ,
192
218
Amount = token [ "size" ] . Value < decimal > ( ) ,
193
219
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 > ( ) ,
219
223
220
- return new List < TValue > ( )
221
- {
222
- processor . Invoke ( token )
223
- } ;
224
+ } ;
225
+ } ) . First ( ) ;
224
226
}
225
227
226
228
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
229
231
{
230
232
url . AppendPayloadToQuery ( payload ) ;
231
233
}
232
-
233
234
return base . ProcessRequestUrl ( url , payload , method ) ;
234
235
}
235
236
@@ -252,10 +253,17 @@ protected override async Task ProcessRequestAsync(IHttpWebRequest request, Dicti
252
253
json = "" ;
253
254
}
254
255
256
+ var passphrase = Passphrase ? . ToUnsecureString ( ) ;
257
+ if ( string . IsNullOrEmpty ( passphrase ) )
258
+ {
259
+ passphrase = PrivateApiKey ? . ToUnsecureString ( ) ;
260
+ }
261
+
255
262
var hexSha384 = CryptoUtility . SHA384Sign (
256
263
$ "{ request . RequestUri . PathAndQuery . Replace ( "/spot" , string . Empty ) } { nonce } { json } ",
257
- PrivateApiKey . ToUnsecureString ( ) ) ;
264
+ passphrase ) ;
258
265
request . AddHeader ( "btse-sign" , hexSha384 ) ;
266
+ request . AddHeader ( "btse-nonce" , nonce ) ;
259
267
request . AddHeader ( "btse-api" , PublicApiKey . ToUnsecureString ( ) ) ;
260
268
await request . WriteToRequestAsync ( json ) ;
261
269
}
@@ -273,6 +281,50 @@ protected override async Task<Dictionary<string, object>> GetNoncePayloadAsync()
273
281
274
282
return result ;
275
283
}
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
+
276
328
}
277
329
278
330
public partial class ExchangeName
0 commit comments