@lightconexyz/lightcone-sdk 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 (158) hide show
  1. package/README.md +232 -0
  2. package/dist/api/client.d.ts +225 -0
  3. package/dist/api/client.d.ts.map +1 -0
  4. package/dist/api/client.js +452 -0
  5. package/dist/api/client.js.map +1 -0
  6. package/dist/api/error.d.ts +58 -0
  7. package/dist/api/error.d.ts.map +1 -0
  8. package/dist/api/error.js +98 -0
  9. package/dist/api/error.js.map +1 -0
  10. package/dist/api/index.d.ts +23 -0
  11. package/dist/api/index.d.ts.map +1 -0
  12. package/dist/api/index.js +51 -0
  13. package/dist/api/index.js.map +1 -0
  14. package/dist/api/types/admin.d.ts +49 -0
  15. package/dist/api/types/admin.d.ts.map +1 -0
  16. package/dist/api/types/admin.js +13 -0
  17. package/dist/api/types/admin.js.map +1 -0
  18. package/dist/api/types/index.d.ts +14 -0
  19. package/dist/api/types/index.d.ts.map +1 -0
  20. package/dist/api/types/index.js +13 -0
  21. package/dist/api/types/index.js.map +1 -0
  22. package/dist/api/types/market.d.ts +186 -0
  23. package/dist/api/types/market.d.ts.map +1 -0
  24. package/dist/api/types/market.js +6 -0
  25. package/dist/api/types/market.js.map +1 -0
  26. package/dist/api/types/order.d.ts +190 -0
  27. package/dist/api/types/order.d.ts.map +1 -0
  28. package/dist/api/types/order.js +6 -0
  29. package/dist/api/types/order.js.map +1 -0
  30. package/dist/api/types/orderbook.d.ts +36 -0
  31. package/dist/api/types/orderbook.d.ts.map +1 -0
  32. package/dist/api/types/orderbook.js +6 -0
  33. package/dist/api/types/orderbook.js.map +1 -0
  34. package/dist/api/types/position.d.ts +60 -0
  35. package/dist/api/types/position.d.ts.map +1 -0
  36. package/dist/api/types/position.js +6 -0
  37. package/dist/api/types/position.js.map +1 -0
  38. package/dist/api/types/price_history.d.ts +68 -0
  39. package/dist/api/types/price_history.d.ts.map +1 -0
  40. package/dist/api/types/price_history.js +13 -0
  41. package/dist/api/types/price_history.js.map +1 -0
  42. package/dist/api/types/trade.d.ts +67 -0
  43. package/dist/api/types/trade.d.ts.map +1 -0
  44. package/dist/api/types/trade.js +13 -0
  45. package/dist/api/types/trade.js.map +1 -0
  46. package/dist/api/validation.d.ts +24 -0
  47. package/dist/api/validation.d.ts.map +1 -0
  48. package/dist/api/validation.js +53 -0
  49. package/dist/api/validation.js.map +1 -0
  50. package/dist/auth.d.ts +80 -0
  51. package/dist/auth.d.ts.map +1 -0
  52. package/dist/auth.js +149 -0
  53. package/dist/auth.js.map +1 -0
  54. package/dist/index.d.ts +55 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +107 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/network.d.ts +5 -0
  59. package/dist/network.d.ts.map +1 -0
  60. package/dist/network.js +8 -0
  61. package/dist/network.js.map +1 -0
  62. package/dist/program/accounts.d.ts +98 -0
  63. package/dist/program/accounts.d.ts.map +1 -0
  64. package/dist/program/accounts.js +319 -0
  65. package/dist/program/accounts.js.map +1 -0
  66. package/dist/program/builder.d.ts +94 -0
  67. package/dist/program/builder.d.ts.map +1 -0
  68. package/dist/program/builder.js +175 -0
  69. package/dist/program/builder.js.map +1 -0
  70. package/dist/program/client.d.ts +56 -0
  71. package/dist/program/client.d.ts.map +1 -0
  72. package/dist/program/client.js +288 -0
  73. package/dist/program/client.js.map +1 -0
  74. package/dist/program/constants.d.ts +108 -0
  75. package/dist/program/constants.d.ts.map +1 -0
  76. package/dist/program/constants.js +112 -0
  77. package/dist/program/constants.js.map +1 -0
  78. package/dist/program/index.d.ts +14 -0
  79. package/dist/program/index.d.ts.map +1 -0
  80. package/dist/program/index.js +149 -0
  81. package/dist/program/index.js.map +1 -0
  82. package/dist/program/instructions.d.ts +248 -0
  83. package/dist/program/instructions.d.ts.map +1 -0
  84. package/dist/program/instructions.js +692 -0
  85. package/dist/program/instructions.js.map +1 -0
  86. package/dist/program/orders.d.ts +151 -0
  87. package/dist/program/orders.d.ts.map +1 -0
  88. package/dist/program/orders.js +417 -0
  89. package/dist/program/orders.js.map +1 -0
  90. package/dist/program/pda.d.ts +73 -0
  91. package/dist/program/pda.d.ts.map +1 -0
  92. package/dist/program/pda.js +131 -0
  93. package/dist/program/pda.js.map +1 -0
  94. package/dist/program/types.d.ts +380 -0
  95. package/dist/program/types.d.ts.map +1 -0
  96. package/dist/program/types.js +27 -0
  97. package/dist/program/types.js.map +1 -0
  98. package/dist/program/utils.d.ts +91 -0
  99. package/dist/program/utils.d.ts.map +1 -0
  100. package/dist/program/utils.js +219 -0
  101. package/dist/program/utils.js.map +1 -0
  102. package/dist/shared/index.d.ts +8 -0
  103. package/dist/shared/index.d.ts.map +1 -0
  104. package/dist/shared/index.js +18 -0
  105. package/dist/shared/index.js.map +1 -0
  106. package/dist/shared/price.d.ts +41 -0
  107. package/dist/shared/price.d.ts.map +1 -0
  108. package/dist/shared/price.js +57 -0
  109. package/dist/shared/price.js.map +1 -0
  110. package/dist/shared/scaling.d.ts +45 -0
  111. package/dist/shared/scaling.d.ts.map +1 -0
  112. package/dist/shared/scaling.js +84 -0
  113. package/dist/shared/scaling.js.map +1 -0
  114. package/dist/shared/types.d.ts +19 -0
  115. package/dist/shared/types.d.ts.map +1 -0
  116. package/dist/shared/types.js +23 -0
  117. package/dist/shared/types.js.map +1 -0
  118. package/dist/websocket/client.d.ts +238 -0
  119. package/dist/websocket/client.d.ts.map +1 -0
  120. package/dist/websocket/client.js +580 -0
  121. package/dist/websocket/client.js.map +1 -0
  122. package/dist/websocket/error.d.ts +47 -0
  123. package/dist/websocket/error.d.ts.map +1 -0
  124. package/dist/websocket/error.js +83 -0
  125. package/dist/websocket/error.js.map +1 -0
  126. package/dist/websocket/handlers.d.ts +97 -0
  127. package/dist/websocket/handlers.d.ts.map +1 -0
  128. package/dist/websocket/handlers.js +277 -0
  129. package/dist/websocket/handlers.js.map +1 -0
  130. package/dist/websocket/index.d.ts +38 -0
  131. package/dist/websocket/index.d.ts.map +1 -0
  132. package/dist/websocket/index.js +75 -0
  133. package/dist/websocket/index.js.map +1 -0
  134. package/dist/websocket/state/index.d.ts +7 -0
  135. package/dist/websocket/state/index.d.ts.map +1 -0
  136. package/dist/websocket/state/index.js +14 -0
  137. package/dist/websocket/state/index.js.map +1 -0
  138. package/dist/websocket/state/orderbook.d.ts +107 -0
  139. package/dist/websocket/state/orderbook.d.ts.map +1 -0
  140. package/dist/websocket/state/orderbook.js +293 -0
  141. package/dist/websocket/state/orderbook.js.map +1 -0
  142. package/dist/websocket/state/price.d.ts +108 -0
  143. package/dist/websocket/state/price.d.ts.map +1 -0
  144. package/dist/websocket/state/price.js +243 -0
  145. package/dist/websocket/state/price.js.map +1 -0
  146. package/dist/websocket/state/user.d.ts +83 -0
  147. package/dist/websocket/state/user.d.ts.map +1 -0
  148. package/dist/websocket/state/user.js +228 -0
  149. package/dist/websocket/state/user.js.map +1 -0
  150. package/dist/websocket/subscriptions.d.ts +143 -0
  151. package/dist/websocket/subscriptions.d.ts.map +1 -0
  152. package/dist/websocket/subscriptions.js +244 -0
  153. package/dist/websocket/subscriptions.js.map +1 -0
  154. package/dist/websocket/types.d.ts +417 -0
  155. package/dist/websocket/types.d.ts.map +1 -0
  156. package/dist/websocket/types.js +195 -0
  157. package/dist/websocket/types.js.map +1 -0
  158. package/package.json +75 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"price.d.ts","sourceRoot":"","sources":["../../../src/websocket/state/price.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGzD;;GAEG;AACH,qBAAa,eAAe;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAEhB,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;IAKnD,QAAQ,IAAI,MAAM;IAIlB,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO;CAMxC;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,4CAA4C;IAC5C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAQ;IAE3C,2BAA2B;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2CAA2C;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,qCAAqC;IACrC,YAAY,EAAE,OAAO,CAAC;IACtB,iDAAiD;IACjD,OAAO,CAAC,QAAQ,CAAgB;IAChC,yCAAyC;IACzC,OAAO,CAAC,WAAW,CAAkC;IACrD,4BAA4B;IAC5B,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,oCAAoC;IACpC,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,iDAAiD;IACjD,OAAO,CAAC,YAAY,CAAkB;gBAE1B,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO;IAM1E;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAsB3C;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAOzC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAmC5B;;OAEG;IACH,cAAc,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAI5C;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAgBxC;;OAEG;IACH,OAAO,IAAI,MAAM,EAAE;IAInB;;OAEG;IACH,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAIlC;;OAEG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMhD;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,SAAS;IAIlC;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,SAAS;IAIlC;;OAEG;IACH,eAAe,IAAI,MAAM,GAAG,SAAS;IAIrC;;OAEG;IACH,cAAc,IAAI,MAAM,GAAG,SAAS;IAIpC;;OAEG;IACH,cAAc,IAAI,MAAM,GAAG,SAAS;IAIpC,wBAAwB;IACxB,WAAW,IAAI,MAAM;IAIrB,kEAAkE;IAClE,WAAW,IAAI,OAAO;IAItB,4BAA4B;IAC5B,aAAa,IAAI,MAAM,GAAG,SAAS;IAInC,oCAAoC;IACpC,UAAU,IAAI,MAAM,GAAG,SAAS;IAIhC;;OAEG;IACH,cAAc,IAAI,UAAU,GAAG,SAAS;IAmBxC,sDAAsD;IACtD,KAAK,IAAI,IAAI;CAOd"}
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ /**
3
+ * Price history state management.
4
+ *
5
+ * Maintains local state for price history candles.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.PriceHistory = exports.PriceHistoryKey = void 0;
9
+ const shared_1 = require("../../shared");
10
+ const types_1 = require("../types");
11
+ /**
12
+ * Key for price history subscriptions.
13
+ */
14
+ class PriceHistoryKey {
15
+ orderbookId;
16
+ resolution;
17
+ constructor(orderbookId, resolution) {
18
+ this.orderbookId = orderbookId;
19
+ this.resolution = resolution;
20
+ }
21
+ toString() {
22
+ return `${this.orderbookId}:${this.resolution}`;
23
+ }
24
+ equals(other) {
25
+ return (this.orderbookId === other.orderbookId &&
26
+ this.resolution === other.resolution);
27
+ }
28
+ }
29
+ exports.PriceHistoryKey = PriceHistoryKey;
30
+ /**
31
+ * Price history state for a single orderbook/resolution pair.
32
+ */
33
+ class PriceHistory {
34
+ /** Maximum number of candles to maintain */
35
+ static MAX_CANDLES = 1000;
36
+ /** Orderbook identifier */
37
+ orderbookId;
38
+ /** Resolution (1m, 5m, 15m, 1h, 4h, 1d) */
39
+ resolution;
40
+ /** Whether OHLCV data is included */
41
+ includeOhlcv;
42
+ /** Candles sorted by timestamp (newest first) */
43
+ _candles = [];
44
+ /** Index by timestamp for fast lookup */
45
+ candleIndex = new Map();
46
+ /** Last server timestamp */
47
+ _lastTimestamp;
48
+ /** Server time from last message */
49
+ _serverTime;
50
+ /** Whether initial snapshot has been received */
51
+ _hasSnapshot = false;
52
+ constructor(orderbookId, resolution, includeOhlcv) {
53
+ this.orderbookId = orderbookId;
54
+ this.resolution = resolution;
55
+ this.includeOhlcv = includeOhlcv;
56
+ }
57
+ /**
58
+ * Apply a snapshot (historical candles).
59
+ */
60
+ applySnapshot(data) {
61
+ // Clear existing state
62
+ this._candles = [];
63
+ this.candleIndex.clear();
64
+ // Apply candles (they come newest-first from server)
65
+ for (const candle of data.prices) {
66
+ const idx = this._candles.length;
67
+ this.candleIndex.set(candle.t, idx);
68
+ this._candles.push(candle);
69
+ }
70
+ this._lastTimestamp = data.last_timestamp;
71
+ this._serverTime = data.server_time;
72
+ this._hasSnapshot = true;
73
+ // Update include_ohlcv if provided
74
+ if (data.include_ohlcv !== undefined) {
75
+ this.includeOhlcv = data.include_ohlcv;
76
+ }
77
+ }
78
+ /**
79
+ * Apply an update (new or updated candle).
80
+ */
81
+ applyUpdate(data) {
82
+ const candle = (0, types_1.toCandle)(data);
83
+ if (candle) {
84
+ this.updateOrAppendCandle(candle);
85
+ }
86
+ }
87
+ /**
88
+ * Update an existing candle or append a new one.
89
+ */
90
+ updateOrAppendCandle(candle) {
91
+ const existingIdx = this.candleIndex.get(candle.t);
92
+ if (existingIdx !== undefined) {
93
+ // Update existing candle
94
+ this._candles[existingIdx] = candle;
95
+ }
96
+ else {
97
+ // New candle - insert at the correct position (newest first)
98
+ const insertPos = this._candles.findIndex((c) => c.t < candle.t);
99
+ const pos = insertPos === -1 ? this._candles.length : insertPos;
100
+ // Update indices for candles that will shift
101
+ for (const [ts, idx] of this.candleIndex.entries()) {
102
+ if (idx >= pos) {
103
+ this.candleIndex.set(ts, idx + 1);
104
+ }
105
+ }
106
+ this.candleIndex.set(candle.t, pos);
107
+ this._candles.splice(pos, 0, candle);
108
+ // Trim to max candles limit
109
+ while (this._candles.length > PriceHistory.MAX_CANDLES) {
110
+ const removed = this._candles.pop();
111
+ if (removed) {
112
+ this.candleIndex.delete(removed.t);
113
+ }
114
+ }
115
+ }
116
+ // Update last timestamp
117
+ if (this._candles.length > 0) {
118
+ this._lastTimestamp = this._candles[0].t;
119
+ }
120
+ }
121
+ /**
122
+ * Handle heartbeat (update server time).
123
+ */
124
+ applyHeartbeat(data) {
125
+ this._serverTime = data.server_time;
126
+ }
127
+ /**
128
+ * Apply any price history event.
129
+ */
130
+ applyEvent(data) {
131
+ switch (data.event_type) {
132
+ case "snapshot":
133
+ this.applySnapshot(data);
134
+ break;
135
+ case "update":
136
+ this.applyUpdate(data);
137
+ break;
138
+ case "heartbeat":
139
+ this.applyHeartbeat(data);
140
+ break;
141
+ default:
142
+ console.warn(`Unknown price history event type: ${data.event_type}`);
143
+ }
144
+ }
145
+ /**
146
+ * Get all candles (newest first).
147
+ */
148
+ candles() {
149
+ return this._candles;
150
+ }
151
+ /**
152
+ * Get the N most recent candles.
153
+ */
154
+ recentCandles(n) {
155
+ return this._candles.slice(0, Math.min(n, this._candles.length));
156
+ }
157
+ /**
158
+ * Get a candle by timestamp.
159
+ */
160
+ getCandle(timestamp) {
161
+ const idx = this.candleIndex.get(timestamp);
162
+ if (idx === undefined)
163
+ return undefined;
164
+ return this._candles[idx];
165
+ }
166
+ /**
167
+ * Get the most recent candle.
168
+ */
169
+ latestCandle() {
170
+ return this._candles[0];
171
+ }
172
+ /**
173
+ * Get the oldest candle.
174
+ */
175
+ oldestCandle() {
176
+ return this._candles[this._candles.length - 1];
177
+ }
178
+ /**
179
+ * Get current midpoint price (from most recent candle).
180
+ */
181
+ currentMidpoint() {
182
+ return this._candles[0]?.m;
183
+ }
184
+ /**
185
+ * Get current best bid (from most recent candle).
186
+ */
187
+ currentBestBid() {
188
+ return this._candles[0]?.bb;
189
+ }
190
+ /**
191
+ * Get current best ask (from most recent candle).
192
+ */
193
+ currentBestAsk() {
194
+ return this._candles[0]?.ba;
195
+ }
196
+ /** Number of candles */
197
+ candleCount() {
198
+ return this._candles.length;
199
+ }
200
+ /** Whether the price history has received its initial snapshot */
201
+ hasSnapshot() {
202
+ return this._hasSnapshot;
203
+ }
204
+ /** Last candle timestamp */
205
+ lastTimestamp() {
206
+ return this._lastTimestamp;
207
+ }
208
+ /** Server time from last message */
209
+ serverTime() {
210
+ return this._serverTime;
211
+ }
212
+ /**
213
+ * Get resolution as enum.
214
+ */
215
+ resolutionEnum() {
216
+ switch (this.resolution) {
217
+ case "1m":
218
+ return shared_1.Resolution.OneMinute;
219
+ case "5m":
220
+ return shared_1.Resolution.FiveMinutes;
221
+ case "15m":
222
+ return shared_1.Resolution.FifteenMinutes;
223
+ case "1h":
224
+ return shared_1.Resolution.OneHour;
225
+ case "4h":
226
+ return shared_1.Resolution.FourHours;
227
+ case "1d":
228
+ return shared_1.Resolution.OneDay;
229
+ default:
230
+ return undefined;
231
+ }
232
+ }
233
+ /** Clear the price history (for disconnect/resync) */
234
+ clear() {
235
+ this._candles = [];
236
+ this.candleIndex.clear();
237
+ this._lastTimestamp = undefined;
238
+ this._serverTime = undefined;
239
+ this._hasSnapshot = false;
240
+ }
241
+ }
242
+ exports.PriceHistory = PriceHistory;
243
+ //# sourceMappingURL=price.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"price.js","sourceRoot":"","sources":["../../../src/websocket/state/price.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Price history state management.\n *\n * Maintains local state for price history candles.\n */\n\nimport { Resolution } from \"../../shared\";\nimport type { Candle, PriceHistoryData } from \"../types\";\nimport { toCandle } from \"../types\";\n\n/**\n * Key for price history subscriptions.\n */\nexport class PriceHistoryKey {\n readonly orderbookId: string;\n readonly resolution: string;\n\n constructor(orderbookId: string, resolution: string) {\n this.orderbookId = orderbookId;\n this.resolution = resolution;\n }\n\n toString(): string {\n return `${this.orderbookId}:${this.resolution}`;\n }\n\n equals(other: PriceHistoryKey): boolean {\n return (\n this.orderbookId === other.orderbookId &&\n this.resolution === other.resolution\n );\n }\n}\n\n/**\n * Price history state for a single orderbook/resolution pair.\n */\nexport class PriceHistory {\n /** Maximum number of candles to maintain */\n private static readonly MAX_CANDLES = 1000;\n\n /** Orderbook identifier */\n readonly orderbookId: string;\n /** Resolution (1m, 5m, 15m, 1h, 4h, 1d) */\n readonly resolution: string;\n /** Whether OHLCV data is included */\n includeOhlcv: boolean;\n /** Candles sorted by timestamp (newest first) */\n private _candles: Candle[] = [];\n /** Index by timestamp for fast lookup */\n private candleIndex: Map<number, number> = new Map();\n /** Last server timestamp */\n private _lastTimestamp?: number;\n /** Server time from last message */\n private _serverTime?: number;\n /** Whether initial snapshot has been received */\n private _hasSnapshot: boolean = false;\n\n constructor(orderbookId: string, resolution: string, includeOhlcv: boolean) {\n this.orderbookId = orderbookId;\n this.resolution = resolution;\n this.includeOhlcv = includeOhlcv;\n }\n\n /**\n * Apply a snapshot (historical candles).\n */\n applySnapshot(data: PriceHistoryData): void {\n // Clear existing state\n this._candles = [];\n this.candleIndex.clear();\n\n // Apply candles (they come newest-first from server)\n for (const candle of data.prices) {\n const idx = this._candles.length;\n this.candleIndex.set(candle.t, idx);\n this._candles.push(candle);\n }\n\n this._lastTimestamp = data.last_timestamp;\n this._serverTime = data.server_time;\n this._hasSnapshot = true;\n\n // Update include_ohlcv if provided\n if (data.include_ohlcv !== undefined) {\n this.includeOhlcv = data.include_ohlcv;\n }\n }\n\n /**\n * Apply an update (new or updated candle).\n */\n applyUpdate(data: PriceHistoryData): void {\n const candle = toCandle(data);\n if (candle) {\n this.updateOrAppendCandle(candle);\n }\n }\n\n /**\n * Update an existing candle or append a new one.\n */\n private updateOrAppendCandle(candle: Candle): void {\n const existingIdx = this.candleIndex.get(candle.t);\n if (existingIdx !== undefined) {\n // Update existing candle\n this._candles[existingIdx] = candle;\n } else {\n // New candle - insert at the correct position (newest first)\n const insertPos = this._candles.findIndex((c) => c.t < candle.t);\n const pos = insertPos === -1 ? this._candles.length : insertPos;\n\n // Update indices for candles that will shift\n for (const [ts, idx] of this.candleIndex.entries()) {\n if (idx >= pos) {\n this.candleIndex.set(ts, idx + 1);\n }\n }\n\n this.candleIndex.set(candle.t, pos);\n this._candles.splice(pos, 0, candle);\n\n // Trim to max candles limit\n while (this._candles.length > PriceHistory.MAX_CANDLES) {\n const removed = this._candles.pop();\n if (removed) {\n this.candleIndex.delete(removed.t);\n }\n }\n }\n\n // Update last timestamp\n if (this._candles.length > 0) {\n this._lastTimestamp = this._candles[0].t;\n }\n }\n\n /**\n * Handle heartbeat (update server time).\n */\n applyHeartbeat(data: PriceHistoryData): void {\n this._serverTime = data.server_time;\n }\n\n /**\n * Apply any price history event.\n */\n applyEvent(data: PriceHistoryData): void {\n switch (data.event_type) {\n case \"snapshot\":\n this.applySnapshot(data);\n break;\n case \"update\":\n this.applyUpdate(data);\n break;\n case \"heartbeat\":\n this.applyHeartbeat(data);\n break;\n default:\n console.warn(`Unknown price history event type: ${data.event_type}`);\n }\n }\n\n /**\n * Get all candles (newest first).\n */\n candles(): Candle[] {\n return this._candles;\n }\n\n /**\n * Get the N most recent candles.\n */\n recentCandles(n: number): Candle[] {\n return this._candles.slice(0, Math.min(n, this._candles.length));\n }\n\n /**\n * Get a candle by timestamp.\n */\n getCandle(timestamp: number): Candle | undefined {\n const idx = this.candleIndex.get(timestamp);\n if (idx === undefined) return undefined;\n return this._candles[idx];\n }\n\n /**\n * Get the most recent candle.\n */\n latestCandle(): Candle | undefined {\n return this._candles[0];\n }\n\n /**\n * Get the oldest candle.\n */\n oldestCandle(): Candle | undefined {\n return this._candles[this._candles.length - 1];\n }\n\n /**\n * Get current midpoint price (from most recent candle).\n */\n currentMidpoint(): string | undefined {\n return this._candles[0]?.m;\n }\n\n /**\n * Get current best bid (from most recent candle).\n */\n currentBestBid(): string | undefined {\n return this._candles[0]?.bb;\n }\n\n /**\n * Get current best ask (from most recent candle).\n */\n currentBestAsk(): string | undefined {\n return this._candles[0]?.ba;\n }\n\n /** Number of candles */\n candleCount(): number {\n return this._candles.length;\n }\n\n /** Whether the price history has received its initial snapshot */\n hasSnapshot(): boolean {\n return this._hasSnapshot;\n }\n\n /** Last candle timestamp */\n lastTimestamp(): number | undefined {\n return this._lastTimestamp;\n }\n\n /** Server time from last message */\n serverTime(): number | undefined {\n return this._serverTime;\n }\n\n /**\n * Get resolution as enum.\n */\n resolutionEnum(): Resolution | undefined {\n switch (this.resolution) {\n case \"1m\":\n return Resolution.OneMinute;\n case \"5m\":\n return Resolution.FiveMinutes;\n case \"15m\":\n return Resolution.FifteenMinutes;\n case \"1h\":\n return Resolution.OneHour;\n case \"4h\":\n return Resolution.FourHours;\n case \"1d\":\n return Resolution.OneDay;\n default:\n return undefined;\n }\n }\n\n /** Clear the price history (for disconnect/resync) */\n clear(): void {\n this._candles = [];\n this.candleIndex.clear();\n this._lastTimestamp = undefined;\n this._serverTime = undefined;\n this._hasSnapshot = false;\n }\n}\n"]}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * User state management.
3
+ *
4
+ * Maintains local state for user orders and balances.
5
+ */
6
+ import type { Order, BalanceEntry, UserEventData } from "../types";
7
+ /**
8
+ * User state tracking orders and balances.
9
+ */
10
+ export declare class UserState {
11
+ /** User public key */
12
+ readonly user: string;
13
+ /** Open orders by order hash */
14
+ orders: Map<string, Order>;
15
+ /** Balances by market_pubkey:deposit_mint key */
16
+ balances: Map<string, BalanceEntry>;
17
+ /** Whether initial snapshot has been received */
18
+ private _hasSnapshot;
19
+ /** Last update timestamp */
20
+ private _lastTimestamp?;
21
+ constructor(user: string);
22
+ /**
23
+ * Apply a snapshot (full user state).
24
+ */
25
+ applySnapshot(data: UserEventData): void;
26
+ /**
27
+ * Apply an order update.
28
+ */
29
+ applyOrderUpdate(data: UserEventData): void;
30
+ /**
31
+ * Apply a balance update.
32
+ */
33
+ applyBalanceUpdate(data: UserEventData): void;
34
+ /**
35
+ * Apply balance from order update.
36
+ */
37
+ private applyBalanceFromOrder;
38
+ /**
39
+ * Apply any user event.
40
+ */
41
+ applyEvent(data: UserEventData): void;
42
+ /**
43
+ * Get an order by hash.
44
+ */
45
+ getOrder(orderHash: string): Order | undefined;
46
+ /**
47
+ * Get all open orders.
48
+ */
49
+ openOrders(): Order[];
50
+ /**
51
+ * Get orders for a specific market.
52
+ */
53
+ ordersForMarket(marketPubkey: string): Order[];
54
+ /**
55
+ * Get orders for a specific orderbook.
56
+ */
57
+ ordersForOrderbook(orderbookId: string): Order[];
58
+ /**
59
+ * Get balance for a market/deposit_mint pair.
60
+ */
61
+ getBalance(marketPubkey: string, depositMint: string): BalanceEntry | undefined;
62
+ /**
63
+ * Get all balances.
64
+ */
65
+ allBalances(): BalanceEntry[];
66
+ /**
67
+ * Get total idle balance for a specific outcome as a string.
68
+ */
69
+ idleBalanceForOutcome(marketPubkey: string, depositMint: string, outcomeIndex: number): string | undefined;
70
+ /**
71
+ * Get total on-book balance for a specific outcome as a string.
72
+ */
73
+ onBookBalanceForOutcome(marketPubkey: string, depositMint: string, outcomeIndex: number): string | undefined;
74
+ /** Number of open orders */
75
+ orderCount(): number;
76
+ /** Whether the user state has received its initial snapshot */
77
+ hasSnapshot(): boolean;
78
+ /** Last update timestamp */
79
+ lastTimestamp(): string | undefined;
80
+ /** Clear the user state (for disconnect/resync) */
81
+ clear(): void;
82
+ }
83
+ //# sourceMappingURL=user.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/websocket/state/user.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,KAAK,EAEL,YAAY,EACZ,aAAa,EACd,MAAM,UAAU,CAAC;AAGlB;;GAEG;AACH,qBAAa,SAAS;IACpB,sBAAsB;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gCAAgC;IAChC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAa;IACvC,iDAAiD;IACjD,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAa;IAChD,iDAAiD;IACjD,OAAO,CAAC,YAAY,CAAkB;IACtC,4BAA4B;IAC5B,OAAO,CAAC,cAAc,CAAC,CAAS;gBAEpB,IAAI,EAAE,MAAM;IAIxB;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAmBxC;;OAEG;IACH,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAoD3C;;OAEG;IACH,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAc7C;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAmB7B;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAgBrC;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAI9C;;OAEG;IACH,UAAU,IAAI,KAAK,EAAE;IAIrB;;OAEG;IACH,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,KAAK,EAAE;IAM9C;;OAEG;IACH,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,KAAK,EAAE;IAMhD;;OAEG;IACH,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAK/E;;OAEG;IACH,WAAW,IAAI,YAAY,EAAE;IAI7B;;OAEG;IACH,qBAAqB,CACnB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,MAAM,GAAG,SAAS;IASrB;;OAEG;IACH,uBAAuB,CACrB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,MAAM,GAAG,SAAS;IASrB,4BAA4B;IAC5B,UAAU,IAAI,MAAM;IAIpB,+DAA+D;IAC/D,WAAW,IAAI,OAAO;IAItB,4BAA4B;IAC5B,aAAa,IAAI,MAAM,GAAG,SAAS;IAInC,mDAAmD;IACnD,KAAK,IAAI,IAAI;CAMd"}
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ /**
3
+ * User state management.
4
+ *
5
+ * Maintains local state for user orders and balances.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.UserState = void 0;
9
+ const price_1 = require("../../shared/price");
10
+ /**
11
+ * User state tracking orders and balances.
12
+ */
13
+ class UserState {
14
+ /** User public key */
15
+ user;
16
+ /** Open orders by order hash */
17
+ orders = new Map();
18
+ /** Balances by market_pubkey:deposit_mint key */
19
+ balances = new Map();
20
+ /** Whether initial snapshot has been received */
21
+ _hasSnapshot = false;
22
+ /** Last update timestamp */
23
+ _lastTimestamp;
24
+ constructor(user) {
25
+ this.user = user;
26
+ }
27
+ /**
28
+ * Apply a snapshot (full user state).
29
+ */
30
+ applySnapshot(data) {
31
+ // Clear existing state
32
+ this.orders.clear();
33
+ this.balances.clear();
34
+ // Apply orders
35
+ for (const order of data.orders) {
36
+ this.orders.set(order.order_hash, order);
37
+ }
38
+ // Apply balances
39
+ for (const [key, balance] of Object.entries(data.balances)) {
40
+ this.balances.set(key, balance);
41
+ }
42
+ this._hasSnapshot = true;
43
+ this._lastTimestamp = data.timestamp;
44
+ }
45
+ /**
46
+ * Apply an order update.
47
+ */
48
+ applyOrderUpdate(data) {
49
+ if (!data.order)
50
+ return;
51
+ const update = data.order;
52
+ const orderHash = update.order_hash;
53
+ // If remaining is 0, the order is fully filled or cancelled - remove it
54
+ if ((0, price_1.isZero)(update.remaining)) {
55
+ this.orders.delete(orderHash);
56
+ }
57
+ else {
58
+ const existing = this.orders.get(orderHash);
59
+ if (existing) {
60
+ // Update existing order
61
+ existing.remaining = update.remaining;
62
+ existing.filled = update.filled;
63
+ }
64
+ else {
65
+ // New order - construct it from the update
66
+ if (data.market_pubkey && data.orderbook_id) {
67
+ console.warn(`Creating order from update (order ${orderHash} not in local state). ` +
68
+ `Some fields may have default values.`);
69
+ const order = {
70
+ order_hash: orderHash,
71
+ market_pubkey: data.market_pubkey,
72
+ orderbook_id: data.orderbook_id,
73
+ side: update.side,
74
+ maker_amount: update.remaining, // Best approximation from available data
75
+ taker_amount: update.remaining, // Use remaining as approximation
76
+ remaining: update.remaining,
77
+ filled: update.filled,
78
+ price: update.price,
79
+ created_at: update.created_at,
80
+ expiration: 0, // Unknown from order updates
81
+ };
82
+ this.orders.set(orderHash, order);
83
+ }
84
+ else {
85
+ console.warn(`Cannot create order ${orderHash}: missing market_pubkey or orderbook_id`);
86
+ }
87
+ }
88
+ }
89
+ // Apply balance updates if present
90
+ if (update.balance) {
91
+ this.applyBalanceFromOrder(data, update.balance);
92
+ }
93
+ this._lastTimestamp = data.timestamp;
94
+ }
95
+ /**
96
+ * Apply a balance update.
97
+ */
98
+ applyBalanceUpdate(data) {
99
+ if (!data.market_pubkey || !data.deposit_mint || !data.balance)
100
+ return;
101
+ const key = `${data.market_pubkey}:${data.deposit_mint}`;
102
+ const entry = {
103
+ market_pubkey: data.market_pubkey,
104
+ deposit_mint: data.deposit_mint,
105
+ outcomes: data.balance.outcomes,
106
+ };
107
+ this.balances.set(key, entry);
108
+ this._lastTimestamp = data.timestamp;
109
+ }
110
+ /**
111
+ * Apply balance from order update.
112
+ */
113
+ applyBalanceFromOrder(data, balance) {
114
+ if (data.market_pubkey && data.deposit_mint) {
115
+ const key = `${data.market_pubkey}:${data.deposit_mint}`;
116
+ const entry = {
117
+ market_pubkey: data.market_pubkey,
118
+ deposit_mint: data.deposit_mint,
119
+ outcomes: balance.outcomes,
120
+ };
121
+ this.balances.set(key, entry);
122
+ }
123
+ else if (data.market_pubkey) {
124
+ // If no deposit_mint, update all existing entries with matching market
125
+ for (const [key, entry] of this.balances.entries()) {
126
+ if (key.startsWith(data.market_pubkey)) {
127
+ entry.outcomes = balance.outcomes;
128
+ }
129
+ }
130
+ }
131
+ }
132
+ /**
133
+ * Apply any user event.
134
+ */
135
+ applyEvent(data) {
136
+ switch (data.event_type) {
137
+ case "snapshot":
138
+ this.applySnapshot(data);
139
+ break;
140
+ case "order_update":
141
+ this.applyOrderUpdate(data);
142
+ break;
143
+ case "balance_update":
144
+ this.applyBalanceUpdate(data);
145
+ break;
146
+ default:
147
+ console.warn(`Unknown user event type: ${data.event_type}`);
148
+ }
149
+ }
150
+ /**
151
+ * Get an order by hash.
152
+ */
153
+ getOrder(orderHash) {
154
+ return this.orders.get(orderHash);
155
+ }
156
+ /**
157
+ * Get all open orders.
158
+ */
159
+ openOrders() {
160
+ return Array.from(this.orders.values());
161
+ }
162
+ /**
163
+ * Get orders for a specific market.
164
+ */
165
+ ordersForMarket(marketPubkey) {
166
+ return this.openOrders().filter((order) => order.market_pubkey === marketPubkey);
167
+ }
168
+ /**
169
+ * Get orders for a specific orderbook.
170
+ */
171
+ ordersForOrderbook(orderbookId) {
172
+ return this.openOrders().filter((order) => order.orderbook_id === orderbookId);
173
+ }
174
+ /**
175
+ * Get balance for a market/deposit_mint pair.
176
+ */
177
+ getBalance(marketPubkey, depositMint) {
178
+ const key = `${marketPubkey}:${depositMint}`;
179
+ return this.balances.get(key);
180
+ }
181
+ /**
182
+ * Get all balances.
183
+ */
184
+ allBalances() {
185
+ return Array.from(this.balances.values());
186
+ }
187
+ /**
188
+ * Get total idle balance for a specific outcome as a string.
189
+ */
190
+ idleBalanceForOutcome(marketPubkey, depositMint, outcomeIndex) {
191
+ const balance = this.getBalance(marketPubkey, depositMint);
192
+ if (!balance)
193
+ return undefined;
194
+ const outcome = balance.outcomes.find((o) => o.outcome_index === outcomeIndex);
195
+ return outcome?.idle;
196
+ }
197
+ /**
198
+ * Get total on-book balance for a specific outcome as a string.
199
+ */
200
+ onBookBalanceForOutcome(marketPubkey, depositMint, outcomeIndex) {
201
+ const balance = this.getBalance(marketPubkey, depositMint);
202
+ if (!balance)
203
+ return undefined;
204
+ const outcome = balance.outcomes.find((o) => o.outcome_index === outcomeIndex);
205
+ return outcome?.on_book;
206
+ }
207
+ /** Number of open orders */
208
+ orderCount() {
209
+ return this.orders.size;
210
+ }
211
+ /** Whether the user state has received its initial snapshot */
212
+ hasSnapshot() {
213
+ return this._hasSnapshot;
214
+ }
215
+ /** Last update timestamp */
216
+ lastTimestamp() {
217
+ return this._lastTimestamp;
218
+ }
219
+ /** Clear the user state (for disconnect/resync) */
220
+ clear() {
221
+ this.orders.clear();
222
+ this.balances.clear();
223
+ this._hasSnapshot = false;
224
+ this._lastTimestamp = undefined;
225
+ }
226
+ }
227
+ exports.UserState = UserState;
228
+ //# sourceMappingURL=user.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.js","sourceRoot":"","sources":["../../../src/websocket/state/user.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * User state management.\n *\n * Maintains local state for user orders and balances.\n */\n\nimport type {\n Order,\n Balance,\n BalanceEntry,\n UserEventData,\n} from \"../types\";\nimport { isZero } from \"../../shared/price\";\n\n/**\n * User state tracking orders and balances.\n */\nexport class UserState {\n /** User public key */\n readonly user: string;\n /** Open orders by order hash */\n orders: Map<string, Order> = new Map();\n /** Balances by market_pubkey:deposit_mint key */\n balances: Map<string, BalanceEntry> = new Map();\n /** Whether initial snapshot has been received */\n private _hasSnapshot: boolean = false;\n /** Last update timestamp */\n private _lastTimestamp?: string;\n\n constructor(user: string) {\n this.user = user;\n }\n\n /**\n * Apply a snapshot (full user state).\n */\n applySnapshot(data: UserEventData): void {\n // Clear existing state\n this.orders.clear();\n this.balances.clear();\n\n // Apply orders\n for (const order of data.orders) {\n this.orders.set(order.order_hash, order);\n }\n\n // Apply balances\n for (const [key, balance] of Object.entries(data.balances)) {\n this.balances.set(key, balance);\n }\n\n this._hasSnapshot = true;\n this._lastTimestamp = data.timestamp;\n }\n\n /**\n * Apply an order update.\n */\n applyOrderUpdate(data: UserEventData): void {\n if (!data.order) return;\n\n const update = data.order;\n const orderHash = update.order_hash;\n\n // If remaining is 0, the order is fully filled or cancelled - remove it\n if (isZero(update.remaining)) {\n this.orders.delete(orderHash);\n } else {\n const existing = this.orders.get(orderHash);\n if (existing) {\n // Update existing order\n existing.remaining = update.remaining;\n existing.filled = update.filled;\n } else {\n // New order - construct it from the update\n if (data.market_pubkey && data.orderbook_id) {\n console.warn(\n `Creating order from update (order ${orderHash} not in local state). ` +\n `Some fields may have default values.`\n );\n const order: Order = {\n order_hash: orderHash,\n market_pubkey: data.market_pubkey,\n orderbook_id: data.orderbook_id,\n side: update.side,\n maker_amount: update.remaining, // Best approximation from available data\n taker_amount: update.remaining, // Use remaining as approximation\n remaining: update.remaining,\n filled: update.filled,\n price: update.price,\n created_at: update.created_at,\n expiration: 0, // Unknown from order updates\n };\n this.orders.set(orderHash, order);\n } else {\n console.warn(\n `Cannot create order ${orderHash}: missing market_pubkey or orderbook_id`\n );\n }\n }\n }\n\n // Apply balance updates if present\n if (update.balance) {\n this.applyBalanceFromOrder(data, update.balance);\n }\n\n this._lastTimestamp = data.timestamp;\n }\n\n /**\n * Apply a balance update.\n */\n applyBalanceUpdate(data: UserEventData): void {\n if (!data.market_pubkey || !data.deposit_mint || !data.balance) return;\n\n const key = `${data.market_pubkey}:${data.deposit_mint}`;\n const entry: BalanceEntry = {\n market_pubkey: data.market_pubkey,\n deposit_mint: data.deposit_mint,\n outcomes: data.balance.outcomes,\n };\n this.balances.set(key, entry);\n\n this._lastTimestamp = data.timestamp;\n }\n\n /**\n * Apply balance from order update.\n */\n private applyBalanceFromOrder(data: UserEventData, balance: Balance): void {\n if (data.market_pubkey && data.deposit_mint) {\n const key = `${data.market_pubkey}:${data.deposit_mint}`;\n const entry: BalanceEntry = {\n market_pubkey: data.market_pubkey,\n deposit_mint: data.deposit_mint,\n outcomes: balance.outcomes,\n };\n this.balances.set(key, entry);\n } else if (data.market_pubkey) {\n // If no deposit_mint, update all existing entries with matching market\n for (const [key, entry] of this.balances.entries()) {\n if (key.startsWith(data.market_pubkey)) {\n entry.outcomes = balance.outcomes;\n }\n }\n }\n }\n\n /**\n * Apply any user event.\n */\n applyEvent(data: UserEventData): void {\n switch (data.event_type) {\n case \"snapshot\":\n this.applySnapshot(data);\n break;\n case \"order_update\":\n this.applyOrderUpdate(data);\n break;\n case \"balance_update\":\n this.applyBalanceUpdate(data);\n break;\n default:\n console.warn(`Unknown user event type: ${data.event_type}`);\n }\n }\n\n /**\n * Get an order by hash.\n */\n getOrder(orderHash: string): Order | undefined {\n return this.orders.get(orderHash);\n }\n\n /**\n * Get all open orders.\n */\n openOrders(): Order[] {\n return Array.from(this.orders.values());\n }\n\n /**\n * Get orders for a specific market.\n */\n ordersForMarket(marketPubkey: string): Order[] {\n return this.openOrders().filter(\n (order) => order.market_pubkey === marketPubkey\n );\n }\n\n /**\n * Get orders for a specific orderbook.\n */\n ordersForOrderbook(orderbookId: string): Order[] {\n return this.openOrders().filter(\n (order) => order.orderbook_id === orderbookId\n );\n }\n\n /**\n * Get balance for a market/deposit_mint pair.\n */\n getBalance(marketPubkey: string, depositMint: string): BalanceEntry | undefined {\n const key = `${marketPubkey}:${depositMint}`;\n return this.balances.get(key);\n }\n\n /**\n * Get all balances.\n */\n allBalances(): BalanceEntry[] {\n return Array.from(this.balances.values());\n }\n\n /**\n * Get total idle balance for a specific outcome as a string.\n */\n idleBalanceForOutcome(\n marketPubkey: string,\n depositMint: string,\n outcomeIndex: number\n ): string | undefined {\n const balance = this.getBalance(marketPubkey, depositMint);\n if (!balance) return undefined;\n const outcome = balance.outcomes.find(\n (o) => o.outcome_index === outcomeIndex\n );\n return outcome?.idle;\n }\n\n /**\n * Get total on-book balance for a specific outcome as a string.\n */\n onBookBalanceForOutcome(\n marketPubkey: string,\n depositMint: string,\n outcomeIndex: number\n ): string | undefined {\n const balance = this.getBalance(marketPubkey, depositMint);\n if (!balance) return undefined;\n const outcome = balance.outcomes.find(\n (o) => o.outcome_index === outcomeIndex\n );\n return outcome?.on_book;\n }\n\n /** Number of open orders */\n orderCount(): number {\n return this.orders.size;\n }\n\n /** Whether the user state has received its initial snapshot */\n hasSnapshot(): boolean {\n return this._hasSnapshot;\n }\n\n /** Last update timestamp */\n lastTimestamp(): string | undefined {\n return this._lastTimestamp;\n }\n\n /** Clear the user state (for disconnect/resync) */\n clear(): void {\n this.orders.clear();\n this.balances.clear();\n this._hasSnapshot = false;\n this._lastTimestamp = undefined;\n }\n}\n"]}