@icgio/icg-exchanges-wrapper 1.14.228 → 1.14.229

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.
@@ -1,10 +1,10 @@
1
1
  const { Pool } = require('pg')
2
2
 
3
3
  /**
4
- * Async PostgreSQL store for order status
4
+ * Async PostgreSQL store for open orders
5
5
  * Non-blocking: all writes are fire-and-forget to avoid blocking the main process
6
6
  */
7
- class OrderStatusStore {
7
+ class OpenOrdersStore {
8
8
  constructor(connection_string) {
9
9
  this.enabled = true
10
10
  this.pool = null
@@ -30,23 +30,27 @@ class OrderStatusStore {
30
30
  })
31
31
 
32
32
  this.pool.on('error', (err) => {
33
- console.error('OrderStatusStore pool error:', err.message)
33
+ console.error('OpenOrdersStore pool error:', err.message)
34
+ })
35
+
36
+ // Set timezone to UTC for all connections
37
+ this.pool.on('connect', async (client) => {
38
+ await client.query("SET timezone = 'UTC'")
34
39
  })
35
40
 
36
41
  await this.ensure_table()
37
42
  this.enabled = true
38
43
  this.start_flush_interval()
39
- console.log('OrderStatusStore initialized successfully')
44
+ console.log('OpenOrdersStore initialized successfully')
40
45
  } catch (err) {
41
- console.error('OrderStatusStore init failed:', err.message)
46
+ console.error('OpenOrdersStore init failed:', err.message)
42
47
  this.enabled = false
43
48
  }
44
49
  }
45
50
 
