ctrader-x 0.1.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.
Files changed (147) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +177 -0
  3. package/dist/auth/index.d.ts +7 -0
  4. package/dist/auth/index.d.ts.map +1 -0
  5. package/dist/auth/index.js +23 -0
  6. package/dist/auth/index.js.map +1 -0
  7. package/dist/auth/spotware-oauth-client.d.ts +31 -0
  8. package/dist/auth/spotware-oauth-client.d.ts.map +1 -0
  9. package/dist/auth/spotware-oauth-client.js +65 -0
  10. package/dist/auth/spotware-oauth-client.js.map +1 -0
  11. package/dist/auth/spotware-oauth-endpoints.d.ts +3 -0
  12. package/dist/auth/spotware-oauth-endpoints.d.ts.map +1 -0
  13. package/dist/auth/spotware-oauth-endpoints.js +8 -0
  14. package/dist/auth/spotware-oauth-endpoints.js.map +1 -0
  15. package/dist/auth/spotware-oauth-error.d.ts +6 -0
  16. package/dist/auth/spotware-oauth-error.d.ts.map +1 -0
  17. package/dist/auth/spotware-oauth-error.js +15 -0
  18. package/dist/auth/spotware-oauth-error.js.map +1 -0
  19. package/dist/auth/spotware-oauth-scope.enum.d.ts +5 -0
  20. package/dist/auth/spotware-oauth-scope.enum.d.ts.map +1 -0
  21. package/dist/auth/spotware-oauth-scope.enum.js +9 -0
  22. package/dist/auth/spotware-oauth-scope.enum.js.map +1 -0
  23. package/dist/auth/spotware-oauth-token.model.d.ts +7 -0
  24. package/dist/auth/spotware-oauth-token.model.d.ts.map +1 -0
  25. package/dist/auth/spotware-oauth-token.model.js +3 -0
  26. package/dist/auth/spotware-oauth-token.model.js.map +1 -0
  27. package/dist/auth/spotware-socket-auth-error.d.ts +5 -0
  28. package/dist/auth/spotware-socket-auth-error.d.ts.map +1 -0
  29. package/dist/auth/spotware-socket-auth-error.js +13 -0
  30. package/dist/auth/spotware-socket-auth-error.js.map +1 -0
  31. package/dist/auth/spotware-socket-authenticator.d.ts +21 -0
  32. package/dist/auth/spotware-socket-authenticator.d.ts.map +1 -0
  33. package/dist/auth/spotware-socket-authenticator.js +80 -0
  34. package/dist/auth/spotware-socket-authenticator.js.map +1 -0
  35. package/dist/client/index.d.ts +3 -0
  36. package/dist/client/index.d.ts.map +1 -0
  37. package/dist/client/index.js +19 -0
  38. package/dist/client/index.js.map +1 -0
  39. package/dist/client/spotware-client.d.ts +56 -0
  40. package/dist/client/spotware-client.d.ts.map +1 -0
  41. package/dist/client/spotware-client.js +172 -0
  42. package/dist/client/spotware-client.js.map +1 -0
  43. package/dist/client/spotware-request-error.d.ts +5 -0
  44. package/dist/client/spotware-request-error.d.ts.map +1 -0
  45. package/dist/client/spotware-request-error.js +13 -0
  46. package/dist/client/spotware-request-error.js.map +1 -0
  47. package/dist/example/example.d.ts +2 -0
  48. package/dist/example/example.d.ts.map +1 -0
  49. package/dist/example/example.js +187 -0
  50. package/dist/example/example.js.map +1 -0
  51. package/dist/example/market-data-example.d.ts +2 -0
  52. package/dist/example/market-data-example.d.ts.map +1 -0
  53. package/dist/example/market-data-example.js +32 -0
  54. package/dist/example/market-data-example.js.map +1 -0
  55. package/dist/example/shared/create-authenticated-client.d.ts +9 -0
  56. package/dist/example/shared/create-authenticated-client.d.ts.map +1 -0
  57. package/dist/example/shared/create-authenticated-client.js +154 -0
  58. package/dist/example/shared/create-authenticated-client.js.map +1 -0
  59. package/dist/example/trading-example.d.ts +2 -0
  60. package/dist/example/trading-example.d.ts.map +1 -0
  61. package/dist/example/trading-example.js +78 -0
  62. package/dist/example/trading-example.js.map +1 -0
  63. package/dist/index.d.ts +7 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +23 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/market-data/index.d.ts +3 -0
  68. package/dist/market-data/index.d.ts.map +1 -0
  69. package/dist/market-data/index.js +19 -0
  70. package/dist/market-data/index.js.map +1 -0
  71. package/dist/market-data/spotware-market-data.d.ts +31 -0
  72. package/dist/market-data/spotware-market-data.d.ts.map +1 -0
  73. package/dist/market-data/spotware-market-data.js +68 -0
  74. package/dist/market-data/spotware-market-data.js.map +1 -0
  75. package/dist/market-data/spotware-symbol-catalog.d.ts +19 -0
  76. package/dist/market-data/spotware-symbol-catalog.d.ts.map +1 -0
  77. package/dist/market-data/spotware-symbol-catalog.js +52 -0
  78. package/dist/market-data/spotware-symbol-catalog.js.map +1 -0
  79. package/dist/shared/sleep.d.ts +5 -0
  80. package/dist/shared/sleep.d.ts.map +1 -0
  81. package/dist/shared/sleep.js +17 -0
  82. package/dist/shared/sleep.js.map +1 -0
  83. package/dist/shared/spotware-scale.d.ts +3 -0
  84. package/dist/shared/spotware-scale.d.ts.map +1 -0
  85. package/dist/shared/spotware-scale.js +12 -0
  86. package/dist/shared/spotware-scale.js.map +1 -0
  87. package/dist/shared/typed-event-emitter.d.ts +13 -0
  88. package/dist/shared/typed-event-emitter.d.ts.map +1 -0
  89. package/dist/shared/typed-event-emitter.js +28 -0
  90. package/dist/shared/typed-event-emitter.js.map +1 -0
  91. package/dist/trading/index.d.ts +2 -0
  92. package/dist/trading/index.d.ts.map +1 -0
  93. package/dist/trading/index.js +18 -0
  94. package/dist/trading/index.js.map +1 -0
  95. package/dist/trading/spotware-trading.d.ts +59 -0
  96. package/dist/trading/spotware-trading.d.ts.map +1 -0
  97. package/dist/trading/spotware-trading.js +97 -0
  98. package/dist/trading/spotware-trading.js.map +1 -0
  99. package/dist/transport/frame-codec.d.ts +6 -0
  100. package/dist/transport/frame-codec.d.ts.map +1 -0
  101. package/dist/transport/frame-codec.js +35 -0
  102. package/dist/transport/frame-codec.js.map +1 -0
  103. package/dist/transport/index.d.ts +4 -0
  104. package/dist/transport/index.d.ts.map +1 -0
  105. package/dist/transport/index.js +20 -0
  106. package/dist/transport/index.js.map +1 -0
  107. package/dist/transport/rate-limiter.d.ts +11 -0
  108. package/dist/transport/rate-limiter.d.ts.map +1 -0
  109. package/dist/transport/rate-limiter.js +48 -0
  110. package/dist/transport/rate-limiter.js.map +1 -0
  111. package/dist/transport/reconnect-backoff.d.ts +12 -0
  112. package/dist/transport/reconnect-backoff.d.ts.map +1 -0
  113. package/dist/transport/reconnect-backoff.js +20 -0
  114. package/dist/transport/reconnect-backoff.js.map +1 -0
  115. package/dist/transport/spotware-host.enum.d.ts +6 -0
  116. package/dist/transport/spotware-host.enum.d.ts.map +1 -0
  117. package/dist/transport/spotware-host.enum.js +10 -0
  118. package/dist/transport/spotware-host.enum.js.map +1 -0
  119. package/dist/transport/spotware-rate-limiter.d.ts +8 -0
  120. package/dist/transport/spotware-rate-limiter.d.ts.map +1 -0
  121. package/dist/transport/spotware-rate-limiter.js +29 -0
  122. package/dist/transport/spotware-rate-limiter.js.map +1 -0
  123. package/dist/transport/spotware-transport.d.ts +63 -0
  124. package/dist/transport/spotware-transport.d.ts.map +1 -0
  125. package/dist/transport/spotware-transport.js +237 -0
  126. package/dist/transport/spotware-transport.js.map +1 -0
  127. package/dist/types/generated/OpenApiCommonMessages.d.ts +49 -0
  128. package/dist/types/generated/OpenApiCommonMessages.d.ts.map +1 -0
  129. package/dist/types/generated/OpenApiCommonMessages.js +285 -0
  130. package/dist/types/generated/OpenApiCommonMessages.js.map +1 -0
  131. package/dist/types/generated/OpenApiCommonModelMessages.d.ts +40 -0
  132. package/dist/types/generated/OpenApiCommonModelMessages.d.ts.map +1 -0
  133. package/dist/types/generated/OpenApiCommonModelMessages.js +140 -0
  134. package/dist/types/generated/OpenApiCommonModelMessages.js.map +1 -0
  135. package/dist/types/generated/OpenApiMessages.d.ts +964 -0
  136. package/dist/types/generated/OpenApiMessages.d.ts.map +1 -0
  137. package/dist/types/generated/OpenApiMessages.js +8754 -0
  138. package/dist/types/generated/OpenApiMessages.js.map +1 -0
  139. package/dist/types/generated/OpenApiModelMessages.d.ts +1116 -0
  140. package/dist/types/generated/OpenApiModelMessages.d.ts.map +1 -0
  141. package/dist/types/generated/OpenApiModelMessages.js +6917 -0
  142. package/dist/types/generated/OpenApiModelMessages.js.map +1 -0
  143. package/dist/types/index.d.ts +5 -0
  144. package/dist/types/index.d.ts.map +1 -0
  145. package/dist/types/index.js +141 -0
  146. package/dist/types/index.js.map +1 -0
  147. package/package.json +61 -0
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TypedEventEmitter = void 0;
4
+ const node_events_1 = require("node:events");
5
+ /**
6
+ * Wraps Node's EventEmitter instead of extending it, so `emit` stays protected:
7
+ * consumers can only listen, only the owning class can trigger events.
8
+ */
9
+ class TypedEventEmitter {
10
+ emitter = new node_events_1.EventEmitter();
11
+ on(event, listener) {
12
+ this.emitter.on(event, listener);
13
+ return this;
14
+ }
15
+ once(event, listener) {
16
+ this.emitter.once(event, listener);
17
+ return this;
18
+ }
19
+ off(event, listener) {
20
+ this.emitter.off(event, listener);
21
+ return this;
22
+ }
23
+ emit(event, ...args) {
24
+ return this.emitter.emit(event, ...args);
25
+ }
26
+ }
27
+ exports.TypedEventEmitter = TypedEventEmitter;
28
+ //# sourceMappingURL=typed-event-emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typed-event-emitter.js","sourceRoot":"","sources":["../../src/shared/typed-event-emitter.ts"],"names":[],"mappings":";;;AAAA,6CAA2C;AAI3C;;;GAGG;AACH,MAAa,iBAAiB;IACT,OAAO,GAAG,IAAI,0BAAY,EAAE,CAAC;IAE9C,EAAE,CAAwC,KAAa,EAAE,QAA4C;QACjG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAwC,CAAC,CAAC;QACjE,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,IAAI,CAAwC,KAAa,EAAE,QAA4C;QACnG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,QAAwC,CAAC,CAAC;QACnE,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,GAAG,CAAwC,KAAa,EAAE,QAA4C;QAClG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,QAAwC,CAAC,CAAC;QAClE,OAAO,IAAI,CAAC;IAChB,CAAC;IAES,IAAI,CAAwC,KAAa,EAAE,GAAG,IAAqB;QACzF,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IAC7C,CAAC;CACJ;AArBD,8CAqBC"}
@@ -0,0 +1,2 @@
1
+ export * from './spotware-trading';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/trading/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,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-trading"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/trading/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,qDAAmC"}
@@ -0,0 +1,59 @@
1
+ import type { SpotwareClient } from '../client';
2
+ import { ProtoOAExecutionEvent, ProtoOAOrder, ProtoOAPosition, ProtoOATimeInForce, ProtoOATradeSide } from '../types';
3
+ export interface IPlaceMarketOrderParams {
4
+ symbolId: number;
5
+ tradeSide: ProtoOATradeSide;
6
+ /** In units (e.g. 1000 for 1000 units) — converted internally, not the wire's cents form. */
7
+ volume: number;
8
+ /** Absolute price (e.g. 1.23456), not scaled. */
9
+ stopLoss?: number;
10
+ /** Absolute price (e.g. 1.23456), not scaled. */
11
+ takeProfit?: number;
12
+ comment?: string;
13
+ label?: string;
14
+ }
15
+ export interface IPlaceLimitOrderParams extends IPlaceMarketOrderParams {
16
+ /** Absolute price (e.g. 1.23456), not scaled. */
17
+ limitPrice: number;
18
+ timeInForce?: ProtoOATimeInForce;
19
+ expirationTimestamp?: number;
20
+ }
21
+ export interface IAmendOrderParams {
22
+ orderId: number;
23
+ /** In units — converted internally, not the wire's cents form. */
24
+ volume?: number;
25
+ limitPrice?: number;
26
+ stopPrice?: number;
27
+ stopLoss?: number;
28
+ takeProfit?: number;
29
+ expirationTimestamp?: number;
30
+ }
31
+ export interface IClosePositionParams {
32
+ positionId: number;
33
+ /** In units — converted internally, not the wire's cents form. */
34
+ volume: number;
35
+ }
36
+ export interface IOpenPositionsAndOrders {
37
+ positions: ProtoOAPosition[];
38
+ orders: ProtoOAOrder[];
39
+ }
40
+ /**
41
+ * Places/modifies/cancels orders and closes positions, via `client`. Knows nothing about
42
+ * market data streaming. Order mutations have no dedicated response message: the outcome
43
+ * arrives as a ProtoOAExecutionEvent on success, or rejects via ProtoOAOrderErrorEvent (which
44
+ * `client` already treats as an error) on failure. Volume here is in units, not the wire's
45
+ * cents representation; prices other than relative SL/TP stay plain decimals, matching the
46
+ * protocol's own convention (confirmed against Spotware's official SDK, not assumed).
47
+ */
48
+ export declare class SpotwareTrading {
49
+ private readonly client;
50
+ constructor(client: SpotwareClient);
51
+ placeMarketOrder(params: IPlaceMarketOrderParams): Promise<ProtoOAExecutionEvent>;
52
+ placeLimitOrder(params: IPlaceLimitOrderParams): Promise<ProtoOAExecutionEvent>;
53
+ amendOrder(params: IAmendOrderParams): Promise<ProtoOAExecutionEvent>;
54
+ cancelOrder(orderId: number): Promise<ProtoOAExecutionEvent>;
55
+ closePosition(params: IClosePositionParams): Promise<ProtoOAExecutionEvent>;
56
+ getOpenPositionsAndOrders(): Promise<IOpenPositionsAndOrders>;
57
+ private placeOrder;
58
+ }
59
+ //# sourceMappingURL=spotware-trading.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spotware-trading.d.ts","sourceRoot":"","sources":["../../src/trading/spotware-trading.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAIH,qBAAqB,EAErB,YAAY,EAGZ,eAAe,EAGf,kBAAkB,EAClB,gBAAgB,EACnB,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,uBAAuB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,6FAA6F;IAC7F,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAuB,SAAQ,uBAAuB;IACnE,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACpC,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,MAAM,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,qBAAa,eAAe;IACZ,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,cAAc;IAEnD,gBAAgB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAIjF,eAAe,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAIzE,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAiBrE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAO5D,aAAa,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAe3E,yBAAyB,IAAI,OAAO,CAAC,uBAAuB,CAAC;YAQrD,UAAU;CAsB3B"}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SpotwareTrading = void 0;
4
+ const spotware_scale_1 = require("../shared/spotware-scale");
5
+ const types_1 = require("../types");
6
+ /**
7
+ * Places/modifies/cancels orders and closes positions, via `client`. Knows nothing about
8
+ * market data streaming. Order mutations have no dedicated response message: the outcome
9
+ * arrives as a ProtoOAExecutionEvent on success, or rejects via ProtoOAOrderErrorEvent (which
10
+ * `client` already treats as an error) on failure. Volume here is in units, not the wire's
11
+ * cents representation; prices other than relative SL/TP stay plain decimals, matching the
12
+ * protocol's own convention (confirmed against Spotware's official SDK, not assumed).
13
+ */
14
+ class SpotwareTrading {
15
+ client;
16
+ constructor(client) {
17
+ this.client = client;
18
+ }
19
+ placeMarketOrder(params) {
20
+ return this.placeOrder({ ...params, orderType: types_1.ProtoOAOrderType.MARKET });
21
+ }
22
+ placeLimitOrder(params) {
23
+ return this.placeOrder({ ...params, orderType: types_1.ProtoOAOrderType.LIMIT });
24
+ }
25
+ async amendOrder(params) {
26
+ const request = types_1.ProtoOAAmendOrderReq.fromPartial({
27
+ ctidTraderAccountId: this.client.ctidTraderAccountId,
28
+ orderId: params.orderId,
29
+ volume: params.volume === undefined ? undefined : params.volume * spotware_scale_1.SPOTWARE_VOLUME_SCALE,
30
+ limitPrice: params.limitPrice,
31
+ stopPrice: params.stopPrice,
32
+ stopLoss: params.stopLoss,
33
+ takeProfit: params.takeProfit,
34
+ expirationTimestamp: params.expirationTimestamp
35
+ });
36
+ const response = await this.client.send(types_1.ProtoOAPayloadType.PROTO_OA_AMEND_ORDER_REQ, types_1.ProtoOAAmendOrderReq.encode(request).finish());
37
+ return types_1.ProtoOAExecutionEvent.decode(response.payload ?? new Uint8Array());
38
+ }
39
+ async cancelOrder(orderId) {
40
+ const request = types_1.ProtoOACancelOrderReq.fromPartial({ ctidTraderAccountId: this.client.ctidTraderAccountId, orderId });
41
+ const response = await this.client.send(types_1.ProtoOAPayloadType.PROTO_OA_CANCEL_ORDER_REQ, types_1.ProtoOACancelOrderReq.encode(request).finish());
42
+ return types_1.ProtoOAExecutionEvent.decode(response.payload ?? new Uint8Array());
43
+ }
44
+ async closePosition(params) {
45
+ const request = types_1.ProtoOAClosePositionReq.fromPartial({
46
+ ctidTraderAccountId: this.client.ctidTraderAccountId,
47
+ positionId: params.positionId,
48
+ volume: params.volume * spotware_scale_1.SPOTWARE_VOLUME_SCALE
49
+ });
50
+ const response = await this.client.send(types_1.ProtoOAPayloadType.PROTO_OA_CLOSE_POSITION_REQ, types_1.ProtoOAClosePositionReq.encode(request).finish());
51
+ return types_1.ProtoOAExecutionEvent.decode(response.payload ?? new Uint8Array());
52
+ }
53
+ async getOpenPositionsAndOrders() {
54
+ const request = types_1.ProtoOAReconcileReq.fromPartial({ ctidTraderAccountId: this.client.ctidTraderAccountId });
55
+ const response = await this.client.send(types_1.ProtoOAPayloadType.PROTO_OA_RECONCILE_REQ, types_1.ProtoOAReconcileReq.encode(request).finish());
56
+ const reconcile = types_1.ProtoOAReconcileRes.decode(response.payload ?? new Uint8Array());
57
+ return { positions: reconcile.position, orders: reconcile.order };
58
+ }
59
+ async placeOrder(params) {
60
+ const request = types_1.ProtoOANewOrderReq.fromPartial({
61
+ ctidTraderAccountId: this.client.ctidTraderAccountId,
62
+ symbolId: params.symbolId,
63
+ orderType: params.orderType,
64
+ tradeSide: params.tradeSide,
65
+ volume: params.volume * spotware_scale_1.SPOTWARE_VOLUME_SCALE,
66
+ limitPrice: 'limitPrice' in params ? params.limitPrice : undefined,
67
+ timeInForce: 'timeInForce' in params ? params.timeInForce : undefined,
68
+ expirationTimestamp: 'expirationTimestamp' in params ? params.expirationTimestamp : undefined,
69
+ stopLoss: params.stopLoss,
70
+ takeProfit: params.takeProfit,
71
+ comment: params.comment,
72
+ label: params.label
73
+ });
74
+ const response = await this.client.send(types_1.ProtoOAPayloadType.PROTO_OA_NEW_ORDER_REQ, encodeNewOrderReq(request));
75
+ return types_1.ProtoOAExecutionEvent.decode(response.payload ?? new Uint8Array());
76
+ }
77
+ }
78
+ exports.SpotwareTrading = SpotwareTrading;
79
+ // ts-proto skips a required field on the wire whenever its value equals the field's implicit
80
+ // proto2 default — for an enum with no explicit `[default = ...]` annotation, that's always its
81
+ // first declared member. orderType and tradeSide have no such annotation, so MARKET (1) and BUY
82
+ // (1) — the two most common choices — get silently dropped, and cTrader's server rejects the
83
+ // message as missing them. Confirmed this isn't fixable via ts-proto's codegen flags
84
+ // (disableProto2DefaultValues only affects fields with an explicit default). Field order doesn't
85
+ // matter on the wire, so appending the dropped ones after the normal encode is a safe, minimal
86
+ // fix that doesn't require hand-editing generated code.
87
+ function encodeNewOrderReq(request) {
88
+ const writer = types_1.ProtoOANewOrderReq.encode(request);
89
+ if (request.orderType === types_1.ProtoOAOrderType.MARKET) {
90
+ writer.uint32(32).int32(request.orderType);
91
+ }
92
+ if (request.tradeSide === types_1.ProtoOATradeSide.BUY) {
93
+ writer.uint32(40).int32(request.tradeSide);
94
+ }
95
+ return writer.finish();
96
+ }
97
+ //# sourceMappingURL=spotware-trading.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spotware-trading.js","sourceRoot":"","sources":["../../src/trading/spotware-trading.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare function encodeFrame(payload: Uint8Array): Buffer;
2
+ export declare class FrameDecoder {
3
+ private buffer;
4
+ push(chunk: Buffer): Buffer[];
5
+ }
6
+ //# sourceMappingURL=frame-codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-codec.d.ts","sourceRoot":"","sources":["../../src/transport/frame-codec.ts"],"names":[],"mappings":"AAMA,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAKvD;AAED,qBAAa,YAAY;IACrB,OAAO,CAAC,MAAM,CAA2B;IAEzC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;CAsBhC"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FrameDecoder = void 0;
4
+ exports.encodeFrame = encodeFrame;
5
+ const LENGTH_PREFIX_BYTES = 4;
6
+ // cTrader closes the socket well before a legitimate message gets anywhere near this size;
7
+ // a length past it means the stream desynced (e.g. a framing bug), not a big message.
8
+ const MAX_FRAME_BYTES = 16 * 1024 * 1024;
9
+ function encodeFrame(payload) {
10
+ const length = Buffer.alloc(LENGTH_PREFIX_BYTES);
11
+ length.writeUInt32BE(payload.byteLength);
12
+ return Buffer.concat([length, payload]);
13
+ }
14
+ class FrameDecoder {
15
+ buffer = Buffer.alloc(0);
16
+ push(chunk) {
17
+ this.buffer = this.buffer.byteLength === 0 ? chunk : Buffer.concat([this.buffer, chunk]);
18
+ const frames = [];
19
+ while (this.buffer.byteLength >= LENGTH_PREFIX_BYTES) {
20
+ const frameLength = this.buffer.readUInt32BE(0);
21
+ if (frameLength > MAX_FRAME_BYTES) {
22
+ throw new Error(`Frame length ${frameLength} exceeds the ${MAX_FRAME_BYTES} byte limit; stream is desynced`);
23
+ }
24
+ const frameEnd = LENGTH_PREFIX_BYTES + frameLength;
25
+ if (this.buffer.byteLength < frameEnd) {
26
+ break;
27
+ }
28
+ frames.push(this.buffer.subarray(LENGTH_PREFIX_BYTES, frameEnd));
29
+ this.buffer = this.buffer.subarray(frameEnd);
30
+ }
31
+ return frames;
32
+ }
33
+ }
34
+ exports.FrameDecoder = FrameDecoder;
35
+ //# sourceMappingURL=frame-codec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-codec.js","sourceRoot":"","sources":["../../src/transport/frame-codec.ts"],"names":[],"mappings":";;;AAMA,kCAKC;AAXD,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B,2FAA2F;AAC3F,sFAAsF;AACtF,MAAM,eAAe,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzC,SAAgB,WAAW,CAAC,OAAmB;IAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACjD,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAEzC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED,MAAa,YAAY;IACb,MAAM,GAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEzC,IAAI,CAAC,KAAa;QACd,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAEzF,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,mBAAmB,EAAE,CAAC;YACnD,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAChD,IAAI,WAAW,GAAG,eAAe,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,gBAAgB,WAAW,gBAAgB,eAAe,iCAAiC,CAAC,CAAC;YACjH,CAAC;YAED,MAAM,QAAQ,GAAG,mBAAmB,GAAG,WAAW,CAAC;YACnD,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,QAAQ,EAAE,CAAC;gBACpC,MAAM;YACV,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC,CAAC;YACjE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC;QAED,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ;AAzBD,oCAyBC"}
@@ -0,0 +1,4 @@
1
+ export * from './reconnect-backoff';
2
+ export * from './spotware-host.enum';
3
+ export * from './spotware-transport';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transport/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,20 @@
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("./reconnect-backoff"), exports);
18
+ __exportStar(require("./spotware-host.enum"), exports);
19
+ __exportStar(require("./spotware-transport"), exports);
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transport/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAoC;AACpC,uDAAqC;AACrC,uDAAqC"}
@@ -0,0 +1,11 @@
1
+ export declare class RateLimiter {
2
+ private readonly intervalMs;
3
+ private readonly queue;
4
+ private lastRunAt;
5
+ private timer;
6
+ constructor(intervalMs: number);
7
+ schedule(): Promise<void>;
8
+ dispose(): void;
9
+ private ensureTimerRunning;
10
+ }
11
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/transport/rate-limiter.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAW;IAKR,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAC/C,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,KAAK,CAA6B;gBAEb,UAAU,EAAE,MAAM;IAE/C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAezB,OAAO,IAAI,IAAI;IAQf,OAAO,CAAC,kBAAkB;CAiB7B"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RateLimiter = void 0;
4
+ class RateLimiter {
5
+ intervalMs;
6
+ queue = [];
7
+ lastRunAt = 0;
8
+ timer;
9
+ constructor(intervalMs) {
10
+ this.intervalMs = intervalMs;
11
+ }
12
+ schedule() {
13
+ return new Promise((resolve) => {
14
+ const elapsedSinceLastRun = Date.now() - this.lastRunAt;
15
+ if (!this.timer && elapsedSinceLastRun >= this.intervalMs) {
16
+ this.lastRunAt = Date.now();
17
+ resolve();
18
+ return;
19
+ }
20
+ this.queue.push(resolve);
21
+ this.ensureTimerRunning();
22
+ });
23
+ }
24
+ dispose() {
25
+ if (this.timer) {
26
+ clearInterval(this.timer);
27
+ this.timer = undefined;
28
+ }
29
+ this.queue.length = 0;
30
+ }
31
+ ensureTimerRunning() {
32
+ if (this.timer) {
33
+ return;
34
+ }
35
+ this.timer = setInterval(() => {
36
+ const next = this.queue.shift();
37
+ if (!next) {
38
+ clearInterval(this.timer);
39
+ this.timer = undefined;
40
+ return;
41
+ }
42
+ this.lastRunAt = Date.now();
43
+ next();
44
+ }, this.intervalMs);
45
+ }
46
+ }
47
+ exports.RateLimiter = RateLimiter;
48
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/transport/rate-limiter.ts"],"names":[],"mappings":";;;AAAA,MAAa,WAAW;IAKS;IAJZ,KAAK,GAAsB,EAAE,CAAC;IACvC,SAAS,GAAG,CAAC,CAAC;IACd,KAAK,CAA6B;IAE1C,YAA6B,UAAkB;QAAlB,eAAU,GAAV,UAAU,CAAQ;IAAG,CAAC;IAEnD,QAAQ;QACJ,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC3B,MAAM,mBAAmB,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YAExD,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,mBAAmB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACxD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC5B,OAAO,EAAE,CAAC;gBACV,OAAO;YACX,CAAC;YAED,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC9B,CAAC,CAAC,CAAC;IACP,CAAC;IAED,OAAO;QACH,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1B,CAAC;IAEO,kBAAkB;QACtB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,OAAO;QACX,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YAChC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACR,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC1B,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;gBACvB,OAAO;YACX,CAAC;YAED,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC5B,IAAI,EAAE,CAAC;QACX,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACxB,CAAC;CACJ;AA/CD,kCA+CC"}
@@ -0,0 +1,12 @@
1
+ export interface IReconnectBackoffOptions {
2
+ baseDelayMs: number;
3
+ maxDelayMs: number;
4
+ factor: number;
5
+ }
6
+ export declare const DEFAULT_RECONNECT_BACKOFF_OPTIONS: IReconnectBackoffOptions;
7
+ /**
8
+ * Equal jitter: half the capped exponential delay is fixed, half is randomized,
9
+ * so retries stay spread out under load instead of synchronizing into bursts.
10
+ */
11
+ export declare function calculateReconnectDelayMs(attempt: number, options?: IReconnectBackoffOptions): number;
12
+ //# sourceMappingURL=reconnect-backoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconnect-backoff.d.ts","sourceRoot":"","sources":["../../src/transport/reconnect-backoff.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,iCAAiC,EAAE,wBAI/C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,wBAA4D,GAAG,MAAM,CAMxI"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_RECONNECT_BACKOFF_OPTIONS = void 0;
4
+ exports.calculateReconnectDelayMs = calculateReconnectDelayMs;
5
+ exports.DEFAULT_RECONNECT_BACKOFF_OPTIONS = {
6
+ baseDelayMs: 500,
7
+ maxDelayMs: 30_000,
8
+ factor: 2
9
+ };
10
+ /**
11
+ * Equal jitter: half the capped exponential delay is fixed, half is randomized,
12
+ * so retries stay spread out under load instead of synchronizing into bursts.
13
+ */
14
+ function calculateReconnectDelayMs(attempt, options = exports.DEFAULT_RECONNECT_BACKOFF_OPTIONS) {
15
+ const exponentialDelay = options.baseDelayMs * options.factor ** attempt;
16
+ const cappedDelay = Math.min(exponentialDelay, options.maxDelayMs);
17
+ const guaranteedDelay = cappedDelay / 2;
18
+ return Math.round(guaranteedDelay + Math.random() * guaranteedDelay);
19
+ }
20
+ //# sourceMappingURL=reconnect-backoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconnect-backoff.js","sourceRoot":"","sources":["../../src/transport/reconnect-backoff.ts"],"names":[],"mappings":";;;AAgBA,8DAMC;AAhBY,QAAA,iCAAiC,GAA6B;IACvE,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,MAAM;IAClB,MAAM,EAAE,CAAC;CACZ,CAAC;AAEF;;;GAGG;AACH,SAAgB,yBAAyB,CAAC,OAAe,EAAE,UAAoC,yCAAiC;IAC5H,MAAM,gBAAgB,GAAG,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC;IACzE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACnE,MAAM,eAAe,GAAG,WAAW,GAAG,CAAC,CAAC;IAExC,OAAO,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,eAAe,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare enum SpotwareHost {
2
+ DEMO = "demo.ctraderapi.com",
3
+ LIVE = "live.ctraderapi.com"
4
+ }
5
+ export declare const SPOTWARE_PORT = 5035;
6
+ //# sourceMappingURL=spotware-host.enum.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spotware-host.enum.d.ts","sourceRoot":"","sources":["../../src/transport/spotware-host.enum.ts"],"names":[],"mappings":"AAAA,oBAAY,YAAY;IACpB,IAAI,wBAAwB;IAC5B,IAAI,wBAAwB;CAC/B;AAED,eAAO,MAAM,aAAa,OAAO,CAAC"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SPOTWARE_PORT = exports.SpotwareHost = void 0;
4
+ var SpotwareHost;
5
+ (function (SpotwareHost) {
6
+ SpotwareHost["DEMO"] = "demo.ctraderapi.com";
7
+ SpotwareHost["LIVE"] = "live.ctraderapi.com";
8
+ })(SpotwareHost || (exports.SpotwareHost = SpotwareHost = {}));
9
+ exports.SPOTWARE_PORT = 5035;
10
+ //# sourceMappingURL=spotware-host.enum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spotware-host.enum.js","sourceRoot":"","sources":["../../src/transport/spotware-host.enum.ts"],"names":[],"mappings":";;;AAAA,IAAY,YAGX;AAHD,WAAY,YAAY;IACpB,4CAA4B,CAAA;IAC5B,4CAA4B,CAAA;AAChC,CAAC,EAHW,YAAY,4BAAZ,YAAY,QAGvB;AAEY,QAAA,aAAa,GAAG,IAAI,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { ProtoOAPayloadType } from '../types';
2
+ export declare class SpotwareRateLimiter {
3
+ private readonly fivePerSecond;
4
+ private readonly fiftyPerSecond;
5
+ throttle(payloadType: ProtoOAPayloadType): Promise<void>;
6
+ dispose(): void;
7
+ }
8
+ //# sourceMappingURL=spotware-rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spotware-rate-limiter.d.ts","sourceRoot":"","sources":["../../src/transport/spotware-rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAe9C,qBAAa,mBAAmB;IAC5B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgD;IAC9E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiD;IAE1E,QAAQ,CAAC,WAAW,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9D,OAAO,IAAI,IAAI;CAIlB"}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SpotwareRateLimiter = void 0;
4
+ const types_1 = require("../types");
5
+ const rate_limiter_1 = require("./rate-limiter");
6
+ // Spotware limits these to 5/sec and everything else to 50/sec.
7
+ // Intervals are set just above the theoretical minimum to stay clear of the limit.
8
+ // https://connect.spotware.com/docs/frequently-asked-questions
9
+ const FIVE_PER_SECOND_INTERVAL_MS = 210;
10
+ const FIFTY_PER_SECOND_INTERVAL_MS = 21;
11
+ const FIVE_PER_SECOND_PAYLOAD_TYPES = new Set([
12
+ types_1.ProtoOAPayloadType.PROTO_OA_GET_TRENDBARS_REQ,
13
+ types_1.ProtoOAPayloadType.PROTO_OA_GET_TICKDATA_REQ,
14
+ types_1.ProtoOAPayloadType.PROTO_OA_DEAL_LIST_REQ
15
+ ]);
16
+ class SpotwareRateLimiter {
17
+ fivePerSecond = new rate_limiter_1.RateLimiter(FIVE_PER_SECOND_INTERVAL_MS);
18
+ fiftyPerSecond = new rate_limiter_1.RateLimiter(FIFTY_PER_SECOND_INTERVAL_MS);
19
+ async throttle(payloadType) {
20
+ const limiter = FIVE_PER_SECOND_PAYLOAD_TYPES.has(payloadType) ? this.fivePerSecond : this.fiftyPerSecond;
21
+ await limiter.schedule();
22
+ }
23
+ dispose() {
24
+ this.fivePerSecond.dispose();
25
+ this.fiftyPerSecond.dispose();
26
+ }
27
+ }
28
+ exports.SpotwareRateLimiter = SpotwareRateLimiter;
29
+ //# sourceMappingURL=spotware-rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spotware-rate-limiter.js","sourceRoot":"","sources":["../../src/transport/spotware-rate-limiter.ts"],"names":[],"mappings":";;;AAAA,oCAA8C;AAC9C,iDAA6C;AAE7C,gEAAgE;AAChE,mFAAmF;AACnF,+DAA+D;AAC/D,MAAM,2BAA2B,GAAG,GAAG,CAAC;AACxC,MAAM,4BAA4B,GAAG,EAAE,CAAC;AAExC,MAAM,6BAA6B,GAAG,IAAI,GAAG,CAAqB;IAC9D,0BAAkB,CAAC,0BAA0B;IAC7C,0BAAkB,CAAC,yBAAyB;IAC5C,0BAAkB,CAAC,sBAAsB;CAC5C,CAAC,CAAC;AAEH,MAAa,mBAAmB;IACX,aAAa,GAAG,IAAI,0BAAW,CAAC,2BAA2B,CAAC,CAAC;IAC7D,cAAc,GAAG,IAAI,0BAAW,CAAC,4BAA4B,CAAC,CAAC;IAEhF,KAAK,CAAC,QAAQ,CAAC,WAA+B;QAC1C,MAAM,OAAO,GAAG,6BAA6B,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC;QAC1G,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO;QACH,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;IAClC,CAAC;CACJ;AAbD,kDAaC"}
@@ -0,0 +1,63 @@
1
+ import type { Duplex } from 'node:stream';
2
+ import { TypedEventEmitter, type EventMap } from '../shared/typed-event-emitter';
3
+ import { ProtoMessage } from '../types';
4
+ import { type IReconnectBackoffOptions } from './reconnect-backoff';
5
+ import { SpotwareHost } from './spotware-host.enum';
6
+ export type SpotwareDisconnectReason = 'manual' | 'dropped';
7
+ /**
8
+ * Resolves once the connection is fully ready to use (e.g. after the TLS handshake).
9
+ * Lets tests substitute a plain local TCP server without touching any transport logic.
10
+ */
11
+ export type SpotwareSocketFactory = (port: number, host: string) => Promise<Duplex>;
12
+ export interface ISpotwareTransportEvents extends EventMap {
13
+ connected: [];
14
+ disconnected: [reason: SpotwareDisconnectReason];
15
+ reconnecting: [attempt: number, delayMs: number];
16
+ message: [message: ProtoMessage];
17
+ error: [error: Error];
18
+ }
19
+ export interface ISpotwareTransportOptions {
20
+ host: SpotwareHost;
21
+ port?: number;
22
+ reconnectBackoff?: IReconnectBackoffOptions;
23
+ socketFactory?: SpotwareSocketFactory;
24
+ staleConnectionTimeoutMs?: number;
25
+ }
26
+ /**
27
+ * Opens the TCP/TLS connection to a cTrader host, frames Protobuf messages on the wire,
28
+ * and auto-reconnects with backoff on any drop that wasn't requested via disconnect().
29
+ * Knows nothing about auth or trading: messages are passed through as the raw ProtoMessage
30
+ * envelope, undecoded, for the client module to interpret.
31
+ */
32
+ export declare class SpotwareTransport extends TypedEventEmitter<ISpotwareTransportEvents> {
33
+ private readonly host;
34
+ private readonly port;
35
+ private readonly reconnectBackoffOptions;
36
+ private readonly socketFactory;
37
+ private readonly staleConnectionTimeoutMs;
38
+ private readonly livenessCheckIntervalMs;
39
+ private readonly rateLimiter;
40
+ private socket;
41
+ private heartbeatTimer;
42
+ private livenessTimer;
43
+ private reconnectTimer;
44
+ private reconnectAttempt;
45
+ private disconnectRequested;
46
+ private lastMessageReceivedAt;
47
+ private hasConnectedOnce;
48
+ constructor(options: ISpotwareTransportOptions);
49
+ connect(): Promise<void>;
50
+ disconnect(): Promise<void>;
51
+ send(message: ProtoMessage): Promise<void>;
52
+ private attemptConnection;
53
+ private handleData;
54
+ private handleClose;
55
+ private scheduleReconnect;
56
+ private clearReconnectTimer;
57
+ private startHeartbeat;
58
+ private startLivenessWatchdog;
59
+ private stopLivenessWatchdog;
60
+ private stopHeartbeat;
61
+ private buildHeartbeatMessage;
62
+ }
63
+ //# sourceMappingURL=spotware-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spotware-transport.d.ts","sourceRoot":"","sources":["../../src/transport/spotware-transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACjF,OAAO,EAAuB,YAAY,EAAoB,MAAM,UAAU,CAAC;AAE/E,OAAO,EAAgE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAElI,OAAO,EAAiB,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAanE,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE5D;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAqBpF,MAAM,WAAW,wBAAyB,SAAQ,QAAQ;IACtD,SAAS,EAAE,EAAE,CAAC;IACd,YAAY,EAAE,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;IACjD,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACjD,OAAO,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACjC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACtC,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAC5C,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,iBAAiB,CAAC,wBAAwB,CAAC;IAC9E,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAA2B;IACnE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwB;IACtD,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAS;IAClD,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAS;IACjD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAEzD,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,qBAAqB,CAAK;IAKlC,OAAO,CAAC,gBAAgB,CAAS;gBAErB,OAAO,EAAE,yBAAyB;IAU9C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMlB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB3B,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;YAelC,iBAAiB;IA+B/B,OAAO,CAAC,UAAU;IAmBlB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,qBAAqB;CAMhC"}