Skip to content

Commit c170407

Browse files
committed
Ai docs
1 parent 6523c42 commit c170407

14 files changed

Lines changed: 2391 additions & 0 deletions

File tree

.cursor/rules/bybit-net.mdc

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
---
2+
description: Conventions for using the Bybit.Net library when working with the Bybit cryptocurrency exchange in C#/.NET. Apply when generating code that interacts with the Bybit API.
3+
globs:
4+
- "**/*.cs"
5+
- "**/*.csproj"
6+
alwaysApply: false
7+
---
8+
9+
# Bybit.Net Conventions
10+
11+
This codebase uses **Bybit.Net** for Bybit cryptocurrency exchange access. Do not write raw `HttpClient` calls to Bybit endpoints.
12+
13+
## Client setup pattern
14+
15+
```csharp
16+
using Bybit.Net;
17+
using Bybit.Net.Clients;
18+
19+
var restClient = new BybitRestClient(options =>
20+
{
21+
options.ApiCredentials = new BybitCredentials("API_KEY", "API_SECRET");
22+
});
23+
```
24+
25+
For public market data only, no credentials are needed: `new BybitRestClient()`.
26+
27+
## Result pattern
28+
29+
All methods return `WebCallResult<T>` for REST or `CallResult<T>` for WebSocket operations. Always check `.Success` before reading `.Data`:
30+
31+
```csharp
32+
var ticker = await restClient.V5Api.ExchangeData.GetSpotTickersAsync("ETHUSDT");
33+
if (!ticker.Success) { return; }
34+
var price = ticker.Data.List.First().LastPrice;
35+
```
36+
37+
## API surface
38+
39+
- `restClient.V5Api.ExchangeData`
40+
- `restClient.V5Api.Account`
41+
- `restClient.V5Api.Trading`
42+
- `restClient.V5Api.SubAccount`
43+
- `restClient.V5Api.CryptoLoan`
44+
- `restClient.V5Api.Earn`
45+
- `restClient.V5Api.SharedClient`
46+
- `socketClient.V5SpotApi`
47+
- `socketClient.V5LinearApi`
48+
- `socketClient.V5InverseApi`
49+
- `socketClient.V5OptionsApi`
50+
- `socketClient.V5SpreadApi`
51+
- `socketClient.V5PrivateApi`
52+
53+
Use `Category.Spot`, `Category.Linear`, `Category.Inverse`, or `Category.Option` when V5 methods require a category.
54+
55+
## Order placement
56+
57+
```csharp
58+
var order = await restClient.V5Api.Trading.PlaceOrderAsync(
59+
Category.Linear,
60+
"ETHUSDT",
61+
OrderSide.Buy,
62+
NewOrderType.Limit,
63+
quantity: 0.1m,
64+
price: 2000m,
65+
timeInForce: TimeInForce.GoodTillCanceled,
66+
positionIdx: PositionIdx.OneWayMode);
67+
```
68+
69+
## WebSocket pattern
70+
71+
```csharp
72+
var sub = await socketClient.V5SpotApi.SubscribeToTickerUpdatesAsync(
73+
"ETHUSDT",
74+
update => Console.WriteLine(update.Data.LastPrice));
75+
76+
if (!sub.Success) { return; }
77+
await socketClient.UnsubscribeAsync(sub.Data);
78+
```
79+
80+
Use `V5PrivateApi` for authenticated order, position, wallet, user trade, and Greek updates.
81+
82+
## Multi-exchange code
83+
84+
For exchange-agnostic code, use CryptoExchange.Net shared clients:
85+
86+
```csharp
87+
var shared = new BybitRestClient().V5Api.SharedClient;
88+
Console.WriteLine(shared.Exchange);
89+
```
90+
91+
## Hard rules
92+
93+
- Never write raw `HttpClient` calls to Bybit endpoints.
94+
- Never invent `SpotApi`, `UsdFuturesApi`, `CoinFuturesApi`, or `GeneralApi` roots for Bybit.Net.
95+
- Never use `.Result` or `.Wait()`; use async methods with `await`.
96+
- Never instantiate clients per request; reuse clients or use DI.
97+
- Never skip checking `WebCallResult.Success` or `CallResult.Success`.
98+
- Never use passphrase credentials for Bybit HMAC authentication.
99+
- Never use non-Bybit symbol formats such as `ETH-USDT`, `ETH_USDT`, `ETH/USD`, or `tETHUSD`.
100+
- Always use the V5 client roots and the correct product category.
101+
- Always store WebSocket subscriptions and unsubscribe on shutdown.
102+
103+
## Reference
104+
105+
- Skill: `CLAUDE.md` in the repository root has fuller examples.
106+
- LLM context: `llms.txt` and `llms-full.txt` in the repository root.
107+
- API map: `docs/ai-api-map.md`.
108+
- Examples: `Examples/ai-friendly/`.

