-
Notifications
You must be signed in to change notification settings - Fork 138
/
ExampleProgram.cs
424 lines (379 loc) · 16.9 KB
/
ExampleProgram.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using BinanceExchange.API;
using BinanceExchange.API.Client;
using BinanceExchange.API.Client.Interfaces;
using BinanceExchange.API.Enums;
using BinanceExchange.API.Market;
using BinanceExchange.API.Models.Request;
using BinanceExchange.API.Models.Response;
using BinanceExchange.API.Models.Response.Error;
using BinanceExchange.API.Models.WebSocket;
using BinanceExchange.API.Utility;
using BinanceExchange.API.Websockets;
using log4net;
using Newtonsoft.Json;
using WebSocketSharp;
namespace BinanceExchange.Console
{
/// <summary>
/// This Console app provides a number of examples of utilising the BinanceDotNet library
/// </summary>
public class ExampleProgram
{
public static async Task Main(string[] args)
{
//Provide your configuration and keys here, this allows the client to function as expected.
string apiKey = "YOUR_API_KEY";
string secretKey = "YOUR_SECRET_KEY";
System.Console.WriteLine("--------------------------");
System.Console.WriteLine("BinanceExchange API - Tester");
System.Console.WriteLine("--------------------------");
//Building a test logger
var exampleProgramLogger = LogManager.GetLogger(typeof(ExampleProgram));
exampleProgramLogger.Debug("Logging Test");
//Initialise the general client client with config
var client = new BinanceClient(new ClientConfiguration()
{
ApiKey = apiKey,
SecretKey = secretKey,
Logger = exampleProgramLogger,
});
System.Console.WriteLine("Interacting with Binance...");
bool DEBUG_ALL = false;
/*
* Code Examples - Make sure you adjust value of DEBUG_ALL
*/
if (DEBUG_ALL)
{
// Test the Client
await client.TestConnectivity();
// Get All Orders
var allOrdersRequest = new AllOrdersRequest()
{
Symbol = "ETHBTC",
Limit = 5,
};
allOrdersRequest = new AllOrdersRequest()
{
Symbol = TradingPairSymbols.BTCPairs.ETH_BTC,
Limit = 5,
};
// Get All Orders
var allOrders = await client.GetAllOrders(allOrdersRequest);
// Get the order book, and use the cache
var orderBook = await client.GetOrderBook("ETHBTC", true);
// Cancel an order
var cancelOrder = await client.CancelOrder(new CancelOrderRequest()
{
NewClientOrderId = "123456",
OrderId = 523531,
OriginalClientOrderId = "789",
Symbol = "ETHBTC",
});
// Create an order with varying options
var createOrder = await client.CreateOrder(new CreateOrderRequest()
{
IcebergQuantity = 100,
Price = 230,
Quantity = 0.6m,
Side = OrderSide.Buy,
Symbol = "ETHBTC",
Type = OrderType.Market,
});
// Get account information
var accountInformation = await client.GetAccountInformation(3500);
// Get account trades
var accountTrades = await client.GetAccountTrades(new AllTradesRequest()
{
FromId = 352262,
Symbol = "ETHBTC",
});
// Get a list of Compressed aggregate trades with varying options
var aggTrades = await client.GetCompressedAggregateTrades(new GetCompressedAggregateTradesRequest()
{
StartTime = DateTime.UtcNow.AddDays(-1),
Symbol = "ETHBTC",
});
// Get current open orders for the specified symbol
var currentOpenOrders = await client.GetCurrentOpenOrders(new CurrentOpenOrdersRequest()
{
Symbol = "ETHBTC",
});
// Get daily ticker
var dailyTicker = await client.GetDailyTicker("ETHBTC");
// Get Symbol Order Book Ticket
var symbolOrderBookTicker = await client.GetSymbolOrderBookTicker();
// Get Symbol Order Price Ticker
var symbolOrderPriceTicker = await client.GetSymbolsPriceTicker();
// Query a specific order on Binance
var orderQuery = await client.QueryOrder(new QueryOrderRequest()
{
OrderId = 5425425,
Symbol = "ETHBTC",
});
// Firing off a request and catching all the different exception types.
try
{
accountTrades = await client.GetAccountTrades(new AllTradesRequest()
{
FromId = 352262,
Symbol = "ETHBTC",
});
}
catch (BinanceBadRequestException badRequestException)
{
}
catch (BinanceServerException serverException)
{
}
catch (BinanceTimeoutException timeoutException)
{
}
catch (BinanceException unknownException)
{
}
}
// Start User Data Stream, ping and close
var userData = await client.StartUserDataStream();
await client.KeepAliveUserDataStream(userData.ListenKey);
await client.CloseUserDataStream(userData.ListenKey);
// Manual WebSocket usage
var manualBinanceWebSocket = new InstanceBinanceWebSocketClient(client);
var socketId = manualBinanceWebSocket.ConnectToDepthWebSocket("ETHBTC", b =>
{
System.Console.Clear();
System.Console.WriteLine($"{JsonConvert.SerializeObject(b.BidDepthDeltas, Formatting.Indented)}");
System.Console.SetWindowPosition(0, 0);
});
#region Advanced Examples
// This builds a local Kline cache, with an initial call to the API and then continues to fill
// the cache with data from the WebSocket connection. It is quite an advanced example as it provides
// additional options such as an Exit Func<T> or timeout, and checks in place for cache instances.
// You could provide additional logic here such as populating a database, ping off more messages, or simply
// timing out a fill for the cache.
var dict = new Dictionary<string, KlineCacheObject>();
//await BuildAndUpdateLocalKlineCache(client, "BNBBTC", KlineInterval.OneMinute,
// new GetKlinesCandlesticksRequest()
// {
// StartTime = DateTime.UtcNow.AddHours(-1),
// EndTime = DateTime.UtcNow,
// Interval = KlineInterval.OneMinute,
// Symbol = "BNBBTC"
// }, new WebSocketConnectionFunc(15000), dict);
// This builds a local depth cache from an initial call to the API and then continues to fill
// the cache with data from the WebSocket
var localDepthCache = await BuildLocalDepthCache(client);
// Build the Buy Sell volume from the results
var volume = ResultTransformations.CalculateTradeVolumeFromDepth("BNBBTC", localDepthCache);
#endregion
System.Console.WriteLine("Complete.");
Thread.Sleep(6000);
manualBinanceWebSocket.CloseWebSocketInstance(socketId);
System.Console.ReadLine();
}
/// <summary>
/// Build local Depth cache from WebSocket and API Call example.
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
private static async Task<Dictionary<string, DepthCacheObject>> BuildLocalDepthCache(IBinanceClient client)
{
// Code example of building out a Dictionary local cache for a symbol using deltas from the WebSocket
var localDepthCache = new Dictionary<string, DepthCacheObject> {{ "BNBBTC", new DepthCacheObject()
{
Asks = new Dictionary<decimal, decimal>(),
Bids = new Dictionary<decimal, decimal>(),
}}};
var bnbBtcDepthCache = localDepthCache["BNBBTC"];
// Get Order Book, and use Cache
var depthResults = await client.GetOrderBook("BNBBTC", true, 100);
//Populate our depth cache
depthResults.Asks.ForEach(a =>
{
if (a.Quantity != 0.00000000M)
{
bnbBtcDepthCache.Asks.Add(a.Price, a.Quantity);
}
});
depthResults.Bids.ForEach(a =>
{
if (a.Quantity != 0.00000000M)
{
bnbBtcDepthCache.Bids.Add(a.Price, a.Quantity);
}
});
// Store the last update from our result set;
long lastUpdateId = depthResults.LastUpdateId;
using (var binanceWebSocketClient = new DisposableBinanceWebSocketClient(client))
{
binanceWebSocketClient.ConnectToDepthWebSocket("BNBBTC", data =>
{
if (lastUpdateId < data.UpdateId)
{
data.BidDepthDeltas.ForEach((bd) =>
{
CorrectlyUpdateDepthCache(bd, bnbBtcDepthCache.Bids);
});
data.AskDepthDeltas.ForEach((ad) =>
{
CorrectlyUpdateDepthCache(ad, bnbBtcDepthCache.Asks);
});
}
lastUpdateId = data.UpdateId;
System.Console.Clear();
System.Console.WriteLine($"{JsonConvert.SerializeObject(bnbBtcDepthCache, Formatting.Indented)}");
System.Console.SetWindowPosition(0, 0);
});
Thread.Sleep(8000);
}
return localDepthCache;
}
/// <summary>
/// Advanced approach to building local Kline Cache from WebSocket and API Call example (refactored)
/// </summary>
/// <param name="binanceClient">The BinanceClient instance</param>
/// <param name="symbol">The Symbol to request</param>
/// <param name="interval">The interval for Klines</param>
/// <param name="klinesCandlesticksRequest">The initial request for Klines</param>
/// <param name="webSocketConnectionFunc">The function to determine exiting the websocket (can be timeout or Func based on external params)</param>
/// <param name="cacheObject">The cache object. Must always be provided, and can exist with data.</param>
/// <returns></returns>
public static async Task BuildAndUpdateLocalKlineCache(IBinanceClient binanceClient,
string symbol,
KlineInterval interval,
GetKlinesCandlesticksRequest klinesCandlesticksRequest,
WebSocketConnectionFunc webSocketConnectionFunc,
Dictionary<string, KlineCacheObject> cacheObject)
{
Guard.AgainstNullOrEmpty(symbol);
Guard.AgainstNull(webSocketConnectionFunc);
Guard.AgainstNull(klinesCandlesticksRequest);
Guard.AgainstNull(cacheObject);
long epochTicks = new DateTime(1970, 1, 1).Ticks;
if (cacheObject.ContainsKey(symbol))
{
if (cacheObject[symbol].KlineInterDictionary.ContainsKey(interval))
{
throw new Exception(
"Symbol and Interval pairing already provided, please use a different interval/symbol or pair.");
}
cacheObject[symbol].KlineInterDictionary.Add(interval, new KlineIntervalCacheObject());
}
else
{
var klineCacheObject = new KlineCacheObject
{
KlineInterDictionary = new Dictionary<KlineInterval, KlineIntervalCacheObject>()
};
cacheObject.Add(symbol, klineCacheObject);
cacheObject[symbol].KlineInterDictionary.Add(interval, new KlineIntervalCacheObject());
}
// Get Kline Results, and use Cache
long ticks = klinesCandlesticksRequest.StartTime.Value.Ticks;
var startTimeKeyTime = (ticks - epochTicks) / TimeSpan.TicksPerSecond;
var klineResults = await binanceClient.GetKlinesCandlesticks(klinesCandlesticksRequest);
var oneMinKlineCache = cacheObject[symbol].KlineInterDictionary[interval];
oneMinKlineCache.TimeKlineDictionary = new Dictionary<long, KlineCandleStick>();
var instanceKlineCache = oneMinKlineCache.TimeKlineDictionary;
//Populate our kline cache with initial results
klineResults.ForEach(k =>
{
instanceKlineCache.Add(((k.OpenTime.Ticks - epochTicks) / TimeSpan.TicksPerSecond), new KlineCandleStick()
{
Close = k.Close,
High = k.High,
Low = k.Low,
Open = k.Open,
Volume = k.Volume,
});
});
// Store the last update from our result set;
using (var binanceWebSocketClient = new DisposableBinanceWebSocketClient(binanceClient))
{
binanceWebSocketClient.ConnectToKlineWebSocket(symbol, interval, data =>
{
var keyTime = (data.Kline.StartTime.Ticks - epochTicks) / TimeSpan.TicksPerSecond;
var klineObj = new KlineCandleStick()
{
Close = data.Kline.Close,
High = data.Kline.High,
Low = data.Kline.Low,
Open = data.Kline.Open,
Volume = data.Kline.Volume,
};
if (!data.Kline.IsBarFinal)
{
if (keyTime < startTimeKeyTime)
{
return;
}
TryAddUpdateKlineCache(instanceKlineCache, keyTime, klineObj);
}
else
{
TryAddUpdateKlineCache(instanceKlineCache, keyTime, klineObj);
}
System.Console.Clear();
System.Console.WriteLine($"{JsonConvert.SerializeObject(instanceKlineCache, Formatting.Indented)}");
System.Console.SetWindowPosition(0, 0);
});
if (webSocketConnectionFunc.IsTimout)
{
Thread.Sleep(webSocketConnectionFunc.Timeout);
}
else
{
while (true)
{
if (!webSocketConnectionFunc.ExitFunction())
{
// Throttle Application
Thread.Sleep(100);
}
else
{
break;
}
}
}
}
}
private static void TryAddUpdateKlineCache(Dictionary<long, KlineCandleStick> primary, long keyTime, KlineCandleStick klineObj)
{
if (primary.ContainsKey(keyTime))
{
primary[keyTime] = klineObj;
}
else
{
primary.Add(keyTime, klineObj);
}
}
private static void CorrectlyUpdateDepthCache(TradeResponse bd, Dictionary<decimal, decimal> depthCache)
{
const decimal defaultIgnoreValue = 0.00000000M;
if (depthCache.ContainsKey(bd.Price))
{
if (bd.Quantity == defaultIgnoreValue)
{
depthCache.Remove(bd.Price);
}
else
{
depthCache[bd.Price] = bd.Quantity;
}
}
else
{
if (bd.Quantity != defaultIgnoreValue)
{
depthCache[bd.Price] = bd.Quantity;
}
}
}
}
}