ctrader-x 0.1.0 → 0.3.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 +302 -2
- package/dist/example/market-data-example.js +16 -0
- package/dist/example/market-data-example.js.map +1 -1
- package/dist/example/trendbars-example.d.ts +2 -0
- package/dist/example/trendbars-example.d.ts.map +1 -0
- package/dist/example/trendbars-example.js +28 -0
- package/dist/example/trendbars-example.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/market-data/spotware-market-data.d.ts +23 -0
- package/dist/market-data/spotware-market-data.d.ts.map +1 -1
- package/dist/market-data/spotware-market-data.js +40 -0
- package/dist/market-data/spotware-market-data.js.map +1 -1
- package/dist/market-data/spotware-symbol-catalog.d.ts +10 -1
- package/dist/market-data/spotware-symbol-catalog.d.ts.map +1 -1
- package/dist/market-data/spotware-symbol-catalog.js +26 -0
- package/dist/market-data/spotware-symbol-catalog.js.map +1 -1
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +18 -0
- package/dist/shared/index.js.map +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CTrader-x
|
|
2
2
|
|
|
3
3
|
A TypeScript SDK for cTrader's Open API — connect, authenticate, stream prices, and trade, with automatic reconnection built in.
|
|
4
4
|
|
|
@@ -117,6 +117,27 @@ if (symbol) {
|
|
|
117
117
|
|
|
118
118
|
`marketData.symbols` is a `SpotwareSymbolCatalog` — it can also be used on its own (`new SpotwareSymbolCatalog(client)`) if you only need symbol lookups.
|
|
119
119
|
|
|
120
|
+
`findByName`/`getAll` return the light symbol list — enough to resolve a name to a `symbolId` and subscribe. For the trading constraints that list doesn't carry (`lotSize`, `minVolume`/`maxVolume`/`stepVolume`, `digits`, `pipPosition`), fetch the full spec:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
const fullSymbol = await marketData.symbols.getFullSymbol(symbol.symbolId);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
See [A note on volume](#a-note-on-volume) under Trading for why these matter before placing an order.
|
|
127
|
+
|
|
128
|
+
Historical bars are a one-off fetch, not a subscription:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { ProtoOATrendbarPeriod } from 'ctrader-x';
|
|
132
|
+
|
|
133
|
+
const bars = await marketData.getTrendbars({
|
|
134
|
+
symbolId: symbol.symbolId,
|
|
135
|
+
period: ProtoOATrendbarPeriod.H1,
|
|
136
|
+
fromTimestamp: Date.now() - 7 * 24 * 60 * 60 * 1000, // last 7 days
|
|
137
|
+
toTimestamp: Date.now()
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
120
141
|
### Trading
|
|
121
142
|
|
|
122
143
|
```typescript
|
|
@@ -137,15 +158,294 @@ const { positions, orders } = await trading.getOpenPositionsAndOrders();
|
|
|
137
158
|
|
|
138
159
|
`volume` here is in **units** of the symbol's base currency (for EURUSD, 1 unit = 1 EUR), not lots. On the wire, cTrader represents volume as "cents of a unit" — `100000` means `1000.00` units — and `trading` converts to and from that form for you, so you always work in whole units through this API, never the wire's scaled integer.
|
|
139
160
|
|
|
140
|
-
Units and lots are not the same thing, and the conversion between them is **not a fixed ratio you can hardcode**. How many units make up "1 lot" is defined per symbol, not by the protocol — retail forex conventionally uses 100,000 units per lot, but that's a market convention, not something cTrader's API guarantees for every symbol (it can differ for indices, commodities, crypto, or simply per broker). The authoritative values — `lotSize`, plus the tradable `minVolume`/`maxVolume`/`stepVolume` range — live on the full symbol spec
|
|
161
|
+
Units and lots are not the same thing, and the conversion between them is **not a fixed ratio you can hardcode**. How many units make up "1 lot" is defined per symbol, not by the protocol — retail forex conventionally uses 100,000 units per lot, but that's a market convention, not something cTrader's API guarantees for every symbol (it can differ for indices, commodities, crypto, or simply per broker). The authoritative values — `lotSize`, plus the tradable `minVolume`/`maxVolume`/`stepVolume` range — live on the full symbol spec, not the lighter one `getAll()`/`findByName()` return:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const fullSymbol = await marketData.symbols.getFullSymbol(symbol.symbolId);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
If your UI works in lots, or you want to validate a volume before sending it, fetch that spec and convert or check against it before calling `trading`. `ctrader-x` gives you the raw values; it doesn't do that conversion or validation for you.
|
|
141
168
|
|
|
142
169
|
More complete, runnable examples live in [`src/example/`](src/example/):
|
|
143
170
|
|
|
144
171
|
```bash
|
|
145
172
|
npm run start:market-data # subscribe to a symbol and print live prices
|
|
146
173
|
npm run start:trading # query positions/orders, place and cancel a safe limit order
|
|
174
|
+
npm run start:trendbars # fetch the last 24h of H1 bars for a symbol
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## API Reference
|
|
178
|
+
|
|
179
|
+
Everything below is exported from the package root (`import { ... } from 'ctrader-x'`). Classes with events expose `once()` and `off()` with the same signatures as `on()`. The generated Protobuf message and enum types (`ProtoOA...`, `Proto...` — roughly 300 of them) aren't listed individually; see [Types](#types) at the end.
|
|
180
|
+
|
|
181
|
+
### Transport
|
|
182
|
+
|
|
183
|
+
#### `SpotwareTransport`
|
|
184
|
+
|
|
185
|
+
Opens the TCP/TLS connection, frames Protobuf messages on the wire, and auto-reconnects with backoff on any drop that wasn't requested via `disconnect()`.
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
class SpotwareTransport {
|
|
189
|
+
constructor(options: ISpotwareTransportOptions);
|
|
190
|
+
|
|
191
|
+
connect(): Promise<void>;
|
|
192
|
+
disconnect(): Promise<void>;
|
|
193
|
+
send(message: ProtoMessage): Promise<void>;
|
|
194
|
+
|
|
195
|
+
on(event: 'connected', listener: () => void): this;
|
|
196
|
+
on(event: 'disconnected', listener: (reason: SpotwareDisconnectReason) => void): this;
|
|
197
|
+
on(event: 'reconnecting', listener: (attempt: number, delayMs: number) => void): this;
|
|
198
|
+
on(event: 'message', listener: (message: ProtoMessage) => void): this;
|
|
199
|
+
on(event: 'error', listener: (error: Error) => void): this;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
interface ISpotwareTransportOptions {
|
|
203
|
+
host: SpotwareHost;
|
|
204
|
+
port?: number; // default: SPOTWARE_PORT (5035)
|
|
205
|
+
reconnectBackoff?: IReconnectBackoffOptions; // default: DEFAULT_RECONNECT_BACKOFF_OPTIONS
|
|
206
|
+
socketFactory?: SpotwareSocketFactory; // default: real tls.connect; override for testing
|
|
207
|
+
staleConnectionTimeoutMs?: number; // default: 30000
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
- `connect()` rejects if the very first attempt fails — likely a config problem worth surfacing, not something to retry silently. A later reconnect attempt that fails keeps retrying with backoff instead of stopping, since by then the target is already known to be reachable.
|
|
212
|
+
- `disconnect()` is an intentional disconnect: no auto-reconnect follows it.
|
|
213
|
+
- A liveness watchdog force-reconnects if no data has been received for `staleConnectionTimeoutMs`. A silent network outage produces no socket-level `error`/`close` on its own — TCP only notices once something tries to use the connection, which can take far longer than that timeout.
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
enum SpotwareHost {
|
|
217
|
+
DEMO = 'demo.ctraderapi.com',
|
|
218
|
+
LIVE = 'live.ctraderapi.com'
|
|
219
|
+
}
|
|
147
220
|
```
|
|
148
221
|
|
|
222
|
+
| Other export | Description |
|
|
223
|
+
| --- | --- |
|
|
224
|
+
| `SPOTWARE_PORT` | `5035` — cTrader's Open API TCP port. |
|
|
225
|
+
| `IReconnectBackoffOptions` | `{ baseDelayMs, maxDelayMs, factor }` |
|
|
226
|
+
| `DEFAULT_RECONNECT_BACKOFF_OPTIONS` | `{ baseDelayMs: 500, maxDelayMs: 30_000, factor: 2 }` |
|
|
227
|
+
| `calculateReconnectDelayMs(attempt, options?)` | Pure function computing the next backoff delay, with jitter. |
|
|
228
|
+
| `SpotwareSocketFactory` | `(port, host) => Promise<Duplex>` — inject a test double instead of a real socket. |
|
|
229
|
+
| `SpotwareDisconnectReason` | `'manual' \| 'dropped'` |
|
|
230
|
+
|
|
231
|
+
### Auth
|
|
232
|
+
|
|
233
|
+
#### `SpotwareOAuthClient`
|
|
234
|
+
|
|
235
|
+
The HTTP half of the OAuth2 flow: authorize URL, code exchange, refresh. Knows nothing about the socket.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
class SpotwareOAuthClient {
|
|
239
|
+
constructor(options: { clientId: string; clientSecret: string });
|
|
240
|
+
|
|
241
|
+
buildAuthorizationUrl(params: { redirectUri: string; scope: SpotwareOAuthScope }): string;
|
|
242
|
+
exchangeAuthorizationCode(params: { code: string; redirectUri: string }): Promise<ISpotwareOAuthToken>;
|
|
243
|
+
refreshAccessToken(params: { refreshToken: string }): Promise<ISpotwareOAuthToken>;
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Throws `SpotwareOAuthError` (`errorCode?: string`, `httpStatus?: number`) on failure. Refresh tokens are single-use — always persist the token returned from a refresh call, not just the original one.
|
|
248
|
+
|
|
249
|
+
#### `SpotwareSocketAuthenticator`
|
|
250
|
+
|
|
251
|
+
The socket half of authentication: `ApplicationAuthReq` → `GetAccountListByAccessTokenReq` → `AccountAuthReq`. Requires an already-connected `SpotwareTransport`.
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
class SpotwareSocketAuthenticator {
|
|
255
|
+
constructor(transport: SpotwareTransport, options?: { responseTimeoutMs?: number }); // default: 10000
|
|
256
|
+
|
|
257
|
+
authenticateApplication(clientId: string, clientSecret: string): Promise<void>;
|
|
258
|
+
listAccounts(accessToken: string): Promise<ProtoOACtidTraderAccount[]>;
|
|
259
|
+
authenticateAccount(ctidTraderAccountId: number, accessToken: string): Promise<void>;
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Throws `SpotwareSocketAuthError` (`errorCode?: string`) on failure. Most consumers won't call this directly — `SpotwareClient` runs this handshake automatically, including after every reconnect. It's exposed for the one-time account discovery step; see [Authenticating for the first time](#authenticating-for-the-first-time).
|
|
264
|
+
|
|
265
|
+
| Other export | Description |
|
|
266
|
+
| --- | --- |
|
|
267
|
+
| `SpotwareOAuthScope` | `enum { ACCOUNTS = 'accounts', TRADING = 'trading' }` |
|
|
268
|
+
| `ISpotwareOAuthToken` | `{ accessToken, refreshToken, tokenType, expiresIn }` |
|
|
269
|
+
| `SpotwareOAuthError` | `Error` subclass — `errorCode?`, `httpStatus?` |
|
|
270
|
+
| `SpotwareSocketAuthError` | `Error` subclass — `errorCode?` |
|
|
271
|
+
|
|
272
|
+
### Client
|
|
273
|
+
|
|
274
|
+
#### `SpotwareClient`
|
|
275
|
+
|
|
276
|
+
Request/response correlation on top of `transport` + `auth`: tags each request with a `clientMsgId` and resolves once the matching response arrives. Re-runs the auth handshake on every `transport` `'connected'` event, including reconnects, and refreshes the token when it's close to expiry.
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
class SpotwareClient {
|
|
280
|
+
constructor(options: ISpotwareClientOptions);
|
|
281
|
+
|
|
282
|
+
readonly ctidTraderAccountId: number;
|
|
283
|
+
|
|
284
|
+
connect(): Promise<void>;
|
|
285
|
+
disconnect(): Promise<void>;
|
|
286
|
+
send(payloadType: number, payload: Uint8Array): Promise<ProtoMessage>;
|
|
287
|
+
|
|
288
|
+
on(event: 'authenticated', listener: () => void): this;
|
|
289
|
+
on(event: 'tokenRefreshed', listener: (token: ISpotwareOAuthToken) => void): this;
|
|
290
|
+
on(event: 'message', listener: (message: ProtoMessage) => void): this;
|
|
291
|
+
on(event: 'error', listener: (error: Error) => void): this;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
interface ISpotwareClientOptions {
|
|
295
|
+
transport: SpotwareTransport;
|
|
296
|
+
oauthClient: SpotwareOAuthClient;
|
|
297
|
+
clientId: string;
|
|
298
|
+
clientSecret: string;
|
|
299
|
+
ctidTraderAccountId: number;
|
|
300
|
+
token: ISpotwareOAuthToken;
|
|
301
|
+
requestTimeoutMs?: number; // default: 10000
|
|
302
|
+
tokenRefreshBufferMs?: number; // default: 300000 (refresh 5 minutes before expiry)
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
- `send()` rejects with `SpotwareRequestError` on a correlated error response, on timeout, or immediately if the connection drops while the request is in flight — it doesn't wait out its own timeout once `transport` already knows the connection is gone.
|
|
307
|
+
- The `'message'` event fires for every message received, including ones with no matching pending request (e.g. spot price events). `market-data` and `trading` are built on this.
|
|
308
|
+
- Always attach an `'error'` listener — per Node's `EventEmitter` convention, an unlistened `'error'` event throws and crashes the process. `SpotwareClient` forwards `transport`'s errors here too, so this one listener covers both.
|
|
309
|
+
|
|
310
|
+
| Other export | Description |
|
|
311
|
+
| --- | --- |
|
|
312
|
+
| `SpotwareRequestError` | `Error` subclass — `errorCode?: string` |
|
|
313
|
+
|
|
314
|
+
### Market data
|
|
315
|
+
|
|
316
|
+
#### `SpotwareMarketData`
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
class SpotwareMarketData {
|
|
320
|
+
constructor(client: SpotwareClient, symbolCatalog?: SpotwareSymbolCatalog);
|
|
321
|
+
|
|
322
|
+
readonly symbols: SpotwareSymbolCatalog;
|
|
323
|
+
|
|
324
|
+
subscribe(symbol: number | string): Promise<void>; // a symbolId, or a name resolved via `symbols`
|
|
325
|
+
unsubscribe(symbol: number | string): Promise<void>;
|
|
326
|
+
getTrendbars(params: IGetTrendbarsParams): Promise<ITrendbar[]>; // a one-off fetch, not a subscription
|
|
327
|
+
|
|
328
|
+
on(event: 'price', listener: (update: ISpotwarePriceUpdate) => void): this;
|
|
329
|
+
on(event: 'error', listener: (error: Error) => void): this;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
interface ISpotwarePriceUpdate {
|
|
333
|
+
symbolId: number;
|
|
334
|
+
bid?: number; // already converted from the wire's fixed-point form
|
|
335
|
+
ask?: number;
|
|
336
|
+
timestamp?: number;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
interface IGetTrendbarsParams {
|
|
340
|
+
symbolId: number;
|
|
341
|
+
period: ProtoOATrendbarPeriod;
|
|
342
|
+
fromTimestamp?: number; // Unix ms, must be >= 0
|
|
343
|
+
toTimestamp?: number; // Unix ms, must be <= 2147483646000 (2038-01-19)
|
|
344
|
+
count?: number; // caps the number of bars, counting back from toTimestamp
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
interface ITrendbar {
|
|
348
|
+
period: ProtoOATrendbarPeriod;
|
|
349
|
+
timestamp?: number; // Unix ms, converted from the wire's utcTimestampInMinutes
|
|
350
|
+
open: number;
|
|
351
|
+
high: number;
|
|
352
|
+
low: number;
|
|
353
|
+
close: number;
|
|
354
|
+
volume: number;
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Re-subscribes to everything currently subscribed whenever `client` re-authenticates (including after a reconnect) — a fresh connection has no memory of prior subscriptions, so without this a reconnect would silently go quiet on price data.
|
|
359
|
+
|
|
360
|
+
`getTrendbars` fetches historical bars once; it doesn't subscribe to anything ongoing. Bar prices (`open`/`high`/`low`/`close`) are converted for you the same way spot prices are — confirmed against cTrader's own documentation, since the field comments in the underlying Protobuf message don't state the scale themselves.
|
|
361
|
+
|
|
362
|
+
#### `SpotwareSymbolCatalog`
|
|
363
|
+
|
|
364
|
+
Public on its own (`new SpotwareSymbolCatalog(client)`), and used internally by `SpotwareMarketData` — `marketData.symbols` is one of these.
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
class SpotwareSymbolCatalog {
|
|
368
|
+
constructor(client: SpotwareClient);
|
|
369
|
+
|
|
370
|
+
getAll(): Promise<ProtoOALightSymbol[]>;
|
|
371
|
+
findByName(symbolName: string): Promise<ProtoOALightSymbol | undefined>; // case-insensitive
|
|
372
|
+
findById(symbolId: number): Promise<ProtoOALightSymbol | undefined>;
|
|
373
|
+
refresh(): Promise<ProtoOALightSymbol[]>; // forces a re-fetch
|
|
374
|
+
|
|
375
|
+
getFullSymbol(symbolId: number): Promise<ProtoOASymbol | undefined>; // lotSize, min/max/stepVolume, digits, pipPosition
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
`getAll`/`findByName`/`findById` share one cached fetch; `getFullSymbol` caches per `symbolId`. Neither caches a failure — the next call retries instead of returning a permanently broken promise.
|
|
380
|
+
|
|
381
|
+
### Trading
|
|
382
|
+
|
|
383
|
+
#### `SpotwareTrading`
|
|
384
|
+
|
|
385
|
+
Places/modifies/cancels orders and closes positions, via `client`. Knows nothing about market data streaming.
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
class SpotwareTrading {
|
|
389
|
+
constructor(client: SpotwareClient);
|
|
390
|
+
|
|
391
|
+
placeMarketOrder(params: IPlaceMarketOrderParams): Promise<ProtoOAExecutionEvent>;
|
|
392
|
+
placeLimitOrder(params: IPlaceLimitOrderParams): Promise<ProtoOAExecutionEvent>;
|
|
393
|
+
amendOrder(params: IAmendOrderParams): Promise<ProtoOAExecutionEvent>;
|
|
394
|
+
cancelOrder(orderId: number): Promise<ProtoOAExecutionEvent>;
|
|
395
|
+
closePosition(params: IClosePositionParams): Promise<ProtoOAExecutionEvent>;
|
|
396
|
+
getOpenPositionsAndOrders(): Promise<IOpenPositionsAndOrders>;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
interface IPlaceMarketOrderParams {
|
|
400
|
+
symbolId: number;
|
|
401
|
+
tradeSide: ProtoOATradeSide;
|
|
402
|
+
volume: number; // in units — see "A note on volume" above
|
|
403
|
+
stopLoss?: number; // absolute price, not scaled
|
|
404
|
+
takeProfit?: number; // absolute price, not scaled
|
|
405
|
+
comment?: string;
|
|
406
|
+
label?: string;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
interface IPlaceLimitOrderParams extends IPlaceMarketOrderParams {
|
|
410
|
+
limitPrice: number; // absolute price, not scaled
|
|
411
|
+
timeInForce?: ProtoOATimeInForce;
|
|
412
|
+
expirationTimestamp?: number;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
interface IAmendOrderParams {
|
|
416
|
+
orderId: number;
|
|
417
|
+
volume?: number; // in units
|
|
418
|
+
limitPrice?: number;
|
|
419
|
+
stopPrice?: number;
|
|
420
|
+
stopLoss?: number;
|
|
421
|
+
takeProfit?: number;
|
|
422
|
+
expirationTimestamp?: number;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
interface IClosePositionParams {
|
|
426
|
+
positionId: number;
|
|
427
|
+
volume: number; // in units
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
interface IOpenPositionsAndOrders {
|
|
431
|
+
positions: ProtoOAPosition[];
|
|
432
|
+
orders: ProtoOAOrder[];
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Order mutations have no dedicated response message — the outcome arrives as a `ProtoOAExecutionEvent` on success, and `send()` rejects with `SpotwareRequestError` on failure. Both are already handled for you; these methods just resolve or reject.
|
|
437
|
+
|
|
438
|
+
### Shared
|
|
439
|
+
|
|
440
|
+
| Export | Description |
|
|
441
|
+
| --- | --- |
|
|
442
|
+
| `SPOTWARE_PRICE_SCALE` | `100_000` — the fixed-point scale for bid/ask and relative SL/TP. Not every price field uses it; see [A note on volume](#a-note-on-volume). |
|
|
443
|
+
| `SPOTWARE_VOLUME_SCALE` | `100` — the fixed-point scale for volume ("cents of a unit"). |
|
|
444
|
+
|
|
445
|
+
### Types
|
|
446
|
+
|
|
447
|
+
Every Protobuf message and enum from Spotware's Open API — `ProtoOANewOrderReq`, `ProtoOATradeSide`, `ProtoOAExecutionEvent`, and roughly 300 more — is generated directly from the official `.proto` files (see [Regenerating protocol types](#regenerating-protocol-types)) and exported from the package root. They aren't listed individually here; each one carries its own field-level doc comments, visible in your editor.
|
|
448
|
+
|
|
149
449
|
## Development
|
|
150
450
|
|
|
151
451
|
### Running tests
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const market_data_1 = require("../market-data");
|
|
4
|
+
const spotware_scale_1 = require("../shared/spotware-scale");
|
|
4
5
|
const create_authenticated_client_1 = require("./shared/create-authenticated-client");
|
|
5
6
|
const SYMBOL_NAME = process.argv[2] ?? 'EURUSD';
|
|
6
7
|
const LISTEN_DURATION_MS = 60_000;
|
|
@@ -17,6 +18,21 @@ async function main() {
|
|
|
17
18
|
if (!symbol) {
|
|
18
19
|
throw new Error(`Symbol "${SYMBOL_NAME}" was not found for this account`);
|
|
19
20
|
}
|
|
21
|
+
// getFullSymbol() is the full per-symbol spec — findByName()/getAll() only return the
|
|
22
|
+
// light list, which doesn't carry the fields you need to validate a volume or round a
|
|
23
|
+
// price correctly, since those are defined per symbol by the broker, not a fixed ratio.
|
|
24
|
+
const fullSymbol = await marketData.symbols.getFullSymbol(symbol.symbolId);
|
|
25
|
+
if (fullSymbol) {
|
|
26
|
+
console.log(`\n${symbol.symbolName} trading constraints:`);
|
|
27
|
+
console.table({
|
|
28
|
+
digits: fullSymbol.digits,
|
|
29
|
+
pipPosition: fullSymbol.pipPosition,
|
|
30
|
+
lotSizeUnits: (fullSymbol.lotSize ?? 0) / spotware_scale_1.SPOTWARE_VOLUME_SCALE,
|
|
31
|
+
minVolumeUnits: (fullSymbol.minVolume ?? 0) / spotware_scale_1.SPOTWARE_VOLUME_SCALE,
|
|
32
|
+
maxVolumeUnits: (fullSymbol.maxVolume ?? 0) / spotware_scale_1.SPOTWARE_VOLUME_SCALE,
|
|
33
|
+
stepVolumeUnits: (fullSymbol.stepVolume ?? 0) / spotware_scale_1.SPOTWARE_VOLUME_SCALE
|
|
34
|
+
});
|
|
35
|
+
}
|
|
20
36
|
console.log(`\nSubscribing to ${symbol.symbolName} (symbolId ${symbol.symbolId})...`);
|
|
21
37
|
await marketData.subscribe(symbol.symbolId);
|
|
22
38
|
console.log(`Listening for price updates for ${LISTEN_DURATION_MS / 1000}s (Ctrl+C to stop earlier)...\n`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"market-data-example.js","sourceRoot":"","sources":["../../src/example/market-data-example.ts"],"names":[],"mappings":";;AAAA,gDAAoD;AACpD,sFAAiF;AAEjF,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC;AAChD,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,KAAK,UAAU,IAAI;IACf,MAAM,MAAM,GAAG,MAAM,IAAA,uDAAyB,GAAE,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,gCAAkB,CAAC,MAAM,CAAC,CAAC;IAClD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IACtF,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE;QAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;QAC1C,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,QAAQ,SAAS,GAAG,QAAQ,GAAG,EAAE,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAChE,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,WAAW,WAAW,kCAAkC,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,UAAU,cAAc,MAAM,CAAC,QAAQ,MAAM,CAAC,CAAC;IACtF,MAAM,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE5C,OAAO,CAAC,GAAG,CAAC,mCAAmC,kBAAkB,GAAG,IAAI,iCAAiC,CAAC,CAAC;IAC3G,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAExE,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;IACpD,MAAM,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;AAC9B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAY,EAAE,EAAE;IAC1B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"market-data-example.js","sourceRoot":"","sources":["../../src/example/market-data-example.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trendbars-example.d.ts","sourceRoot":"","sources":["../../src/example/trendbars-example.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const market_data_1 = require("../market-data");
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
const create_authenticated_client_1 = require("./shared/create-authenticated-client");
|
|
6
|
+
async function main() {
|
|
7
|
+
const client = await (0, create_authenticated_client_1.createAuthenticatedClient)();
|
|
8
|
+
const marketData = new market_data_1.SpotwareMarketData(client);
|
|
9
|
+
const symbolName = process.argv[2] ?? 'EURUSD';
|
|
10
|
+
const symbol = await marketData.symbols.findByName(symbolName);
|
|
11
|
+
if (!symbol) {
|
|
12
|
+
throw new Error(`Symbol "${symbolName}" was not found for this account`);
|
|
13
|
+
}
|
|
14
|
+
const bars = await marketData.getTrendbars({
|
|
15
|
+
symbolId: symbol.symbolId,
|
|
16
|
+
period: types_1.ProtoOATrendbarPeriod.H1,
|
|
17
|
+
fromTimestamp: Date.now() - 24 * 60 * 60 * 1000,
|
|
18
|
+
toTimestamp: Date.now()
|
|
19
|
+
});
|
|
20
|
+
console.log(`\nFetched ${bars.length} H1 bars for ${symbol.symbolName}:`);
|
|
21
|
+
console.table(bars.map((bar) => ({ ...bar, timestamp: bar.timestamp ? new Date(bar.timestamp).toISOString() : undefined })));
|
|
22
|
+
await client.disconnect();
|
|
23
|
+
}
|
|
24
|
+
main().catch((error) => {
|
|
25
|
+
console.error(error);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=trendbars-example.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trendbars-example.js","sourceRoot":"","sources":["../../src/example/trendbars-example.ts"],"names":[],"mappings":";;AAAA,gDAAoD;AACpD,oCAAiD;AACjD,sFAAiF;AAEjF,KAAK,UAAU,IAAI;IACf,MAAM,MAAM,GAAG,MAAM,IAAA,uDAAyB,GAAE,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,gCAAkB,CAAC,MAAM,CAAC,CAAC;IAElD,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC;IAC/C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IAC/D,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,WAAW,UAAU,kCAAkC,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,YAAY,CAAC;QACvC,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,6BAAqB,CAAC,EAAE;QAChC,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;QAC/C,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;KAC1B,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,MAAM,gBAAgB,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC;IAC1E,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;IAE7H,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;AAC9B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAY,EAAE,EAAE;IAC1B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./auth"), exports);
|
|
18
18
|
__exportStar(require("./client"), exports);
|
|
19
19
|
__exportStar(require("./market-data"), exports);
|
|
20
|
+
__exportStar(require("./shared"), exports);
|
|
20
21
|
__exportStar(require("./trading"), exports);
|
|
21
22
|
__exportStar(require("./transport"), exports);
|
|
22
23
|
__exportStar(require("./types"), exports);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yCAAuB;AACvB,2CAAyB;AACzB,gDAA8B;AAC9B,4CAA0B;AAC1B,8CAA4B;AAC5B,0CAAwB"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yCAAuB;AACvB,2CAAyB;AACzB,gDAA8B;AAC9B,2CAAyB;AACzB,4CAA0B;AAC1B,8CAA4B;AAC5B,0CAAwB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SpotwareClient } from '../client';
|
|
2
2
|
import { TypedEventEmitter, type EventMap } from '../shared/typed-event-emitter';
|
|
3
|
+
import { ProtoOATrendbarPeriod } from '../types';
|
|
3
4
|
import { SpotwareSymbolCatalog } from './spotware-symbol-catalog';
|
|
4
5
|
export interface ISpotwarePriceUpdate {
|
|
5
6
|
symbolId: number;
|
|
@@ -11,6 +12,26 @@ export interface ISpotwareMarketDataEvents extends EventMap {
|
|
|
11
12
|
price: [update: ISpotwarePriceUpdate];
|
|
12
13
|
error: [error: Error];
|
|
13
14
|
}
|
|
15
|
+
export interface IGetTrendbarsParams {
|
|
16
|
+
symbolId: number;
|
|
17
|
+
period: ProtoOATrendbarPeriod;
|
|
18
|
+
/** Unix time in milliseconds. Must be >= 0. */
|
|
19
|
+
fromTimestamp?: number;
|
|
20
|
+
/** Unix time in milliseconds. Must be <= 2147483646000 (2038-01-19). */
|
|
21
|
+
toTimestamp?: number;
|
|
22
|
+
/** Caps the number of bars returned, counting back from toTimestamp. */
|
|
23
|
+
count?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface ITrendbar {
|
|
26
|
+
period: ProtoOATrendbarPeriod;
|
|
27
|
+
/** Unix time in milliseconds, converted from the wire's utcTimestampInMinutes. */
|
|
28
|
+
timestamp?: number;
|
|
29
|
+
open: number;
|
|
30
|
+
high: number;
|
|
31
|
+
low: number;
|
|
32
|
+
close: number;
|
|
33
|
+
volume: number;
|
|
34
|
+
}
|
|
14
35
|
/**
|
|
15
36
|
* Subscribes to spot prices via `client` and exposes them as a clean 'price' event, decimal
|
|
16
37
|
* bid/ask already converted. Re-subscribes to everything currently subscribed whenever
|
|
@@ -24,6 +45,8 @@ export declare class SpotwareMarketData extends TypedEventEmitter<ISpotwareMarke
|
|
|
24
45
|
constructor(client: SpotwareClient, symbolCatalog?: SpotwareSymbolCatalog);
|
|
25
46
|
subscribe(symbol: number | string): Promise<void>;
|
|
26
47
|
unsubscribe(symbol: number | string): Promise<void>;
|
|
48
|
+
/** Fetches historical bars — a one-off request, not a subscription. */
|
|
49
|
+
getTrendbars(params: IGetTrendbarsParams): Promise<ITrendbar[]>;
|
|
27
50
|
private resolveSymbolId;
|
|
28
51
|
private handleMessage;
|
|
29
52
|
private resubscribeAll;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spotware-market-data.d.ts","sourceRoot":"","sources":["../../src/market-data/spotware-market-data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"spotware-market-data.d.ts","sourceRoot":"","sources":["../../src/market-data/spotware-market-data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACjF,OAAO,EAQH,qBAAqB,EAExB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,MAAM,WAAW,oBAAoB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,yBAA0B,SAAQ,QAAQ;IACvD,KAAK,EAAE,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACtC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,+CAA+C;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACtB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,iBAAiB,CAAC,yBAAyB,CAAC;IAChF,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC;IAExC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;gBAE7C,MAAM,EAAE,cAAc,EAAE,aAAa,CAAC,EAAE,qBAAqB;IASnE,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAajD,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAazD,uEAAuE;IACjE,YAAY,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;YAevD,eAAe;IAa7B,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,cAAc;CAgBzB"}
|
|
@@ -32,6 +32,19 @@ class SpotwareMarketData extends typed_event_emitter_1.TypedEventEmitter {
|
|
|
32
32
|
await this.client.send(types_1.ProtoOAPayloadType.PROTO_OA_UNSUBSCRIBE_SPOTS_REQ, types_1.ProtoOAUnsubscribeSpotsReq.encode(types_1.ProtoOAUnsubscribeSpotsReq.fromPartial({ ctidTraderAccountId: this.client.ctidTraderAccountId, symbolId: [symbolId] })).finish());
|
|
33
33
|
this.subscribedSymbolIds.delete(symbolId);
|
|
34
34
|
}
|
|
35
|
+
/** Fetches historical bars — a one-off request, not a subscription. */
|
|
36
|
+
async getTrendbars(params) {
|
|
37
|
+
const request = types_1.ProtoOAGetTrendbarsReq.fromPartial({
|
|
38
|
+
ctidTraderAccountId: this.client.ctidTraderAccountId,
|
|
39
|
+
symbolId: params.symbolId,
|
|
40
|
+
period: params.period,
|
|
41
|
+
fromTimestamp: params.fromTimestamp,
|
|
42
|
+
toTimestamp: params.toTimestamp,
|
|
43
|
+
count: params.count
|
|
44
|
+
});
|
|
45
|
+
const response = await this.client.send(types_1.ProtoOAPayloadType.PROTO_OA_GET_TRENDBARS_REQ, encodeGetTrendbarsReq(request));
|
|
46
|
+
return types_1.ProtoOAGetTrendbarsRes.decode(response.payload ?? new Uint8Array()).trendbar.map(toCleanTrendbar);
|
|
47
|
+
}
|
|
35
48
|
async resolveSymbolId(symbol) {
|
|
36
49
|
if (typeof symbol === 'number') {
|
|
37
50
|
return symbol;
|
|
@@ -65,4 +78,31 @@ class SpotwareMarketData extends typed_event_emitter_1.TypedEventEmitter {
|
|
|
65
78
|
}
|
|
66
79
|
}
|
|
67
80
|
exports.SpotwareMarketData = SpotwareMarketData;
|
|
81
|
+
// ts-proto skips a required field on the wire whenever its value equals the field's implicit
|
|
82
|
+
// proto2 default — for an enum with no explicit `[default = ...]` annotation, that's always its
|
|
83
|
+
// first declared member. `period` has no such annotation, so M1 (1) — the single most common
|
|
84
|
+
// period to request — gets silently dropped, the same class of bug fixed for orderType/tradeSide
|
|
85
|
+
// in the trading module. Field order doesn't matter on the wire, so appending it after the
|
|
86
|
+
// normal encode is a safe, minimal fix that doesn't require hand-editing generated code.
|
|
87
|
+
function encodeGetTrendbarsReq(request) {
|
|
88
|
+
const writer = types_1.ProtoOAGetTrendbarsReq.encode(request);
|
|
89
|
+
if (request.period === types_1.ProtoOATrendbarPeriod.M1) {
|
|
90
|
+
writer.uint32(40).int32(request.period);
|
|
91
|
+
}
|
|
92
|
+
return writer.finish();
|
|
93
|
+
}
|
|
94
|
+
// Bar prices are delta-encoded off `low` and fixed-point scaled, confirmed against cTrader's
|
|
95
|
+
// own help center docs (not assumed): open = (low + deltaOpen) / scale, etc.
|
|
96
|
+
function toCleanTrendbar(trendbar) {
|
|
97
|
+
const low = trendbar.low ?? 0;
|
|
98
|
+
return {
|
|
99
|
+
period: trendbar.period ?? types_1.ProtoOATrendbarPeriod.M1,
|
|
100
|
+
timestamp: trendbar.utcTimestampInMinutes === undefined ? undefined : trendbar.utcTimestampInMinutes * 60_000,
|
|
101
|
+
low: low / spotware_scale_1.SPOTWARE_PRICE_SCALE,
|
|
102
|
+
open: (low + (trendbar.deltaOpen ?? 0)) / spotware_scale_1.SPOTWARE_PRICE_SCALE,
|
|
103
|
+
high: (low + (trendbar.deltaHigh ?? 0)) / spotware_scale_1.SPOTWARE_PRICE_SCALE,
|
|
104
|
+
close: (low + (trendbar.deltaClose ?? 0)) / spotware_scale_1.SPOTWARE_PRICE_SCALE,
|
|
105
|
+
volume: trendbar.volume
|
|
106
|
+
};
|
|
107
|
+
}
|
|
68
108
|
//# sourceMappingURL=spotware-market-data.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spotware-market-data.js","sourceRoot":"","sources":["../../src/market-data/spotware-market-data.ts"],"names":[],"mappings":";;;AACA,6DAAgE;AAChE,uEAAiF;AACjF,
|
|
1
|
+
{"version":3,"file":"spotware-market-data.js","sourceRoot":"","sources":["../../src/market-data/spotware-market-data.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SpotwareClient } from '../client';
|
|
2
|
-
import { ProtoOALightSymbol } from '../types';
|
|
2
|
+
import { ProtoOALightSymbol, ProtoOASymbol } from '../types';
|
|
3
3
|
/**
|
|
4
4
|
* Fetches and caches the account's symbol list, and resolves a ticker name (e.g. "EURUSD")
|
|
5
5
|
* to the numeric symbolId every other request actually needs. Public on its own — trading
|
|
@@ -9,11 +9,20 @@ import { ProtoOALightSymbol } from '../types';
|
|
|
9
9
|
export declare class SpotwareSymbolCatalog {
|
|
10
10
|
private readonly client;
|
|
11
11
|
private symbolsPromise;
|
|
12
|
+
private readonly fullSymbolPromises;
|
|
12
13
|
constructor(client: SpotwareClient);
|
|
13
14
|
getAll(): Promise<ProtoOALightSymbol[]>;
|
|
14
15
|
findByName(symbolName: string): Promise<ProtoOALightSymbol | undefined>;
|
|
15
16
|
findById(symbolId: number): Promise<ProtoOALightSymbol | undefined>;
|
|
16
17
|
refresh(): Promise<ProtoOALightSymbol[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Fetches the full symbol spec — lotSize, minVolume/maxVolume/stepVolume, digits,
|
|
20
|
+
* pipPosition, and everything else `getAll()`'s light list doesn't carry. Needed to
|
|
21
|
+
* validate a volume or round a price correctly before placing an order: those constraints
|
|
22
|
+
* are defined per symbol by the broker, not by a fixed, hardcodable ratio.
|
|
23
|
+
*/
|
|
24
|
+
getFullSymbol(symbolId: number): Promise<ProtoOASymbol | undefined>;
|
|
17
25
|
private fetchSymbols;
|
|
26
|
+
private fetchFullSymbol;
|
|
18
27
|
}
|
|
19
28
|
//# sourceMappingURL=spotware-symbol-catalog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spotware-symbol-catalog.d.ts","sourceRoot":"","sources":["../../src/market-data/spotware-symbol-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,
|
|
1
|
+
{"version":3,"file":"spotware-symbol-catalog.d.ts","sourceRoot":"","sources":["../../src/market-data/spotware-symbol-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EACH,kBAAkB,EAElB,aAAa,EAKhB,MAAM,UAAU,CAAC;AAElB;;;;;GAKG;AACH,qBAAa,qBAAqB;IAIlB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,OAAO,CAAC,cAAc,CAA4C;IAClE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAyD;gBAE/D,MAAM,EAAE,cAAc;IAE7C,MAAM,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAQvC,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAOvE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAOzE,OAAO,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAMxC;;;;;OAKG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;YAU3D,YAAY;YAiBZ,eAAe;CAWhC"}
|
|
@@ -11,6 +11,7 @@ const types_1 = require("../types");
|
|
|
11
11
|
class SpotwareSymbolCatalog {
|
|
12
12
|
client;
|
|
13
13
|
symbolsPromise;
|
|
14
|
+
fullSymbolPromises = new Map();
|
|
14
15
|
constructor(client) {
|
|
15
16
|
this.client = client;
|
|
16
17
|
}
|
|
@@ -34,6 +35,20 @@ class SpotwareSymbolCatalog {
|
|
|
34
35
|
this.symbolsPromise = this.fetchSymbols();
|
|
35
36
|
return this.symbolsPromise;
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Fetches the full symbol spec — lotSize, minVolume/maxVolume/stepVolume, digits,
|
|
40
|
+
* pipPosition, and everything else `getAll()`'s light list doesn't carry. Needed to
|
|
41
|
+
* validate a volume or round a price correctly before placing an order: those constraints
|
|
42
|
+
* are defined per symbol by the broker, not by a fixed, hardcodable ratio.
|
|
43
|
+
*/
|
|
44
|
+
async getFullSymbol(symbolId) {
|
|
45
|
+
let promise = this.fullSymbolPromises.get(symbolId);
|
|
46
|
+
if (!promise) {
|
|
47
|
+
promise = this.fetchFullSymbol(symbolId);
|
|
48
|
+
this.fullSymbolPromises.set(symbolId, promise);
|
|
49
|
+
}
|
|
50
|
+
return promise;
|
|
51
|
+
}
|
|
37
52
|
async fetchSymbols() {
|
|
38
53
|
try {
|
|
39
54
|
const request = types_1.ProtoOASymbolsListReq.fromPartial({ ctidTraderAccountId: this.client.ctidTraderAccountId });
|
|
@@ -47,6 +62,17 @@ class SpotwareSymbolCatalog {
|
|
|
47
62
|
throw error;
|
|
48
63
|
}
|
|
49
64
|
}
|
|
65
|
+
async fetchFullSymbol(symbolId) {
|
|
66
|
+
try {
|
|
67
|
+
const request = types_1.ProtoOASymbolByIdReq.fromPartial({ ctidTraderAccountId: this.client.ctidTraderAccountId, symbolId: [symbolId] });
|
|
68
|
+
const response = await this.client.send(types_1.ProtoOAPayloadType.PROTO_OA_SYMBOL_BY_ID_REQ, types_1.ProtoOASymbolByIdReq.encode(request).finish());
|
|
69
|
+
return types_1.ProtoOASymbolByIdRes.decode(response.payload ?? new Uint8Array()).symbol.find((symbol) => symbol.symbolId === symbolId);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
this.fullSymbolPromises.delete(symbolId);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
50
76
|
}
|
|
51
77
|
exports.SpotwareSymbolCatalog = SpotwareSymbolCatalog;
|
|
52
78
|
//# sourceMappingURL=spotware-symbol-catalog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spotware-symbol-catalog.js","sourceRoot":"","sources":["../../src/market-data/spotware-symbol-catalog.ts"],"names":[],"mappings":";;;AACA,
|
|
1
|
+
{"version":3,"file":"spotware-symbol-catalog.js","sourceRoot":"","sources":["../../src/market-data/spotware-symbol-catalog.ts"],"names":[],"mappings":";;;AACA,oCAQkB;AAElB;;;;;GAKG;AACH,MAAa,qBAAqB;IAID;IAHrB,cAAc,CAA4C;IACjD,kBAAkB,GAAG,IAAI,GAAG,EAA8C,CAAC;IAE5F,YAA6B,MAAsB;QAAtB,WAAM,GAAN,MAAM,CAAgB;IAAG,CAAC;IAEvD,KAAK,CAAC,MAAM;QACR,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACvB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAC9C,CAAC;QAED,OAAO,IAAI,CAAC,cAAc,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,UAAkB;QAC/B,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACnD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAEpC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,WAAW,EAAE,KAAK,UAAU,CAAC,CAAC;IACrF,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,QAAgB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAEpC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;IAClE,CAAC;IAED,iFAAiF;IACjF,OAAO;QACH,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAE1C,OAAO,IAAI,CAAC,cAAc,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,aAAa,CAAC,QAAgB;QAChC,IAAI,OAAO,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACpD,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;YACzC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,OAAO,CAAC;IACnB,CAAC;IAEO,KAAK,CAAC,YAAY;QACtB,IAAI,CAAC;YACD,MAAM,OAAO,GAAG,6BAAqB,CAAC,WAAW,CAAC,EAAE,mBAAmB,EAAE,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC,CAAC;YAC5G,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CACnC,0BAAkB,CAAC,yBAAyB,EAC5C,6BAAqB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CACjD,CAAC;YAEF,OAAO,6BAAqB,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,IAAI,IAAI,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;QACrF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,4EAA4E;YAC5E,uCAAuC;YACvC,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;YAChC,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,eAAe,CAAC,QAAgB;QAC1C,IAAI,CAAC;YACD,MAAM,OAAO,GAAG,4BAAoB,CAAC,WAAW,CAAC,EAAE,mBAAmB,EAAE,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACjI,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,0BAAkB,CAAC,yBAAyB,EAAE,4BAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;YAErI,OAAO,4BAAoB,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,IAAI,IAAI,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QACnI,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACzC,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,CAAC;CACJ;AA9ED,sDA8EC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./spotware-scale"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mDAAiC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ctrader-x",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"start": "ts-node src/example/example.ts",
|
|
26
26
|
"start:market-data": "ts-node src/example/market-data-example.ts",
|
|
27
27
|
"start:trading": "ts-node src/example/trading-example.ts",
|
|
28
|
+
"start:trendbars": "ts-node src/example/trendbars-example.ts",
|
|
28
29
|
"test": "vitest run",
|
|
29
30
|
"test:watch": "vitest",
|
|
30
31
|
"generate:types": "grpc_tools_node_protoc --plugin=protoc-gen-ts_proto=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=./src/types/generated --ts_proto_opt=esModuleInterop=true,outputServices=none,useOptionals=messages,unrecognizedEnum=false -I proto proto/OpenApiCommonModelMessages.proto proto/OpenApiCommonMessages.proto proto/OpenApiModelMessages.proto proto/OpenApiMessages.proto"
|