.github/copilot-instructions.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Copilot Instructions for Bybit.Net
2+
3+
This repository is **Bybit.Net**, a strongly typed C#/.NET client library for the Bybit cryptocurrency exchange API. It is part of the CryptoExchange.Net ecosystem.
4+
5+
When generating code that consumes Bybit.Net, follow these conventions.
6+
7+
## Use Bybit.Net, not raw HTTP
8+
9+
Never generate raw `HttpClient` calls to Bybit REST or WebSocket endpoints. Always use `BybitRestClient` or `BybitSocketClient` so request signing, receive-window handling, rate limiting, response parsing, and reconnect behavior stay correct.
10+
11+
## Client setup
12+
13+
```csharp
14+
using Bybit.Net;
15+
using Bybit.Net.Clients;
16+
17+
var restClient = new BybitRestClient(options =>
18+
{
19+
options.ApiCredentials = new BybitCredentials("API_KEY", "API_SECRET");
20+
});
21+
```
22+
23+
For public market data only, credentials are optional:
24+
25+
```csharp
26+
var publicClient = new BybitRestClient();
27+
```
28+
29+
## Result handling
30+
31+
REST methods return `WebCallResult<T>` and WebSocket subscription methods return `CallResult<UpdateSubscription>`. Always check `.Success` before reading `.Data`; inspect `.Error` on failure.
32+
33+
## V5 API structure
34+
35+
- `restClient.V5Api.ExchangeData` for symbols, tickers, klines, order books, trades, funding, open interest
36+
- `restClient.V5Api.Account` for balances, assets, transfers, deposits, withdrawals, account settings
37+
- `restClient.V5Api.Trading` for orders, order history, user trades, positions, spread trading
38+
- `restClient.V5Api.SubAccount`, `CryptoLoan`, and `Earn` for specialized V5 endpoints
39+
- `restClient.V5Api.SharedClient` for CryptoExchange.Net shared REST abstractions
40+
- `socketClient.V5SpotApi`, `V5LinearApi`, `V5InverseApi`, `V5OptionsApi`, `V5SpreadApi`, `V5PrivateApi` for WebSocket streams
41+
42+
Use `Category.Spot`, `Category.Linear`, `Category.Inverse`, or `Category.Option` when a V5 REST method asks for a product category.
43+
44+
## Order placement
45+
46+
Use `restClient.V5Api.Trading.PlaceOrderAsync`. Pass the correct category and Bybit symbol format:
47+
48+
```csharp
49+
var order = await restClient.V5Api.Trading.PlaceOrderAsync(
50+
Category.Linear,
51+
"ETHUSDT",
52+
OrderSide.Buy,
53+
NewOrderType.Limit,
54+
quantity: 0.1m,
55+
price: 2000m,
56+
timeInForce: TimeInForce.GoodTillCanceled,
57+
positionIdx: PositionIdx.OneWayMode);
58+
```
59+
60+
## WebSocket pattern
61+
62+
Store the returned `UpdateSubscription` and unsubscribe on shutdown:
63+
64+
```csharp
65+
var sub = await socketClient.V5SpotApi.SubscribeToTickerUpdatesAsync(
66+
"ETHUSDT",
67+
update => Console.WriteLine(update.Data.LastPrice));
68+
69+
if (!sub.Success) { return; }
70+
await socketClient.UnsubscribeAsync(sub.Data);
71+
```
72+
73+
## Cross-exchange
74+
75+
For exchange-agnostic code, use `CryptoExchange.Net.SharedApis` through `.SharedClient` properties, for example `new BybitRestClient().V5Api.SharedClient`.
76+
77+
## Avoid
78+
79+
- Legacy or non-existent client roots such as `SpotApi`, `UsdFuturesApi`, `GeneralApi`, or `socketClient.SpotApi`
80+
- Raw Bybit URLs and hand-written signing
81+
- Generic `ApiCredentials` when Bybit-specific credentials are expected
82+
- API passphrases; Bybit HMAC credentials are key and secret
83+
- Synchronous `.Result` / `.Wait()`
84+
- Creating clients per request
85+
- Reading `.Data` before checking `.Success`
86+
- Non-Bybit symbol formats such as `ETH-USDT`, `ETH_USDT`, `ETH/USD`, or `tETHUSD`
87+
88+
## Reference
89+
90+
For detailed patterns and pitfalls see `CLAUDE.md`, `llms.txt`, `llms-full.txt`, and `docs/ai-api-map.md` in the repository root. Compilable examples live in `Examples/ai-friendly/`.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
using NUnit.Framework;
2+
using System;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace Bybit.UnitTests.Documentation
10+
{
11+
[TestFixture]
12+
public class AiExampleCompileTests
13+
{
14+
[Test]
15+
public async Task AiFriendlyExamples_ShouldCompile()
16+
{
17+
var repositoryRoot = FindRepositoryRoot();
18+
var examplesDirectory = Path.Combine(repositoryRoot, "Examples", "ai-friendly");
19+
20+
Assert.That(Directory.Exists(examplesDirectory), Is.True, $"Missing examples directory: {examplesDirectory}");
21+
22+
var exampleFiles = Directory.GetFiles(examplesDirectory, "*.cs").OrderBy(x => x).ToArray();
23+
Assert.That(exampleFiles, Is.Not.Empty);
24+
25+
foreach (var exampleFile in exampleFiles)
26+
await CompileExampleAsync(repositoryRoot, exampleFile).ConfigureAwait(false);
27+
}
28+
29+
private static async Task CompileExampleAsync(string repositoryRoot, string exampleFile)
30+
{
31+
var exampleName = Path.GetFileNameWithoutExtension(exampleFile);
32+
var workDirectory = Path.Combine(repositoryRoot, "obj", "ai-example-compile", exampleName);
33+
34+
if (Directory.Exists(workDirectory))
35+
Directory.Delete(workDirectory, recursive: true);
36+
37+
Directory.CreateDirectory(workDirectory);
38+
39+
File.Copy(exampleFile, Path.Combine(workDirectory, "Program.cs"));
40+
File.WriteAllText(
41+
Path.Combine(workDirectory, "Example.csproj"),
42+
CreateProjectFile(),
43+
Encoding.UTF8);
44+
45+
var result = await RunDotnetBuildAsync(workDirectory).ConfigureAwait(false);
46+
Assert.That(result.ExitCode, Is.EqualTo(0), $"Example {Path.GetFileName(exampleFile)} failed to compile.{Environment.NewLine}{result.Output}");
47+
}
48+
49+
private static string CreateProjectFile()
50+
{
51+
var assemblyDirectory = AppContext.BaseDirectory;
52+
var references = Directory.GetFiles(assemblyDirectory, "*.dll")
53+
.OrderBy(x => x)
54+
.Select(path => $@" <Reference Include=""{Path.GetFileNameWithoutExtension(path)}"">
55+
<HintPath>{path}</HintPath>
56+
</Reference>");
57+
58+
return $@"<Project Sdk=""Microsoft.NET.Sdk"">
59+
<PropertyGroup>
60+
<OutputType>Exe</OutputType>
61+
<TargetFramework>net10.0</TargetFramework>
62+
<ImplicitUsings>enable</ImplicitUsings>
63+
<Nullable>enable</Nullable>
64+
<NoWarn>$(NoWarn);CS1998</NoWarn>
65+
</PropertyGroup>
66+
<ItemGroup>
67+
{string.Join(Environment.NewLine, references)}
68+
</ItemGroup>
69+
</Project>
70+
";
71+
}
72+
73+
private static async Task<ProcessResult> RunDotnetBuildAsync(string workDirectory)
74+
{
75+
using var process = new Process();
76+
process.StartInfo = new ProcessStartInfo
77+
{
78+
FileName = "dotnet",
79+
Arguments = "build --nologo /p:UseAppHost=false /p:OutDir=obj/build/",
80+
WorkingDirectory = workDirectory,
81+
RedirectStandardOutput = true,
82+
RedirectStandardError = true,
83+
UseShellExecute = false,
84+
CreateNoWindow = true
85+
};
86+
87+
var output = new StringBuilder();
88+
process.OutputDataReceived += (_, args) =>
89+
{
90+
if (args.Data != null)
91+
output.AppendLine(args.Data);
92+
};
93+
process.ErrorDataReceived += (_, args) =>
94+
{
95+
if (args.Data != null)
96+
output.AppendLine(args.Data);
97+
};
98+
99+
process.Start();
100+
process.BeginOutputReadLine();
101+
process.BeginErrorReadLine();
102+
103+
var exited = await Task.Run(() => process.WaitForExit(60000)).ConfigureAwait(false);
104+
if (!exited)
105+
{
106+
process.Kill(entireProcessTree: true);
107+
return new ProcessResult(-1, output + "dotnet build timed out.");
108+
}
109+
110+
return new ProcessResult(process.ExitCode, output.ToString());
111+
}
112+
113+
private static string FindRepositoryRoot()
114+
{
115+
var directory = new DirectoryInfo(AppContext.BaseDirectory);
116+
while (directory != null)
117+
{
118+
if (File.Exists(Path.Combine(directory.FullName, "ByBit.Net.sln")))
119+
return directory.FullName;
120+
121+
directory = directory.Parent;
122+
}
123+
124+
throw new DirectoryNotFoundException("Could not find ByBit.Net.sln in the current directory tree.");
125+
}
126+
127+
private readonly record struct ProcessResult(int ExitCode, string Output);
128+
}
129+
}

0 commit comments

Comments
 (0)