topstepx-api 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 +736 -0
- package/dist/index.d.mts +731 -0
- package/dist/index.d.ts +731 -0
- package/dist/index.js +800 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +776 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
# topstepx-api
|
|
2
|
+
|
|
3
|
+
A framework-agnostic TypeScript client for the TopstepX trading API with REST and SignalR data feeds via WebSocket.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Full REST API coverage (accounts, orders, positions, trades, contracts, history)
|
|
8
|
+
- Real-time WebSocket data via SignalR (quotes, trades, market depth)
|
|
9
|
+
- Automatic token management and refresh
|
|
10
|
+
- TypeScript-first with complete type definitions
|
|
11
|
+
- Works with any Node.js framework (Express, Fastify, NestJS, etc.)
|
|
12
|
+
- Dual ESM/CommonJS support
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install topstepx-api
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Environment Setup
|
|
21
|
+
|
|
22
|
+
Create a `.env` file in your project root:
|
|
23
|
+
|
|
24
|
+
```env
|
|
25
|
+
TOPSTEP_USERNAME=your_username
|
|
26
|
+
TOPSTEP_API_KEY=your_api_key
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Get your API key from your TopstepX account settings.
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { TopstepXClient, OrderType, OrderSide } from 'topstepx-api';
|
|
35
|
+
|
|
36
|
+
const client = new TopstepXClient({
|
|
37
|
+
username: process.env.TOPSTEP_USERNAME!,
|
|
38
|
+
apiKey: process.env.TOPSTEP_API_KEY!,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
await client.connect();
|
|
42
|
+
|
|
43
|
+
// Get accounts
|
|
44
|
+
const accounts = await client.accounts.search({ onlyActiveAccounts: true });
|
|
45
|
+
console.log('Accounts:', accounts);
|
|
46
|
+
|
|
47
|
+
// Place a market order
|
|
48
|
+
const order = await client.orders.place({
|
|
49
|
+
accountId: accounts[0].id,
|
|
50
|
+
contractId: 'CON.F.US.ENQ.M25',
|
|
51
|
+
type: OrderType.Market,
|
|
52
|
+
side: OrderSide.Buy,
|
|
53
|
+
size: 1,
|
|
54
|
+
});
|
|
55
|
+
console.log('Order placed:', order.orderId);
|
|
56
|
+
|
|
57
|
+
// Disconnect when done
|
|
58
|
+
await client.disconnect();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## API Reference
|
|
62
|
+
|
|
63
|
+
### TopstepXClient
|
|
64
|
+
|
|
65
|
+
The main client class that provides access to all APIs.
|
|
66
|
+
|
|
67
|
+
#### Constructor
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
const client = new TopstepXClient({
|
|
71
|
+
username: string; // Required: TopstepX username
|
|
72
|
+
apiKey: string; // Required: TopstepX API key
|
|
73
|
+
baseUrl?: string; // Optional: API base URL (default: https://api.topstepx.com)
|
|
74
|
+
marketHubUrl?: string; // Optional: Market WebSocket URL
|
|
75
|
+
userHubUrl?: string; // Optional: User WebSocket URL
|
|
76
|
+
autoRefresh?: boolean; // Optional: Auto-refresh tokens (default: true)
|
|
77
|
+
tokenValidityHours?: number; // Optional: Token validity period (default: 24)
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### Methods
|
|
82
|
+
|
|
83
|
+
| Method | Description |
|
|
84
|
+
|--------|-------------|
|
|
85
|
+
| `connect()` | Authenticate and establish WebSocket connections |
|
|
86
|
+
| `disconnect()` | Close all connections and cleanup |
|
|
87
|
+
| `getToken()` | Get the current session token |
|
|
88
|
+
| `isConnected` | Check if WebSocket connections are active |
|
|
89
|
+
|
|
90
|
+
#### Events
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
client.on('connected', () => console.log('Connected'));
|
|
94
|
+
client.on('disconnected', () => console.log('Disconnected'));
|
|
95
|
+
client.on('error', (error) => console.error('Error:', error));
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### Accounts API
|
|
101
|
+
|
|
102
|
+
Access via `client.accounts`
|
|
103
|
+
|
|
104
|
+
#### search
|
|
105
|
+
|
|
106
|
+
Search for accounts.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
const accounts = await client.accounts.search({
|
|
110
|
+
onlyActiveAccounts: boolean;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Returns: Account[]
|
|
114
|
+
interface Account {
|
|
115
|
+
id: number;
|
|
116
|
+
name: string;
|
|
117
|
+
canTrade: boolean;
|
|
118
|
+
isVisible: boolean;
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
### Orders API
|
|
125
|
+
|
|
126
|
+
Access via `client.orders`
|
|
127
|
+
|
|
128
|
+
#### place
|
|
129
|
+
|
|
130
|
+
Place a new order.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { OrderType, OrderSide } from 'topstepx-api';
|
|
134
|
+
|
|
135
|
+
const result = await client.orders.place({
|
|
136
|
+
accountId: number;
|
|
137
|
+
contractId: string;
|
|
138
|
+
type: OrderType; // Market, Limit, Stop, StopLimit
|
|
139
|
+
side: OrderSide; // Buy, Sell
|
|
140
|
+
size: number;
|
|
141
|
+
limitPrice?: number; // Required for Limit/StopLimit
|
|
142
|
+
stopPrice?: number; // Required for Stop/StopLimit
|
|
143
|
+
trailPrice?: number; // Optional trailing stop price
|
|
144
|
+
customTag?: string; // Optional custom identifier
|
|
145
|
+
linkedOrderId?: number; // Optional linked order (OCO)
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Returns: { orderId: number, success: boolean, ... }
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### cancel
|
|
152
|
+
|
|
153
|
+
Cancel an existing order.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
await client.orders.cancel({
|
|
157
|
+
accountId: number;
|
|
158
|
+
orderId: number;
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### modify
|
|
163
|
+
|
|
164
|
+
Modify an existing order.
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
await client.orders.modify({
|
|
168
|
+
accountId: number;
|
|
169
|
+
orderId: number;
|
|
170
|
+
size?: number;
|
|
171
|
+
limitPrice?: number;
|
|
172
|
+
stopPrice?: number;
|
|
173
|
+
trailPrice?: number;
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### search
|
|
178
|
+
|
|
179
|
+
Search historical orders.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const orders = await client.orders.search({
|
|
183
|
+
accountId: number;
|
|
184
|
+
startTimestamp?: string; // ISO 8601 format
|
|
185
|
+
endTimestamp?: string;
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Returns: Order[]
|
|
189
|
+
interface Order {
|
|
190
|
+
id: number;
|
|
191
|
+
accountId: number;
|
|
192
|
+
contractId: string;
|
|
193
|
+
creationTimestamp: string;
|
|
194
|
+
updateTimestamp: string | null;
|
|
195
|
+
status: OrderStatus;
|
|
196
|
+
type: OrderType;
|
|
197
|
+
side: OrderSide;
|
|
198
|
+
size: number;
|
|
199
|
+
limitPrice: number | null;
|
|
200
|
+
stopPrice: number | null;
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### searchOpen
|
|
205
|
+
|
|
206
|
+
Get currently open orders.
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
const openOrders = await client.orders.searchOpen({
|
|
210
|
+
accountId: number;
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
### Positions API
|
|
217
|
+
|
|
218
|
+
Access via `client.positions`
|
|
219
|
+
|
|
220
|
+
#### searchOpen
|
|
221
|
+
|
|
222
|
+
Get open positions.
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const positions = await client.positions.searchOpen({
|
|
226
|
+
accountId: number;
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Returns: Position[]
|
|
230
|
+
interface Position {
|
|
231
|
+
id: number;
|
|
232
|
+
accountId: number;
|
|
233
|
+
contractId: string;
|
|
234
|
+
creationTimestamp: string;
|
|
235
|
+
type: PositionType; // Long, Short
|
|
236
|
+
size: number;
|
|
237
|
+
averagePrice: number;
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### close
|
|
242
|
+
|
|
243
|
+
Close a position entirely.
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
await client.positions.close({
|
|
247
|
+
accountId: number;
|
|
248
|
+
contractId: string;
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### partialClose
|
|
253
|
+
|
|
254
|
+
Partially close a position.
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
await client.positions.partialClose({
|
|
258
|
+
accountId: number;
|
|
259
|
+
contractId: string;
|
|
260
|
+
size: number; // Number of contracts to close
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
### Trades API
|
|
267
|
+
|
|
268
|
+
Access via `client.trades`
|
|
269
|
+
|
|
270
|
+
#### search
|
|
271
|
+
|
|
272
|
+
Search trade history.
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
const trades = await client.trades.search({
|
|
276
|
+
accountId: number;
|
|
277
|
+
startTimestamp: string; // ISO 8601 format
|
|
278
|
+
endTimestamp: string;
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Returns: Trade[]
|
|
282
|
+
interface Trade {
|
|
283
|
+
id: number;
|
|
284
|
+
accountId: number;
|
|
285
|
+
contractId: string;
|
|
286
|
+
creationTimestamp: string;
|
|
287
|
+
price: number;
|
|
288
|
+
profitAndLoss: number | null;
|
|
289
|
+
fees: number;
|
|
290
|
+
side: OrderSide;
|
|
291
|
+
size: number;
|
|
292
|
+
voided: boolean;
|
|
293
|
+
orderId: number;
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
### Contracts API
|
|
300
|
+
|
|
301
|
+
Access via `client.contracts`
|
|
302
|
+
|
|
303
|
+
#### search
|
|
304
|
+
|
|
305
|
+
Search for contracts/symbols.
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
const contracts = await client.contracts.search({
|
|
309
|
+
searchText: string; // e.g., "ES", "NQ", "CL"
|
|
310
|
+
live: boolean; // true for live, false for sim
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// Returns: Contract[]
|
|
314
|
+
interface Contract {
|
|
315
|
+
id: string;
|
|
316
|
+
name: string;
|
|
317
|
+
description: string;
|
|
318
|
+
tickSize: number;
|
|
319
|
+
tickValue: number;
|
|
320
|
+
activeContract: boolean;
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### searchById
|
|
325
|
+
|
|
326
|
+
Get a specific contract by ID.
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
const contract = await client.contracts.searchById({
|
|
330
|
+
contractId: string; // e.g., "CON.F.US.ENQ.M25"
|
|
331
|
+
live: boolean;
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Returns: Contract | null
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
### History API
|
|
340
|
+
|
|
341
|
+
Access via `client.history`
|
|
342
|
+
|
|
343
|
+
#### retrieveBars
|
|
344
|
+
|
|
345
|
+
Get historical OHLCV bars.
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { BarUnit } from 'topstepx-api';
|
|
349
|
+
|
|
350
|
+
const bars = await client.history.retrieveBars({
|
|
351
|
+
contractId: string;
|
|
352
|
+
live: boolean;
|
|
353
|
+
startTime: string; // ISO 8601 format
|
|
354
|
+
endTime: string;
|
|
355
|
+
unit: BarUnit; // Second, Minute, Hour, Day, Week, Month
|
|
356
|
+
unitNumber: number; // e.g., 5 for 5-minute bars
|
|
357
|
+
limit: number; // Max bars to return
|
|
358
|
+
includePartialBar: boolean;
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// Returns: Bar[]
|
|
362
|
+
interface Bar {
|
|
363
|
+
t: string; // timestamp
|
|
364
|
+
o: number; // open
|
|
365
|
+
h: number; // high
|
|
366
|
+
l: number; // low
|
|
367
|
+
c: number; // close
|
|
368
|
+
v: number; // volume
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
### Market Hub (Real-time Market Data)
|
|
375
|
+
|
|
376
|
+
Access via `client.marketHub`
|
|
377
|
+
|
|
378
|
+
Subscribe to real-time market data via WebSocket.
|
|
379
|
+
|
|
380
|
+
#### Subscribing
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
// Subscribe to all market data for a contract
|
|
384
|
+
await client.marketHub.subscribe('CON.F.US.ENQ.M25');
|
|
385
|
+
|
|
386
|
+
// Or subscribe selectively
|
|
387
|
+
await client.marketHub.subscribeQuotes('CON.F.US.ENQ.M25');
|
|
388
|
+
await client.marketHub.subscribeTrades('CON.F.US.ENQ.M25');
|
|
389
|
+
await client.marketHub.subscribeDepth('CON.F.US.ENQ.M25');
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
#### Unsubscribing
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
await client.marketHub.unsubscribe('CON.F.US.ENQ.M25');
|
|
396
|
+
|
|
397
|
+
// Or unsubscribe selectively
|
|
398
|
+
await client.marketHub.unsubscribeQuotes('CON.F.US.ENQ.M25');
|
|
399
|
+
await client.marketHub.unsubscribeTrades('CON.F.US.ENQ.M25');
|
|
400
|
+
await client.marketHub.unsubscribeDepth('CON.F.US.ENQ.M25');
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
#### Events
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
// Quote updates
|
|
407
|
+
client.marketHub.on('quote', ({ contractId, data }) => {
|
|
408
|
+
for (const quote of data) {
|
|
409
|
+
console.log(`${contractId}: Bid ${quote.bestBid} / Ask ${quote.bestAsk}`);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// Trade updates
|
|
414
|
+
client.marketHub.on('trade', ({ contractId, data }) => {
|
|
415
|
+
for (const trade of data) {
|
|
416
|
+
console.log(`${contractId}: ${trade.volume} @ ${trade.price}`);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// Market depth updates
|
|
421
|
+
client.marketHub.on('depth', ({ contractId, data }) => {
|
|
422
|
+
for (const level of data) {
|
|
423
|
+
console.log(`${contractId}: ${level.volume} @ ${level.price}`);
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
#### Event Types
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
interface Quote {
|
|
432
|
+
symbol: string;
|
|
433
|
+
lastPrice: number;
|
|
434
|
+
bestBid: number;
|
|
435
|
+
bestAsk: number;
|
|
436
|
+
change: number;
|
|
437
|
+
changePercent: number;
|
|
438
|
+
volume: number;
|
|
439
|
+
lastUpdated: string;
|
|
440
|
+
timestamp: string;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
interface MarketTrade {
|
|
444
|
+
symbolId: string;
|
|
445
|
+
price: number;
|
|
446
|
+
timestamp: string;
|
|
447
|
+
type: 0 | 1; // 0 = Bid, 1 = Ask
|
|
448
|
+
volume: number;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
interface MarketDepth {
|
|
452
|
+
price: number;
|
|
453
|
+
volume: number;
|
|
454
|
+
currentVolume: number;
|
|
455
|
+
type: number;
|
|
456
|
+
timestamp: string;
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
### User Hub (Real-time Account Data)
|
|
463
|
+
|
|
464
|
+
Access via `client.userHub`
|
|
465
|
+
|
|
466
|
+
Subscribe to real-time account updates via WebSocket.
|
|
467
|
+
|
|
468
|
+
#### Subscribing
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
// Subscribe to all account updates
|
|
472
|
+
await client.userHub.subscribe(accountId);
|
|
473
|
+
|
|
474
|
+
// Or subscribe selectively
|
|
475
|
+
await client.userHub.subscribeOrders(accountId);
|
|
476
|
+
await client.userHub.subscribePositions(accountId);
|
|
477
|
+
await client.userHub.subscribeTrades(accountId);
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
#### Unsubscribing
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
await client.userHub.unsubscribe(accountId);
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
#### Events
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
// Order updates
|
|
490
|
+
client.userHub.on('order', (order) => {
|
|
491
|
+
console.log(`Order ${order.id}: ${order.status}`);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// Position updates
|
|
495
|
+
client.userHub.on('position', (position) => {
|
|
496
|
+
console.log(`Position: ${position.size} contracts @ ${position.averagePrice}`);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// Trade executions
|
|
500
|
+
client.userHub.on('trade', (trade) => {
|
|
501
|
+
console.log(`Trade: ${trade.size} @ ${trade.price}, P&L: ${trade.profitAndLoss}`);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
// Account updates
|
|
505
|
+
client.userHub.on('account', (account) => {
|
|
506
|
+
console.log(`Account ${account.name}: Can trade = ${account.canTrade}`);
|
|
507
|
+
});
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## Enums
|
|
513
|
+
|
|
514
|
+
```typescript
|
|
515
|
+
import {
|
|
516
|
+
OrderType,
|
|
517
|
+
OrderSide,
|
|
518
|
+
OrderStatus,
|
|
519
|
+
BarUnit,
|
|
520
|
+
PositionType,
|
|
521
|
+
TradeType,
|
|
522
|
+
} from 'topstepx-api';
|
|
523
|
+
|
|
524
|
+
// OrderType
|
|
525
|
+
OrderType.Limit // 1
|
|
526
|
+
OrderType.Market // 2
|
|
527
|
+
OrderType.Stop // 3
|
|
528
|
+
OrderType.StopLimit // 4
|
|
529
|
+
|
|
530
|
+
// OrderSide
|
|
531
|
+
OrderSide.Buy // 0
|
|
532
|
+
OrderSide.Sell // 1
|
|
533
|
+
|
|
534
|
+
// OrderStatus
|
|
535
|
+
OrderStatus.Pending // 0
|
|
536
|
+
OrderStatus.Working // 1
|
|
537
|
+
OrderStatus.Filled // 2
|
|
538
|
+
OrderStatus.Cancelled // 3
|
|
539
|
+
OrderStatus.Rejected // 4
|
|
540
|
+
OrderStatus.PartiallyFilled // 5
|
|
541
|
+
|
|
542
|
+
// BarUnit
|
|
543
|
+
BarUnit.Second // 1
|
|
544
|
+
BarUnit.Minute // 2
|
|
545
|
+
BarUnit.Hour // 3
|
|
546
|
+
BarUnit.Day // 4
|
|
547
|
+
BarUnit.Week // 5
|
|
548
|
+
BarUnit.Month // 6
|
|
549
|
+
|
|
550
|
+
// PositionType
|
|
551
|
+
PositionType.Long // 0
|
|
552
|
+
PositionType.Short // 1
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## Error Handling
|
|
558
|
+
|
|
559
|
+
The library provides typed errors for different failure scenarios:
|
|
560
|
+
|
|
561
|
+
```typescript
|
|
562
|
+
import {
|
|
563
|
+
TopstepXError,
|
|
564
|
+
AuthenticationError,
|
|
565
|
+
ApiError,
|
|
566
|
+
ConnectionError,
|
|
567
|
+
} from 'topstepx-api';
|
|
568
|
+
|
|
569
|
+
try {
|
|
570
|
+
await client.connect();
|
|
571
|
+
await client.orders.place({ ... });
|
|
572
|
+
} catch (error) {
|
|
573
|
+
if (error instanceof AuthenticationError) {
|
|
574
|
+
console.error('Auth failed:', error.message, error.code);
|
|
575
|
+
} else if (error instanceof ApiError) {
|
|
576
|
+
console.error(`API error on ${error.endpoint}:`, error.message);
|
|
577
|
+
} else if (error instanceof ConnectionError) {
|
|
578
|
+
console.error('WebSocket error:', error.message);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
All errors extend `TopstepXError` and include:
|
|
584
|
+
- `message` - Error description
|
|
585
|
+
- `code` - Error code (if applicable)
|
|
586
|
+
- `timestamp` - When the error occurred
|
|
587
|
+
- `toJSON()` - Serialize for logging
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## Complete Example
|
|
592
|
+
|
|
593
|
+
```typescript
|
|
594
|
+
import {
|
|
595
|
+
TopstepXClient,
|
|
596
|
+
OrderType,
|
|
597
|
+
OrderSide,
|
|
598
|
+
BarUnit,
|
|
599
|
+
ApiError,
|
|
600
|
+
} from 'topstepx-api';
|
|
601
|
+
import 'dotenv/config';
|
|
602
|
+
|
|
603
|
+
async function main() {
|
|
604
|
+
const client = new TopstepXClient({
|
|
605
|
+
username: process.env.TOPSTEP_USERNAME!,
|
|
606
|
+
apiKey: process.env.TOPSTEP_API_KEY!,
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
try {
|
|
610
|
+
// Connect
|
|
611
|
+
await client.connect();
|
|
612
|
+
console.log('Connected to TopstepX');
|
|
613
|
+
|
|
614
|
+
// Get accounts
|
|
615
|
+
const accounts = await client.accounts.search({ onlyActiveAccounts: true });
|
|
616
|
+
const account = accounts[0];
|
|
617
|
+
console.log(`Using account: ${account.name} (${account.id})`);
|
|
618
|
+
|
|
619
|
+
// Search for ES contract
|
|
620
|
+
const contracts = await client.contracts.search({
|
|
621
|
+
searchText: 'ES',
|
|
622
|
+
live: false,
|
|
623
|
+
});
|
|
624
|
+
const esContract = contracts.find(c => c.activeContract);
|
|
625
|
+
console.log(`Found contract: ${esContract?.id}`);
|
|
626
|
+
|
|
627
|
+
// Get historical data
|
|
628
|
+
const endTime = new Date();
|
|
629
|
+
const startTime = new Date(endTime.getTime() - 24 * 60 * 60 * 1000);
|
|
630
|
+
|
|
631
|
+
const bars = await client.history.retrieveBars({
|
|
632
|
+
contractId: esContract!.id,
|
|
633
|
+
live: false,
|
|
634
|
+
startTime: startTime.toISOString(),
|
|
635
|
+
endTime: endTime.toISOString(),
|
|
636
|
+
unit: BarUnit.Hour,
|
|
637
|
+
unitNumber: 1,
|
|
638
|
+
limit: 24,
|
|
639
|
+
includePartialBar: false,
|
|
640
|
+
});
|
|
641
|
+
console.log(`Retrieved ${bars.length} hourly bars`);
|
|
642
|
+
|
|
643
|
+
// Subscribe to real-time quotes
|
|
644
|
+
client.marketHub.on('quote', ({ contractId, data }) => {
|
|
645
|
+
const quote = data[0];
|
|
646
|
+
console.log(`${contractId}: ${quote.bestBid} / ${quote.bestAsk}`);
|
|
647
|
+
});
|
|
648
|
+
await client.marketHub.subscribeQuotes(esContract!.id);
|
|
649
|
+
|
|
650
|
+
// Subscribe to account updates
|
|
651
|
+
client.userHub.on('order', (order) => {
|
|
652
|
+
console.log(`Order update: ${order.id} - ${order.status}`);
|
|
653
|
+
});
|
|
654
|
+
await client.userHub.subscribe(account.id);
|
|
655
|
+
|
|
656
|
+
// Place a limit order
|
|
657
|
+
const order = await client.orders.place({
|
|
658
|
+
accountId: account.id,
|
|
659
|
+
contractId: esContract!.id,
|
|
660
|
+
type: OrderType.Limit,
|
|
661
|
+
side: OrderSide.Buy,
|
|
662
|
+
size: 1,
|
|
663
|
+
limitPrice: bars[bars.length - 1].c - 10, // 10 points below last close
|
|
664
|
+
});
|
|
665
|
+
console.log(`Placed order: ${order.orderId}`);
|
|
666
|
+
|
|
667
|
+
// Check open orders
|
|
668
|
+
const openOrders = await client.orders.searchOpen({ accountId: account.id });
|
|
669
|
+
console.log(`Open orders: ${openOrders.length}`);
|
|
670
|
+
|
|
671
|
+
// Cancel the order
|
|
672
|
+
await client.orders.cancel({
|
|
673
|
+
accountId: account.id,
|
|
674
|
+
orderId: order.orderId,
|
|
675
|
+
});
|
|
676
|
+
console.log('Order cancelled');
|
|
677
|
+
|
|
678
|
+
// Keep running for real-time updates
|
|
679
|
+
await new Promise(resolve => setTimeout(resolve, 30000));
|
|
680
|
+
|
|
681
|
+
} catch (error) {
|
|
682
|
+
if (error instanceof ApiError) {
|
|
683
|
+
console.error(`API Error [${error.endpoint}]: ${error.message}`);
|
|
684
|
+
} else {
|
|
685
|
+
console.error('Error:', error);
|
|
686
|
+
}
|
|
687
|
+
} finally {
|
|
688
|
+
await client.disconnect();
|
|
689
|
+
console.log('Disconnected');
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
main();
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
---
|
|
697
|
+
|
|
698
|
+
## TypeScript Support
|
|
699
|
+
|
|
700
|
+
All types are exported for full TypeScript support:
|
|
701
|
+
|
|
702
|
+
```typescript
|
|
703
|
+
import type {
|
|
704
|
+
// Config
|
|
705
|
+
TopstepXClientConfig,
|
|
706
|
+
TopstepXClientEvents,
|
|
707
|
+
|
|
708
|
+
// REST types
|
|
709
|
+
Account,
|
|
710
|
+
Order,
|
|
711
|
+
Position,
|
|
712
|
+
Trade,
|
|
713
|
+
Contract,
|
|
714
|
+
Bar,
|
|
715
|
+
|
|
716
|
+
// Request types
|
|
717
|
+
PlaceOrderRequest,
|
|
718
|
+
ModifyOrderRequest,
|
|
719
|
+
SearchOrdersRequest,
|
|
720
|
+
RetrieveBarsRequest,
|
|
721
|
+
|
|
722
|
+
// WebSocket types
|
|
723
|
+
Quote,
|
|
724
|
+
MarketTrade,
|
|
725
|
+
MarketDepth,
|
|
726
|
+
OrderUpdate,
|
|
727
|
+
PositionUpdate,
|
|
728
|
+
TradeUpdate,
|
|
729
|
+
} from 'topstepx-api';
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
## License
|
|
735
|
+
|
|
736
|
+
MIT
|