ctrader-ts 0.1.11 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +259 -91
- package/dist/bin/auth.js +8 -8
- package/dist/bin/auth.js.map +1 -1
- package/dist/cli/index.js +169 -26
- package/dist/cli/index.js.map +1 -1
- package/dist/src/{client.d.ts → core/client.d.ts} +57 -15
- package/dist/src/core/client.d.ts.map +1 -0
- package/dist/src/{client.js → core/client.js} +306 -49
- package/dist/src/core/client.js.map +1 -0
- package/dist/src/core/config.d.ts.map +1 -0
- package/dist/src/{config.js → core/config.js} +13 -13
- package/dist/src/core/config.js.map +1 -0
- package/dist/src/{connect.d.ts → core/connect.d.ts} +3 -3
- package/dist/src/core/connect.d.ts.map +1 -0
- package/dist/src/{connect.js → core/connect.js} +4 -6
- package/dist/src/core/connect.js.map +1 -0
- package/dist/src/{connection.d.ts → core/connection.d.ts} +48 -4
- package/dist/src/core/connection.d.ts.map +1 -0
- package/dist/src/{connection.js → core/connection.js} +70 -14
- package/dist/src/core/connection.js.map +1 -0
- package/dist/src/{errors.d.ts → core/errors.d.ts} +5 -0
- package/dist/src/core/errors.d.ts.map +1 -0
- package/dist/src/core/errors.js +85 -0
- package/dist/src/core/errors.js.map +1 -0
- package/dist/src/{helpers.d.ts → core/helpers.d.ts} +3 -3
- package/dist/src/core/helpers.d.ts.map +1 -0
- package/dist/src/{helpers.js → core/helpers.js} +9 -7
- package/dist/src/core/helpers.js.map +1 -0
- package/dist/src/{symbol-cache.d.ts → core/symbol-cache.d.ts} +2 -2
- package/dist/src/core/symbol-cache.d.ts.map +1 -0
- package/dist/src/core/symbol-cache.js.map +1 -0
- package/dist/src/index.d.ts +26 -11
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +24 -9
- package/dist/src/index.js.map +1 -1
- package/dist/src/modules/account.d.ts +68 -2
- package/dist/src/modules/account.d.ts.map +1 -1
- package/dist/src/modules/account.js +91 -34
- package/dist/src/modules/account.js.map +1 -1
- package/dist/src/modules/auth.d.ts +29 -2
- package/dist/src/modules/auth.d.ts.map +1 -1
- package/dist/src/modules/auth.js +42 -10
- package/dist/src/modules/auth.js.map +1 -1
- package/dist/src/modules/market.d.ts +51 -3
- package/dist/src/modules/market.d.ts.map +1 -1
- package/dist/src/modules/market.js +57 -16
- package/dist/src/modules/market.js.map +1 -1
- package/dist/src/modules/trading.d.ts +69 -3
- package/dist/src/modules/trading.d.ts.map +1 -1
- package/dist/src/modules/trading.js +74 -35
- package/dist/src/modules/trading.js.map +1 -1
- package/dist/src/protocol/codec.d.ts.map +1 -0
- package/dist/src/{codec.js → protocol/codec.js} +2 -2
- package/dist/src/protocol/codec.js.map +1 -0
- package/dist/src/{enums.d.ts → protocol/enums.d.ts} +177 -0
- package/dist/src/protocol/enums.d.ts.map +1 -0
- package/dist/src/{enums.js → protocol/enums.js} +177 -0
- package/dist/src/protocol/enums.js.map +1 -0
- package/dist/src/protocol/proto/messages.d.ts +7496 -0
- package/dist/src/protocol/proto/messages.js +21525 -0
- package/dist/src/protocol/proto/proto/messages.d.ts +7496 -0
- package/dist/src/protocol/proto/proto/messages.js +21525 -0
- package/dist/src/{types.d.ts → protocol/types.d.ts} +151 -3
- package/dist/src/protocol/types.d.ts.map +1 -0
- package/dist/src/{types.js.map → protocol/types.js.map} +1 -1
- package/llms.txt +280 -0
- package/package.json +89 -61
- package/dist/src/client.d.ts.map +0 -1
- package/dist/src/client.js.map +0 -1
- package/dist/src/codec.d.ts.map +0 -1
- package/dist/src/codec.js.map +0 -1
- package/dist/src/config.d.ts.map +0 -1
- package/dist/src/config.js.map +0 -1
- package/dist/src/connect.d.ts.map +0 -1
- package/dist/src/connect.js.map +0 -1
- package/dist/src/connection.d.ts.map +0 -1
- package/dist/src/connection.js.map +0 -1
- package/dist/src/enums.d.ts.map +0 -1
- package/dist/src/enums.js.map +0 -1
- package/dist/src/errors.d.ts.map +0 -1
- package/dist/src/errors.js +0 -47
- package/dist/src/errors.js.map +0 -1
- package/dist/src/helpers.d.ts.map +0 -1
- package/dist/src/helpers.js.map +0 -1
- package/dist/src/proto/messages.d.ts +0 -7076
- package/dist/src/proto/messages.js +0 -23289
- package/dist/src/proto/proto/messages.d.ts +0 -7076
- package/dist/src/proto/proto/messages.js +0 -23289
- package/dist/src/symbol-cache.d.ts.map +0 -1
- package/dist/src/symbol-cache.js.map +0 -1
- package/dist/src/types.d.ts.map +0 -1
- /package/dist/src/{config.d.ts → core/config.d.ts} +0 -0
- /package/dist/src/{symbol-cache.js → core/symbol-cache.js} +0 -0
- /package/dist/src/{codec.d.ts → protocol/codec.d.ts} +0 -0
- /package/dist/src/{types.js → protocol/types.js} +0 -0
package/README.md
CHANGED
|
@@ -1,99 +1,172 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="./assets/banner.png" alt="ctrader-ts" width="100%" />
|
|
3
|
+
<img src="./assets/banner.png" alt="ctrader-ts — TypeScript client for cTrader Open API" width="100%" />
|
|
4
4
|
|
|
5
5
|
<br />
|
|
6
6
|
<br />
|
|
7
7
|
|
|
8
|
+
[](https://www.npmjs.com/package/ctrader-ts)
|
|
8
9
|
[](https://www.typescriptlang.org/)
|
|
9
10
|
[](https://nodejs.org/)
|
|
10
11
|
[](LICENSE)
|
|
11
12
|
[](https://help.ctrader.com/open-api/)
|
|
12
13
|
|
|
13
|
-
**
|
|
14
|
+
**The TypeScript/Node.js client for the cTrader Open API**
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
Trade forex, CFDs, and commodities programmatically. Build trading bots, portfolio dashboards, and AI trading agents.
|
|
17
|
+
|
|
18
|
+
*Unofficial community project · Not affiliated with Spotware* 🤝
|
|
19
|
+
|
|
20
|
+
[Getting Started](#-getting-started) · [API Reference](#-library-api) · [CLI](#%EF%B8%8F-cli) · [Examples](#-examples) · [For AI Agents](#-for-ai-agents)
|
|
16
21
|
|
|
17
22
|
</div>
|
|
18
23
|
|
|
19
24
|
---
|
|
20
25
|
|
|
21
|
-
##
|
|
26
|
+
## Why ctrader-ts?
|
|
22
27
|
|
|
23
|
-
There's no official TypeScript SDK for the cTrader Open API. The API
|
|
28
|
+
There's no official TypeScript SDK for the cTrader Open API. The raw API is a protobuf/WebSocket protocol — powerful but painful to use directly. This library wraps it into a clean, type-safe interface:
|
|
24
29
|
|
|
25
30
|
```ts
|
|
31
|
+
import { connect } from "ctrader-ts";
|
|
32
|
+
|
|
26
33
|
const ct = await connect();
|
|
27
34
|
const pos = await ct.buy("EURUSD", { lots: 0.1, sl: { pips: 50 }, tp: { equity: 0.02 } });
|
|
35
|
+
await ct.modify(pos.positionId, { sl: { pips: 30 } }); // preserves existing TP
|
|
28
36
|
await ct.close(pos.positionId);
|
|
29
37
|
```
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
**What you get:**
|
|
40
|
+
|
|
41
|
+
- **Zero boilerplate** — `connect()` handles auth, WebSocket setup, heartbeat, and reconnection
|
|
42
|
+
- **Human-friendly units** — trade in lots, set SL/TP in pips, dollars, or equity %. No unit conversions.
|
|
43
|
+
- **Full type safety** — every method, parameter, and response is typed. No `any`, no `@ts-ignore`.
|
|
44
|
+
- **Auto-retry** — rate-limit errors are retried automatically with exponential backoff
|
|
45
|
+
- **Actionable errors** — error messages tell you exactly what went wrong and how to fix it
|
|
46
|
+
- **AI-agent ready** — designed for LLM agents to understand and use without documentation lookup
|
|
47
|
+
- **CLI included** — every operation available from the terminal with `--json` output
|
|
48
|
+
- **Live streaming** — `watchState()` pushes real-time balance, equity, and position updates. No polling.
|
|
32
49
|
|
|
33
50
|
---
|
|
34
51
|
|
|
35
|
-
## 🚀 Getting
|
|
52
|
+
## 🚀 Getting Started
|
|
36
53
|
|
|
37
|
-
|
|
54
|
+
### Install
|
|
38
55
|
|
|
39
56
|
```bash
|
|
40
57
|
npm install ctrader-ts
|
|
41
58
|
```
|
|
42
59
|
|
|
43
|
-
|
|
60
|
+
### Authenticate
|
|
61
|
+
|
|
62
|
+
You need a cTrader Open API application. Create one at [openapi.ctrader.com/apps](https://openapi.ctrader.com/apps), then run:
|
|
44
63
|
|
|
45
64
|
```bash
|
|
46
|
-
|
|
47
|
-
cd ctrader-ts
|
|
48
|
-
npm install && npm run build && npm link
|
|
65
|
+
npx ctrader-ts auth
|
|
49
66
|
```
|
|
50
67
|
|
|
51
|
-
|
|
68
|
+
The interactive wizard walks you through OAuth in 3 steps. Credentials are saved to `~/.config/ctrader-ts/config.json`.
|
|
52
69
|
|
|
53
|
-
|
|
70
|
+
**Environment variables** work too:
|
|
54
71
|
|
|
55
72
|
```bash
|
|
56
|
-
|
|
73
|
+
export CTRADER_CLIENT_ID=your_client_id
|
|
74
|
+
export CTRADER_CLIENT_SECRET=your_client_secret
|
|
75
|
+
export CTRADER_ACCESS_TOKEN=your_access_token
|
|
76
|
+
export CTRADER_ACCOUNT_ID=12345678
|
|
77
|
+
export CTRADER_ENVIRONMENT=demo # or live
|
|
57
78
|
```
|
|
58
79
|
|
|
59
|
-
|
|
80
|
+
### Connect and trade
|
|
60
81
|
|
|
61
|
-
|
|
82
|
+
```ts
|
|
83
|
+
import { connect } from "ctrader-ts";
|
|
62
84
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
const ct = await connect();
|
|
86
|
+
|
|
87
|
+
// Get a complete account snapshot
|
|
88
|
+
const state = await ct.getState();
|
|
89
|
+
console.log(`Balance: $${state.balance}, Equity: $${state.equity}`);
|
|
90
|
+
console.log(`Open positions: ${state.positions.length}`);
|
|
91
|
+
|
|
92
|
+
// Each position includes computed convenience fields
|
|
93
|
+
for (const pos of state.positions) {
|
|
94
|
+
console.log(`${pos.volumeInLots} lots at ${pos.entryPrice}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Place a trade
|
|
98
|
+
const pos = await ct.buy("EURUSD", {
|
|
99
|
+
lots: 0.1,
|
|
100
|
+
sl: { pips: 50 },
|
|
101
|
+
tp: { dollars: 25 },
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
console.log(`Opened position ${pos.positionId} at ${pos.price}`);
|
|
105
|
+
|
|
106
|
+
ct.disconnect();
|
|
69
107
|
```
|
|
70
108
|
|
|
71
109
|
---
|
|
72
110
|
|
|
73
|
-
## 📦 Library
|
|
111
|
+
## 📦 Library API
|
|
112
|
+
|
|
113
|
+
### Account & Portfolio
|
|
74
114
|
|
|
75
115
|
```ts
|
|
76
|
-
|
|
116
|
+
// Complete account snapshot — call this first
|
|
117
|
+
const state = await ct.getState();
|
|
118
|
+
// → { balance, equity, usedMargin, freeMargin, marginLevel, unrealizedPnl,
|
|
119
|
+
// positions (enriched), orders, moneyDigits }
|
|
120
|
+
|
|
121
|
+
// Trader profile with leverage
|
|
122
|
+
const trader = await ct.getTrader();
|
|
123
|
+
// → { ...traderFields, leverage: 100 } (computed from leverageInCents)
|
|
124
|
+
|
|
125
|
+
// Position + order snapshot
|
|
126
|
+
const { positions, orders } = await ct.getPositions();
|
|
127
|
+
|
|
128
|
+
// Deal and order history (defaults to last 24h)
|
|
129
|
+
const { deals } = await ct.getDeals();
|
|
130
|
+
const { deals: recentDeals } = await ct.getDeals({ from: Date.now() - 7 * 86400000 });
|
|
131
|
+
const { orders: orderHistory } = await ct.getOrders({ from: Date.now() - 86400000 });
|
|
77
132
|
|
|
78
|
-
|
|
133
|
+
// Margin estimation before trading
|
|
134
|
+
const { margins } = await ct.getExpectedMargin("EURUSD", [0.1, 0.5, 1.0]);
|
|
79
135
|
```
|
|
80
136
|
|
|
81
|
-
###
|
|
137
|
+
### Live Account Streaming
|
|
82
138
|
|
|
83
|
-
|
|
139
|
+
Stream real-time account state — no polling. Balance, equity, positions, and margin update automatically:
|
|
84
140
|
|
|
85
141
|
```ts
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
//
|
|
91
|
-
|
|
142
|
+
// Watch account state in real-time — handler fires on every change
|
|
143
|
+
const stop = await ct.watchState((state) => {
|
|
144
|
+
console.log(`Equity: ${state.equity}, P&L: ${state.unrealizedPnl}`);
|
|
145
|
+
console.log(`Reason: ${state.reason}`);
|
|
146
|
+
// reason: "init" | "execution" | "trader_updated" | "margin_changed" | "spot"
|
|
147
|
+
for (const pos of state.positions) {
|
|
148
|
+
console.log(` ${pos.volumeInLots} lots, entry: ${pos.entryPrice}`);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Throttle spot updates (default 500ms) to control update frequency
|
|
153
|
+
const stop2 = await ct.watchState(handler, { throttleMs: 1000 });
|
|
154
|
+
|
|
155
|
+
// Stop watching
|
|
156
|
+
await stop();
|
|
92
157
|
```
|
|
93
158
|
|
|
94
|
-
|
|
159
|
+
Under the hood, `watchState()` combines:
|
|
160
|
+
- **Execution events** — position opens, closes, modifications
|
|
161
|
+
- **Balance events** — deposits, withdrawals, swap charges
|
|
162
|
+
- **Margin events** — margin requirement changes
|
|
163
|
+
- **Spot prices** — live bid/ask for computing unrealized P&L and equity
|
|
95
164
|
|
|
96
|
-
|
|
165
|
+
All push-based from the server. Zero polling.
|
|
166
|
+
|
|
167
|
+
### Trading
|
|
168
|
+
|
|
169
|
+
**Market orders** — execute immediately, return the opened `Position`:
|
|
97
170
|
|
|
98
171
|
```ts
|
|
99
172
|
const p1 = await ct.buy("EURUSD", { lots: 0.1 });
|
|
@@ -111,42 +184,55 @@ await ct.buyStop("EURUSD", { lots: 0.1, stopPrice: 1.1050 });
|
|
|
111
184
|
await ct.sellStop("EURUSD", { lots: 0.1, stopPrice: 1.0950 });
|
|
112
185
|
```
|
|
113
186
|
|
|
114
|
-
###
|
|
187
|
+
### SL/TP — three ways, zero math
|
|
115
188
|
|
|
116
189
|
No manual pip calculations. Every order accepts `sl` and `tp` in whichever unit makes sense:
|
|
117
190
|
|
|
118
|
-
| | Example | What it means |
|
|
191
|
+
| Unit | Example | What it means |
|
|
119
192
|
|---|---|---|
|
|
120
193
|
| **Pips** | `{ pips: 50 }` | 50 pips from entry — symbol-aware (handles JPY, gold, etc.) |
|
|
121
194
|
| **Dollars** | `{ dollars: 25 }` | Lose/gain exactly $25 on this trade |
|
|
122
195
|
| **Equity %** | `{ equity: 0.02 }` | Risk 2% of your account equity |
|
|
123
196
|
|
|
124
|
-
The library
|
|
197
|
+
The library automatically resolves symbol details and current prices to compute the exact values.
|
|
125
198
|
|
|
126
|
-
###
|
|
199
|
+
### Position Management
|
|
127
200
|
|
|
128
201
|
Positions use their real cTrader `positionId` — no invented ID system:
|
|
129
202
|
|
|
130
203
|
```ts
|
|
131
|
-
//
|
|
132
|
-
await ct.modify(
|
|
204
|
+
// Modify SL/TP — preserves the other when only one is changed
|
|
205
|
+
await ct.modify(pos.positionId, { sl: { pips: 30 } }); // keeps existing TP
|
|
206
|
+
await ct.modify(pos.positionId, { tp: { dollars: 50 } }); // keeps existing SL
|
|
207
|
+
await ct.modify(pos.positionId, { sl: { pips: 30 }, tp: { dollars: 50 } }); // change both
|
|
208
|
+
|
|
209
|
+
// Resize — change position volume to a new target
|
|
210
|
+
await ct.resize(pos.positionId, 0.2); // increase to 0.2 lots
|
|
211
|
+
await ct.resize(pos.positionId, 0.05); // decrease to 0.05 lots
|
|
133
212
|
|
|
134
213
|
// Close — full or partial
|
|
135
|
-
await ct.close(
|
|
136
|
-
await ct.close(
|
|
214
|
+
await ct.close(pos.positionId);
|
|
215
|
+
await ct.close(pos.positionId, { lots: 0.02 }); // partial close
|
|
137
216
|
|
|
138
|
-
//
|
|
139
|
-
await ct.closeSymbol("EURUSD");
|
|
140
|
-
await ct.closeAll();
|
|
217
|
+
// Bulk operations
|
|
218
|
+
await ct.closeSymbol("EURUSD"); // close all EURUSD positions
|
|
219
|
+
await ct.closeAll(); // close everything
|
|
141
220
|
|
|
142
221
|
// Cancel pending order
|
|
143
222
|
await ct.cancelOrder(orderId);
|
|
144
223
|
```
|
|
145
224
|
|
|
146
|
-
###
|
|
225
|
+
### Market Data
|
|
147
226
|
|
|
148
227
|
```ts
|
|
149
|
-
//
|
|
228
|
+
// Available symbols
|
|
229
|
+
const symbols = await ct.getSymbols();
|
|
230
|
+
|
|
231
|
+
// Full details for a single symbol
|
|
232
|
+
const info = await ct.getSymbolInfo("EURUSD");
|
|
233
|
+
// → { digits, pipPosition, minVolume, maxVolume, stepVolume, lotSize, swapLong, swapShort, ... }
|
|
234
|
+
|
|
235
|
+
// Stream live bid/ask — returns an unsubscribe function
|
|
150
236
|
const stop = await ct.watchSpots(["EURUSD", "GBPUSD"], (price) => {
|
|
151
237
|
console.log(price.symbol, price.bidDecimal, price.askDecimal);
|
|
152
238
|
});
|
|
@@ -166,16 +252,9 @@ const { ticks } = await ct.getTickData("EURUSD", {
|
|
|
166
252
|
});
|
|
167
253
|
```
|
|
168
254
|
|
|
169
|
-
###
|
|
170
|
-
|
|
171
|
-
```ts
|
|
172
|
-
const { deals } = await ct.getDeals({ maxRows: 50 });
|
|
173
|
-
const { positions, orders } = await ct.getPositions();
|
|
174
|
-
const trader = await ct.getTrader();
|
|
175
|
-
const { margins } = await ct.getExpectedMargin("EURUSD", [0.1, 0.5, 1.0]);
|
|
176
|
-
```
|
|
255
|
+
### Events
|
|
177
256
|
|
|
178
|
-
|
|
257
|
+
All return an unsubscribe function:
|
|
179
258
|
|
|
180
259
|
```ts
|
|
181
260
|
ct.onExecution((e) => console.log("fill:", e.executionType, e.position?.positionId));
|
|
@@ -186,7 +265,7 @@ ct.onTokenInvalidated(() => console.warn("token expired — run ctrader-ts aut
|
|
|
186
265
|
ct.onClientDisconnect((e) => console.warn("server dropped connection:", e.reason));
|
|
187
266
|
```
|
|
188
267
|
|
|
189
|
-
###
|
|
268
|
+
### Raw Protocol Access
|
|
190
269
|
|
|
191
270
|
Everything the high-level API doesn't expose is available via `ct.raw`:
|
|
192
271
|
|
|
@@ -197,7 +276,7 @@ ct.raw.market.subscribeLiveTrendbar(symbolId, TrendbarPeriod.M1);
|
|
|
197
276
|
ct.raw.auth.refreshToken(refreshToken);
|
|
198
277
|
```
|
|
199
278
|
|
|
200
|
-
###
|
|
279
|
+
### connect() options
|
|
201
280
|
|
|
202
281
|
```ts
|
|
203
282
|
const ct = await connect({
|
|
@@ -211,57 +290,76 @@ const ct = await connect({
|
|
|
211
290
|
|
|
212
291
|
## 🖥️ CLI
|
|
213
292
|
|
|
214
|
-
|
|
293
|
+
Every operation available from the terminal. Add `--json` for pipe-friendly structured output.
|
|
215
294
|
|
|
216
295
|
```bash
|
|
217
|
-
#
|
|
296
|
+
# Auth
|
|
218
297
|
ctrader-ts auth
|
|
219
298
|
|
|
220
|
-
#
|
|
221
|
-
ctrader-ts state
|
|
222
|
-
ctrader-ts positions
|
|
299
|
+
# Account
|
|
300
|
+
ctrader-ts state # full account snapshot
|
|
301
|
+
ctrader-ts positions # open positions + orders
|
|
223
302
|
|
|
224
|
-
#
|
|
303
|
+
# Trade
|
|
225
304
|
ctrader-ts buy EURUSD 0.1 --sl-pips 50 --tp-pips 100
|
|
226
305
|
ctrader-ts sell USDJPY 0.1 --sl-dollars 30
|
|
227
306
|
ctrader-ts buy XAUUSD 0.01 --sl-equity 0.02
|
|
228
307
|
|
|
229
|
-
#
|
|
308
|
+
# Pending orders
|
|
230
309
|
ctrader-ts buy-limit EURUSD 0.1 1.0800
|
|
231
310
|
ctrader-ts sell-limit EURUSD 0.1 1.1200
|
|
232
311
|
ctrader-ts buy-stop EURUSD 0.1 1.1050
|
|
233
312
|
|
|
234
|
-
#
|
|
235
|
-
ctrader-ts close 12345678
|
|
236
|
-
ctrader-ts close 12345678 --lots 0.05
|
|
313
|
+
# Manage positions
|
|
314
|
+
ctrader-ts close 12345678 # full close
|
|
315
|
+
ctrader-ts close 12345678 --lots 0.05 # partial
|
|
237
316
|
ctrader-ts modify 12345678 --sl-pips 30 --tp-dollars 50
|
|
317
|
+
ctrader-ts resize 12345678 0.2 # resize to 0.2 lots
|
|
238
318
|
ctrader-ts close-symbol EURUSD
|
|
239
319
|
ctrader-ts close-all
|
|
240
320
|
ctrader-ts cancel 87654321
|
|
241
321
|
|
|
242
|
-
#
|
|
243
|
-
ctrader-ts
|
|
244
|
-
ctrader-ts
|
|
322
|
+
# Market data
|
|
323
|
+
ctrader-ts symbols # list all symbols
|
|
324
|
+
ctrader-ts symbol-info EURUSD # full symbol details
|
|
325
|
+
ctrader-ts watch EURUSD GBPUSD # live prices, Ctrl+C to stop
|
|
326
|
+
ctrader-ts bars EURUSD H1 2024-01-01 2024-12-31 # historical candles
|
|
245
327
|
|
|
246
|
-
#
|
|
328
|
+
# History
|
|
247
329
|
ctrader-ts history --from 2024-01-01 --to 2024-12-31
|
|
248
330
|
|
|
249
|
-
#
|
|
331
|
+
# JSON output for piping
|
|
250
332
|
ctrader-ts state --json
|
|
251
333
|
ctrader-ts positions --json | jq '.positions[].positionId'
|
|
252
334
|
```
|
|
253
335
|
|
|
254
336
|
---
|
|
255
337
|
|
|
256
|
-
##
|
|
338
|
+
## 📁 Examples
|
|
257
339
|
|
|
258
|
-
|
|
340
|
+
See the [`examples/`](./examples) directory for runnable scripts:
|
|
259
341
|
|
|
260
|
-
-
|
|
261
|
-
- **
|
|
262
|
-
- **
|
|
263
|
-
|
|
264
|
-
|
|
342
|
+
- **[`basic-trade.ts`](./examples/basic-trade.ts)** — Open a position with SL/TP, modify the stop loss, close the position
|
|
343
|
+
- **[`watch-and-trade.ts`](./examples/watch-and-trade.ts)** — Stream live prices and react to conditions
|
|
344
|
+
- **[`portfolio-report.ts`](./examples/portfolio-report.ts)** — Generate an account report with positions, leverage, and deal history
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
npx ts-node examples/basic-trade.ts
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## 🤖 For AI Agents
|
|
353
|
+
|
|
354
|
+
Designed from the ground up for LLM-powered trading agents:
|
|
355
|
+
|
|
356
|
+
- **`getState()` first** — one call gives a complete picture: balance, equity, margin, all positions with `volumeInLots` and `entryPrice`, all pending orders
|
|
357
|
+
- **Human units everywhere** — `{ pips }`, `{ dollars }`, `{ equity }` — no protocol encoding knowledge needed
|
|
358
|
+
- **Real position IDs** — every `buy()`/`sell()` returns the actual `positionId`, so modify/resize/close are unambiguous
|
|
359
|
+
- **Actionable errors** — every error includes a `hint` property with recovery instructions (e.g. "Run `ctrader-ts auth` to re-authenticate")
|
|
360
|
+
- **Auto-retry** — rate-limit errors retry automatically — agents don't need retry logic
|
|
361
|
+
- **CLI with `--json`** — agents with shell access get structured output without Node.js in the loop
|
|
362
|
+
- **`llms.txt`** — machine-readable API reference included in the package
|
|
265
363
|
|
|
266
364
|
```ts
|
|
267
365
|
import { connect, CTraderError } from "ctrader-ts";
|
|
@@ -275,27 +373,97 @@ try {
|
|
|
275
373
|
console.log("opened", pos.positionId);
|
|
276
374
|
} catch (e) {
|
|
277
375
|
if (e instanceof CTraderError) {
|
|
278
|
-
|
|
279
|
-
if (e.
|
|
280
|
-
if (e.
|
|
376
|
+
console.error(e.hint); // "Your access token has expired. Run `ctrader-ts auth`..."
|
|
377
|
+
if (e.isAuthError) { /* re-authenticate */ }
|
|
378
|
+
if (e.isRateLimit) { /* auto-retried, only thrown after 3 failures */ }
|
|
379
|
+
if (e.isMaintenance) { /* server down, try later */ }
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## ⚙️ Error Handling
|
|
387
|
+
|
|
388
|
+
Every error from the cTrader API becomes a typed `CTraderError` with:
|
|
389
|
+
|
|
390
|
+
| Property | Type | Description |
|
|
391
|
+
|----------|------|-------------|
|
|
392
|
+
| `code` | `string` | Machine-readable error code |
|
|
393
|
+
| `description` | `string` | Server-provided description |
|
|
394
|
+
| `hint` | `string \| undefined` | Actionable recovery instructions |
|
|
395
|
+
| `isAuthError` | `boolean` | Token expired / not authenticated |
|
|
396
|
+
| `isRateLimit` | `boolean` | Too many requests (auto-retried first) |
|
|
397
|
+
| `isMaintenance` | `boolean` | Server under maintenance |
|
|
398
|
+
| `retryAfter` | `number \| undefined` | Wait time in ms (rate limit only) |
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
import { CTraderError, RequestTimeoutError, NotConnectedError } from "ctrader-ts";
|
|
402
|
+
|
|
403
|
+
try {
|
|
404
|
+
await ct.buy("EURUSD", { lots: 999 });
|
|
405
|
+
} catch (e) {
|
|
406
|
+
if (e instanceof CTraderError) {
|
|
407
|
+
console.error(e.message);
|
|
408
|
+
// "[NOT_ENOUGH_MONEY] Insufficient free margin — Reduce position size or close
|
|
409
|
+
// existing positions to free up margin."
|
|
281
410
|
}
|
|
282
411
|
}
|
|
283
412
|
```
|
|
284
413
|
|
|
285
414
|
---
|
|
286
415
|
|
|
287
|
-
## 🔄
|
|
416
|
+
## 🔄 Reliability
|
|
417
|
+
|
|
418
|
+
### Auto-Reconnection
|
|
288
419
|
|
|
289
|
-
|
|
420
|
+
Connection drops are handled automatically:
|
|
290
421
|
|
|
291
422
|
- Reconnects with exponential backoff (2s → 60s max)
|
|
292
|
-
- Re-authenticates after reconnect (app
|
|
423
|
+
- Re-authenticates after reconnect (app + account auth)
|
|
293
424
|
- Restores all active spot / depth / trendbar subscriptions
|
|
294
425
|
|
|
295
|
-
|
|
426
|
+
### Auto-Retry on Rate Limit
|
|
427
|
+
|
|
428
|
+
Rate-limit errors (`REQUEST_FREQUENCY_EXCEEDED`) are retried automatically:
|
|
429
|
+
|
|
430
|
+
- Exponential backoff: 1s → 2s → 4s
|
|
431
|
+
- Uses server-provided `retryAfter` when available
|
|
432
|
+
- Up to 3 retries before throwing to your code
|
|
433
|
+
|
|
434
|
+
No user code needed for either.
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## 🏗️ Architecture
|
|
439
|
+
|
|
440
|
+
```
|
|
441
|
+
connect() → CTrader (high-level, human-friendly API)
|
|
442
|
+
└─ CTrader
|
|
443
|
+
├─ connection → CTraderConnection (TLS/TCP, heartbeat, reconnect, auto-retry)
|
|
444
|
+
└─ raw
|
|
445
|
+
├─ auth → CTraderAuth (app/account auth, token management)
|
|
446
|
+
├─ trading → CTraderTrading (order CRUD, execution events)
|
|
447
|
+
├─ account → CTraderAccount (balance, history, margin)
|
|
448
|
+
└─ market → CTraderMarket (symbols, spots, trendbars, depth)
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
| Layer | When to use |
|
|
452
|
+
|-------|------------|
|
|
453
|
+
| `ct.buy()`, `ct.getState()` | Most of the time — clean API, human units |
|
|
454
|
+
| `ct.raw.trading.*` | Need parameters the high-level API doesn't expose |
|
|
455
|
+
| `ct.connection.*` | Direct WebSocket control, custom event handling |
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## 📜 License
|
|
460
|
+
|
|
461
|
+
MIT — use it however you want.
|
|
296
462
|
|
|
297
463
|
---
|
|
298
464
|
|
|
299
|
-
|
|
465
|
+
<div align="center">
|
|
300
466
|
|
|
301
|
-
|
|
467
|
+
**[npm](https://www.npmjs.com/package/ctrader-ts)** · **[GitHub](https://github.com/thecommandcat/ctrader-ts)** · **[cTrader Open API Docs](https://help.ctrader.com/open-api/)**
|
|
468
|
+
|
|
469
|
+
</div>
|
package/dist/bin/auth.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as p from "@clack/prompts";
|
|
3
|
-
import {
|
|
4
|
-
import { CTraderConnection } from "../src/connection.js";
|
|
3
|
+
import { DEMO_ENDPOINT, getConfigPath, loadStoredConfig, saveConfig, } from "../src/core/config.js";
|
|
4
|
+
import { CTraderConnection } from "../src/core/connection.js";
|
|
5
5
|
import { CTraderAuth } from "../src/modules/auth.js";
|
|
6
6
|
const OAUTH_TOKEN_URL = "https://openapi.ctrader.com/apps/token";
|
|
7
7
|
const REDIRECT_URI = "https://openapi.ctrader.com";
|
|
@@ -18,13 +18,13 @@ async function exchangeCodeForToken(code, clientId, clientSecret) {
|
|
|
18
18
|
if (!res.ok)
|
|
19
19
|
throw new Error(`HTTP ${res.status} from token endpoint`);
|
|
20
20
|
const body = (await res.json());
|
|
21
|
-
if (body
|
|
22
|
-
throw new Error(`${body
|
|
23
|
-
if (!body
|
|
21
|
+
if (body.errorCode)
|
|
22
|
+
throw new Error(`${body.errorCode}: ${body.description}`);
|
|
23
|
+
if (!body.accessToken)
|
|
24
24
|
throw new Error("No accessToken in response");
|
|
25
25
|
return {
|
|
26
|
-
accessToken: body
|
|
27
|
-
refreshToken: body
|
|
26
|
+
accessToken: body.accessToken,
|
|
27
|
+
refreshToken: body.refreshToken,
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
function buildAuthUrl(clientId) {
|
|
@@ -191,7 +191,7 @@ async function doOAuthFlow(clientId, clientSecret) {
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
run().catch((err) => {
|
|
194
|
-
process.stderr.write(String(err)
|
|
194
|
+
process.stderr.write(`${String(err)}\n`);
|
|
195
195
|
process.exit(1);
|
|
196
196
|
});
|
|
197
197
|
//# sourceMappingURL=auth.js.map
|
package/dist/bin/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../bin/auth.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../bin/auth.ts"],"names":[],"mappings":"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"}
|