@danielgroen/dxtrade-api 1.0.24 → 1.0.25

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
@@ -26,7 +26,7 @@ npm install dxtrade-api
26
26
  - [x] Position metrics (per-position P&L)
27
27
  - [x] Account metrics, trade journal & trade history
28
28
  - [x] Symbol search & instrument info
29
- - [x] OHLC / price bar data
29
+ - [x] OHLC / price bar data (one-shot & streaming)
30
30
  - [x] PnL assessments
31
31
  - [x] Multi-broker support (FTMO, Eightcap, Lark Funding)
32
32
  - [x] Persistent WebSocket with `connect()`
@@ -34,7 +34,7 @@ npm install dxtrade-api
34
34
  - [x] Full TypeScript support
35
35
  - [ ] Batch orders
36
36
  - [ ] Modify existing orders
37
- - [ ] Real-time price streaming
37
+ - [x] Real-time OHLC streaming
38
38
 
39
39
  ## Quick Start
40
40
 
@@ -48,8 +48,8 @@ const client = new DxtradeClient({
48
48
  accountId: "optional_account_id",
49
49
  });
50
50
 
51
- // connect() = auth + persistent WebSocket (recommended)
52
- await client.connect();
51
+ // await client.auth(); // Auth only
52
+ await client.connect(); // Auth + persistent WebSocket (recommended)
53
53
 
54
54
  const suggestions = await client.symbols.search("EURUSD");
55
55
  const symbol = suggestions[0];
@@ -63,17 +63,17 @@ const order = await client.orders.submit({
63
63
  });
64
64
 
65
65
  console.log(`Order ${order.orderId}: ${order.status}`);
66
- client.disconnect();
66
+ client.disconnect(); // disconnect stream -- only needed if client.connect()
67
67
  ```
68
68
 
69
69
  ## Connection Modes
70
70
 
71
71
  ```ts
72
- // Persistent WebSocket (recommended) — reuses one WS for all data, enables streaming
72
+ // 1. Persistent WebSocket (recommended) — reuses one WS for all data, enables streaming
73
73
  await client.connect();
74
74
  client.disconnect(); // when done
75
75
 
76
- // Lightweight — auth only, each data call opens a temporary WebSocket
76
+ // 2. Lightweight — auth only, each data call opens a temporary WebSocket
77
77
  await client.auth();
78
78
  ```
79
79
 
@@ -113,11 +113,10 @@ BROKER.FTMO // "https://dxtrade.ftmo.com"
113
113
 
114
114
  ### Positions
115
115
 
116
- - `client.positions.get()` — Get all open positions
116
+ - `client.positions.get()` — Get all open positions with P&L metrics merged (margin, plOpen, marketValue, etc.)
117
117
  - `client.positions.close(params)` — Close a position (supports partial closes via the quantity field)
118
118
  - `client.positions.closeAll()` — Close all open positions with market orders
119
- - `client.positions.metrics()` — Get position-level P&L metrics
120
- - `client.positions.stream(callback)` — Stream real-time position updates (requires `connect()`). Returns an unsubscribe function.
119
+ - `client.positions.stream(callback)` — Stream real-time position updates with live P&L (requires `connect()`). Returns an unsubscribe function.
121
120
 
122
121
  ### Orders
123
122
 
@@ -145,6 +144,7 @@ BROKER.FTMO // "https://dxtrade.ftmo.com"
145
144
  ### OHLC
146
145
 
147
146
  - `client.ohlc.get(params)` — Fetch OHLC price bars for a symbol (resolution, range, maxBars, priceField)
147
+ - `client.ohlc.stream(params, callback)` — Stream real-time OHLC bar updates (requires `connect()`). Returns a promise that resolves with an unsubscribe function after the snapshot is received.
148
148
 
149
149
  ### Assessments
150
150
 
@@ -196,6 +196,8 @@ npm run example:symbols:info:btc
196
196
  npm run example:instruments:get
197
197
  npm run example:instruments:get:forex
198
198
  npm run example:ohlc:get
199
+ npm run example:ohlc:stream
200
+ npm run example:ohlc:stream:btc
199
201
  npm run example:assessments:get
200
202
  npm run example:assessments:get:btc
201
203
  ```
package/dist/index.d.mts CHANGED
@@ -100,7 +100,8 @@ declare enum WS_MESSAGE {
100
100
  }
