mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-11 16:32:57 +00:00
Added AI documentation
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
// 01-shared-clients-quickstart.cs
|
||||
//
|
||||
// Demonstrates: the SharedApis pattern — same code calling multiple exchanges.
|
||||
//
|
||||
// Setup:
|
||||
// dotnet add package Binance.Net
|
||||
// dotnet add package JK.OKX.Net
|
||||
|
||||
using Binance.Net.Clients;
|
||||
using OKX.Net.Clients;
|
||||
using CryptoExchange.Net.SharedApis;
|
||||
|
||||
// ---- THE PATTERN ----
|
||||
// Each exchange library exposes `.SharedClient` properties on its API surfaces.
|
||||
// Those implement common interfaces from CryptoExchange.Net.SharedApis.
|
||||
// You write code against the interface — it works against any exchange.
|
||||
|
||||
ISpotTickerRestClient binance = new BinanceRestClient().SpotApi.SharedClient;
|
||||
ISpotTickerRestClient okx = new OKXRestClient().UnifiedApi.SharedClient;
|
||||
|
||||
// ---- SYMBOL NORMALIZATION ----
|
||||
// Different exchanges use different formats: "BTCUSDT" (Binance), "BTC-USDT" (OKX).
|
||||
// SharedSymbol normalizes — pass it instead of raw strings to shared methods.
|
||||
var btcusdt = new SharedSymbol(TradingMode.Spot, "BTC", "USDT");
|
||||
|
||||
// ---- AGNOSTIC METHOD — runs against any exchange ----
|
||||
async Task PrintTicker(ISpotTickerRestClient client, SharedSymbol symbol)
|
||||
{
|
||||
var result = await client.GetSpotTickerAsync(new GetTickerRequest(symbol));
|
||||
if (!result.Success)
|
||||
{
|
||||
Console.WriteLine($"[{client.Exchange}] Failed: {result.Error}");
|
||||
return;
|
||||
}
|
||||
// SharedSpotTicker has a unified shape regardless of the source exchange
|
||||
Console.WriteLine($"[{client.Exchange}] {result.Data.Symbol}: last={result.Data.LastPrice}, 24h-vol={result.Data.Volume}");
|
||||
}
|
||||
|
||||
await PrintTicker(binance, btcusdt);
|
||||
await PrintTicker(okx, btcusdt);
|
||||
|
||||
// ---- WEBSOCKET PATTERN ----
|
||||
ITickerSocketClient binanceTickerSocket = new BinanceSocketClient().SpotApi.SharedClient;
|
||||
ITickerSocketClient okxTickerSocket = new OKXSocketClient().UnifiedApi.SharedClient;
|
||||
|
||||
var sub1 = await binanceTickerSocket.SubscribeToTickerUpdatesAsync(
|
||||
new SubscribeTickerRequest(btcusdt),
|
||||
update => Console.WriteLine($"[{binanceTickerSocket.Exchange}] {update.Data.Symbol}: {update.Data.LastPrice}"));
|
||||
|
||||
var sub2 = await okxTickerSocket.SubscribeToTickerUpdatesAsync(
|
||||
new SubscribeTickerRequest(btcusdt),
|
||||
update => Console.WriteLine($"[{okxTickerSocket.Exchange}] {update.Data.Symbol}: {update.Data.LastPrice}"));
|
||||
|
||||
Console.WriteLine("Press Enter to exit");
|
||||
Console.ReadLine();
|
||||
|
||||
if (sub1.Success) await binanceTickerSocket.UnsubscribeAsync(sub1.Data);
|
||||
if (sub2.Success) await okxTickerSocket.UnsubscribeAsync(sub2.Data);
|
||||
|
||||
// Common variations:
|
||||
// Add Bybit: ITickerRestClient bybit = new BybitRestClient().V5Api.SharedClient;
|
||||
// Add Kraken: ITickerRestClient kraken = new KrakenRestClient().SpotApi.SharedClient;
|
||||
// Add Coinbase: ITickerRestClient cb = new CoinbaseRestClient().AdvancedTradeApi.SharedClient;
|
||||
// Other interfaces: ISpotOrderRestClient (place/cancel orders), IBalanceRestClient (balances),
|
||||
// IFuturesOrderRestClient, IPositionRestClient, IOrderBookSocketClient, etc.
|
||||
@@ -0,0 +1,70 @@
|
||||
// 02-multi-exchange-tickers.cs
|
||||
//
|
||||
// Demonstrates: aggregating ticker data across N exchanges concurrently.
|
||||
// Pattern is foundational for arbitrage scanners, best-execution routers,
|
||||
// portfolio dashboards, and cross-exchange comparison tools.
|
||||
//
|
||||
// Setup:
|
||||
// dotnet add package Binance.Net
|
||||
// dotnet add package JK.OKX.Net
|
||||
// dotnet add package Bybit.Net
|
||||
|
||||
using Binance.Net.Clients;
|
||||
using OKX.Net.Clients;
|
||||
using Bybit.Net.Clients;
|
||||
using CryptoExchange.Net.SharedApis;
|
||||
|
||||
// ---- BUILD A LIST OF EXCHANGE CLIENTS ----
|
||||
// All implement ISpotTickerRestClient, so we can iterate uniformly.
|
||||
var exchanges = new List<ISpotTickerRestClient>
|
||||
{
|
||||
new BinanceRestClient().SpotApi.SharedClient,
|
||||
new OKXRestClient().UnifiedApi.SharedClient,
|
||||
new BybitRestClient().V5Api.SharedClient,
|
||||
// Add as many as you want — same interface
|
||||
};
|
||||
|
||||
var symbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT");
|
||||
|
||||
// ---- CONCURRENT FETCH ----
|
||||
// Fire all requests in parallel, await all together.
|
||||
// Each request runs on its own connection — no inter-exchange interference.
|
||||
var tasks = exchanges
|
||||
.Select(c => FetchAsync(c, symbol))
|
||||
.ToList();
|
||||
|
||||
var results = await Task.WhenAll(tasks);
|
||||
|
||||
// ---- PRINT SORTED BY PRICE ----
|
||||
// Highest bid first — useful for "where to sell" decisions.
|
||||
foreach (var r in results.Where(r => r != null).OrderByDescending(r => r!.LastPrice))
|
||||
{
|
||||
Console.WriteLine($"{r!.Exchange,-12} {r.LastPrice,15} (24h vol: {r.Volume:F2})");
|
||||
}
|
||||
|
||||
// ---- HELPER ----
|
||||
async Task<TickerSnapshot?> FetchAsync(ISpotTickerRestClient client, SharedSymbol sym)
|
||||
{
|
||||
var result = await client.GetSpotTickerAsync(new GetTickerRequest(sym));
|
||||
if (!result.Success)
|
||||
{
|
||||
Console.WriteLine($"[{client.Exchange}] error: {result.Error}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TickerSnapshot(
|
||||
Exchange: client.Exchange,
|
||||
Symbol: result.Data.Symbol,
|
||||
LastPrice: result.Data.LastPrice ?? 0,
|
||||
Volume: result.Data.Volume ?? 0);
|
||||
}
|
||||
|
||||
record TickerSnapshot(string Exchange, string Symbol, decimal LastPrice, decimal Volume);
|
||||
|
||||
// Common variations:
|
||||
// Periodic polling: wrap in `while(true) { await ...; await Task.Delay(...); }`
|
||||
// Better: use ITickerSocketClient for push updates instead of polling
|
||||
// With timeout per call: pass `ct: cts.Token` and use `CancellationTokenSource(timeout)`
|
||||
// With retry: wrap FetchAsync in retry policy (see Binance.Net 05-error-handling.cs)
|
||||
// Different metric: use IBookTickerRestClient for tighter best-bid/ask data
|
||||
// Spread analysis: instead of ticker, use IOrderBookRestClient and compute mid/spread
|
||||
@@ -0,0 +1,112 @@
|
||||
// 03-cross-exchange-arbitrage-skeleton.cs
|
||||
//
|
||||
// Demonstrates: skeleton pattern for a cross-exchange spot arbitrage scanner.
|
||||
// This is a structural example — production arbitrage requires also:
|
||||
// - real-time WebSocket feeds (not REST polling)
|
||||
// - orderbook depth analysis (not just ticker)
|
||||
// - slippage / fees modeling
|
||||
// - withdrawal availability and timing
|
||||
// - inventory management on both sides
|
||||
// Use this as a starting structure, not a deployable bot.
|
||||
//
|
||||
// Setup:
|
||||
// dotnet add package Binance.Net
|
||||
// dotnet add package JK.OKX.Net
|
||||
// dotnet add package Bybit.Net
|
||||
|
||||
using Binance.Net.Clients;
|
||||
using OKX.Net.Clients;
|
||||
using Bybit.Net.Clients;
|
||||
using CryptoExchange.Net.SharedApis;
|
||||
|
||||
// ---- CONFIGURATION ----
|
||||
// Symbols to monitor and minimum profit threshold (gross, before fees)
|
||||
var symbols = new[]
|
||||
{
|
||||
new SharedSymbol(TradingMode.Spot, "BTC", "USDT"),
|
||||
new SharedSymbol(TradingMode.Spot, "ETH", "USDT"),
|
||||
new SharedSymbol(TradingMode.Spot, "SOL", "USDT"),
|
||||
};
|
||||
|
||||
const decimal minSpreadBps = 30; // 0.30% — must exceed total fees on both legs
|
||||
|
||||
// ---- USE BOOK TICKER FOR TIGHTER SPREADS ----
|
||||
// IBookTickerRestClient gives best bid/ask, narrower than 24h ticker.
|
||||
// For real arbitrage you'd use IOrderBookSocketClient for depth + push updates.
|
||||
var exchanges = new List<IBookTickerRestClient>
|
||||
{
|
||||
new BinanceRestClient().SpotApi.SharedClient,
|
||||
new OKXRestClient().UnifiedApi.SharedClient,
|
||||
new BybitRestClient().V5Api.SharedClient,
|
||||
};
|
||||
|
||||
// ---- MAIN LOOP (simplified: REST polling, 5-second intervals) ----
|
||||
// In production: replace with concurrent WebSocket subscriptions.
|
||||
while (true)
|
||||
{
|
||||
foreach (var symbol in symbols)
|
||||
{
|
||||
await ScanSymbolAsync(symbol, exchanges);
|
||||
}
|
||||
|
||||
Console.WriteLine($"--- waiting 5s --- ({DateTime.UtcNow:HH:mm:ss})");
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
|
||||
// ---- SCAN ONE SYMBOL ACROSS ALL EXCHANGES ----
|
||||
async Task ScanSymbolAsync(SharedSymbol symbol, List<IBookTickerRestClient> clients)
|
||||
{
|
||||
// Fetch best bid/ask from every exchange in parallel
|
||||
var tasks = clients.Select(c => GetBookAsync(c, symbol)).ToArray();
|
||||
var quotes = (await Task.WhenAll(tasks)).Where(q => q != null).Cast<Quote>().ToList();
|
||||
|
||||
if (quotes.Count < 2) return;
|
||||
|
||||
// Find best buy venue (lowest ask) and best sell venue (highest bid)
|
||||
var bestBuy = quotes.OrderBy(q => q.AskPrice).First();
|
||||
var bestSell = quotes.OrderByDescending(q => q.BidPrice).First();
|
||||
|
||||
if (bestBuy.Exchange == bestSell.Exchange) return; // no cross-venue arbitrage
|
||||
|
||||
// Spread in basis points
|
||||
var spreadBps = (bestSell.BidPrice - bestBuy.AskPrice) / bestBuy.AskPrice * 10_000;
|
||||
|
||||
if (spreadBps >= minSpreadBps)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[{symbol.BaseAsset}/{symbol.QuoteAsset}] BUY {bestBuy.Exchange}@{bestBuy.AskPrice} " +
|
||||
$"SELL {bestSell.Exchange}@{bestSell.BidPrice} " +
|
||||
$"spread={spreadBps:F1}bps");
|
||||
|
||||
// Production hooks would go here:
|
||||
// - check available inventory on both venues
|
||||
// - simulate execution against orderbook depth
|
||||
// - compute net P&L after fees
|
||||
// - if profitable, execute via ISpotOrderRestClient on both venues
|
||||
}
|
||||
}
|
||||
|
||||
async Task<Quote?> GetBookAsync(IBookTickerRestClient client, SharedSymbol symbol)
|
||||
{
|
||||
var result = await client.GetBookTickerAsync(new GetBookTickerRequest(symbol));
|
||||
if (!result.Success || result.Data?.BestBidPrice == null || result.Data.BestAskPrice == null)
|
||||
return null;
|
||||
|
||||
return new Quote(
|
||||
Exchange: client.Exchange,
|
||||
BidPrice: result.Data.BestBidPrice.Value,
|
||||
AskPrice: result.Data.BestAskPrice.Value);
|
||||
}
|
||||
|
||||
record Quote(string Exchange, decimal BidPrice, decimal AskPrice);
|
||||
|
||||
// Production checklist (NOT in this skeleton):
|
||||
// ✓ Use WebSocket book tickers (IBookTickerSocketClient) instead of REST polling
|
||||
// ✓ Track full orderbook depth (IOrderBookSocketClient) to estimate fill price for size > top-of-book
|
||||
// ✓ Model fees per exchange per pair (taker vs maker, BNB discount, etc.)
|
||||
// ✓ Track inventory on both venues — can't sell what you don't have
|
||||
// ✓ Account for withdrawal delays if rebalancing inventory
|
||||
// ✓ Set hard P&L stops, position limits, maximum exposure per pair
|
||||
// ✓ Use ISpotOrderRestClient with reduce-only / IOC order types for atomic execution
|
||||
// ✓ Monitor connection health and have failover logic
|
||||
// ✓ Log everything — arbitrage P&L analysis requires complete audit trails
|
||||
@@ -0,0 +1,28 @@
|
||||
# AI-Friendly Examples
|
||||
|
||||
Cross-exchange examples using `CryptoExchange.Net.SharedApis`. These examples are optimized for AI coding assistants and quick onboarding.
|
||||
|
||||
## Files
|
||||
|
||||
| File | What it shows |
|
||||
|---|---|
|
||||
| `01-shared-clients-quickstart.cs` | Same code calling Binance and OKX via SharedApis |
|
||||
| `02-multi-exchange-tickers.cs` | Aggregating ticker data across N exchanges concurrently |
|
||||
| `03-cross-exchange-arbitrage-skeleton.cs` | Pattern for building a price difference scanner |
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
dotnet new console -n MyMultiExchangeApp
|
||||
cd MyMultiExchangeApp
|
||||
|
||||
# Add the exchange libraries you want
|
||||
dotnet add package Binance.Net
|
||||
dotnet add package JK.OKX.Net
|
||||
dotnet add package Bybit.Net
|
||||
|
||||
# Copy example file content into Program.cs and run
|
||||
dotnet run
|
||||
```
|
||||
|
||||
These are public market data examples — no API keys needed.
|
||||
Reference in New Issue
Block a user