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,56 @@
1
+ import { SpotwareOAuthClient, type ISpotwareOAuthToken } from '../auth';
2
+ import { TypedEventEmitter, type EventMap } from '../shared/typed-event-emitter';
3
+ import { ProtoMessage } from '../types';
4
+ import type { SpotwareTransport } from '../transport';
5
+ export interface ISpotwareClientOptions {
6
+ transport: SpotwareTransport;
7
+ oauthClient: SpotwareOAuthClient;
8
+ clientId: string;
9
+ clientSecret: string;
10
+ ctidTraderAccountId: number;
11
+ token: ISpotwareOAuthToken;
12
+ requestTimeoutMs?: number;
13
+ tokenRefreshBufferMs?: number;
14
+ }
15
+ export interface ISpotwareClientEvents extends EventMap {
16
+ authenticated: [];
17
+ tokenRefreshed: [token: ISpotwareOAuthToken];
18
+ message: [message: ProtoMessage];
19
+ error: [error: Error];
20
+ }
21
+ /**
22
+ * Request/response correlation on top of transport + auth: `send()` tags each request with a
23
+ * clientMsgId and resolves once the matching response arrives, refreshing the access token
24
+ * first if it's close to expiry. Re-runs the app/account auth handshake on every transport
25
+ * 'connected' event, including reconnects, since a fresh TCP connection has no socket-level
26
+ * auth state. Doesn't know what an order or a price is — every unsolicited message (spot
27
+ * events, etc.) is just forwarded as-is via the 'message' event for market-data/trading to
28
+ * interpret.
29
+ */
30
+ export declare class SpotwareClient extends TypedEventEmitter<ISpotwareClientEvents> {
31
+ private readonly transport;
32
+ private readonly oauthClient;
33
+ private readonly authenticator;
34
+ private readonly clientId;
35
+ private readonly clientSecret;
36
+ private readonly _ctidTraderAccountId;
37
+ private readonly requestTimeoutMs;
38
+ private readonly tokenRefreshBufferMs;
39
+ private readonly pendingRequests;
40
+ private token;
41
+ private tokenIssuedAt;
42
+ private tokenRefreshInFlight;
43
+ private authenticationInFlight;
44
+ constructor(options: ISpotwareClientOptions);
45
+ get ctidTraderAccountId(): number;
46
+ connect(): Promise<void>;
47
+ disconnect(): Promise<void>;
48
+ send(payloadType: number, payload: Uint8Array): Promise<ProtoMessage>;
49
+ private handleConnected;
50
+ private authenticate;
51
+ private handleMessage;
52
+ private rejectPendingRequests;
53
+ private ensureValidToken;
54
+ private refreshToken;
55
+ }
56
+ //# sourceMappingURL=spotware-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spotware-client.d.ts","sourceRoot":"","sources":["../../src/client/spotware-client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAA+B,KAAK,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACrG,OAAO,EAAE,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACjF,OAAO,EAAiB,YAAY,EAAiF,MAAM,UAAU,CAAC;AACtI,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAWtD,MAAM,WAAW,sBAAsB;IACnC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,WAAW,EAAE,mBAAmB,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,mBAAmB,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,qBAAsB,SAAQ,QAAQ;IACnD,aAAa,EAAE,EAAE,CAAC;IAClB,cAAc,EAAE,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;IAC7C,OAAO,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACjC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,qBAAa,cAAe,SAAQ,iBAAiB,CAAC,qBAAqB,CAAC;IACxE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAC5D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAE9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAsC;IACtE,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,oBAAoB,CAA4B;IACxD,OAAO,CAAC,sBAAsB,CAA4B;gBAE9C,OAAO,EAAE,sBAAsB;IAqB3C,IAAI,mBAAmB,IAAI,MAAM,CAEhC;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC;IA4C3E,OAAO,CAAC,eAAe;YAST,YAAY;IAO1B,OAAO,CAAC,aAAa;IAmCrB,OAAO,CAAC,qBAAqB;YAQf,gBAAgB;YAmBhB,YAAY;CAK7B"}
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SpotwareClient = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const auth_1 = require("../auth");
6
+ const typed_event_emitter_1 = require("../shared/typed-event-emitter");
7
+ const types_1 = require("../types");
8
+ const spotware_request_error_1 = require("./spotware-request-error");
9
+ const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;
10
+ const DEFAULT_TOKEN_REFRESH_BUFFER_MS = 5 * 60_000;
11
+ /**
12
+ * Request/response correlation on top of transport + auth: `send()` tags each request with a
13
+ * clientMsgId and resolves once the matching response arrives, refreshing the access token
14
+ * first if it's close to expiry. Re-runs the app/account auth handshake on every transport
15
+ * 'connected' event, including reconnects, since a fresh TCP connection has no socket-level
16
+ * auth state. Doesn't know what an order or a price is — every unsolicited message (spot
17
+ * events, etc.) is just forwarded as-is via the 'message' event for market-data/trading to
18
+ * interpret.
19
+ */
20
+ class SpotwareClient extends typed_event_emitter_1.TypedEventEmitter {
21
+ transport;
22
+ oauthClient;
23
+ authenticator;
24
+ clientId;
25
+ clientSecret;
26
+ _ctidTraderAccountId;
27
+ requestTimeoutMs;
28
+ tokenRefreshBufferMs;
29
+ pendingRequests = new Map();
30
+ token;
31
+ tokenIssuedAt = Date.now();
32
+ tokenRefreshInFlight;
33
+ authenticationInFlight;
34
+ constructor(options) {
35
+ super();
36
+ this.transport = options.transport;
37
+ this.oauthClient = options.oauthClient;
38
+ this.clientId = options.clientId;
39
+ this.clientSecret = options.clientSecret;
40
+ this._ctidTraderAccountId = options.ctidTraderAccountId;
41
+ this.token = options.token;
42
+ this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
43
+ this.tokenRefreshBufferMs = options.tokenRefreshBufferMs ?? DEFAULT_TOKEN_REFRESH_BUFFER_MS;
44
+ this.authenticator = new auth_1.SpotwareSocketAuthenticator(this.transport, { responseTimeoutMs: this.requestTimeoutMs });
45
+ this.transport.on('connected', () => this.handleConnected());
46
+ this.transport.on('message', (message) => this.handleMessage(message));
47
+ this.transport.on('error', (error) => this.emit('error', error));
48
+ this.transport.on('disconnected', (reason) => {
49
+ const message = reason === 'manual' ? 'The connection was closed' : 'The connection was lost';
50
+ this.rejectPendingRequests(new spotware_request_error_1.SpotwareRequestError(`${message} while waiting for a response`));
51
+ });
52
+ }
53
+ get ctidTraderAccountId() {
54
+ return this._ctidTraderAccountId;
55
+ }
56
+ async connect() {
57
+ await this.transport.connect();
58
+ await this.authenticationInFlight;
59
+ }
60
+ disconnect() {
61
+ return this.transport.disconnect();
62
+ }
63
+ async send(payloadType, payload) {
64
+ if (this.authenticationInFlight) {
65
+ await this.authenticationInFlight;
66
+ }
67
+ await this.ensureValidToken();
68
+ const clientMsgId = (0, node_crypto_1.randomUUID)();
69
+ return new Promise((resolve, reject) => {
70
+ let timeout;
71
+ const settle = (pending) => {
72
+ this.pendingRequests.set(clientMsgId, pending);
73
+ };
74
+ const cleanup = () => {
75
+ clearTimeout(timeout);
76
+ this.pendingRequests.delete(clientMsgId);
77
+ };
78
+ settle({
79
+ resolve: (message) => {
80
+ cleanup();
81
+ resolve(message);
82
+ },
83
+ reject: (error) => {
84
+ cleanup();
85
+ reject(error);
86
+ }
87
+ });
88
+ timeout = setTimeout(() => {
89
+ this.pendingRequests.get(clientMsgId)?.reject(new spotware_request_error_1.SpotwareRequestError(`Timed out waiting for a response to "${clientMsgId}"`));
90
+ }, this.requestTimeoutMs);
91
+ this.transport.send(types_1.ProtoMessage.fromPartial({ payloadType, payload, clientMsgId })).catch((error) => {
92
+ this.pendingRequests.get(clientMsgId)?.reject(error);
93
+ });
94
+ });
95
+ }
96
+ // Fires on the first connect and on every auto-reconnect: a new TCP connection has no
97
+ // socket-level auth state, so this re-establishes it every time, not just once.
98
+ handleConnected() {
99
+ // Left set after settling rather than cleared: awaiting an already-settled promise
100
+ // is a harmless no-op, and clearing it here would race connect()'s own await below
101
+ // on an unrealistically fast (but not impossible) authentication.
102
+ const attempt = this.authenticate();
103
+ this.authenticationInFlight = attempt;
104
+ attempt.catch((error) => this.emit('error', error));
105
+ }
106
+ async authenticate() {
107
+ await this.ensureValidToken();
108
+ await this.authenticator.authenticateApplication(this.clientId, this.clientSecret);
109
+ await this.authenticator.authenticateAccount(this._ctidTraderAccountId, this.token.accessToken);
110
+ this.emit('authenticated');
111
+ }
112
+ handleMessage(message) {
113
+ this.emit('message', message);
114
+ const pending = message.clientMsgId ? this.pendingRequests.get(message.clientMsgId) : undefined;
115
+ if (!pending) {
116
+ return;
117
+ }
118
+ if (message.payloadType === types_1.ProtoOAPayloadType.PROTO_OA_ERROR_RES) {
119
+ const error = types_1.ProtoOAErrorRes.decode(message.payload ?? new Uint8Array());
120
+ pending.reject(new spotware_request_error_1.SpotwareRequestError(error.description ?? error.errorCode, error.errorCode));
121
+ return;
122
+ }
123
+ if (message.payloadType === types_1.ProtoPayloadType.ERROR_RES) {
124
+ const error = types_1.ProtoErrorRes.decode(message.payload ?? new Uint8Array());
125
+ pending.reject(new spotware_request_error_1.SpotwareRequestError(error.description ?? error.errorCode, error.errorCode));
126
+ return;
127
+ }
128
+ // Order mutations (new/amend/cancel/close) have no dedicated _Res message: the outcome
129
+ // arrives as an ExecutionEvent (success) or this event (failure), both carrying the
130
+ // same clientMsgId. Without this, a rejected order would resolve as if it succeeded.
131
+ if (message.payloadType === types_1.ProtoOAPayloadType.PROTO_OA_ORDER_ERROR_EVENT) {
132
+ const error = types_1.ProtoOAOrderErrorEvent.decode(message.payload ?? new Uint8Array());
133
+ pending.reject(new spotware_request_error_1.SpotwareRequestError(error.description ?? error.errorCode, error.errorCode));
134
+ return;
135
+ }
136
+ pending.resolve(message);
137
+ }
138
+ // Called when the transport disconnects for any reason, so in-flight requests fail
139
+ // immediately with a clear cause instead of each waiting out its own full timeout for
140
+ // a response that can no longer arrive.
141
+ rejectPendingRequests(error) {
142
+ for (const pending of [...this.pendingRequests.values()]) {
143
+ pending.reject(error);
144
+ }
145
+ }
146
+ // Refresh tokens are single-use: concurrent callers must await the same in-flight
147
+ // refresh rather than each requesting their own, which would invalidate the others.
148
+ async ensureValidToken() {
149
+ if (this.tokenRefreshInFlight) {
150
+ await this.tokenRefreshInFlight;
151
+ return;
152
+ }
153
+ const expiresAt = this.tokenIssuedAt + this.token.expiresIn * 1000;
154
+ if (Date.now() < expiresAt - this.tokenRefreshBufferMs) {
155
+ return;
156
+ }
157
+ this.tokenRefreshInFlight = this.refreshToken();
158
+ try {
159
+ await this.tokenRefreshInFlight;
160
+ }
161
+ finally {
162
+ this.tokenRefreshInFlight = undefined;
163
+ }
164
+ }
165
+ async refreshToken() {
166
+ this.token = await this.oauthClient.refreshAccessToken({ refreshToken: this.token.refreshToken });
167
+ this.tokenIssuedAt = Date.now();
168
+ this.emit('tokenRefreshed', this.token);
169
+ }
170
+ }
171
+ exports.SpotwareClient = SpotwareClient;
172
+ //# sourceMappingURL=spotware-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spotware-client.js","sourceRoot":"","sources":["../../src/client/spotware-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare class SpotwareRequestError extends Error {
2
+ readonly errorCode?: string | undefined;
3
+ constructor(message: string, errorCode?: string | undefined);
4
+ }
5
+ //# sourceMappingURL=spotware-request-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spotware-request-error.d.ts","sourceRoot":"","sources":["../../src/client/spotware-request-error.ts"],"names":[],"mappings":"AAAA,qBAAa,oBAAqB,SAAQ,KAAK;aAGvB,SAAS,CAAC,EAAE,MAAM;gBADlC,OAAO,EAAE,MAAM,EACC,SAAS,CAAC,EAAE,MAAM,YAAA;CAKzC"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SpotwareRequestError = void 0;
4
+ class SpotwareRequestError extends Error {
5
+ errorCode;
6
+ constructor(message, errorCode) {
7
+ super(message);
8
+ this.errorCode = errorCode;
9
+ this.name = 'SpotwareRequestError';
10
+ }
11
+ }
12
+ exports.SpotwareRequestError = SpotwareRequestError;
13
+ //# sourceMappingURL=spotware-request-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spotware-request-error.js","sourceRoot":"","sources":["../../src/client/spotware-request-error.ts"],"names":[],"mappings":";;;AAAA,MAAa,oBAAqB,SAAQ,KAAK;IAGvB;IAFpB,YACI,OAAe,EACC,SAAkB;QAElC,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,cAAS,GAAT,SAAS,CAAS;QAGlC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACvC,CAAC;CACJ;AARD,oDAQC"}
@@ -0,0 +1,2 @@
1
+ import 'dotenv/config';
2
+ //# sourceMappingURL=example.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example.d.ts","sourceRoot":"","sources":["../../src/example/example.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC"}
@@ -0,0 +1,187 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ require("dotenv/config");
37
+ const fs = __importStar(require("node:fs/promises"));
38
+ const http = __importStar(require("node:http"));
39
+ const path = __importStar(require("node:path"));
40
+ const auth_1 = require("../auth");
41
+ const client_1 = require("../client");
42
+ const transport_1 = require("../transport");
43
+ const types_1 = require("../types");
44
+ const TOKEN_FILE_PATH = path.resolve(__dirname, '../../.spotware-token.json');
45
+ const REDIRECT_PORT = Number(process.env.SPOTWARE__OAUTH_REDIRECT_PORT ?? 3939);
46
+ const REDIRECT_URI = `http://localhost:${REDIRECT_PORT}/callback`;
47
+ function requireEnv(name) {
48
+ const value = process.env[name];
49
+ if (!value) {
50
+ throw new Error(`Missing required environment variable "${name}"`);
51
+ }
52
+ return value;
53
+ }
54
+ async function readStoredToken() {
55
+ try {
56
+ return JSON.parse(await fs.readFile(TOKEN_FILE_PATH, 'utf8'));
57
+ }
58
+ catch {
59
+ return undefined;
60
+ }
61
+ }
62
+ async function writeStoredToken(token) {
63
+ await fs.writeFile(TOKEN_FILE_PATH, JSON.stringify(token, null, 4), 'utf8');
64
+ }
65
+ // The authorization code only lives for ~1 minute, so this listener must already be
66
+ // up before the user is sent to the browser, not started after.
67
+ function waitForAuthorizationCode(port) {
68
+ return new Promise((resolve, reject) => {
69
+ const server = http.createServer((request, response) => {
70
+ const url = new URL(request.url ?? '/', `http://localhost:${port}`);
71
+ const code = url.searchParams.get('code');
72
+ const errorParam = url.searchParams.get('error');
73
+ response.writeHead(200, { 'Content-Type': 'text/html' });
74
+ response.end(errorParam
75
+ ? '<p>Authorization failed. You can close this tab.</p>'
76
+ : '<p>Authorized. You can close this tab.</p>');
77
+ server.close();
78
+ if (errorParam) {
79
+ reject(new Error(`Authorization was denied: ${errorParam}`));
80
+ }
81
+ else if (code) {
82
+ resolve(code);
83
+ }
84
+ else {
85
+ reject(new Error('No authorization code was returned'));
86
+ }
87
+ });
88
+ server.listen(port);
89
+ });
90
+ }
91
+ async function obtainToken(oauthClient) {
92
+ const stored = await readStoredToken();
93
+ if (stored) {
94
+ console.log('Found a stored token, refreshing it (refresh tokens are single-use, so the file gets rewritten)...');
95
+ const refreshed = await oauthClient.refreshAccessToken({
96
+ refreshToken: stored.refreshToken,
97
+ });
98
+ await writeStoredToken(refreshed);
99
+ return refreshed;
100
+ }
101
+ const authorizationUrl = oauthClient.buildAuthorizationUrl({
102
+ redirectUri: REDIRECT_URI,
103
+ scope: auth_1.SpotwareOAuthScope.TRADING,
104
+ });
105
+ console.log('No stored token found. Open this URL in a browser to authorize the app:\n');
106
+ console.log(authorizationUrl);
107
+ console.log(`\nWaiting for the redirect on ${REDIRECT_URI} ...`);
108
+ console.log(`(Make sure "${REDIRECT_URI}" is registered as a redirect URI on this app in the cTrader Open API portal.)`);
109
+ const code = await waitForAuthorizationCode(REDIRECT_PORT);
110
+ const token = await oauthClient.exchangeAuthorizationCode({
111
+ code,
112
+ redirectUri: REDIRECT_URI,
113
+ });
114
+ await writeStoredToken(token);
115
+ return token;
116
+ }
117
+ // Runs on a short-lived transport just to discover which demo account is linked to this
118
+ // token — SpotwareClient itself needs a ctidTraderAccountId up front, so this has to happen
119
+ // before it can be constructed.
120
+ async function discoverDemoAccountId(clientId, clientSecret, accessToken) {
121
+ const transport = new transport_1.SpotwareTransport({ host: transport_1.SpotwareHost.DEMO });
122
+ transport.on('error', (error) => console.error('Discovery transport error:', error.message));
123
+ await transport.connect();
124
+ const authenticator = new auth_1.SpotwareSocketAuthenticator(transport);
125
+ await authenticator.authenticateApplication(clientId, clientSecret);
126
+ console.log('Listing accounts linked to this token...');
127
+ const accounts = await authenticator.listAccounts(accessToken);
128
+ console.table(accounts.map((account) => ({
129
+ ctidTraderAccountId: account.ctidTraderAccountId,
130
+ isLive: account.isLive ?? false,
131
+ traderLogin: account.traderLogin,
132
+ })));
133
+ const demoAccount = accounts.find((account) => !account.isLive);
134
+ if (!demoAccount) {
135
+ throw new Error('No demo account is linked to this token. Link a demo account via cTrader before running this script.');
136
+ }
137
+ await transport.disconnect();
138
+ return demoAccount.ctidTraderAccountId;
139
+ }
140
+ async function main() {
141
+ const clientId = requireEnv('SPOTWARE__CLIENT_ID');
142
+ const clientSecret = requireEnv('SPOTWARE__CLIENT_SECRET');
143
+ const oauthClient = new auth_1.SpotwareOAuthClient({ clientId, clientSecret });
144
+ const token = await obtainToken(oauthClient);
145
+ const ctidTraderAccountId = await discoverDemoAccountId(clientId, clientSecret, token.accessToken);
146
+ console.log(`\nConnecting SpotwareClient for demo account ${ctidTraderAccountId}...`);
147
+ const transport = new transport_1.SpotwareTransport({ host: transport_1.SpotwareHost.DEMO });
148
+ const client = new client_1.SpotwareClient({
149
+ transport,
150
+ oauthClient,
151
+ clientId,
152
+ clientSecret,
153
+ ctidTraderAccountId,
154
+ token,
155
+ });
156
+ transport.on('reconnecting', (attempt, delayMs) => console.log(`Reconnecting (attempt ${attempt}) in ${delayMs}ms...`));
157
+ client.on('authenticated', () => console.log('Authenticated (application + account).'));
158
+ client.on('tokenRefreshed', (refreshed) => {
159
+ console.log('Access token refreshed.');
160
+ void writeStoredToken(refreshed);
161
+ });
162
+ client.on('error', (error) => console.error('Client error:', error.message));
163
+ await client.connect();
164
+ console.log('\nSending PROTO_OA_VERSION_REQ through SpotwareClient to confirm request/response correlation works end to end...');
165
+ const response = await client.send(types_1.ProtoOAPayloadType.PROTO_OA_VERSION_REQ, types_1.ProtoOAVersionReq.encode(types_1.ProtoOAVersionReq.fromPartial({})).finish());
166
+ const version = types_1.ProtoOAVersionRes.decode(response.payload ?? new Uint8Array());
167
+ console.log(`Server version: ${version.version}`);
168
+ // This intentionally runs indefinitely so that the user can test the reconnection
169
+ // mechanism, by switching the wifi on/off, and wait for the reconnection logs
170
+ // to appear before turning it back on.
171
+ setInterval(async () => {
172
+ console.log('*'.repeat(50));
173
+ try {
174
+ const response = await client.send(types_1.ProtoOAPayloadType.PROTO_OA_VERSION_REQ, types_1.ProtoOAVersionReq.encode(types_1.ProtoOAVersionReq.fromPartial({})).finish());
175
+ const version = types_1.ProtoOAVersionRes.decode(response.payload ?? new Uint8Array());
176
+ console.log(`Server version: ${version.version}`);
177
+ }
178
+ catch (error) {
179
+ console.log(`Error getting server version: ${error}`);
180
+ }
181
+ }, 1000 * 5);
182
+ }
183
+ main().catch((error) => {
184
+ console.error(error);
185
+ process.exit(1);
186
+ });
187
+ //# sourceMappingURL=example.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example.js","sourceRoot":"","sources":["../../src/example/example.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=market-data-example.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"market-data-example.d.ts","sourceRoot":"","sources":["../../src/example/market-data-example.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const market_data_1 = require("../market-data");
4
+ const create_authenticated_client_1 = require("./shared/create-authenticated-client");
5
+ const SYMBOL_NAME = process.argv[2] ?? 'EURUSD';
6
+ const LISTEN_DURATION_MS = 60_000;
7
+ async function main() {
8
+ const client = await (0, create_authenticated_client_1.createAuthenticatedClient)();
9
+ const marketData = new market_data_1.SpotwareMarketData(client);
10
+ marketData.on('error', (error) => console.error('Market data error:', error.message));
11
+ marketData.on('price', (update) => {
12
+ const bid = update.bid?.toFixed(5) ?? '-';
13
+ const ask = update.ask?.toFixed(5) ?? '-';
14
+ console.log(`[${update.symbolId}] bid=${bid} ask=${ask}`);
15
+ });
16
+ const symbol = await marketData.symbols.findByName(SYMBOL_NAME);
17
+ if (!symbol) {
18
+ throw new Error(`Symbol "${SYMBOL_NAME}" was not found for this account`);
19
+ }
20
+ console.log(`\nSubscribing to ${symbol.symbolName} (symbolId ${symbol.symbolId})...`);
21
+ await marketData.subscribe(symbol.symbolId);
22
+ console.log(`Listening for price updates for ${LISTEN_DURATION_MS / 1000}s (Ctrl+C to stop earlier)...\n`);
23
+ await new Promise((resolve) => setTimeout(resolve, LISTEN_DURATION_MS));
24
+ console.log(`\nUnsubscribing and disconnecting...`);
25
+ await marketData.unsubscribe(symbol.symbolId);
26
+ await client.disconnect();
27
+ }
28
+ main().catch((error) => {
29
+ console.error(error);
30
+ process.exit(1);
31
+ });
32
+ //# sourceMappingURL=market-data-example.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"market-data-example.js","sourceRoot":"","sources":["../../src/example/market-data-example.ts"],"names":[],"mappings":";;AAAA,gDAAoD;AACpD,sFAAiF;AAEjF,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC;AAChD,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,KAAK,UAAU,IAAI;IACf,MAAM,MAAM,GAAG,MAAM,IAAA,uDAAyB,GAAE,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,gCAAkB,CAAC,MAAM,CAAC,CAAC;IAClD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IACtF,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE;QAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;QAC1C,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,QAAQ,SAAS,GAAG,QAAQ,GAAG,EAAE,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAChE,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,WAAW,WAAW,kCAAkC,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,UAAU,cAAc,MAAM,CAAC,QAAQ,MAAM,CAAC,CAAC;IACtF,MAAM,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE5C,OAAO,CAAC,GAAG,CAAC,mCAAmC,kBAAkB,GAAG,IAAI,iCAAiC,CAAC,CAAC;IAC3G,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAExE,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;IACpD,MAAM,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;AAC9B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAY,EAAE,EAAE;IAC1B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import 'dotenv/config';
2
+ import { SpotwareClient } from '../../client';
3
+ /**
4
+ * Runs the full one-time setup shared by every example script: OAuth (browser flow or cached
5
+ * token refresh), demo account discovery, and a connected, authenticated SpotwareClient. Each
6
+ * example script can then focus purely on the module it's demonstrating.
7
+ */
8
+ export declare function createAuthenticatedClient(): Promise<SpotwareClient>;
9
+ //# sourceMappingURL=create-authenticated-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-authenticated-client.d.ts","sourceRoot":"","sources":["../../../src/example/shared/create-authenticated-client.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AAMvB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AA4G9C;;;;GAIG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,cAAc,CAAC,CAuBzE"}