ctrader-x 0.2.0 → 0.3.1

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 CHANGED
@@ -1,4 +1,4 @@
1
- # ctrader-x
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
 
@@ -125,6 +125,19 @@ const fullSymbol = await marketData.symbols.getFullSymbol(symbol.symbolId);
125
125
 
126
126
  See [A note on volume](#a-note-on-volume) under Trading for why these matter before placing an order.
127
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
+
128
141
  ### Trading
129
142
 
130
143
  ```typescript
@@ -158,8 +171,394 @@ More complete, runnable examples live in [`src/example/`](src/example/):
158
171
  ```bash
159
172
  npm run start:market-data # subscribe to a symbol and print live prices
160
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'`). For a signatures-only cheat sheet, skip to [Quick reference](#quick-reference). 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
+ }
220
+ ```
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
+ }
161
356
  ```
162
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
+
449
+ ## Quick reference
450
+
451
+ Everything the package exports, in one place, for when you already know the concepts and just need the signature. The [API Reference](#api-reference) above has the same material with the reasoning behind it.
452
+
453
+ ### Methods
454
+
455
+ | Class | Member | Description |
456
+ | --- | --- | --- |
457
+ | `SpotwareTransport` | `connect(): Promise<void>` | Opens the connection. Rejects if the *first* attempt fails; later reconnects keep retrying. |
458
+ | | `disconnect(): Promise<void>` | Intentional close — no auto-reconnect follows. |
459
+ | | `send(message: ProtoMessage): Promise<void>` | Sends a raw framed message, rate-limited per Spotware's documented limits. |
460
+ | `SpotwareOAuthClient` | `buildAuthorizationUrl({ redirectUri, scope }): string` | The URL to send the user to for browser authorization. |
461
+ | | `exchangeAuthorizationCode({ code, redirectUri }): Promise<ISpotwareOAuthToken>` | Trades the redirect code for tokens. The code expires in ~1 minute. |
462
+ | | `refreshAccessToken({ refreshToken }): Promise<ISpotwareOAuthToken>` | Refresh tokens are single-use — persist what comes back. |
463
+ | `SpotwareSocketAuthenticator` | `authenticateApplication(clientId, clientSecret): Promise<void>` | `ApplicationAuthReq`. Once per connection. |
464
+ | | `listAccounts(accessToken): Promise<ProtoOACtidTraderAccount[]>` | The only way to discover a `ctidTraderAccountId` from a bare access token. |
465
+ | | `authenticateAccount(ctidTraderAccountId, accessToken): Promise<void>` | `AccountAuthReq`. Once per account. |
466
+ | `SpotwareClient` | `readonly ctidTraderAccountId: number` | The account this client is bound to. |
467
+ | | `connect(): Promise<void>` | Connects and completes the auth handshake. |
468
+ | | `disconnect(): Promise<void>` | Closes the underlying transport. |
469
+ | | `send(payloadType, payload): Promise<ProtoMessage>` | Correlated request. Rejects with `SpotwareRequestError` on error, timeout, or a drop mid-flight. |
470
+ | `SpotwareMarketData` | `readonly symbols: SpotwareSymbolCatalog` | The catalog used to resolve names to ids. |
471
+ | | `subscribe(symbol: number \| string): Promise<void>` | By `symbolId` or by name. Re-applied automatically after a reconnect. |
472
+ | | `unsubscribe(symbol: number \| string): Promise<void>` | |
473
+ | | `getTrendbars(params: IGetTrendbarsParams): Promise<ITrendbar[]>` | One-off historical fetch, not a subscription. Prices already converted. |
474
+ | `SpotwareSymbolCatalog` | `getAll(): Promise<ProtoOALightSymbol[]>` | Cached after the first call. |
475
+ | | `findByName(symbolName): Promise<ProtoOALightSymbol \| undefined>` | Case-insensitive. |
476
+ | | `findById(symbolId): Promise<ProtoOALightSymbol \| undefined>` | |
477
+ | | `refresh(): Promise<ProtoOALightSymbol[]>` | Forces a re-fetch of the list. |
478
+ | | `getFullSymbol(symbolId): Promise<ProtoOASymbol \| undefined>` | Full spec — `lotSize`, volume bounds, `digits`, `pipPosition`. Cached per id. |
479
+ | `SpotwareTrading` | `placeMarketOrder(params): Promise<ProtoOAExecutionEvent>` | |
480
+ | | `placeLimitOrder(params): Promise<ProtoOAExecutionEvent>` | |
481
+ | | `amendOrder(params): Promise<ProtoOAExecutionEvent>` | |
482
+ | | `cancelOrder(orderId): Promise<ProtoOAExecutionEvent>` | |
483
+ | | `closePosition(params): Promise<ProtoOAExecutionEvent>` | |
484
+ | | `getOpenPositionsAndOrders(): Promise<IOpenPositionsAndOrders>` | |
485
+
486
+ Constructors: `new SpotwareTransport(options)`, `new SpotwareOAuthClient({ clientId, clientSecret })`, `new SpotwareSocketAuthenticator(transport, options?)`, `new SpotwareClient(options)`, `new SpotwareMarketData(client, symbolCatalog?)`, `new SpotwareSymbolCatalog(client)`, `new SpotwareTrading(client)`.
487
+
488
+ ### Events
489
+
490
+ Every emitter also exposes `once()` and `off()` with the same signatures as `on()`.
491
+
492
+ | Class | Event | Listener arguments |
493
+ | --- | --- | --- |
494
+ | `SpotwareTransport` | `connected` | — |
495
+ | | `disconnected` | `(reason: SpotwareDisconnectReason)` |
496
+ | | `reconnecting` | `(attempt: number, delayMs: number)` |
497
+ | | `message` | `(message: ProtoMessage)` |
498
+ | | `error` | `(error: Error)` |
499
+ | `SpotwareClient` | `authenticated` | — (fires again after every reconnect) |
500
+ | | `tokenRefreshed` | `(token: ISpotwareOAuthToken)` — persist this |
501
+ | | `message` | `(message: ProtoMessage)` — every message, correlated or not |
502
+ | | `error` | `(error: Error)` — includes forwarded transport errors |
503
+ | `SpotwareMarketData` | `price` | `(update: ISpotwarePriceUpdate)` |
504
+ | | `error` | `(error: Error)` |
505
+
506
+ Always attach an `'error'` listener: per Node's `EventEmitter` convention, an unlistened `'error'` event throws and crashes the process.
507
+
508
+ ### Interfaces
509
+
510
+ | Interface | Shape |
511
+ | --- | --- |
512
+ | `ISpotwareTransportOptions` | `{ host, port?, reconnectBackoff?, socketFactory?, staleConnectionTimeoutMs? }` |
513
+ | `ISpotwareClientOptions` | `{ transport, oauthClient, clientId, clientSecret, ctidTraderAccountId, token, requestTimeoutMs?, tokenRefreshBufferMs? }` |
514
+ | `ISpotwareOAuthToken` | `{ accessToken, refreshToken, tokenType, expiresIn }` |
515
+ | `IReconnectBackoffOptions` | `{ baseDelayMs, maxDelayMs, factor }` |
516
+ | `ISpotwarePriceUpdate` | `{ symbolId, bid?, ask?, timestamp? }` — decimal prices, already unscaled |
517
+ | `IGetTrendbarsParams` | `{ symbolId, period, fromTimestamp?, toTimestamp?, count? }` — timestamps in Unix ms |
518
+ | `ITrendbar` | `{ period, timestamp?, open, high, low, close, volume }` |
519
+ | `IPlaceMarketOrderParams` | `{ symbolId, tradeSide, volume, stopLoss?, takeProfit?, comment?, label? }` — volume in units |
520
+ | `IPlaceLimitOrderParams` | `IPlaceMarketOrderParams` + `{ limitPrice, timeInForce?, expirationTimestamp? }` |
521
+ | `IAmendOrderParams` | `{ orderId, volume?, limitPrice?, stopPrice?, stopLoss?, takeProfit?, expirationTimestamp? }` |
522
+ | `IClosePositionParams` | `{ positionId, volume }` — volume in units |
523
+ | `IOpenPositionsAndOrders` | `{ positions: ProtoOAPosition[], orders: ProtoOAOrder[] }` |
524
+
525
+ ### Constants, enums and types
526
+
527
+ | Export | Value or shape |
528
+ | --- | --- |
529
+ | `SpotwareHost` | `enum { DEMO = 'demo.ctraderapi.com', LIVE = 'live.ctraderapi.com' }` |
530
+ | `SpotwareOAuthScope` | `enum { ACCOUNTS = 'accounts', TRADING = 'trading' }` |
531
+ | `SPOTWARE_PORT` | `5035` |
532
+ | `SPOTWARE_PRICE_SCALE` | `100_000` |
533
+ | `SPOTWARE_VOLUME_SCALE` | `100` |
534
+ | `DEFAULT_RECONNECT_BACKOFF_OPTIONS` | `{ baseDelayMs: 500, maxDelayMs: 30_000, factor: 2 }` |
535
+ | `calculateReconnectDelayMs(attempt, options?)` | Pure function — next backoff delay, with jitter. |
536
+ | `SpotwareDisconnectReason` | `'manual' \| 'dropped'` |
537
+ | `SpotwareSocketFactory` | `(port: number, host: string) => Promise<Duplex>` |
538
+
539
+ ### Errors
540
+
541
+ All extend `Error`, so `instanceof` works.
542
+
543
+ | Error | Extra fields | Thrown by |
544
+ | --- | --- | --- |
545
+ | `SpotwareOAuthError` | `errorCode?: string`, `httpStatus?: number` | `SpotwareOAuthClient` |
546
+ | `SpotwareSocketAuthError` | `errorCode?: string` | `SpotwareSocketAuthenticator` |
547
+ | `SpotwareRequestError` | `errorCode?: string` | `SpotwareClient.send()`, and everything built on it |
548
+
549
+ ### Defaults
550
+
551
+ | Setting | Default | Set via |
552
+ | --- | --- | --- |
553
+ | Port | `5035` | `ISpotwareTransportOptions.port` |
554
+ | Stale connection timeout | `30_000` ms | `ISpotwareTransportOptions.staleConnectionTimeoutMs` |
555
+ | Reconnect backoff | `500` ms base, `30_000` ms cap, factor `2` | `ISpotwareTransportOptions.reconnectBackoff` |
556
+ | Auth handshake timeout | `10_000` ms | `ISpotwareSocketAuthenticatorOptions.responseTimeoutMs` |
557
+ | Request timeout | `10_000` ms | `ISpotwareClientOptions.requestTimeoutMs` |
558
+ | Token refresh buffer | `300_000` ms (5 min before expiry) | `ISpotwareClientOptions.tokenRefreshBufferMs` |
559
+ | Heartbeat interval | `10_000` ms | not configurable — Spotware requires at least one every 10s |
560
+ | Rate limits | 5 req/s historical, 50 req/s everything else | not configurable — enforced automatically, per connection |
561
+
163
562
  ## Development
164
563
 
165
564
  ### Running tests
@@ -1 +1 @@
1
- {"version":3,"file":"spotware-socket-authenticator.d.ts","sourceRoot":"","sources":["../../src/auth/spotware-socket-authenticator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAIH,wBAAwB,EAO3B,MAAM,UAAU,CAAC;AAKlB,MAAM,WAAW,mCAAmC;IAChD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,qBAAa,2BAA2B;IAIhC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAH9B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAGtB,SAAS,EAAE,iBAAiB,EAC7C,OAAO,GAAE,mCAAwC;IAK/C,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY9E,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC;IActE,mBAAmB,CAAC,mBAAmB,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1F,OAAO,CAAC,YAAY;CA0CvB"}
1
+ {"version":3,"file":"spotware-socket-authenticator.d.ts","sourceRoot":"","sources":["../../src/auth/spotware-socket-authenticator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAIH,wBAAwB,EAO3B,MAAM,UAAU,CAAC;AAKlB,MAAM,WAAW,mCAAmC;IAChD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,qBAAa,2BAA2B;IAIhC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAH9B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAGtB,SAAS,EAAE,iBAAiB,EAC7C,OAAO,GAAE,mCAAwC;IAK/C,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY9E,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC;IActE,mBAAmB,CAAC,mBAAmB,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1F,OAAO,CAAC,YAAY;CAoDvB"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SpotwareSocketAuthenticator = void 0;
4
+ const node_crypto_1 = require("node:crypto");
4
5
  const types_1 = require("../types");
5
6
  const spotware_socket_auth_error_1 = require("./spotware-socket-auth-error");
6
7
  const DEFAULT_RESPONSE_TIMEOUT_MS = 10_000;
@@ -40,6 +41,7 @@ class SpotwareSocketAuthenticator {
40
41
  }), types_1.ProtoOAPayloadType.PROTO_OA_ACCOUNT_AUTH_RES);
41
42
  }
42
43
  sendAndAwait(message, expectedPayloadType) {
44
+ const clientMsgId = (0, node_crypto_1.randomUUID)();
43
45
  return new Promise((resolve, reject) => {
44
46
  let timeout;
45
47
  const cleanup = () => {
@@ -47,6 +49,13 @@ class SpotwareSocketAuthenticator {
47
49
  this.transport.off('message', onMessage);
48
50
  };
49
51
  const onMessage = (received) => {
52
+ // Two handshakes can overlap on one socket (e.g. a reconnect firing while the
53
+ // previous attempt is still in flight), and both would be waiting on the same
54
+ // payloadType. Without this, one response would settle both — leaving an
55
+ // account that never got a reply believing it is authenticated.
56
+ if (received.clientMsgId && received.clientMsgId !== clientMsgId) {
57
+ return;
58
+ }
50
59
  if (received.payloadType === expectedPayloadType) {
51
60
  cleanup();
52
61
  resolve(received);
@@ -69,7 +78,7 @@ class SpotwareSocketAuthenticator {
69
78
  reject(new spotware_socket_auth_error_1.SpotwareSocketAuthError(`Timed out waiting for payloadType ${expectedPayloadType}`));
70
79
  }, this.responseTimeoutMs);
71
80
  this.transport.on('message', onMessage);
72
- this.transport.send(message).catch((error) => {
81
+ this.transport.send(types_1.ProtoMessage.fromPartial({ ...message, clientMsgId })).catch((error) => {
73
82
  cleanup();
74
83
  reject(error);
75
84
  });
@@ -1 +1 @@
1
- {"version":3,"file":"spotware-socket-authenticator.js","sourceRoot":"","sources":["../../src/auth/spotware-socket-authenticator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spotware-socket-authenticator.js","sourceRoot":"","sources":["../../src/auth/spotware-socket-authenticator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=trendbars-example.d.ts.map
@@ -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
@@ -1,6 +1,7 @@
1
1
  export * from './auth';
2
2
  export * from './client';
3
3
  export * from './market-data';
4
+ export * from './shared';
4
5
  export * from './trading';
5
6
  export * from './transport';
6
7
  export * from './types';
@@ -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;AAEjF,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;;;;;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;YAa3C,eAAe;IAa7B,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,cAAc;CAgBzB"}
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":"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"}
1
+ {"version":3,"file":"spotware-market-data.js","sourceRoot":"","sources":["../../src/market-data/spotware-market-data.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from './spotware-scale';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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.2.0",
3
+ "version": "0.3.1",
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"