From 6e4dbcf7b13820bc24d27550cd10229a4ee13444 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Thu, 7 May 2026 13:28:20 +0200 Subject: [PATCH 1/3] Added AI documentation --- .cursor/rules/cryptoexchange-net.mdc | 71 ++++++++ .github/copilot-instructions.md | 52 ++++++ CLAUDE.md | 169 ++++++++++++++++++ .../01-shared-clients-quickstart.cs | 65 +++++++ .../ai-friendly/02-multi-exchange-tickers.cs | 70 ++++++++ .../03-cross-exchange-arbitrage-skeleton.cs | 112 ++++++++++++ Examples/ai-friendly/README.md | 28 +++ README.md | 17 ++ llms.txt | 34 ++++ 9 files changed, 618 insertions(+) create mode 100644 .cursor/rules/cryptoexchange-net.mdc create mode 100644 .github/copilot-instructions.md create mode 100644 CLAUDE.md create mode 100644 Examples/ai-friendly/01-shared-clients-quickstart.cs create mode 100644 Examples/ai-friendly/02-multi-exchange-tickers.cs create mode 100644 Examples/ai-friendly/03-cross-exchange-arbitrage-skeleton.cs create mode 100644 Examples/ai-friendly/README.md create mode 100644 llms.txt diff --git a/.cursor/rules/cryptoexchange-net.mdc b/.cursor/rules/cryptoexchange-net.mdc new file mode 100644 index 00000000..0a0ab57e --- /dev/null +++ b/.cursor/rules/cryptoexchange-net.mdc @@ -0,0 +1,71 @@ +--- +description: Conventions for cross-exchange code using CryptoExchange.Net SharedApis abstractions. Apply when generating C# code that interacts with multiple cryptocurrency exchanges through a unified interface. +globs: + - "**/*.cs" + - "**/*.csproj" +alwaysApply: false +--- + +# CryptoExchange.Net Conventions + +This codebase uses **CryptoExchange.Net** abstractions for multi-exchange access. Each exchange has its own library (Binance.Net, Bybit.Net, OKX.Net, ...). Use `CryptoExchange.Net.SharedApis` for code that should work across exchanges. + +## Multi-exchange pattern + +```csharp +using Binance.Net.Clients; +using OKX.Net.Clients; +using CryptoExchange.Net.SharedApis; + +ISpotTickerRestClient binance = new BinanceRestClient().SpotApi.SharedClient; +ISpotTickerRestClient okx = new OKXRestClient().UnifiedApi.SharedClient; + +var symbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT"); + +var ticker = await binance.GetSpotTickerAsync(new GetTickerRequest(symbol)); +// ticker.Data.LastPrice — same model regardless of exchange +``` + +## Symbol normalization + +`SharedSymbol(TradingMode.Spot, "BTC", "USDT")` is portable. Each library translates to its native format internally. Don't pass raw strings like `"BTCUSDT"` to shared methods. + +## Result pattern + +Same `WebCallResult` / `CallResult` everywhere. Always check `.Success`. `.Exchange` property identifies which exchange responded — useful for logging. + +## Available shared interfaces + +- REST tickers/symbols/orderbook/klines/trades, orders (spot/futures, regular/trigger/TP-SL), balances, positions, fees, deposits/withdrawals, transfers +- WebSocket tickers, book tickers, order book, trades, klines, user data + +Each exchange documents which it implements. Not every exchange supports every operation. + +## Multi-exchange aggregation + +Run requests across exchanges concurrently via `Task.WhenAll` — the library is async-safe and concurrent requests are the norm. + +```csharp +var clients = new ISpotTickerRestClient[] { binance, okx, bybit }; +var tasks = clients.Select(c => c.GetSpotTickerAsync(new GetTickerRequest(symbol))); +var results = await Task.WhenAll(tasks); +``` + +## Hard rules + +- ❌ Never install `CryptoExchange.Net` alone and expect to call exchanges — it's a base library; you need exchange-specific packages +- ❌ Never mix exchange-specific models in cross-exchange code (use `SharedSymbol`, `SharedSpotTicker`, etc.) +- ❌ Never use `.Result` / `.Wait()` — async-only +- ❌ Never iterate sequentially when concurrency is fine — `Task.WhenAll` is your friend +- ❌ Never instantiate clients per-request — reuse via DI +- ✅ Always use `.SharedClient` for cross-exchange code +- ✅ Always check `.Success` before reading `.Data` +- ✅ Always log with `.Exchange` so multi-exchange logs are decipherable +- ✅ Always handle "not supported on this exchange" errors gracefully + +## Reference + +- `CLAUDE.md` in repo root has fuller examples +- `llms.txt` for AI context +- `Examples/ai-friendly/` for compilable examples +- For single-exchange code, see that exchange's library (e.g., Binance.Net `CLAUDE.md`) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..bfabe429 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,52 @@ +# Copilot Instructions for CryptoExchange.Net + +This repository is **CryptoExchange.Net** — the base library powering 28+ cryptocurrency exchange wrappers in C#/.NET (Binance.Net, Bybit.Net, OKX.Net, Kraken.Net, Coinbase.Net, etc.). + +When generating code in this ecosystem, follow these conventions: + +## You don't install CryptoExchange.Net directly + +Install the exchange-specific library you need (`Binance.Net`, `JK.OKX.Net`, `Bybit.Net`, ...) or `CryptoClients.Net` for the bundle. CryptoExchange.Net is pulled in as a dependency. + +## Multi-exchange code uses SharedApis + +For code that must work against multiple exchanges, use `CryptoExchange.Net.SharedApis` interfaces accessed via `.SharedClient` properties on each exchange's API surface: + +```csharp +using CryptoExchange.Net.SharedApis; + +ISpotTickerRestClient binance = new BinanceRestClient().SpotApi.SharedClient; +ISpotTickerRestClient okx = new OKXRestClient().UnifiedApi.SharedClient; + +var symbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT"); +var ticker = await binance.GetSpotTickerAsync(new GetTickerRequest(symbol)); +``` + +Same code works on every exchange that implements the interface. Use `Task.WhenAll` for concurrent multi-exchange calls. + +## Single-exchange code uses the exchange's own client + +For Binance-only code, use `BinanceRestClient` directly (see Binance.Net repo `CLAUDE.md`). SharedApis is for portability — use it when you need that. + +## Result pattern + +Every method returns `WebCallResult` or `CallResult`. Check `.Success` before `.Data`. `.Error` has structured info. `.Exchange` on shared clients identifies which exchange responded. + +## Available shared interfaces + +REST: tickers, symbols, orderbook, klines, trades, orders (spot/futures, trigger, TP-SL), balances, positions, fees, deposits/withdrawals, transfers. +WebSocket: tickers, book tickers, orderbook, trades, klines, user data. + +Each exchange library implements a subset. Check exchange docs for support matrix. + +## Avoid + +- Installing `CryptoExchange.Net` alone and trying to call exchange APIs (need exchange-specific packages) +- Mixing exchange-native models in cross-exchange code (use Shared* types) +- Synchronous `.Result` / `.Wait()` (use `await`) +- Instantiating clients per-request (use DI, reuse instances) +- Sequential per-exchange calls when parallel is fine (`Task.WhenAll`) + +## Reference + +For detailed patterns see `CLAUDE.md` and `llms.txt` in repo root, `examples/ai-friendly/` for compilable examples. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..027ddf96 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,169 @@ +--- +name: cryptoexchange-net +description: Use CryptoExchange.Net abstractions when generating C#/.NET code that needs to work across MULTIPLE cryptocurrency exchanges (Binance + Bybit + OKX + Kraken + Coinbase + ...) — for arbitrage, best-execution routing, multi-exchange portfolio dashboards, exchange-agnostic trading bots, or comparison tools. Triggers on requests mentioning multi-exchange, cross-exchange, arbitrage, exchange-agnostic, or unified crypto API in C#. Also triggers when the user is implementing a new exchange library following the CryptoExchange.Net pattern. +--- + +# CryptoExchange.Net Skill + +## When to use + +CryptoExchange.Net is the **base library** powering 28+ exchange-specific libraries (Binance.Net, Bybit.Net, OKX.Net, Kraken.Net, Coinbase.Net, etc.). You don't install it directly — you install the exchange libraries, which depend on it. + +**Three usage modes:** + +1. **You target ONE exchange** → use that exchange's library directly (e.g., Binance.Net), see its CLAUDE.md. +2. **You target MULTIPLE exchanges** → install each library you need + use `CryptoExchange.Net.SharedApis` interfaces — write code once, runs against any exchange. **This is the main use case for this skill.** +3. **You want ALL exchanges in one package** → install `CryptoClients.Net`, get `ExchangeRestClient` and `ExchangeSocketClient` with everything bundled. + +## Installation + +For a multi-exchange project: + +```bash +dotnet add package Binance.Net +dotnet add package JK.OKX.Net +dotnet add package Bybit.Net +# ... etc +``` + +Or the bundle: + +```bash +dotnet add package CryptoClients.Net +``` + +## Core Pattern: Shared Interfaces + +Every exchange library exposes `.SharedClient` properties on its API surfaces. These implement the same interfaces from `CryptoExchange.Net.SharedApis`. + +```csharp +using Binance.Net.Clients; +using OKX.Net.Clients; +using Bybit.Net.Clients; +using CryptoExchange.Net.SharedApis; + +// All three implement ISpotTickerRestClient +ISpotTickerRestClient binance = new BinanceRestClient().SpotApi.SharedClient; +ISpotTickerRestClient okx = new OKXRestClient().UnifiedApi.SharedClient; +ISpotTickerRestClient bybit = new BybitRestClient().V5Api.SharedClient; + +// Single agnostic call — works against any of them +var symbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT"); +var ticker = await binance.GetSpotTickerAsync(new GetTickerRequest(symbol)); +// ticker.Data.LastPrice, ticker.Data.HighPrice, etc. — same model regardless of exchange +``` + +## Core Pattern: SharedSymbol + +Different exchanges format symbols differently — Binance uses `BTCUSDT`, OKX uses `BTC-USDT`, others may have other formats. `SharedSymbol` normalizes this: + +```csharp +var btcusdt = new SharedSymbol(TradingMode.Spot, "BTC", "USDT"); +// Each exchange library translates SharedSymbol → its native format internally. + +// For futures: +var btcusdtPerp = new SharedSymbol(TradingMode.PerpetualLinear, "BTC", "USDT"); +``` + +For exchanges that use exotic asset names, see the AssetAliases configuration. + +## Available Shared Interfaces + +**REST:** + +- Market data: `ISpotTickerRestClient`, `IBookTickerRestClient`, `ISpotSymbolRestClient`, `IFuturesSymbolRestClient`, `IOrderBookRestClient`, `IRecentTradeRestClient`, `IKlineRestClient` +- Orders: `ISpotOrderRestClient`, `IFuturesOrderRestClient`, `ISpotOrderClientIdRestClient`, `IFuturesOrderClientIdRestClient`, `ISpotTriggerOrderRestClient`, `IFuturesTriggerOrderRestClient`, `IFuturesTpSlRestClient` +- Account: `IBalanceRestClient`, `IPositionRestClient`, `IFeeRestClient`, `ITransferRestClient`, `IDepositRestClient`, `IWithdrawalRestClient` + +**WebSocket:** + +- `ITickerSocketClient`, `IBookTickerSocketClient` +- `IOrderBookSocketClient`, `ITradeSocketClient`, `IKlineSocketClient` +- `IUserTradeSocketClient`, `ISpotOrderSocketClient`, `IFuturesOrderSocketClient`, `IPositionSocketClient`, `IBalanceSocketClient` + +Each exchange documents which interfaces it implements (some exchanges don't support every operation). + +## Core Pattern: Result Handling + +Same as exchange-specific libraries — `WebCallResult` (REST) or `CallResult` (WebSocket) with `.Success`, `.Data`, `.Error`. Always check `.Success` first. + +```csharp +var result = await sharedClient.GetSpotTickerAsync(new GetTickerRequest(symbol)); +if (!result.Success) +{ + Console.WriteLine($"[{sharedClient.Exchange}] Error: {result.Error}"); + return; +} +Console.WriteLine($"[{sharedClient.Exchange}] {result.Data.LastPrice}"); +``` + +`.Exchange` property on every shared client tells you which exchange you're talking to — useful for logging. + +## Core Pattern: Multi-Exchange Aggregation + +```csharp +var clients = new ISpotTickerRestClient[] +{ + new BinanceRestClient().SpotApi.SharedClient, + new OKXRestClient().UnifiedApi.SharedClient, + new BybitRestClient().V5Api.SharedClient, +}; + +var symbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT"); + +// Fetch concurrently from all exchanges +var tasks = clients.Select(c => c.GetSpotTickerAsync(new GetTickerRequest(symbol))).ToArray(); +var results = await Task.WhenAll(tasks); + +for (int i = 0; i < clients.Length; i++) +{ + if (results[i].Success) + Console.WriteLine($"{clients[i].Exchange}: {results[i].Data!.LastPrice}"); +} +``` + +## Per-Exchange Setup + +Each exchange library has its own credentials class and options. See each library's CLAUDE.md for specifics. The pattern is consistent: `XxxRestClient(options => { options.ApiCredentials = new XxxCredentials(...); })`. + +## Dependency Injection + +Each exchange library has its own `services.AddXxx(...)` extension. They all share the same option-builder pattern. Register only the ones you use: + +```csharp +services.AddBinance(restOpts => { /*...*/ }, socketOpts => { /*...*/ }); +services.AddOKX(restOpts => { /*...*/ }, socketOpts => { /*...*/ }); +// Inject IBinanceRestClient, IOKXRestClient, etc. +``` + +For one-package access: `services.AddCryptoClients(...)` from `CryptoClients.Net`. + +## Common Pitfalls — AVOID + +- **Do NOT install `CryptoExchange.Net` and try to call exchange APIs directly** — it's a base abstraction; you need an exchange library. +- **Do NOT try to use one exchange's models with another's client** — use the SharedApis types (`SharedSymbol`, `SharedSpotTicker`, `SharedSpotOrder`, etc.) for cross-exchange code. +- **Do NOT block on async operations** — use `await` throughout. `Task.WhenAll` for parallelism across exchanges. +- **Do NOT assume every exchange supports every operation** — check exchange docs or the library's implementation. Operations may return errors like "not supported on this exchange". +- **Do NOT instantiate clients per-request** — reuse via DI. +- **Do NOT iterate exchanges sequentially when concurrency is fine** — use `Task.WhenAll` for ~Nx speedup. + +## Implementing a New Exchange Library + +If you're building a NEW exchange wrapper following the CryptoExchange.Net pattern (rare but valuable): + +- Inherit from `RestApiClient` and `SocketApiClient` base classes +- Define your own `XxxCredentials` extending `ApiCredentials` (or use `ApiCredentials` directly) +- Implement `AuthenticationProvider` for the exchange's signing scheme +- Implement the relevant `Shared*` interfaces on your API client classes for cross-exchange support +- Follow the same `XxxRestOptions` / `XxxSocketOptions` pattern + +See existing libraries (Binance.Net, Bybit.Net) as reference implementations. + +## Reference + +- Source: https://github.com/JKorf/CryptoExchange.Net +- Documentation: https://cryptoexchange.jkorf.dev/ +- SharedApis docs: https://cryptoexchange.jkorf.dev/CryptoExchange.Net/idocs_shared.html +- Bundle (all exchanges): https://github.com/JKorf/CryptoClients.Net +- Demo app: https://github.com/JKorf/CryptoManager.Net +- Discord: https://discord.gg/MSpeEtSY8t diff --git a/Examples/ai-friendly/01-shared-clients-quickstart.cs b/Examples/ai-friendly/01-shared-clients-quickstart.cs new file mode 100644 index 00000000..c8ced227 --- /dev/null +++ b/Examples/ai-friendly/01-shared-clients-quickstart.cs @@ -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. diff --git a/Examples/ai-friendly/02-multi-exchange-tickers.cs b/Examples/ai-friendly/02-multi-exchange-tickers.cs new file mode 100644 index 00000000..0e3da316 --- /dev/null +++ b/Examples/ai-friendly/02-multi-exchange-tickers.cs @@ -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 +{ + 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 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 diff --git a/Examples/ai-friendly/03-cross-exchange-arbitrage-skeleton.cs b/Examples/ai-friendly/03-cross-exchange-arbitrage-skeleton.cs new file mode 100644 index 00000000..d1b9916a --- /dev/null +++ b/Examples/ai-friendly/03-cross-exchange-arbitrage-skeleton.cs @@ -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 +{ + 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 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().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 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 diff --git a/Examples/ai-friendly/README.md b/Examples/ai-friendly/README.md new file mode 100644 index 00000000..7b1c2104 --- /dev/null +++ b/Examples/ai-friendly/README.md @@ -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. diff --git a/README.md b/README.md index 21f409de..32102044 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,23 @@ Note that the CryptoExchange.Net package itself can not be used directly for acc For more information on what CryptoExchange.Net and it's client libraries offers see the [Documentation](https://cryptoexchange.jkorf.dev/). +### For AI Coding Assistants + +This library and the entire CryptoExchange.Net ecosystem provide first-class support for AI coding assistants. The relevant skill files are in this repository: + +- **Claude Code**: `CLAUDE.md` (auto-detected at repo root) +- **Cursor**: `.cursor/rules/cryptoexchange-net.mdc` +- **GitHub Copilot**: `.github/copilot-instructions.md` +- **Other tools** (Windsurf, Codex, Continue, Aider, etc.): `llms.txt` at repo root +- **Compilable examples**: `Examples/ai-friendly/` + +For single-exchange code, see also the AI files in each exchange's repository (Binance.Net, Bybit.Net, OKX.Net, ...) — they cover exchange-specific patterns. + +**Quick prompt to verify your assistant is using these:** +> "Show me how to fetch BTC/USDT spot tickers from Binance and OKX concurrently in C# using the SharedApis pattern." + +The expected output should use `.SharedClient` properties, `SharedSymbol`, `ISpotTickerRestClient`, and `Task.WhenAll`. + ### CryptoExchange.Net Ecosystem Full list of all libraries part of the CryptoExchange.Net ecosystem. Consider using a referral link to support development, as well as potentially get some trading fee discount! diff --git a/llms.txt b/llms.txt new file mode 100644 index 00000000..c0dfd793 --- /dev/null +++ b/llms.txt @@ -0,0 +1,34 @@ +# CryptoExchange.Net + +> Base C#/.NET library for cryptocurrency exchange API client implementations. Provides a standardized abstraction (REST, WebSocket, authentication, rate limiting, error handling, order book management, shared cross-exchange interfaces) that 28+ exchange-specific libraries are built on top of. + +CryptoExchange.Net itself is not used directly — install one of the exchange-specific libraries (Binance.Net, Bybit.Net, OKX.Net, Kraken.Net, Coinbase.Net, etc.) or `CryptoClients.Net` to access all exchanges via a single bundle. The base library is what makes the entire ecosystem feel consistent: same `WebCallResult` result pattern, same WebSocket subscription model, same DI registration, same shared interfaces across all exchanges. Current version: 11.x. Targets netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0. Native AOT supported. + +The standout feature for cross-exchange code is `CryptoExchange.Net.SharedApis` — a set of interfaces (`ISpotTickerRestClient`, `ISpotOrderRestClient`, `IBalanceRestClient`, etc.) implemented by every exchange library. Same call signature works against any exchange. + +## Documentation + +- [README](https://github.com/JKorf/CryptoExchange.Net/blob/master/README.md): Overview, full ecosystem table (28+ exchange libraries), installation per exchange, complete release notes +- [Documentation Site](https://cryptoexchange.jkorf.dev/): Full documentation hub with sections per topic +- [SharedApis Documentation](https://cryptoexchange.jkorf.dev/CryptoExchange.Net/idocs_shared.html): Cross-exchange shared interface guide + +## Examples + +- [AI-friendly examples directory](https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/ai-friendly): Compact, fully runnable examples optimized for AI assistants +- [Shared Clients Quickstart](https://github.com/JKorf/CryptoExchange.Net/blob/master/Examples/ai-friendly/01-shared-clients-quickstart.cs): Same code calling multiple exchanges via SharedApis +- [Multi-Exchange Tickers](https://github.com/JKorf/CryptoExchange.Net/blob/master/Examples/ai-friendly/02-multi-exchange-tickers.cs): Aggregating ticker data across N exchanges concurrently +- [Cross-Exchange Arbitrage Skeleton](https://github.com/JKorf/CryptoExchange.Net/blob/master/Examples/ai-friendly/03-cross-exchange-arbitrage-skeleton.cs): Pattern for building a price difference scanner +- [Full Examples Repository](https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples): ConsoleClient with multiple exchange exchanges, BlazorClient, SharedClients + +## Reference + +- [Ecosystem libraries list](https://github.com/JKorf/CryptoExchange.Net#cryptoexchangenet-ecosystem): Aster, Binance, BingX, Bitfinex, Bitget, BitMart, BitMEX, Bitstamp, BloFin, Bybit, Coinbase, CoinEx, CoinW, CoinGecko, Crypto.com, DeepCoin, Gate.io, HTX, HyperLiquid, Kraken, Kucoin, Mexc, OKX, Polymarket, Toobit, Upbit, Weex, WhiteBit, XT +- [CryptoClients.Net](https://github.com/JKorf/CryptoClients.Net): Single bundle package for all exchange libraries +- [CryptoManager.Net](https://github.com/JKorf/CryptoManager.Net): Full demo application using CryptoClients.Net +- [NuGet Package](https://www.nuget.org/packages/CryptoExchange.Net): Latest stable release on NuGet + +## Optional + +- [Discord Community](https://discord.gg/MSpeEtSY8t): Maintainer-supported Discord for ecosystem-wide discussion +- [GitHub Issues](https://github.com/JKorf/CryptoExchange.Net/issues): Bug reports and feature requests +- [GitHub Sponsors](https://github.com/sponsors/JKorf): Support the maintainer From 8c4cf62d9f8b6266fbbf895d4fcb4971dfc332f5 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Thu, 7 May 2026 14:15:50 +0200 Subject: [PATCH 2/3] Fixed some examples --- Examples/ai-friendly/01-shared-clients-quickstart.cs | 10 +++++----- Examples/ai-friendly/02-multi-exchange-tickers.cs | 2 +- .../03-cross-exchange-arbitrage-skeleton.cs | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Examples/ai-friendly/01-shared-clients-quickstart.cs b/Examples/ai-friendly/01-shared-clients-quickstart.cs index c8ced227..458b2d3f 100644 --- a/Examples/ai-friendly/01-shared-clients-quickstart.cs +++ b/Examples/ai-friendly/01-shared-clients-quickstart.cs @@ -54,12 +54,12 @@ var sub2 = await okxTickerSocket.SubscribeToTickerUpdatesAsync( Console.WriteLine("Press Enter to exit"); Console.ReadLine(); -if (sub1.Success) await binanceTickerSocket.UnsubscribeAsync(sub1.Data); -if (sub2.Success) await okxTickerSocket.UnsubscribeAsync(sub2.Data); +if (sub1.Success) await sub1.Data.CloseAsync(); +if (sub2.Success) await sub2.Data.CloseAsync(); // 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; +// Add Bybit: ISpotTickerRestClient bybit = new BybitRestClient().V5Api.SharedClient; +// Add Kraken: ISpotTickerRestClient kraken = new KrakenRestClient().SpotApi.SharedClient; +// Add Coinbase: ISpotTickerRestClient cb = new CoinbaseRestClient().AdvancedTradeApi.SharedClient; // Other interfaces: ISpotOrderRestClient (place/cancel orders), IBalanceRestClient (balances), // IFuturesOrderRestClient, IPositionRestClient, IOrderBookSocketClient, etc. diff --git a/Examples/ai-friendly/02-multi-exchange-tickers.cs b/Examples/ai-friendly/02-multi-exchange-tickers.cs index 0e3da316..b0a8e61a 100644 --- a/Examples/ai-friendly/02-multi-exchange-tickers.cs +++ b/Examples/ai-friendly/02-multi-exchange-tickers.cs @@ -56,7 +56,7 @@ async Task FetchAsync(ISpotTickerRestClient client, SharedSymbo Exchange: client.Exchange, Symbol: result.Data.Symbol, LastPrice: result.Data.LastPrice ?? 0, - Volume: result.Data.Volume ?? 0); + Volume: result.Data.Volume); } record TickerSnapshot(string Exchange, string Symbol, decimal LastPrice, decimal Volume); diff --git a/Examples/ai-friendly/03-cross-exchange-arbitrage-skeleton.cs b/Examples/ai-friendly/03-cross-exchange-arbitrage-skeleton.cs index d1b9916a..1db5467d 100644 --- a/Examples/ai-friendly/03-cross-exchange-arbitrage-skeleton.cs +++ b/Examples/ai-friendly/03-cross-exchange-arbitrage-skeleton.cs @@ -89,13 +89,13 @@ async Task ScanSymbolAsync(SharedSymbol symbol, List clie async Task GetBookAsync(IBookTickerRestClient client, SharedSymbol symbol) { var result = await client.GetBookTickerAsync(new GetBookTickerRequest(symbol)); - if (!result.Success || result.Data?.BestBidPrice == null || result.Data.BestAskPrice == null) + if (!result.Success || result.Data == null) return null; return new Quote( Exchange: client.Exchange, - BidPrice: result.Data.BestBidPrice.Value, - AskPrice: result.Data.BestAskPrice.Value); + BidPrice: result.Data.BestBidPrice, + AskPrice: result.Data.BestAskPrice); } record Quote(string Exchange, decimal BidPrice, decimal AskPrice); @@ -107,6 +107,6 @@ record Quote(string Exchange, decimal BidPrice, decimal AskPrice); // ✓ 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 +// ✓ Use ISpotOrderRestClient with exchange-supported IOC/fill-or-kill order options where available // ✓ Monitor connection health and have failover logic // ✓ Log everything — arbitrage P&L analysis requires complete audit trails From 7dcb2241c6703093f4c8e0378f6e41ee898320af Mon Sep 17 00:00:00 2001 From: Jkorf Date: Sat, 9 May 2026 21:56:08 +0200 Subject: [PATCH 3/3] Ai docs --- .cursor/rules/cryptoexchange-net.mdc | 4 ++-- .github/copilot-instructions.md | 4 ++-- CLAUDE.md => AGENTS.md | 0 README.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename CLAUDE.md => AGENTS.md (100%) diff --git a/.cursor/rules/cryptoexchange-net.mdc b/.cursor/rules/cryptoexchange-net.mdc index 0a0ab57e..3f22d6df 100644 --- a/.cursor/rules/cryptoexchange-net.mdc +++ b/.cursor/rules/cryptoexchange-net.mdc @@ -65,7 +65,7 @@ var results = await Task.WhenAll(tasks); ## Reference -- `CLAUDE.md` in repo root has fuller examples +- `AGENTS.md` in repo root has fuller examples - `llms.txt` for AI context - `Examples/ai-friendly/` for compilable examples -- For single-exchange code, see that exchange's library (e.g., Binance.Net `CLAUDE.md`) +- For single-exchange code, see that exchange's library (e.g., Binance.Net `AGENTS.md`) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index bfabe429..a377d9ee 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -26,7 +26,7 @@ Same code works on every exchange that implements the interface. Use `Task.WhenA ## Single-exchange code uses the exchange's own client -For Binance-only code, use `BinanceRestClient` directly (see Binance.Net repo `CLAUDE.md`). SharedApis is for portability — use it when you need that. +For Binance-only code, use `BinanceRestClient` directly (see Binance.Net repo `AGENTS.md`). SharedApis is for portability — use it when you need that. ## Result pattern @@ -49,4 +49,4 @@ Each exchange library implements a subset. Check exchange docs for support matri ## Reference -For detailed patterns see `CLAUDE.md` and `llms.txt` in repo root, `examples/ai-friendly/` for compilable examples. +For detailed patterns see `AGENTS.md` and `llms.txt` in repo root, `examples/ai-friendly/` for compilable examples. diff --git a/CLAUDE.md b/AGENTS.md similarity index 100% rename from CLAUDE.md rename to AGENTS.md diff --git a/README.md b/README.md index 32102044..c2f86bf4 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ For more information on what CryptoExchange.Net and it's client libraries offers This library and the entire CryptoExchange.Net ecosystem provide first-class support for AI coding assistants. The relevant skill files are in this repository: -- **Claude Code**: `CLAUDE.md` (auto-detected at repo root) +- **Agents**: `AGENTS.md` (auto-detected at repo root) - **Cursor**: `.cursor/rules/cryptoexchange-net.mdc` - **GitHub Copilot**: `.github/copilot-instructions.md` - **Other tools** (Windsurf, Codex, Continue, Aider, etc.): `llms.txt` at repo root