46
51
  async ensure_table() {
47
52
  const create_table_query = `
48
- CREATE TABLE IF NOT EXISTS order_status (
49
- id SERIAL PRIMARY KEY,
53
+ CREATE TABLE IF NOT EXISTS open_orders (
50
54
  exchange VARCHAR(64) NOT NULL,
51
55
  pair VARCHAR(32) NOT NULL,
52
56
  order_type VARCHAR(64),
@@ -62,13 +66,11 @@ class OrderStatusStore {
62
66
  open_time TIMESTAMPTZ,
63
67
  close_time TIMESTAMPTZ,
64
68
  note VARCHAR(256),
65
- created_at TIMESTAMPTZ DEFAULT NOW(),
66
- UNIQUE(exchange, pair, type, order_id, status, created_at)
69
+ updated_at TIMESTAMPTZ DEFAULT (NOW() AT TIME ZONE 'UTC'),
70
+ PRIMARY KEY (exchange, pair, type, order_id)
67
71
  );
68
- CREATE INDEX IF NOT EXISTS idx_order_status_exchange_pair ON order_status(exchange, pair);
69
- CREATE INDEX IF NOT EXISTS idx_order_status_order_id ON order_status(order_id);
70
- CREATE INDEX IF NOT EXISTS idx_order_status_created_at ON order_status(created_at);
71
- CREATE INDEX IF NOT EXISTS idx_order_status_status ON order_status(status);
72
+ CREATE INDEX IF NOT EXISTS idx_open_orders_status ON open_orders(status);
73
+ CREATE INDEX IF NOT EXISTS idx_open_orders_exchange ON open_orders(exchange);
72
74
  `
73
75
 
74
76
  const client = await this.pool.connect()
@@ -85,7 +87,7 @@ class OrderStatusStore {
85
87
 
86
88
  this.flush_interval = setInterval(() => {
87
89
  this.flush().catch((err) => {
88
- console.error('OrderStatusStore flush error:', err.message)
90
+ console.error('OpenOrdersStore flush error:', err.message)
89
91
  })
90
92
  }, this.flush_interval_ms)
91
93
  }
@@ -109,6 +111,15 @@ class OrderStatusStore {
109
111
  store(exchange, pair, type, order_id, data, note) {
110
112
  if (!this.enabled) return
111
113
 
114
+ // Convert Date objects to UTC ISO strings for consistent timezone handling
115
+ const to_utc_iso = (date) => {
116
+ if (!date) return null
117
+ if (date instanceof Date) {
118
+ return date.toISOString()
119
+ }
120
+ return date
121
+ }
122
+
112
123
  const record = {
113
124
  exchange,
114
125
  pair,
@@ -122,8 +133,8 @@ class OrderStatusStore {
122
133
  f_percentage: data.f_percentage || null,
123
134
  t_amount: data.t_amount || null,
124
135
  fees: data.fees ? JSON.stringify(data.fees) : null,
125
- open_time: data.open_time || null,
126
- close_time: data.close_time || null,
136
+ open_time: to_utc_iso(data.open_time),
137
+ close_time: to_utc_iso(data.close_time),
127
138
  note: note || null,
128
139
  }
129
140
 
@@ -143,35 +154,56 @@ class OrderStatusStore {
143
154
 
144
155
  const batch = this.queue.splice(0, 100) // Process up to 100 at a time
145
156
 
146
- const insert_query = `
147
- INSERT INTO order_status (
157
+ const upsert_query = `
158
+ INSERT INTO open_orders (
148
159
  exchange, pair, type, order_id, order_type, status,
149
160
  price, amount, f_amount, f_percentage, t_amount,
150
- fees, open_time, close_time, note
151
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
152
- ON CONFLICT (exchange, pair, type, order_id, status, created_at) DO NOTHING
161
+ fees, open_time, close_time, note, updated_at
162
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, (NOW() AT TIME ZONE 'UTC'))
163
+ ON CONFLICT (exchange, pair, type, order_id) DO UPDATE SET
164
+ order_type = EXCLUDED.order_type,
165
+ status = EXCLUDED.status,
166
+ price = EXCLUDED.price,
167
+ amount = EXCLUDED.amount,
168
+ f_amount = EXCLUDED.f_amount,
169
+ f_percentage = EXCLUDED.f_percentage,
170
+ t_amount = EXCLUDED.t_amount,
171
+ fees = EXCLUDED.fees,
172
+ open_time = EXCLUDED.open_time,
173
+ close_time = EXCLUDED.close_time,
174
+ note = EXCLUDED.note,
175
+ updated_at = (NOW() AT TIME ZONE 'UTC')
176
+ `
177
+
178
+ const delete_query = `
179
+ DELETE FROM open_orders
180
+ WHERE exchange = $1 AND pair = $2 AND type = $3 AND order_id = $4
153
181
  `
154
182
 
155
183
  const client = await this.pool.connect()
156
184
  try {
157
185
  for (const record of batch) {
158
- await client.query(insert_query, [
159
- record.exchange,
160
- record.pair,
161
- record.type,
162
- record.order_id,
163
- record.order_type,
164
- record.status,
165
- record.price,
166
- record.amount,
167
- record.f_amount,
168
- record.f_percentage,
169
- record.t_amount,
170
- record.fees,
171
- record.open_time,
172
- record.close_time,
173
- record.note,
174
- ])
186
+ if (record.status === 'DELETED') {
187
+ await client.query(delete_query, [record.exchange, record.pair, record.type, record.order_id])
188
+ } else {
189
+ await client.query(upsert_query, [
190
+ record.exchange,
191
+ record.pair,
192
+ record.type,
193
+ record.order_id,
194
+ record.order_type,
195
+ record.status,
196
+ record.price,
197
+ record.amount,
198
+ record.f_amount,
199
+ record.f_percentage,
200
+ record.t_amount,
201
+ record.fees,
202
+ record.open_time,
203
+ record.close_time,
204
+ record.note,
205
+ ])
206
+ }
175
207
  }
176
208
  } catch (err) {
177
209
  // Re-queue failed records
@@ -182,130 +214,13 @@ class OrderStatusStore {
182
214
  }
183
215
  }
184
216
 
185
- /**
186
- * Query order status history
187
- * @param {object} filters - { exchange, pair, order_id, status, limit, offset }
188
- * @returns {Promise<Array>}
189
- */
190
- async query(filters = {}) {
191
- if (!this.enabled || !this.table_ready) {
192
- return []
193
- }
194
-
195
- const conditions = []
196
- const params = []
197
- let param_index = 1
198
-
199
- if (filters.exchange) {
200
- conditions.push(`exchange = $${param_index++}`)
201
- params.push(filters.exchange)
202
- }
203
- if (filters.pair) {
204
- conditions.push(`pair = $${param_index++}`)
205
- params.push(filters.pair)
206
- }
207
- if (filters.order_id) {
208
- conditions.push(`order_id = $${param_index++}`)
209
- params.push(filters.order_id)
210
- }
211
- if (filters.status) {
212
- conditions.push(`status = $${param_index++}`)
213
- params.push(filters.status)
214
- }
215
- if (filters.from_time) {
216
- conditions.push(`created_at >= $${param_index++}`)
217
- params.push(filters.from_time)
218
- }
219
- if (filters.to_time) {
220
- conditions.push(`created_at <= $${param_index++}`)
221
- params.push(filters.to_time)
222
- }
223
-
224
- const where_clause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
225
- const limit = filters.limit || 100
226
- const offset = filters.offset || 0
227
-
228
- const query_str = `
229
- SELECT * FROM order_status
230
- ${where_clause}
231
- ORDER BY created_at DESC
232
- LIMIT ${limit} OFFSET ${offset}
233
- `
234
-
235
- const client = await this.pool.connect()
236
- try {
237
- const result = await client.query(query_str, params)
238
- return result.rows
239
- } finally {
240
- client.release()
241
- }
242
- }
243
-
244
- /**
245
- * Get aggregated stats for orders
246
- * @param {object} filters - { exchange, pair, from_time, to_time }
247
- * @returns {Promise<object>}
248
- */
249
- async get_stats(filters = {}) {
250
- if (!this.enabled || !this.table_ready) {
251
- return null
252
- }
253
-
254
- const conditions = []
255
- const params = []
256
- let param_index = 1
257
-
258
- if (filters.exchange) {
259
- conditions.push(`exchange = $${param_index++}`)
260
- params.push(filters.exchange)
261
- }
262
- if (filters.pair) {
263
- conditions.push(`pair = $${param_index++}`)
264
- params.push(filters.pair)
265
- }
266
- if (filters.from_time) {
267
- conditions.push(`created_at >= $${param_index++}`)
268
- params.push(filters.from_time)
269
- }
270
- if (filters.to_time) {
271
- conditions.push(`created_at <= $${param_index++}`)
272
- params.push(filters.to_time)
273
- }
274
-
275
- const where_clause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
276
-
277
- const query_str = `
278
- SELECT
279
- status,
280
- COUNT(*) as count,
281
- SUM(COALESCE(f_amount, 0)) as total_filled_amount
282
- FROM order_status
283
- ${where_clause}
284
- GROUP BY status
285
- `
286
-
287
- const client = await this.pool.connect()
288
- try {
289
- const result = await client.query(query_str, params)
290
- return result.rows.reduce((acc, row) => {
291
- acc[row.status] = {
292
- count: parseInt(row.count, 10),
293
- total_filled_amount: parseFloat(row.total_filled_amount) || 0,
294
- }
295
- return acc
296
- }, {})
297
- } finally {
298
- client.release()
299
- }
300
- }
301
-
302
217
  async close() {
303
218
  this.stop_flush_interval()
304
219
  if (this.queue.length > 0) {
305
220
  try {
306
221
  await this.flush()
307
222
  } catch (err) {
308
- console.error('OrderStatusStore final flush error:', err.message)
223
+ console.error('OpenOrdersStore final flush error:', err.message)
309
224
  }
310
225
  }
311
226
  if (this.pool) {
@@ -315,4 +230,4 @@ class OrderStatusStore {
315
230
  }
316
231
  }
317
232
 
318
- module.exports = OrderStatusStore
233
+ module.exports = OpenOrdersStore
@@ -7,7 +7,7 @@ const utils = require('@icgio/icg-utils').utils
7
7
  const { coinmarketcap: get_coinmarketcap_rates, observable: Observable } = require('@icgio/icg-utils')
8
8
 
9
9
  const Account = require('./account.js')
10
- const OrderStatusStore = require('../lib/order-status-store.js')
10
+ const OpenOrdersStore = require('../lib/order-status-store.js')
11
11
 
12
12
  // Default precision functions - will be overridden if provided in settings
13
13
  let round_price = (exchange, pair, price) => price
@@ -126,7 +126,7 @@ module.exports = class Orders {
126
126
  this.balance_benchmark = settings.balance_benchmark || {}
127
127
  this.min_reserve_balance = settings.min_reserve_balance || {}
128
128
  // Initialize order status store for PostgreSQL persistence (async, non-blocking)
129
- this.order_status_store = new OrderStatusStore(cd.order_status_store_connection_string)
129
+ this.order_status_store = new OpenOrdersStore(cd.order_status_store_connection_string)
130
130
  for (let exchange in exchange_pairs) {
131
131
  let api_key_temp = cd[exchange]['api_keys'] || cd[exchange]['api_key'],
132
132
  secret_key_temp = cd[exchange]['secret_keys'] || cd[exchange]['secret_key'],
@@ -761,8 +761,11 @@ module.exports = class Orders {
761
761
  this.order_status_store.store(exchange, pair, type, order_id, order_data, note)
762
762
  }
763
763
  delete_order_info(exchange, pair, type, order_id, note) {
764
+ const order_data = _.get(this.order_info, [exchange, pair, type, order_id], {})
764
765
  this.order_info[exchange][pair][type] = _.omit(this.order_info[exchange][pair][type], [order_id])
765
766
  console.log('DELETE %s: %s %s %s %s', note, exchange, pair, type, order_id)
767
+ // Store deletion to PostgreSQL (non-blocking)
768
+ this.order_status_store.store(exchange, pair, type, order_id, { ...order_data, status: 'DELETED' }, note)
766
769
  }
767
770
  check_balances() {
768
771
  let balances_handler = (exchange, res) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icgio/icg-exchanges-wrapper",
3
- "version": "1.14.228",
3
+ "version": "1.14.229",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "homepage": "https://github.com/icgio/icg-exchanges-wrapper#readme",
19
19
  "dependencies": {
20
- "@icgio/icg-exchanges": "^1.32.118",
20
+ "@icgio/icg-exchanges": "^1.32.119",
21
21
  "@icgio/icg-utils": "^1.9.38",
22
22
  "lodash": "^4.17.20",
23
23
  "pg": "^8.13.1",