@icgio/icg-exchanges-wrapper 1.14.268 → 1.15.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.
- package/README.md +0 -12
- package/index.js +0 -5
- package/middleware/orders.js +10 -14
- package/package.json +2 -2
- package/lib/ws-data-client.js +0 -248
package/README.md
CHANGED
|
@@ -66,16 +66,6 @@ await orders.cancel_order(exchange, pair, order_id)
|
|
|
66
66
|
const status = await orders.get_order_status(exchange, pair, order_id)
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
### WebSocket Data Client
|
|
70
|
-
|
|
71
|
-
```javascript
|
|
72
|
-
const { ws_data_client, send_log } = require('@icgio/icg-exchanges-wrapper')
|
|
73
|
-
|
|
74
|
-
// Send log messages via WebSocket
|
|
75
|
-
send_log('info', 'Operation completed successfully')
|
|
76
|
-
send_log('error', 'An error occurred')
|
|
77
|
-
```
|
|
78
|
-
|
|
79
69
|
## Project Structure
|
|
80
70
|
|
|
81
71
|
```
|
|
@@ -91,8 +81,6 @@ icg-exchanges-wrapper/
|
|
|
91
81
|
├── middleware/
|
|
92
82
|
│ ├── account.js # Account middleware
|
|
93
83
|
│ └── orders.js # Order management middleware
|
|
94
|
-
├── lib/
|
|
95
|
-
│ └── ws-data-client.js # WebSocket data client
|
|
96
84
|
└── package.json
|
|
97
85
|
```
|
|
98
86
|
|
package/index.js
CHANGED
|
@@ -4,7 +4,6 @@ const get_exchanges_trades = require('./functions/get-exchanges-trades.js')
|
|
|
4
4
|
const get_exchanges_open_orders = require('./functions/get-exchanges-open-orders.js')
|
|
5
5
|
const get_exchanges_deposits = require('./functions/get-exchanges-deposits.js')
|
|
6
6
|
const get_exchanges_withdrawals = require('./functions/get-exchanges-withdrawals.js')
|
|
7
|
-
const { ws_data_client } = require('./lib/ws-data-client.js')
|
|
8
7
|
|
|
9
8
|
module.exports.functions = {
|
|
10
9
|
get_exchanges_rates,
|
|
@@ -18,7 +17,3 @@ module.exports.functions = {
|
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
module.exports.orders = require('./middleware/orders.js')
|
|
21
|
-
|
|
22
|
-
// WebSocket data client for log streaming
|
|
23
|
-
module.exports.ws_data_client = ws_data_client
|
|
24
|
-
module.exports.send_log = (level, message) => ws_data_client.send_log(level, message)
|
package/middleware/orders.js
CHANGED
|
@@ -7,10 +7,9 @@ const utils = require('@icgio/icg-utils').utils
|
|
|
7
7
|
const { crypto_rates: get_crypto_rates, observable: Observable } = require('@icgio/icg-utils')
|
|
8
8
|
|
|
9
9
|
const Account = require('./account.js')
|
|
10
|
-
const { ws_data_client: default_ws_data_client } = require('../lib/ws-data-client.js')
|
|
11
10
|
|
|
12
|
-
// Module-level
|
|
13
|
-
let
|
|
11
|
+
// Module-level ws_client reference (must be provided via settings.ws_client)
|
|
12
|
+
let ws_client = null
|
|
14
13
|
|
|
15
14
|
// Default precision functions - will be overridden if provided in settings
|
|
16
15
|
let round_price = (exchange, pair, price) => price
|
|
@@ -246,14 +245,11 @@ module.exports = class Orders {
|
|
|
246
245
|
this.process_query_order(this.query_order)
|
|
247
246
|
}, PROCESS_OPEN_ORDERS_TRADERS_INTERVAL_MS)
|
|
248
247
|
}
|
|
249
|
-
// Initialize WS
|
|
250
|
-
//
|
|
248
|
+
// Initialize WS client for streaming to dashboard backend
|
|
249
|
+
// ws_client must be provided via settings (no built-in fallback)
|
|
251
250
|
if (settings.ws_client) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
} else if (settings.ws_backend_secret && settings.client_id) {
|
|
255
|
-
ws_data_client.init(settings.ws_backend_secret, settings.client_id)
|
|
256
|
-
ws_data_client.set_order_info_ref(this.order_info)
|
|
251
|
+
ws_client = settings.ws_client
|
|
252
|
+
ws_client.set_order_info_ref(this.order_info)
|
|
257
253
|
}
|
|
258
254
|
}
|
|
259
255
|
init_account(balances, benchmarks) {
|
|
@@ -811,8 +807,8 @@ module.exports = class Orders {
|
|
|
811
807
|
// Send order update via WebSocket to dashboard backend
|
|
812
808
|
const order_data = _.get(this.order_info, [exchange, pair, type, order_id], {})
|
|
813
809
|
// Only send N-FILLED and later statuses (skip INIT which has temporary order_id)
|
|
814
|
-
if (dict.status !== 'INIT' && order_data.status !== 'INIT') {
|
|
815
|
-
|
|
810
|
+
if (dict.status !== 'INIT' && order_data.status !== 'INIT' && ws_client) {
|
|
811
|
+
ws_client.send_order({
|
|
816
812
|
action: 'update',
|
|
817
813
|
exchange,
|
|
818
814
|
pair,
|
|
@@ -829,8 +825,8 @@ module.exports = class Orders {
|
|
|
829
825
|
console.log('DELETE %s: %s %s %s %s', note, exchange, pair, type, order_id)
|
|
830
826
|
|
|
831
827
|
// Send delete event via WebSocket (only for non-INIT orders)
|
|
832
|
-
if (order_data.status !== 'INIT') {
|
|
833
|
-
|
|
828
|
+
if (order_data.status !== 'INIT' && ws_client) {
|
|
829
|
+
ws_client.send_order({
|
|
834
830
|
action: 'delete',
|
|
835
831
|
exchange,
|
|
836
832
|
pair,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@icgio/icg-exchanges-wrapper",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
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.
|
|
20
|
+
"@icgio/icg-exchanges": "^1.32.139",
|
|
21
21
|
"@icgio/icg-utils": "^1.9.44",
|
|
22
22
|
"lodash": "^4.17.23",
|
|
23
23
|
"pg": "^8.17.2"
|
package/lib/ws-data-client.js
DELETED
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
const WebSocket = require('ws')
|
|
2
|
-
const { randomUUID: uuid } = require('crypto')
|
|
3
|
-
|
|
4
|
-
// Hardcoded backend URL for production
|
|
5
|
-
const WS_BACKEND_URL = 'wss://api-prod.icg.io/ws/data/ingest'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* WebSocket client for streaming logs and order status to dashboard backend.
|
|
9
|
-
* Singleton instance shared across the application.
|
|
10
|
-
*/
|
|
11
|
-
class WSDataClient {
|
|
12
|
-
constructor() {
|
|
13
|
-
this.backend_url = WS_BACKEND_URL
|
|
14
|
-
this.secret = null
|
|
15
|
-
this.client_id = null
|
|
16
|
-
this.ws = null
|
|
17
|
-
this.queue = []
|
|
18
|
-
this.max_queue = 100
|
|
19
|
-
this.reconnect_interval = 5000
|
|
20
|
-
this.enabled = false
|
|
21
|
-
this.connected = false
|
|
22
|
-
this.reconnect_timer = null
|
|
23
|
-
this.sync_interval = null
|
|
24
|
-
this.sync_interval_ms = 30 * 1000 // 30 seconds
|
|
25
|
-
this.order_info_ref = null // Reference to Orders.order_info for periodic sync
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Initialize the WS client with connection details.
|
|
30
|
-
* @param {string} secret - Shared secret for authentication
|
|
31
|
-
* @param {string} client_id - Client identifier (e.g., "FREEZONE")
|
|
32
|
-
*/
|
|
33
|
-
init(secret, client_id) {
|
|
34
|
-
if (!secret || !client_id) {
|
|
35
|
-
console.log('[WSDataClient] Missing config (secret or client_id), disabled')
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
this.secret = secret
|
|
40
|
-
this.client_id = client_id
|
|
41
|
-
this.enabled = true
|
|
42
|
-
|
|
43
|
-
console.log('[WSDataClient] Initializing for client:', client_id, 'URL:', this.backend_url)
|
|
44
|
-
this.connect()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Set reference to order_info for periodic sync.
|
|
49
|
-
* @param {object} order_info - Reference to Orders.order_info object
|
|
50
|
-
*/
|
|
51
|
-
set_order_info_ref(order_info) {
|
|
52
|
-
this.order_info_ref = order_info
|
|
53
|
-
|
|
54
|
-
// Start periodic sync if not already started
|
|
55
|
-
if (!this.sync_interval && this.enabled) {
|
|
56
|
-
this.start_periodic_sync()
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Start periodic full sync of order_info (every 30 seconds).
|
|
62
|
-
*/
|
|
63
|
-
start_periodic_sync() {
|
|
64
|
-
if (this.sync_interval) return
|
|
65
|
-
|
|
66
|
-
// Send initial reset on startup
|
|
67
|
-
this.send_reset('orders')
|
|
68
|
-
|
|
69
|
-
this.sync_interval = setInterval(() => {
|
|
70
|
-
if (this.order_info_ref && this.connected) {
|
|
71
|
-
this.send_order_sync(this.order_info_ref)
|
|
72
|
-
}
|
|
73
|
-
}, this.sync_interval_ms)
|
|
74
|
-
|
|
75
|
-
console.log('[WSDataClient] Periodic sync started (every %ds)', this.sync_interval_ms / 1000)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Connect to the dashboard backend WebSocket.
|
|
80
|
-
*/
|
|
81
|
-
connect() {
|
|
82
|
-
if (!this.enabled) return
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
this.ws = new WebSocket(this.backend_url, {
|
|
86
|
-
headers: {
|
|
87
|
-
'x-client-secret': this.secret,
|
|
88
|
-
'x-client-id': this.client_id,
|
|
89
|
-
},
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
this.ws.on('open', () => {
|
|
93
|
-
console.log('[WSDataClient] Connected to backend')
|
|
94
|
-
this.connected = true
|
|
95
|
-
this.flush_queue()
|
|
96
|
-
|
|
97
|
-
// Send reset on reconnect to clear stale state
|
|
98
|
-
this.send_reset('orders')
|
|
99
|
-
|
|
100
|
-
// Send full sync immediately after connect
|
|
101
|
-
if (this.order_info_ref) {
|
|
102
|
-
this.send_order_sync(this.order_info_ref)
|
|
103
|
-
}
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
this.ws.on('close', (code, reason) => {
|
|
107
|
-
console.log('[WSDataClient] Disconnected:', code, reason?.toString())
|
|
108
|
-
this.connected = false
|
|
109
|
-
this.schedule_reconnect()
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
this.ws.on('error', (err) => {
|
|
113
|
-
console.error('[WSDataClient] Error:', err.message)
|
|
114
|
-
this.connected = false
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
this.ws.on('message', (data) => {
|
|
118
|
-
// Handle any messages from backend (e.g., acknowledgments)
|
|
119
|
-
try {
|
|
120
|
-
const msg = JSON.parse(data.toString())
|
|
121
|
-
if (msg.type === 'error') {
|
|
122
|
-
console.error('[WSDataClient] Backend error:', msg.message)
|
|
123
|
-
}
|
|
124
|
-
} catch (e) {
|
|
125
|
-
// Ignore parse errors
|
|
126
|
-
}
|
|
127
|
-
})
|
|
128
|
-
} catch (err) {
|
|
129
|
-
console.error('[WSDataClient] Connection error:', err.message)
|
|
130
|
-
this.schedule_reconnect()
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Schedule reconnection attempt.
|
|
136
|
-
*/
|
|
137
|
-
schedule_reconnect() {
|
|
138
|
-
if (this.reconnect_timer) return
|
|
139
|
-
|
|
140
|
-
this.reconnect_timer = setTimeout(() => {
|
|
141
|
-
this.reconnect_timer = null
|
|
142
|
-
console.log('[WSDataClient] Attempting reconnect...')
|
|
143
|
-
this.connect()
|
|
144
|
-
}, this.reconnect_interval)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Send a message to the backend.
|
|
149
|
-
* @param {string} type - Message type
|
|
150
|
-
* @param {object} data - Message data
|
|
151
|
-
*/
|
|
152
|
-
send(type, data) {
|
|
153
|
-
if (!this.enabled) return
|
|
154
|
-
|
|
155
|
-
const msg = JSON.stringify({
|
|
156
|
-
type,
|
|
157
|
-
client: this.client_id,
|
|
158
|
-
ts: Date.now(),
|
|
159
|
-
...data,
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
if (this.connected && this.ws?.readyState === WebSocket.OPEN) {
|
|
163
|
-
this.ws.send(msg)
|
|
164
|
-
} else {
|
|
165
|
-
// Queue message for later
|
|
166
|
-
this.queue.push(msg)
|
|
167
|
-
if (this.queue.length > this.max_queue) {
|
|
168
|
-
this.queue.shift() // Drop oldest
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Flush queued messages after reconnection.
|
|
175
|
-
*/
|
|
176
|
-
flush_queue() {
|
|
177
|
-
while (this.queue.length > 0 && this.connected && this.ws?.readyState === WebSocket.OPEN) {
|
|
178
|
-
const msg = this.queue.shift()
|
|
179
|
-
this.ws.send(msg)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Send reset event to clear state on backend.
|
|
185
|
-
* @param {string} scope - What to reset ('orders', 'logs', 'all')
|
|
186
|
-
*/
|
|
187
|
-
send_reset(scope = 'orders') {
|
|
188
|
-
this.send('reset', { scope })
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Send full order_info sync to backend.
|
|
193
|
-
* @param {object} order_info - Complete order_info object
|
|
194
|
-
*/
|
|
195
|
-
send_order_sync(order_info) {
|
|
196
|
-
this.send('order_sync', { data: order_info })
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Send order update event to backend.
|
|
201
|
-
* @param {object} event - Order event data
|
|
202
|
-
*/
|
|
203
|
-
send_order(event) {
|
|
204
|
-
this.send('order', event)
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Send log entry to backend.
|
|
209
|
-
* @param {string} level - Log level ('info', 'warn', 'error')
|
|
210
|
-
* @param {string} message - Log message
|
|
211
|
-
*/
|
|
212
|
-
send_log(level, message) {
|
|
213
|
-
this.send('log', {
|
|
214
|
-
id: uuid(),
|
|
215
|
-
level,
|
|
216
|
-
msg: message,
|
|
217
|
-
})
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Close the WebSocket connection.
|
|
222
|
-
*/
|
|
223
|
-
close() {
|
|
224
|
-
this.enabled = false
|
|
225
|
-
|
|
226
|
-
if (this.sync_interval) {
|
|
227
|
-
clearInterval(this.sync_interval)
|
|
228
|
-
this.sync_interval = null
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (this.reconnect_timer) {
|
|
232
|
-
clearTimeout(this.reconnect_timer)
|
|
233
|
-
this.reconnect_timer = null
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (this.ws) {
|
|
237
|
-
this.ws.close()
|
|
238
|
-
this.ws = null
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
this.connected = false
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Singleton instance
|
|
246
|
-
const ws_data_client = new WSDataClient()
|
|
247
|
-
|
|
248
|
-
module.exports = { WSDataClient, ws_data_client }
|