101
101
  declare namespace WS_MESSAGE {
102
102
  enum SUBTOPIC {
103
- BIG_CHART_COMPONENT = "BigChartComponentPresenter-4"
103
+ BIG_CHART_COMPONENT = "BigChartComponentPresenter-4",
104
+ OHLC_STREAM = "OHLCStreamPresenter-0"
104
105
  }
105
106
  }
106
107
 
@@ -389,13 +390,28 @@ declare namespace Position {
389
390
  stopLoss: number | null;
390
391
  }
391
392
  interface Metrics {
392
- positionCode: string;
393
- openPl: number;
394
- openPlPerLot: number;
395
- currentPrice: number;
396
- convertedOpenPl: number;
393
+ uid: string;
394
+ accountId: string;
395
+ margin: number;
396
+ plOpen: number;
397
+ plClosed: number;
398
+ totalCommissions: number;
399
+ totalFinancing: number;
400
+ plRate: number;
401
+ averagePrice: number;
402
+ marketValue: number;
397
403
  [key: string]: unknown;
398
404
  }
405
+ interface Full extends Get {
406
+ margin: number;
407
+ plOpen: number;
408
+ plClosed: number;
409
+ totalCommissions: number;
410
+ totalFinancing: number;
411
+ plRate: number;
412
+ averagePrice: number;
413
+ marketValue: number;
414
+ }
399
415
  interface Close {
400
416
  legs: {
401
417
  instrumentId: number;
@@ -441,16 +457,14 @@ declare namespace Symbol {
441
457
  declare class PositionsDomain {
442
458
  private _ctx;
443
459
  constructor(_ctx: ClientContext);
444
- /** Get all open positions via WebSocket. */
445
- get(): Promise<Position.Get[]>;
460
+ /** Get all open positions with P&L metrics merged. */
461
+ get(): Promise<Position.Full[]>;
446
462
  /** Close a position. Supports partial closes by specifying a quantity smaller than the full position size. */
447
463
  close(params: Position.Close): Promise<void>;
448
464
  /** Close all open positions with market orders. */
449
465
  closeAll(): Promise<void>;
450
- /** Get position-level P&L metrics via WebSocket. */
451
- metrics(): Promise<Position.Metrics[]>;
452
- /** Stream real-time position updates. Requires connect(). Returns unsubscribe function. */
453
- stream(callback: (positions: Position.Get[]) => void): () => void;
466
+ /** Stream real-time position updates with P&L metrics. Requires connect(). Returns unsubscribe function. */
467
+ stream(callback: (positions: Position.Full[]) => void): () => void;
454
468
  }
455
469
  declare class OrdersDomain {
456
470
  private _ctx;
@@ -519,6 +533,8 @@ declare class OhlcDomain {
519
533
  * @param params.priceField - "bid" or "ask" (default: "bid")
520
534
  */
521
535
  get(params: OHLC.Params): Promise<OHLC.Bar[]>;
536
+ /** Stream real-time OHLC bar updates. Requires connect(). Returns unsubscribe function. */
537
+ stream(params: OHLC.Params, callback: (bars: OHLC.Bar[]) => void): Promise<() => void>;
522
538
  }
523
539
  declare class AssessmentsDomain {
524
540
  private _ctx;
@@ -554,7 +570,7 @@ declare class DxtradeClient {
554
570
  readonly symbols: SymbolsDomain;
555
571
  /** Instrument operations: get (with optional filtering). */
556
572
  readonly instruments: InstrumentsDomain;
557
- /** OHLC price bar operations: get. */
573
+ /** OHLC price bar operations: get, stream. */
558
574
  readonly ohlc: OhlcDomain;
559
575
  /** PnL assessment operations: get. */
560
576
  readonly assessments: AssessmentsDomain;
package/dist/index.d.ts CHANGED
@@ -100,7 +100,8 @@ declare enum WS_MESSAGE {
100
100
  }
101
101
  declare namespace WS_MESSAGE {
102
102
  enum SUBTOPIC {
103
- BIG_CHART_COMPONENT = "BigChartComponentPresenter-4"
103
+ BIG_CHART_COMPONENT = "BigChartComponentPresenter-4",
104
+ OHLC_STREAM = "OHLCStreamPresenter-0"
104
105
  }
105
106
  }
106
107
 
@@ -389,13 +390,28 @@ declare namespace Position {
389
390
  stopLoss: number | null;
390
391
  }
391
392
  interface Metrics {
392
- positionCode: string;
393
- openPl: number;
394
- openPlPerLot: number;
395
- currentPrice: number;
396
- convertedOpenPl: number;
393
+ uid: string;
394
+ accountId: string;
395
+ margin: number;
396
+ plOpen: number;
397
+ plClosed: number;
398
+ totalCommissions: number;
399
+ totalFinancing: number;
400
+ plRate: number;
401
+ averagePrice: number;
402
+ marketValue: number;
397
403
  [key: string]: unknown;
398
404
  }
405
+ interface Full extends Get {
406
+ margin: number;
407
+ plOpen: number;
408
+ plClosed: number;
409
+ totalCommissions: number;
410
+ totalFinancing: number;
411
+ plRate: number;
412
+ averagePrice: number;
413
+ marketValue: number;
414
+ }
399
415
  interface Close {
400
416
  legs: {
401
417
  instrumentId: number;
@@ -441,16 +457,14 @@ declare namespace Symbol {
441
457
  declare class PositionsDomain {
442
458
  private _ctx;
443
459
  constructor(_ctx: ClientContext);
444
- /** Get all open positions via WebSocket. */
445
- get(): Promise<Position.Get[]>;
460
+ /** Get all open positions with P&L metrics merged. */
461
+ get(): Promise<Position.Full[]>;
446
462
  /** Close a position. Supports partial closes by specifying a quantity smaller than the full position size. */
447
463
  close(params: Position.Close): Promise<void>;
448
464
  /** Close all open positions with market orders. */
449
465
  closeAll(): Promise<void>;
450
- /** Get position-level P&L metrics via WebSocket. */
451
- metrics(): Promise<Position.Metrics[]>;
452
- /** Stream real-time position updates. Requires connect(). Returns unsubscribe function. */
453
- stream(callback: (positions: Position.Get[]) => void): () => void;
466
+ /** Stream real-time position updates with P&L metrics. Requires connect(). Returns unsubscribe function. */
467
+ stream(callback: (positions: Position.Full[]) => void): () => void;
454
468
  }
455
469
  declare class OrdersDomain {
456
470
  private _ctx;
@@ -519,6 +533,8 @@ declare class OhlcDomain {
519
533
  * @param params.priceField - "bid" or "ask" (default: "bid")
520
534
  */
521
535
  get(params: OHLC.Params): Promise<OHLC.Bar[]>;
536
+ /** Stream real-time OHLC bar updates. Requires connect(). Returns unsubscribe function. */
537
+ stream(params: OHLC.Params, callback: (bars: OHLC.Bar[]) => void): Promise<() => void>;
522
538
  }
523
539
  declare class AssessmentsDomain {
524
540
  private _ctx;
@@ -554,7 +570,7 @@ declare class DxtradeClient {
554
570
  readonly symbols: SymbolsDomain;
555
571
  /** Instrument operations: get (with optional filtering). */
556
572
  readonly instruments: InstrumentsDomain;
557
- /** OHLC price bar operations: get. */
573
+ /** OHLC price bar operations: get, stream. */
558
574
  readonly ohlc: OhlcDomain;
559
575
  /** PnL assessment operations: get. */
560
576
  readonly assessments: AssessmentsDomain;
package/dist/index.js CHANGED
@@ -152,6 +152,7 @@ var WS_MESSAGE = /* @__PURE__ */ ((WS_MESSAGE2) => {
152
152
  let SUBTOPIC;
153
153
  ((SUBTOPIC2) => {
154
154
  SUBTOPIC2["BIG_CHART_COMPONENT"] = "BigChartComponentPresenter-4";
155
+ SUBTOPIC2["OHLC_STREAM"] = "OHLCStreamPresenter-0";
155
156
  })(SUBTOPIC = WS_MESSAGE2.SUBTOPIC || (WS_MESSAGE2.SUBTOPIC = {}));
156
157
  })(WS_MESSAGE || (WS_MESSAGE = {}));
157
158
 
@@ -488,6 +489,95 @@ async function getInstruments(ctx, params = {}, timeout = 3e4) {
488
489
 
489
490
  // src/domains/ohlc/ohlc.ts
490
491
  var import_ws4 = __toESM(require("ws"));
492
+ async function streamOHLC(ctx, params, callback) {
493
+ if (!ctx.wsManager) {
494
+ ctx.throwError(
495
+ "STREAM_REQUIRES_CONNECT" /* STREAM_REQUIRES_CONNECT */,
496
+ "Streaming requires a persistent WebSocket. Use connect() instead of auth()."
497
+ );
498
+ }
499
+ const { symbol, resolution = 60, range = 432e3, maxBars = 3500, priceField = "bid" } = params;
500
+ const subtopic = WS_MESSAGE.SUBTOPIC.OHLC_STREAM;
501
+ const headers = authHeaders(ctx.csrf, Cookies.serialize(ctx.cookies));
502
+ const snapshotBars = [];
503
+ let snapshotDone = false;
504
+ let resolveSnapshot = null;
505
+ const onChartFeed = (body) => {
506
+ if (body?.subtopic !== subtopic) return;
507
+ const data = body.data;
508
+ if (!Array.isArray(data)) return;
509
+ if (!snapshotDone) {
510
+ snapshotBars.push(...data);
511
+ if (body.snapshotEnd) {
512
+ snapshotDone = true;
513
+ callback([...snapshotBars]);
514
+ resolveSnapshot?.();
515
+ }
516
+ } else {
517
+ callback(data);
518
+ }
519
+ };
520
+ ctx.wsManager.on("chartFeedSubtopic" /* CHART_FEED_SUBTOPIC */, onChartFeed);
521
+ try {
522
+ await retryRequest(
523
+ {
524
+ method: "PUT",
525
+ url: endpoints.subscribeInstruments(ctx.broker),
526
+ data: { instruments: [symbol] },
527
+ headers
528
+ },
529
+ ctx.retries
530
+ );
531
+ await retryRequest(
532
+ {
533
+ method: "PUT",
534
+ url: endpoints.charts(ctx.broker),
535
+ data: {
536
+ chartIds: [],
537
+ requests: [
538
+ {
539
+ aggregationPeriodSeconds: resolution,
540
+ extendedSession: true,
541
+ forexPriceField: priceField,
542
+ id: 0,
543
+ maxBarsCount: maxBars,
544
+ range,
545
+ studySubscription: [],
546
+ subtopic,
547
+ symbol
548
+ }
549
+ ]
550
+ },
551
+ headers
552
+ },
553
+ ctx.retries
554
+ );
555
+ } catch (error) {
556
+ ctx.wsManager.removeListener("chartFeedSubtopic" /* CHART_FEED_SUBTOPIC */, onChartFeed);
557
+ const message = error instanceof Error ? error.message : "Unknown error";
558
+ ctx.throwError("OHLC_ERROR" /* OHLC_ERROR */, `OHLC stream subscription error: ${message}`);
559
+ }
560
+ await new Promise((resolve, reject) => {
561
+ if (snapshotDone) return resolve();
562
+ const timer = setTimeout(() => {
563
+ if (snapshotBars.length > 0) {
564
+ snapshotDone = true;
565
+ callback([...snapshotBars]);
566
+ resolve();
567
+ } else {
568
+ ctx.wsManager?.removeListener("chartFeedSubtopic" /* CHART_FEED_SUBTOPIC */, onChartFeed);
569
+ reject(new DxtradeError("OHLC_TIMEOUT" /* OHLC_TIMEOUT */, "OHLC stream snapshot timed out"));
570
+ }
571
+ }, 3e4);
572
+ resolveSnapshot = () => {
573
+ clearTimeout(timer);
574
+ resolve();
575
+ };
576
+ });
577
+ return () => {
578
+ ctx.wsManager?.removeListener("chartFeedSubtopic" /* CHART_FEED_SUBTOPIC */, onChartFeed);
579
+ };
580
+ }
491
581
  async function getOHLC(ctx, params, timeout = 3e4) {
492
582
  ctx.ensureSession();
493
583
  const { symbol, resolution = 60, range = 432e3, maxBars = 3500, priceField = "bid" } = params;
@@ -892,6 +982,23 @@ async function submitOrder(ctx, params) {
892
982
 
893
983
  // src/domains/position/position.ts
894
984
  var import_ws7 = __toESM(require("ws"));
985
+ function mergePositionsWithMetrics(positions, metrics) {
986
+ const metricsMap = new Map(metrics.map((m) => [m.uid, m]));
987
+ return positions.map((pos) => {
988
+ const m = metricsMap.get(pos.uid);
989
+ return {
990
+ ...pos,
991
+ margin: m?.margin ?? 0,
992
+ plOpen: m?.plOpen ?? 0,
993
+ plClosed: m?.plClosed ?? 0,
994
+ totalCommissions: m?.totalCommissions ?? 0,
995
+ totalFinancing: m?.totalFinancing ?? 0,
996
+ plRate: m?.plRate ?? 0,
997
+ averagePrice: m?.averagePrice ?? 0,
998
+ marketValue: m?.marketValue ?? 0
999
+ };
1000
+ });
1001
+ }
895
1002
  function streamPositions(ctx, callback) {
896
1003
  if (!ctx.wsManager) {
897
1004
  ctx.throwError(
@@ -899,25 +1006,38 @@ function streamPositions(ctx, callback) {
899
1006
  "Streaming requires a persistent WebSocket. Use connect() instead of auth()."
900
1007
  );
901
1008
  }
902
- const listener = (body) => callback(body);
903
- ctx.wsManager.on("POSITIONS" /* POSITIONS */, listener);
904
- const cached = ctx.wsManager.getCached("POSITIONS" /* POSITIONS */);
905
- if (cached !== void 0) {
906
- callback(cached);
907
- }
1009
+ const emit = () => {
1010
+ const positions = ctx.wsManager.getCached("POSITIONS" /* POSITIONS */);
1011
+ const metrics = ctx.wsManager.getCached("POSITION_METRICS" /* POSITION_METRICS */);
1012
+ if (positions && metrics) {
1013
+ callback(mergePositionsWithMetrics(positions, metrics));
1014
+ }
1015
+ };
1016
+ const onPositions = () => emit();
1017
+ const onMetrics = () => emit();
1018
+ ctx.wsManager.on("POSITIONS" /* POSITIONS */, onPositions);
1019
+ ctx.wsManager.on("POSITION_METRICS" /* POSITION_METRICS */, onMetrics);
1020
+ emit();
908
1021
  return () => {
909
- ctx.wsManager?.removeListener("POSITIONS" /* POSITIONS */, listener);
1022
+ ctx.wsManager?.removeListener("POSITIONS" /* POSITIONS */, onPositions);
1023
+ ctx.wsManager?.removeListener("POSITION_METRICS" /* POSITION_METRICS */, onMetrics);
910
1024
  };
911
1025
  }
912
1026
  async function getPositions(ctx) {
913
1027
  ctx.ensureSession();
914
1028
  if (ctx.wsManager) {
915
- return ctx.wsManager.waitFor("POSITIONS" /* POSITIONS */);
1029
+ const [positions, metrics] = await Promise.all([
1030
+ ctx.wsManager.waitFor("POSITIONS" /* POSITIONS */),
1031
+ ctx.wsManager.waitFor("POSITION_METRICS" /* POSITION_METRICS */)
1032
+ ]);
1033
+ return mergePositionsWithMetrics(positions, metrics);
916
1034
  }
917
1035
  const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
918
1036
  const cookieStr = Cookies.serialize(ctx.cookies);
919
1037
  return new Promise((resolve, reject) => {
920
1038
  const ws = new import_ws7.default(wsUrl, { headers: { Cookie: cookieStr } });
1039
+ let positions = null;
1040
+ let metrics = null;
921
1041
  const timer = setTimeout(() => {
922
1042
  ws.close();
923
1043
  reject(new DxtradeError("ACCOUNT_POSITIONS_TIMEOUT" /* ACCOUNT_POSITIONS_TIMEOUT */, "Account positions timed out"));
@@ -927,45 +1047,21 @@ async function getPositions(ctx) {
927
1047
  if (shouldLog(msg, ctx.debug)) debugLog(msg);
928
1048
  if (typeof msg === "string") return;
929
1049
  if (msg.type === "POSITIONS" /* POSITIONS */) {
930
- clearTimeout(timer);
931
- ws.close();
932
- resolve(msg.body);
1050
+ positions = msg.body;
933
1051
  }
934
- });
935
- ws.on("error", (error) => {
936
- clearTimeout(timer);
937
- ws.close();
938
- reject(new DxtradeError("ACCOUNT_POSITIONS_ERROR" /* ACCOUNT_POSITIONS_ERROR */, `Account positions error: ${error.message}`));
939
- });
940
- });
941
- }
942
- async function getPositionMetrics(ctx, timeout = 3e4) {
943
- ctx.ensureSession();
944
- if (ctx.wsManager) {
945
- return ctx.wsManager.waitFor("POSITION_METRICS" /* POSITION_METRICS */, timeout);
946
- }
947
- const wsUrl = endpoints.websocket(ctx.broker, ctx.atmosphereId);
948
- const cookieStr = Cookies.serialize(ctx.cookies);
949
- return new Promise((resolve, reject) => {
950
- const ws = new import_ws7.default(wsUrl, { headers: { Cookie: cookieStr } });
951
- const timer = setTimeout(() => {
952
- ws.close();
953
- reject(new DxtradeError("POSITION_METRICS_TIMEOUT" /* POSITION_METRICS_TIMEOUT */, "Position metrics timed out"));
954
- }, timeout);
955
- ws.on("message", (data) => {
956
- const msg = parseWsData(data);
957
- if (shouldLog(msg, ctx.debug)) debugLog(msg);
958
- if (typeof msg === "string") return;
959
1052
  if (msg.type === "POSITION_METRICS" /* POSITION_METRICS */) {
1053
+ metrics = msg.body;
1054
+ }
1055
+ if (positions && metrics) {
960
1056
  clearTimeout(timer);
961
1057
  ws.close();
962
- resolve(msg.body);
1058
+ resolve(mergePositionsWithMetrics(positions, metrics));
963
1059
  }
964
1060
  });
965
1061
  ws.on("error", (error) => {
966
1062
  clearTimeout(timer);
967
1063
  ws.close();
968
- reject(new DxtradeError("POSITION_METRICS_ERROR" /* POSITION_METRICS_ERROR */, `Position metrics error: ${error.message}`));
1064
+ reject(new DxtradeError("ACCOUNT_POSITIONS_ERROR" /* ACCOUNT_POSITIONS_ERROR */, `Account positions error: ${error.message}`));
969
1065
  });
970
1066
  });
971
1067
  }
@@ -1152,7 +1248,7 @@ var PositionsDomain = class {
1152
1248
  constructor(_ctx) {
1153
1249
  this._ctx = _ctx;
1154
1250
  }
1155
- /** Get all open positions via WebSocket. */
1251
+ /** Get all open positions with P&L metrics merged. */
1156
1252
  get() {
1157
1253
  return getPositions(this._ctx);
1158
1254
  }
@@ -1164,11 +1260,7 @@ var PositionsDomain = class {
1164
1260
  closeAll() {
1165
1261
  return closeAllPositions(this._ctx);
1166
1262
  }
1167
- /** Get position-level P&L metrics via WebSocket. */
1168
- metrics() {
1169
- return getPositionMetrics(this._ctx);
1170
- }
1171
- /** Stream real-time position updates. Requires connect(). Returns unsubscribe function. */
1263
+ /** Stream real-time position updates with P&L metrics. Requires connect(). Returns unsubscribe function. */
1172
1264
  stream(callback) {
1173
1265
  return streamPositions(this._ctx, callback);
1174
1266
  }
@@ -1263,6 +1355,10 @@ var OhlcDomain = class {
1263
1355
  get(params) {
1264
1356
  return getOHLC(this._ctx, params);
1265
1357
  }
1358
+ /** Stream real-time OHLC bar updates. Requires connect(). Returns unsubscribe function. */
1359
+ stream(params, callback) {
1360
+ return streamOHLC(this._ctx, params, callback);
1361
+ }
1266
1362
  };
1267
1363
  var AssessmentsDomain = class {
1268
1364
  constructor(_ctx) {
@@ -1285,7 +1381,7 @@ var DxtradeClient = class {
1285
1381
  symbols;
1286
1382
  /** Instrument operations: get (with optional filtering). */
1287
1383
  instruments;
1288
- /** OHLC price bar operations: get. */
1384
+ /** OHLC price bar operations: get, stream. */
1289
1385
  ohlc;
1290
1386
  /** PnL assessment operations: get. */
1291
1387
  assessments;