@backtest-kit/ui 0.0.1
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 +126 -0
- package/build/.gitkeep +0 -0
- package/build/index.cjs +1320 -0
- package/build/index.mjs +1312 -0
- package/build/modules/frontend/build/assets/index-DGpfEm4w.js +1 -0
- package/build/modules/frontend/build/assets/index-DqRtQQzL.js +230 -0
- package/build/modules/frontend/build/assets/index-DrRm3Jka.css +1 -0
- package/build/modules/frontend/build/assets/markdownit-BOD7oTDJ.js +1 -0
- package/build/modules/frontend/build/images/cubes.png +0 -0
- package/build/modules/frontend/build/images/dots.png +0 -0
- package/build/modules/frontend/build/images/snow.png +0 -0
- package/build/modules/frontend/build/images/square.png +0 -0
- package/build/modules/frontend/build/images/triangle.png +0 -0
- package/build/modules/frontend/build/index.html +155 -0
- package/build/modules/frontend/build/logo/icon512_maskable.png +0 -0
- package/build/modules/frontend/build/logo/icon512_rounded.png +0 -0
- package/build/modules/frontend/build/manifest.json +23 -0
- package/package.json +115 -0
- package/types.d.ts +160 -0
package/build/index.mjs
ADDED
|
@@ -0,0 +1,1312 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import { isObject, singleshot, pickDocuments, str, memoize, errorData, getErrorMessage } from 'functools-kit';
|
|
3
|
+
import micro from 'micro';
|
|
4
|
+
import Router from 'router';
|
|
5
|
+
import finalhandler from 'finalhandler';
|
|
6
|
+
import serveHandler from 'serve-handler';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import { createActivator } from 'di-kit';
|
|
9
|
+
import { Exchange, Notification, Storage } from 'backtest-kit';
|
|
10
|
+
import fs, { readdir, readFile } from 'fs/promises';
|
|
11
|
+
import path, { join, dirname } from 'path';
|
|
12
|
+
import { createRequire } from 'module';
|
|
13
|
+
import { existsSync } from 'fs';
|
|
14
|
+
|
|
15
|
+
const CC_WWWROOT_PATH = process.env.CC_WWWROOT_PATH || "";
|
|
16
|
+
const CC_WWWROOT_HOST = process.env.CC_WWWROOT_HOST || "0.0.0.0";
|
|
17
|
+
const CC_WWWROOT_PORT = parseInt(process.env.CC_WWWROOT_PORT) || 60050;
|
|
18
|
+
const CC_ENABLE_MOCK = !!parseInt(process.env.CC_ENABLE_MOCK) || false;
|
|
19
|
+
|
|
20
|
+
const router$6 = Router({
|
|
21
|
+
params: true,
|
|
22
|
+
});
|
|
23
|
+
router$6.get("/api/v1/health/health_check", async (req, res) => {
|
|
24
|
+
const [cpuLoad] = os.loadavg();
|
|
25
|
+
return await micro.send(res, 200, {
|
|
26
|
+
uptime: process.uptime(),
|
|
27
|
+
memoryUsage: process.memoryUsage(),
|
|
28
|
+
cpuLoad,
|
|
29
|
+
pid: process.pid,
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
function omit(obj, ...keys) {
|
|
34
|
+
const keySet = new Set(keys);
|
|
35
|
+
return Object.keys(obj).reduce((acc, key) => {
|
|
36
|
+
if (keySet.has(key)) {
|
|
37
|
+
return acc;
|
|
38
|
+
}
|
|
39
|
+
const value = obj[key];
|
|
40
|
+
if (isObject(value)) {
|
|
41
|
+
acc[key] = omit(value, ...keys);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
acc[key] = value;
|
|
45
|
+
}
|
|
46
|
+
return acc;
|
|
47
|
+
}, {});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { provide, inject, init, override } = createActivator("ui");
|
|
51
|
+
|
|
52
|
+
const baseServices$1 = {
|
|
53
|
+
loggerService: Symbol("loggerService"),
|
|
54
|
+
exchangeService: Symbol('exchangeService'),
|
|
55
|
+
};
|
|
56
|
+
const connectionServices$1 = {
|
|
57
|
+
symbolConnectionService: Symbol("symbolConnectionService"),
|
|
58
|
+
};
|
|
59
|
+
const metaServices$1 = {
|
|
60
|
+
symbolMetaService: Symbol("symbolMetaService"),
|
|
61
|
+
};
|
|
62
|
+
const mockServices$1 = {
|
|
63
|
+
notificationMockService: Symbol("notificationMockService"),
|
|
64
|
+
storageMockService: Symbol("storageMockService"),
|
|
65
|
+
exchangeMockService: Symbol("exchangeMockService"),
|
|
66
|
+
};
|
|
67
|
+
const viewServices$1 = {
|
|
68
|
+
notificationViewService: Symbol("notificationViewService"),
|
|
69
|
+
storageViewService: Symbol("storageViewService"),
|
|
70
|
+
exchangeViewService: Symbol("exchangeViewService"),
|
|
71
|
+
};
|
|
72
|
+
const TYPES = {
|
|
73
|
+
...baseServices$1,
|
|
74
|
+
...connectionServices$1,
|
|
75
|
+
...metaServices$1,
|
|
76
|
+
...mockServices$1,
|
|
77
|
+
...viewServices$1,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const INTERVAL_MINUTES = {
|
|
81
|
+
"1m": 1,
|
|
82
|
+
"3m": 3,
|
|
83
|
+
"5m": 5,
|
|
84
|
+
"15m": 15,
|
|
85
|
+
"30m": 30,
|
|
86
|
+
"1h": 60,
|
|
87
|
+
"2h": 120,
|
|
88
|
+
"4h": 240,
|
|
89
|
+
"6h": 360,
|
|
90
|
+
"8h": 480,
|
|
91
|
+
};
|
|
92
|
+
const STEP_TICKS = {
|
|
93
|
+
"1m": 960,
|
|
94
|
+
"3m": 960,
|
|
95
|
+
"5m": 960,
|
|
96
|
+
"15m": 960,
|
|
97
|
+
"30m": 960,
|
|
98
|
+
"1h": 960,
|
|
99
|
+
"2h": 960,
|
|
100
|
+
"4h": 960,
|
|
101
|
+
"6h": 960,
|
|
102
|
+
"8h": 960,
|
|
103
|
+
};
|
|
104
|
+
class ExchangeService {
|
|
105
|
+
constructor() {
|
|
106
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
107
|
+
this.getRangeCandles = async (dto) => {
|
|
108
|
+
this.loggerService.log("exchangeService getRangeCandles", {
|
|
109
|
+
dto,
|
|
110
|
+
});
|
|
111
|
+
const step = INTERVAL_MINUTES[dto.interval];
|
|
112
|
+
const tick = STEP_TICKS[dto.interval];
|
|
113
|
+
if (!step || !tick) {
|
|
114
|
+
throw new Error(`Unsupported interval: ${dto.interval}`);
|
|
115
|
+
}
|
|
116
|
+
const offsetMs = tick * step * 60 * 1000;
|
|
117
|
+
const sDate = dto.signalStartTime - offsetMs;
|
|
118
|
+
const eDate = Math.min(dto.signalStopTime + offsetMs, Date.now() - 1);
|
|
119
|
+
return await Exchange.getRawCandles(dto.symbol, dto.interval, {
|
|
120
|
+
exchangeName: dto.exchangeName,
|
|
121
|
+
}, undefined, sDate, eDate);
|
|
122
|
+
};
|
|
123
|
+
this.getPointCandles = async (dto) => {
|
|
124
|
+
this.loggerService.log("exchangeService getPointCandles", {
|
|
125
|
+
dto,
|
|
126
|
+
});
|
|
127
|
+
const step = INTERVAL_MINUTES[dto.interval];
|
|
128
|
+
const tick = STEP_TICKS[dto.interval];
|
|
129
|
+
if (!step || !tick) {
|
|
130
|
+
throw new Error(`Unsupported interval: ${dto.interval}`);
|
|
131
|
+
}
|
|
132
|
+
const offsetMs = tick * step * 60 * 1000;
|
|
133
|
+
const sDate = dto.currentTime - offsetMs;
|
|
134
|
+
const eDate = Math.min(dto.currentTime + offsetMs, Date.now() - 1);
|
|
135
|
+
return await Exchange.getRawCandles(dto.symbol, dto.interval, {
|
|
136
|
+
exchangeName: dto.exchangeName,
|
|
137
|
+
}, undefined, sDate, eDate);
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const NOOP_LOGGER = {
|
|
143
|
+
log() {
|
|
144
|
+
},
|
|
145
|
+
debug() {
|
|
146
|
+
},
|
|
147
|
+
info() {
|
|
148
|
+
},
|
|
149
|
+
warn() {
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
class LoggerService {
|
|
153
|
+
constructor() {
|
|
154
|
+
this._commonLogger = NOOP_LOGGER;
|
|
155
|
+
this.log = async (topic, ...args) => {
|
|
156
|
+
await this._commonLogger.log(topic, ...args);
|
|
157
|
+
};
|
|
158
|
+
this.debug = async (topic, ...args) => {
|
|
159
|
+
await this._commonLogger.debug(topic, ...args);
|
|
160
|
+
};
|
|
161
|
+
this.info = async (topic, ...args) => {
|
|
162
|
+
await this._commonLogger.info(topic, ...args);
|
|
163
|
+
};
|
|
164
|
+
this.warn = async (topic, ...args) => {
|
|
165
|
+
await this._commonLogger.warn(topic, ...args);
|
|
166
|
+
};
|
|
167
|
+
this.setLogger = (logger) => {
|
|
168
|
+
this._commonLogger = logger;
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const MOCK_PATH$1 = "./mock/notifications.json";
|
|
174
|
+
const READ_NOTIFICATION_LIST_FN = singleshot(async () => {
|
|
175
|
+
const data = await fs.readFile(MOCK_PATH$1, "utf-8");
|
|
176
|
+
return JSON.parse(data);
|
|
177
|
+
});
|
|
178
|
+
const DEFAULT_LIMIT$1 = 25;
|
|
179
|
+
const DEFAULT_OFFSET$1 = 0;
|
|
180
|
+
const CREATE_FILTER_LIST_FN$1 = (filterData) => Object.keys(filterData).map((key) => (row) => new RegExp(filterData[key], "i").test(row[key]));
|
|
181
|
+
class NotificationMockService {
|
|
182
|
+
constructor() {
|
|
183
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
184
|
+
this.findByFilter = async (filterData, limit = DEFAULT_LIMIT$1, offset = DEFAULT_OFFSET$1) => {
|
|
185
|
+
this.loggerService.log("notificationMockService findByFilter", {
|
|
186
|
+
filterData,
|
|
187
|
+
limit,
|
|
188
|
+
offset,
|
|
189
|
+
});
|
|
190
|
+
const iter = pickDocuments(limit, offset);
|
|
191
|
+
const filterList = CREATE_FILTER_LIST_FN$1(filterData);
|
|
192
|
+
for (const notification of await this.getList()) {
|
|
193
|
+
let isOk = true;
|
|
194
|
+
for (const filterFn of filterList) {
|
|
195
|
+
isOk = isOk && filterFn(notification);
|
|
196
|
+
}
|
|
197
|
+
if (!isOk) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (iter([notification]).done) {
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return iter().rows;
|
|
205
|
+
};
|
|
206
|
+
this.getList = async () => {
|
|
207
|
+
this.loggerService.log("notificationMockService getList");
|
|
208
|
+
return await READ_NOTIFICATION_LIST_FN();
|
|
209
|
+
};
|
|
210
|
+
this.getOne = async (id) => {
|
|
211
|
+
this.loggerService.log("notificationMockService getOne");
|
|
212
|
+
const notificationList = await this.getList();
|
|
213
|
+
return notificationList.find((item) => item.id === id) ?? null;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const MOCK_PATH = "./mock/db";
|
|
219
|
+
const READ_BACKTEST_STORAGE_FN = singleshot(async () => {
|
|
220
|
+
const dbPath = join(process.cwd(), MOCK_PATH);
|
|
221
|
+
const files = await readdir(dbPath);
|
|
222
|
+
const signals = [];
|
|
223
|
+
for (const file of files) {
|
|
224
|
+
if (!file.endsWith(".json")) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const filePath = join(dbPath, file);
|
|
228
|
+
signals.push(JSON.parse(await readFile(filePath, "utf-8")));
|
|
229
|
+
}
|
|
230
|
+
return signals;
|
|
231
|
+
});
|
|
232
|
+
class StorageMockService {
|
|
233
|
+
constructor() {
|
|
234
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
235
|
+
this.findSignalById = async (signalId) => {
|
|
236
|
+
this.loggerService.log("storageMockService findSignalById", {
|
|
237
|
+
signalId,
|
|
238
|
+
});
|
|
239
|
+
const signalList = await READ_BACKTEST_STORAGE_FN();
|
|
240
|
+
const signalMap = new Map(signalList.map((signal) => [signal.id, signal]));
|
|
241
|
+
const signalValue = signalMap.get(signalId);
|
|
242
|
+
return signalValue ?? null;
|
|
243
|
+
};
|
|
244
|
+
this.listSignalLive = async () => {
|
|
245
|
+
this.loggerService.log("storageMockService listSignalLive");
|
|
246
|
+
return Promise.resolve([]);
|
|
247
|
+
};
|
|
248
|
+
this.listSignalBacktest = async () => {
|
|
249
|
+
this.loggerService.log("storageMockService listSignalBacktest");
|
|
250
|
+
return await READ_BACKTEST_STORAGE_FN();
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
class ExchangeMockService {
|
|
256
|
+
constructor() {
|
|
257
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
258
|
+
this.storageMockService = inject(TYPES.storageMockService);
|
|
259
|
+
this.exchangeService = inject(TYPES.exchangeService);
|
|
260
|
+
this.getSignalCandles = async (signalId, interval) => {
|
|
261
|
+
this.loggerService.log("exchangeMockService getSignalCandles", {
|
|
262
|
+
signalId,
|
|
263
|
+
interval,
|
|
264
|
+
});
|
|
265
|
+
const signal = await this.storageMockService.findSignalById(signalId);
|
|
266
|
+
if (!signal) {
|
|
267
|
+
throw new Error(`Signal with ID ${signalId} not found`);
|
|
268
|
+
}
|
|
269
|
+
const { pendingAt, scheduledAt, createdAt = pendingAt || scheduledAt, updatedAt, } = signal;
|
|
270
|
+
return await this.exchangeService.getRangeCandles({
|
|
271
|
+
symbol: signal.symbol,
|
|
272
|
+
exchangeName: signal.exchangeName,
|
|
273
|
+
signalStartTime: createdAt,
|
|
274
|
+
signalStopTime: updatedAt,
|
|
275
|
+
interval,
|
|
276
|
+
});
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const DEFAULT_LIMIT = 25;
|
|
282
|
+
const DEFAULT_OFFSET = 0;
|
|
283
|
+
const CREATE_FILTER_LIST_FN = (filterData) => Object.keys(filterData).map((key) => (row) => new RegExp(filterData[key], "i").test(row[key]));
|
|
284
|
+
class NotificationViewService {
|
|
285
|
+
constructor() {
|
|
286
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
287
|
+
this.notificationMockService = inject(TYPES.notificationMockService);
|
|
288
|
+
this.findByFilter = async (filterData, limit = DEFAULT_LIMIT, offset = DEFAULT_OFFSET) => {
|
|
289
|
+
this.loggerService.log("notificationViewService findByFilter", {
|
|
290
|
+
filterData,
|
|
291
|
+
limit,
|
|
292
|
+
offset,
|
|
293
|
+
});
|
|
294
|
+
if (CC_ENABLE_MOCK) {
|
|
295
|
+
return await this.notificationMockService.findByFilter(filterData, limit, offset);
|
|
296
|
+
}
|
|
297
|
+
const iter = pickDocuments(limit, offset);
|
|
298
|
+
const filterList = CREATE_FILTER_LIST_FN(filterData);
|
|
299
|
+
for (const notification of await this.getList()) {
|
|
300
|
+
let isOk = true;
|
|
301
|
+
for (const filterFn of filterList) {
|
|
302
|
+
isOk = isOk && filterFn(notification);
|
|
303
|
+
}
|
|
304
|
+
if (!isOk) {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (iter([notification]).done) {
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return iter().rows;
|
|
312
|
+
};
|
|
313
|
+
this.getList = async () => {
|
|
314
|
+
this.loggerService.log("notificationViewService getList");
|
|
315
|
+
if (CC_ENABLE_MOCK) {
|
|
316
|
+
return await this.notificationMockService.getList();
|
|
317
|
+
}
|
|
318
|
+
const notificationList = [];
|
|
319
|
+
for (const notification of await Notification.getData(false)) {
|
|
320
|
+
notificationList.push(notification);
|
|
321
|
+
}
|
|
322
|
+
for (const notification of await Notification.getData(true)) {
|
|
323
|
+
notificationList.push(notification);
|
|
324
|
+
}
|
|
325
|
+
return notificationList;
|
|
326
|
+
};
|
|
327
|
+
this.getOne = async (id) => {
|
|
328
|
+
this.loggerService.log("notificationViewService getOne", {
|
|
329
|
+
id,
|
|
330
|
+
});
|
|
331
|
+
if (CC_ENABLE_MOCK) {
|
|
332
|
+
return await this.notificationMockService.getOne(id);
|
|
333
|
+
}
|
|
334
|
+
const notificationList = await this.getList();
|
|
335
|
+
return notificationList.find((item) => item.id === id) ?? null;
|
|
336
|
+
};
|
|
337
|
+
this.init = singleshot(async () => {
|
|
338
|
+
this.loggerService.log("notificationViewService init");
|
|
339
|
+
if (CC_ENABLE_MOCK) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
Notification.enable();
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
class StorageViewService {
|
|
348
|
+
constructor() {
|
|
349
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
350
|
+
this.storageMockService = inject(TYPES.storageMockService);
|
|
351
|
+
this.findSignalById = async (signalId) => {
|
|
352
|
+
this.loggerService.log("storageViewService findSignalById", {
|
|
353
|
+
signalId,
|
|
354
|
+
});
|
|
355
|
+
if (CC_ENABLE_MOCK) {
|
|
356
|
+
return await this.storageMockService.findSignalById(signalId);
|
|
357
|
+
}
|
|
358
|
+
return await Storage.findSignalById(signalId);
|
|
359
|
+
};
|
|
360
|
+
this.listSignalLive = async () => {
|
|
361
|
+
this.loggerService.log("storageViewService listSignalLive");
|
|
362
|
+
if (CC_ENABLE_MOCK) {
|
|
363
|
+
return await this.storageMockService.listSignalLive();
|
|
364
|
+
}
|
|
365
|
+
return await Storage.listSignalLive();
|
|
366
|
+
};
|
|
367
|
+
this.listSignalBacktest = async () => {
|
|
368
|
+
this.loggerService.log("storageViewService listSignalBacktest");
|
|
369
|
+
if (CC_ENABLE_MOCK) {
|
|
370
|
+
return await this.storageMockService.listSignalBacktest();
|
|
371
|
+
}
|
|
372
|
+
return await Storage.listSignalBacktest();
|
|
373
|
+
};
|
|
374
|
+
this.init = singleshot(async () => {
|
|
375
|
+
this.loggerService.log("storageViewService init");
|
|
376
|
+
if (CC_ENABLE_MOCK) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
Storage.enable();
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
class ExchangeViewService {
|
|
385
|
+
constructor() {
|
|
386
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
387
|
+
this.storageViewService = inject(TYPES.storageViewService);
|
|
388
|
+
this.exchangeService = inject(TYPES.exchangeService);
|
|
389
|
+
this.exchangeMockService = inject(TYPES.exchangeMockService);
|
|
390
|
+
this.getSignalCandles = async (signalId, interval) => {
|
|
391
|
+
this.loggerService.log("exchangeViewService getCandles", {
|
|
392
|
+
signalId,
|
|
393
|
+
interval,
|
|
394
|
+
});
|
|
395
|
+
if (CC_ENABLE_MOCK) {
|
|
396
|
+
return await this.exchangeMockService.getSignalCandles(signalId, interval);
|
|
397
|
+
}
|
|
398
|
+
const signal = await this.storageViewService.findSignalById(signalId);
|
|
399
|
+
if (!signal) {
|
|
400
|
+
throw new Error(`Signal with ID ${signalId} not found`);
|
|
401
|
+
}
|
|
402
|
+
const { pendingAt, scheduledAt, createdAt = pendingAt || scheduledAt, updatedAt, } = signal;
|
|
403
|
+
return await this.exchangeService.getRangeCandles({
|
|
404
|
+
symbol: signal.symbol,
|
|
405
|
+
exchangeName: signal.exchangeName,
|
|
406
|
+
signalStartTime: createdAt,
|
|
407
|
+
signalStopTime: updatedAt,
|
|
408
|
+
interval,
|
|
409
|
+
});
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const symbol_list = [
|
|
415
|
+
{
|
|
416
|
+
icon: "/icon/btc.png",
|
|
417
|
+
logo: "/icon/128/btc.png",
|
|
418
|
+
symbol: "BTCUSDT",
|
|
419
|
+
displayName: "Bitcoin",
|
|
420
|
+
color: "#F7931A",
|
|
421
|
+
priority: 50,
|
|
422
|
+
description: str.newline("Bitcoin - the first and most popular cryptocurrency", "Digital gold with a limited supply of 21 million coins", "Used as a reserve asset and a store of value", "Highly liquid market with large trading volumes"),
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
icon: "/icon/eth.png",
|
|
426
|
+
logo: "/icon/128/eth.png",
|
|
427
|
+
symbol: "ETHUSDT",
|
|
428
|
+
color: "#6F42C1",
|
|
429
|
+
displayName: "Ethereum",
|
|
430
|
+
priority: 50,
|
|
431
|
+
description: str.newline("Ethereum - a blockchain platform for smart contracts", "The foundation of DeFi and NFT ecosystems", "Transition to Proof-of-Stake (Ethereum 2.0)", "Second largest cryptocurrency by market capitalization"),
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
icon: "/icon/bnb.png",
|
|
435
|
+
logo: "/icon/128/bnb.png",
|
|
436
|
+
symbol: "BNBUSDT",
|
|
437
|
+
color: "#F3BA2F",
|
|
438
|
+
displayName: "BNB",
|
|
439
|
+
priority: 50,
|
|
440
|
+
description: str.newline("Binance Coin - the native token of the largest exchange", "Used for trading fee discounts", "Core of BNB Smart Chain for decentralized apps (DApps)", "Regular token burns reduce the circulating supply"),
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
icon: "/icon/xrp.png",
|
|
444
|
+
logo: "/icon/128/xrp.png",
|
|
445
|
+
symbol: "XRPUSDT",
|
|
446
|
+
color: "#23292F",
|
|
447
|
+
displayName: "Ripple",
|
|
448
|
+
priority: 100,
|
|
449
|
+
description: str.newline("XRP - a digital asset for international payments", "Fast and low-cost cross-border transfers", "Used by banks and financial institutions", "Low fees and transaction times of 3-5 seconds"),
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
icon: "/icon/sol.png",
|
|
453
|
+
logo: "/icon/128/sol.png",
|
|
454
|
+
symbol: "SOLUSDT",
|
|
455
|
+
color: "#00e676",
|
|
456
|
+
displayName: "Solana",
|
|
457
|
+
priority: 100,
|
|
458
|
+
description: str.newline("Solana - a high-performance blockchain", "Up to 65,000 transactions per second", "A popular platform for NFT and DeFi projects", "Low fees and fast transaction confirmation"),
|
|
459
|
+
},
|
|
460
|
+
];
|
|
461
|
+
|
|
462
|
+
const require$2 = createRequire(import.meta.url);
|
|
463
|
+
const getSymbolList = singleshot(() => {
|
|
464
|
+
try {
|
|
465
|
+
const modulePath = require$2.resolve(path.join(process.cwd(), `./config/symbol.config.cjs`));
|
|
466
|
+
console.log(`Using ${modulePath} implementation as symbol.config.cjs`);
|
|
467
|
+
return require$2(modulePath);
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
console.log(`Using default implementation for symbol.config.cjs`, error);
|
|
471
|
+
return symbol_list;
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
class SymbolConnectionService {
|
|
475
|
+
constructor() {
|
|
476
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
477
|
+
this.getSymbolList = singleshot(async () => {
|
|
478
|
+
this.loggerService.log("symbolConnectionService getSymbolList");
|
|
479
|
+
const symbolListRaw = await getSymbolList();
|
|
480
|
+
const uniqueSymbols = new Set();
|
|
481
|
+
const symbolList = symbolListRaw
|
|
482
|
+
.filter((item) => {
|
|
483
|
+
if (uniqueSymbols.has(item.symbol)) {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
uniqueSymbols.add(item.symbol);
|
|
487
|
+
return true;
|
|
488
|
+
})
|
|
489
|
+
.map(({ priority, displayName, symbol, logo, icon, ...other }, idx) => ({
|
|
490
|
+
symbol,
|
|
491
|
+
icon,
|
|
492
|
+
logo: logo ?? icon,
|
|
493
|
+
priority: priority ?? idx,
|
|
494
|
+
displayName: displayName ?? symbol,
|
|
495
|
+
index: idx,
|
|
496
|
+
...other,
|
|
497
|
+
}));
|
|
498
|
+
symbolList.sort(({ priority: a_p, index: a_x }, { priority: b_p, index: b_x }) => b_p - a_p || a_x - b_x);
|
|
499
|
+
return symbolList;
|
|
500
|
+
});
|
|
501
|
+
this.init = singleshot(async () => {
|
|
502
|
+
this.loggerService.log("symbolConnectionService init");
|
|
503
|
+
getSymbolList();
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
class SymbolMetaService {
|
|
509
|
+
constructor() {
|
|
510
|
+
this.symbolConnectionService = inject(TYPES.symbolConnectionService);
|
|
511
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
512
|
+
this.getSymbolList = singleshot(async () => {
|
|
513
|
+
this.loggerService.log("symbolMetaService getSymbolList");
|
|
514
|
+
const symbolList = await this.symbolConnectionService.getSymbolList();
|
|
515
|
+
return symbolList.map(({ symbol }) => symbol);
|
|
516
|
+
});
|
|
517
|
+
this.getSymbolMap = singleshot(async () => {
|
|
518
|
+
this.loggerService.log("symbolMetaService getSymbolMap");
|
|
519
|
+
const symbolList = await this.symbolConnectionService.getSymbolList();
|
|
520
|
+
return symbolList.reduce((acm, { symbol, ...other }) => ({
|
|
521
|
+
...acm,
|
|
522
|
+
[symbol]: { symbol, ...other },
|
|
523
|
+
}), {});
|
|
524
|
+
});
|
|
525
|
+
this.getSymbol = memoize(([symbol]) => `${symbol}`, async (symbol) => {
|
|
526
|
+
this.loggerService.log("symbolMetaService getSymbol", {
|
|
527
|
+
symbol,
|
|
528
|
+
});
|
|
529
|
+
const symbolList = await this.symbolConnectionService.getSymbolList();
|
|
530
|
+
const target = symbolList.find((item) => item.symbol === symbol);
|
|
531
|
+
if (!target) {
|
|
532
|
+
throw new Error(`symbolMetaService getSymbol no item found symbol=${symbol}`);
|
|
533
|
+
}
|
|
534
|
+
return target;
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
{
|
|
540
|
+
provide(TYPES.loggerService, () => new LoggerService());
|
|
541
|
+
provide(TYPES.exchangeService, () => new ExchangeService());
|
|
542
|
+
}
|
|
543
|
+
{
|
|
544
|
+
provide(TYPES.symbolConnectionService, () => new SymbolConnectionService());
|
|
545
|
+
}
|
|
546
|
+
{
|
|
547
|
+
provide(TYPES.symbolMetaService, () => new SymbolMetaService());
|
|
548
|
+
}
|
|
549
|
+
{
|
|
550
|
+
provide(TYPES.notificationMockService, () => new NotificationMockService());
|
|
551
|
+
provide(TYPES.storageMockService, () => new StorageMockService());
|
|
552
|
+
provide(TYPES.exchangeMockService, () => new ExchangeMockService());
|
|
553
|
+
}
|
|
554
|
+
{
|
|
555
|
+
provide(TYPES.notificationViewService, () => new NotificationViewService());
|
|
556
|
+
provide(TYPES.storageViewService, () => new StorageViewService());
|
|
557
|
+
provide(TYPES.exchangeViewService, () => new ExchangeViewService());
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const baseServices = {
|
|
561
|
+
loggerService: inject(TYPES.loggerService),
|
|
562
|
+
exchangeService: inject(TYPES.exchangeService),
|
|
563
|
+
};
|
|
564
|
+
const connectionServices = {
|
|
565
|
+
symbolConnectionService: inject(TYPES.symbolConnectionService),
|
|
566
|
+
};
|
|
567
|
+
const metaServices = {
|
|
568
|
+
symbolMetaService: inject(TYPES.symbolMetaService),
|
|
569
|
+
};
|
|
570
|
+
const mockServices = {
|
|
571
|
+
notificationMockService: inject(TYPES.notificationMockService),
|
|
572
|
+
storageMockService: inject(TYPES.storageMockService),
|
|
573
|
+
exchangeMockService: inject(TYPES.exchangeMockService),
|
|
574
|
+
};
|
|
575
|
+
const viewServices = {
|
|
576
|
+
notificationViewService: inject(TYPES.notificationViewService),
|
|
577
|
+
storageViewService: inject(TYPES.storageViewService),
|
|
578
|
+
exchangeViewService: inject(TYPES.exchangeViewService),
|
|
579
|
+
};
|
|
580
|
+
const ioc = {
|
|
581
|
+
...baseServices,
|
|
582
|
+
...connectionServices,
|
|
583
|
+
...metaServices,
|
|
584
|
+
...mockServices,
|
|
585
|
+
...viewServices,
|
|
586
|
+
};
|
|
587
|
+
init();
|
|
588
|
+
|
|
589
|
+
const router$5 = Router({
|
|
590
|
+
params: true,
|
|
591
|
+
});
|
|
592
|
+
// ExchangeMockService endpoints
|
|
593
|
+
router$5.post("/api/v1/mock/candles_signal", async (req, res) => {
|
|
594
|
+
try {
|
|
595
|
+
const request = await micro.json(req);
|
|
596
|
+
const { signalId, interval, requestId, serviceName } = request;
|
|
597
|
+
const data = await ioc.exchangeMockService.getSignalCandles(signalId, interval);
|
|
598
|
+
const result = {
|
|
599
|
+
data,
|
|
600
|
+
status: "ok",
|
|
601
|
+
error: "",
|
|
602
|
+
requestId,
|
|
603
|
+
serviceName,
|
|
604
|
+
};
|
|
605
|
+
ioc.loggerService.log("/api/v1/mock/candles_signal ok", {
|
|
606
|
+
request,
|
|
607
|
+
result: omit(result, "data"),
|
|
608
|
+
});
|
|
609
|
+
return await micro.send(res, 200, result);
|
|
610
|
+
}
|
|
611
|
+
catch (error) {
|
|
612
|
+
ioc.loggerService.log("/api/v1/mock/candles_signal error", {
|
|
613
|
+
error: errorData(error),
|
|
614
|
+
});
|
|
615
|
+
return await micro.send(res, 200, {
|
|
616
|
+
status: "error",
|
|
617
|
+
error: getErrorMessage(error),
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
router$5.post("/api/v1/mock/candles_point", async (req, res) => {
|
|
622
|
+
try {
|
|
623
|
+
const request = await micro.json(req);
|
|
624
|
+
const { currentTime, interval, requestId, serviceName, symbol, exchangeName } = request;
|
|
625
|
+
const data = await ioc.exchangeService.getPointCandles({
|
|
626
|
+
currentTime,
|
|
627
|
+
interval,
|
|
628
|
+
symbol,
|
|
629
|
+
exchangeName,
|
|
630
|
+
});
|
|
631
|
+
const result = {
|
|
632
|
+
data,
|
|
633
|
+
status: "ok",
|
|
634
|
+
error: "",
|
|
635
|
+
requestId,
|
|
636
|
+
serviceName,
|
|
637
|
+
};
|
|
638
|
+
ioc.loggerService.log("/api/v1/mock/candles_point ok", {
|
|
639
|
+
request,
|
|
640
|
+
result: omit(result, "data"),
|
|
641
|
+
});
|
|
642
|
+
return await micro.send(res, 200, result);
|
|
643
|
+
}
|
|
644
|
+
catch (error) {
|
|
645
|
+
ioc.loggerService.log("/api/v1/mock/candles_point error", {
|
|
646
|
+
error: errorData(error),
|
|
647
|
+
});
|
|
648
|
+
return await micro.send(res, 200, {
|
|
649
|
+
status: "error",
|
|
650
|
+
error: getErrorMessage(error),
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
// NotificationMockService endpoints
|
|
655
|
+
router$5.post("/api/v1/mock/notification_list", async (req, res) => {
|
|
656
|
+
try {
|
|
657
|
+
const request = await micro.json(req);
|
|
658
|
+
const { requestId, serviceName } = request;
|
|
659
|
+
const data = await ioc.notificationMockService.getList();
|
|
660
|
+
const result = {
|
|
661
|
+
data,
|
|
662
|
+
status: "ok",
|
|
663
|
+
error: "",
|
|
664
|
+
requestId,
|
|
665
|
+
serviceName,
|
|
666
|
+
};
|
|
667
|
+
ioc.loggerService.log("/api/v1/mock/notification_list ok", {
|
|
668
|
+
request,
|
|
669
|
+
result: omit(result, "data"),
|
|
670
|
+
});
|
|
671
|
+
return await micro.send(res, 200, result);
|
|
672
|
+
}
|
|
673
|
+
catch (error) {
|
|
674
|
+
ioc.loggerService.log("/api/v1/mock/notification_list error", {
|
|
675
|
+
error: errorData(error),
|
|
676
|
+
});
|
|
677
|
+
return await micro.send(res, 200, {
|
|
678
|
+
status: "error",
|
|
679
|
+
error: getErrorMessage(error),
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
router$5.post("/api/v1/mock/notification_one/:id", async (req, res) => {
|
|
684
|
+
try {
|
|
685
|
+
const request = await micro.json(req);
|
|
686
|
+
const { requestId, serviceName } = request;
|
|
687
|
+
const id = req.params.id;
|
|
688
|
+
const data = await ioc.notificationMockService.getOne(id);
|
|
689
|
+
const result = {
|
|
690
|
+
data,
|
|
691
|
+
status: "ok",
|
|
692
|
+
error: "",
|
|
693
|
+
requestId,
|
|
694
|
+
serviceName,
|
|
695
|
+
};
|
|
696
|
+
ioc.loggerService.log("/api/v1/mock/notification_one/:id ok", {
|
|
697
|
+
request,
|
|
698
|
+
result: omit(result, "data"),
|
|
699
|
+
});
|
|
700
|
+
return await micro.send(res, 200, result);
|
|
701
|
+
}
|
|
702
|
+
catch (error) {
|
|
703
|
+
ioc.loggerService.log("/api/v1/mock/notification_one/:id error", {
|
|
704
|
+
error: errorData(error),
|
|
705
|
+
});
|
|
706
|
+
return await micro.send(res, 200, {
|
|
707
|
+
status: "error",
|
|
708
|
+
error: getErrorMessage(error),
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
router$5.post("/api/v1/mock/notification_filter", async (req, res) => {
|
|
713
|
+
try {
|
|
714
|
+
const request = await micro.json(req);
|
|
715
|
+
const { requestId, serviceName, filterData, limit, offset } = request;
|
|
716
|
+
const data = await ioc.notificationMockService.findByFilter(filterData, limit, offset);
|
|
717
|
+
const result = {
|
|
718
|
+
data,
|
|
719
|
+
status: "ok",
|
|
720
|
+
error: "",
|
|
721
|
+
requestId,
|
|
722
|
+
serviceName,
|
|
723
|
+
};
|
|
724
|
+
ioc.loggerService.log("/api/v1/mock/notification_filter ok", {
|
|
725
|
+
request,
|
|
726
|
+
result: omit(result, "data"),
|
|
727
|
+
});
|
|
728
|
+
return await micro.send(res, 200, result);
|
|
729
|
+
}
|
|
730
|
+
catch (error) {
|
|
731
|
+
ioc.loggerService.log("/api/v1/mock/notification_filter error", {
|
|
732
|
+
error: errorData(error),
|
|
733
|
+
});
|
|
734
|
+
return await micro.send(res, 200, {
|
|
735
|
+
status: "error",
|
|
736
|
+
error: getErrorMessage(error),
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
// StorageMockService endpoints
|
|
741
|
+
router$5.post("/api/v1/mock/storage_one/:id", async (req, res) => {
|
|
742
|
+
try {
|
|
743
|
+
const request = await micro.json(req);
|
|
744
|
+
const { requestId, serviceName } = request;
|
|
745
|
+
const signalId = req.params.id;
|
|
746
|
+
const data = await ioc.storageMockService.findSignalById(signalId);
|
|
747
|
+
const result = {
|
|
748
|
+
data,
|
|
749
|
+
status: "ok",
|
|
750
|
+
error: "",
|
|
751
|
+
requestId,
|
|
752
|
+
serviceName,
|
|
753
|
+
};
|
|
754
|
+
ioc.loggerService.log("/api/v1/mock/storage_one/:id ok", {
|
|
755
|
+
request,
|
|
756
|
+
result: omit(result, "data"),
|
|
757
|
+
});
|
|
758
|
+
return await micro.send(res, 200, result);
|
|
759
|
+
}
|
|
760
|
+
catch (error) {
|
|
761
|
+
ioc.loggerService.log("/api/v1/mock/storage_one/:id error", {
|
|
762
|
+
error: errorData(error),
|
|
763
|
+
});
|
|
764
|
+
return await micro.send(res, 200, {
|
|
765
|
+
status: "error",
|
|
766
|
+
error: getErrorMessage(error),
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
router$5.post("/api/v1/mock/storage_list/live", async (req, res) => {
|
|
771
|
+
try {
|
|
772
|
+
const request = await micro.json(req);
|
|
773
|
+
const { requestId, serviceName } = request;
|
|
774
|
+
const data = await ioc.storageMockService.listSignalLive();
|
|
775
|
+
const result = {
|
|
776
|
+
data,
|
|
777
|
+
status: "ok",
|
|
778
|
+
error: "",
|
|
779
|
+
requestId,
|
|
780
|
+
serviceName,
|
|
781
|
+
};
|
|
782
|
+
ioc.loggerService.log("/api/v1/mock/storage_list/live ok", {
|
|
783
|
+
request,
|
|
784
|
+
result: omit(result, "data"),
|
|
785
|
+
});
|
|
786
|
+
return await micro.send(res, 200, result);
|
|
787
|
+
}
|
|
788
|
+
catch (error) {
|
|
789
|
+
ioc.loggerService.log("/api/v1/mock/storage_list/live error", {
|
|
790
|
+
error: errorData(error),
|
|
791
|
+
});
|
|
792
|
+
return await micro.send(res, 200, {
|
|
793
|
+
status: "error",
|
|
794
|
+
error: getErrorMessage(error),
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
router$5.post("/api/v1/mock/storage_list/backtest", async (req, res) => {
|
|
799
|
+
try {
|
|
800
|
+
const request = await micro.json(req);
|
|
801
|
+
const { requestId, serviceName } = request;
|
|
802
|
+
const data = await ioc.storageMockService.listSignalBacktest();
|
|
803
|
+
const result = {
|
|
804
|
+
data,
|
|
805
|
+
status: "ok",
|
|
806
|
+
error: "",
|
|
807
|
+
requestId,
|
|
808
|
+
serviceName,
|
|
809
|
+
};
|
|
810
|
+
ioc.loggerService.log("/api/v1/mock/storage_list/backtest ok", {
|
|
811
|
+
request,
|
|
812
|
+
result: omit(result, "data"),
|
|
813
|
+
});
|
|
814
|
+
return await micro.send(res, 200, result);
|
|
815
|
+
}
|
|
816
|
+
catch (error) {
|
|
817
|
+
ioc.loggerService.log("/api/v1/mock/storage_list/backtest error", {
|
|
818
|
+
error: errorData(error),
|
|
819
|
+
});
|
|
820
|
+
return await micro.send(res, 200, {
|
|
821
|
+
status: "error",
|
|
822
|
+
error: getErrorMessage(error),
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
const router$4 = Router({
|
|
828
|
+
params: true,
|
|
829
|
+
});
|
|
830
|
+
// ExchangeViewService endpoints
|
|
831
|
+
router$4.post("/api/v1/view/candles_signal", async (req, res) => {
|
|
832
|
+
try {
|
|
833
|
+
const request = await micro.json(req);
|
|
834
|
+
const { signalId, interval, requestId, serviceName } = request;
|
|
835
|
+
const data = await ioc.exchangeViewService.getSignalCandles(signalId, interval);
|
|
836
|
+
const result = {
|
|
837
|
+
data,
|
|
838
|
+
status: "ok",
|
|
839
|
+
error: "",
|
|
840
|
+
requestId,
|
|
841
|
+
serviceName,
|
|
842
|
+
};
|
|
843
|
+
ioc.loggerService.log("/api/v1/view/candles_signal ok", {
|
|
844
|
+
request,
|
|
845
|
+
result: omit(result, "data"),
|
|
846
|
+
});
|
|
847
|
+
return await micro.send(res, 200, result);
|
|
848
|
+
}
|
|
849
|
+
catch (error) {
|
|
850
|
+
ioc.loggerService.log("/api/v1/view/candles_signal error", {
|
|
851
|
+
error: errorData(error),
|
|
852
|
+
});
|
|
853
|
+
return await micro.send(res, 200, {
|
|
854
|
+
status: "error",
|
|
855
|
+
error: getErrorMessage(error),
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
router$4.post("/api/v1/view/candles_point", async (req, res) => {
|
|
860
|
+
try {
|
|
861
|
+
const request = await micro.json(req);
|
|
862
|
+
const { currentTime, interval, requestId, serviceName, symbol, exchangeName } = request;
|
|
863
|
+
const data = await ioc.exchangeService.getPointCandles({
|
|
864
|
+
currentTime,
|
|
865
|
+
interval,
|
|
866
|
+
symbol,
|
|
867
|
+
exchangeName,
|
|
868
|
+
});
|
|
869
|
+
const result = {
|
|
870
|
+
data,
|
|
871
|
+
status: "ok",
|
|
872
|
+
error: "",
|
|
873
|
+
requestId,
|
|
874
|
+
serviceName,
|
|
875
|
+
};
|
|
876
|
+
ioc.loggerService.log("/api/v1/view/candles_point ok", {
|
|
877
|
+
request,
|
|
878
|
+
result: omit(result, "data"),
|
|
879
|
+
});
|
|
880
|
+
return await micro.send(res, 200, result);
|
|
881
|
+
}
|
|
882
|
+
catch (error) {
|
|
883
|
+
ioc.loggerService.log("/api/v1/view/candles_point error", {
|
|
884
|
+
error: errorData(error),
|
|
885
|
+
});
|
|
886
|
+
return await micro.send(res, 200, {
|
|
887
|
+
status: "error",
|
|
888
|
+
error: getErrorMessage(error),
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
// NotificationViewService endpoints
|
|
893
|
+
router$4.post("/api/v1/view/notification_list", async (req, res) => {
|
|
894
|
+
try {
|
|
895
|
+
const request = await micro.json(req);
|
|
896
|
+
const { requestId, serviceName } = request;
|
|
897
|
+
const data = await ioc.notificationViewService.getList();
|
|
898
|
+
const result = {
|
|
899
|
+
data,
|
|
900
|
+
status: "ok",
|
|
901
|
+
error: "",
|
|
902
|
+
requestId,
|
|
903
|
+
serviceName,
|
|
904
|
+
};
|
|
905
|
+
ioc.loggerService.log("/api/v1/view/notification_list ok", {
|
|
906
|
+
request,
|
|
907
|
+
result: omit(result, "data"),
|
|
908
|
+
});
|
|
909
|
+
return await micro.send(res, 200, result);
|
|
910
|
+
}
|
|
911
|
+
catch (error) {
|
|
912
|
+
ioc.loggerService.log("/api/v1/view/notification_list error", {
|
|
913
|
+
error: errorData(error),
|
|
914
|
+
});
|
|
915
|
+
return await micro.send(res, 200, {
|
|
916
|
+
status: "error",
|
|
917
|
+
error: getErrorMessage(error),
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
router$4.post("/api/v1/view/notification_one/:id", async (req, res) => {
|
|
922
|
+
try {
|
|
923
|
+
const request = await micro.json(req);
|
|
924
|
+
const { requestId, serviceName } = request;
|
|
925
|
+
const id = req.params.id;
|
|
926
|
+
const data = await ioc.notificationViewService.getOne(id);
|
|
927
|
+
const result = {
|
|
928
|
+
data,
|
|
929
|
+
status: "ok",
|
|
930
|
+
error: "",
|
|
931
|
+
requestId,
|
|
932
|
+
serviceName,
|
|
933
|
+
};
|
|
934
|
+
ioc.loggerService.log("/api/v1/view/notification_one/:id ok", {
|
|
935
|
+
request,
|
|
936
|
+
result: omit(result, "data"),
|
|
937
|
+
});
|
|
938
|
+
return await micro.send(res, 200, result);
|
|
939
|
+
}
|
|
940
|
+
catch (error) {
|
|
941
|
+
ioc.loggerService.log("/api/v1/view/notification_one/:id error", {
|
|
942
|
+
error: errorData(error),
|
|
943
|
+
});
|
|
944
|
+
return await micro.send(res, 200, {
|
|
945
|
+
status: "error",
|
|
946
|
+
error: getErrorMessage(error),
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
router$4.post("/api/v1/view/notification_filter", async (req, res) => {
|
|
951
|
+
try {
|
|
952
|
+
const request = await micro.json(req);
|
|
953
|
+
const { requestId, serviceName, filterData, limit, offset } = request;
|
|
954
|
+
const data = await ioc.notificationViewService.findByFilter(filterData, limit, offset);
|
|
955
|
+
const result = {
|
|
956
|
+
data,
|
|
957
|
+
status: "ok",
|
|
958
|
+
error: "",
|
|
959
|
+
requestId,
|
|
960
|
+
serviceName,
|
|
961
|
+
};
|
|
962
|
+
ioc.loggerService.log("/api/v1/view/notification_filter ok", {
|
|
963
|
+
request,
|
|
964
|
+
result: omit(result, "data"),
|
|
965
|
+
});
|
|
966
|
+
return await micro.send(res, 200, result);
|
|
967
|
+
}
|
|
968
|
+
catch (error) {
|
|
969
|
+
ioc.loggerService.log("/api/v1/view/notification_filter error", {
|
|
970
|
+
error: errorData(error),
|
|
971
|
+
});
|
|
972
|
+
return await micro.send(res, 200, {
|
|
973
|
+
status: "error",
|
|
974
|
+
error: getErrorMessage(error),
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
// StorageViewService endpoints
|
|
979
|
+
router$4.post("/api/v1/view/storage_one/:id", async (req, res) => {
|
|
980
|
+
try {
|
|
981
|
+
const request = await micro.json(req);
|
|
982
|
+
const { requestId, serviceName } = request;
|
|
983
|
+
const signalId = req.params.id;
|
|
984
|
+
const data = await ioc.storageViewService.findSignalById(signalId);
|
|
985
|
+
const result = {
|
|
986
|
+
data,
|
|
987
|
+
status: "ok",
|
|
988
|
+
error: "",
|
|
989
|
+
requestId,
|
|
990
|
+
serviceName,
|
|
991
|
+
};
|
|
992
|
+
ioc.loggerService.log("/api/v1/view/storage_one/:id ok", {
|
|
993
|
+
request,
|
|
994
|
+
result: omit(result, "data"),
|
|
995
|
+
});
|
|
996
|
+
return await micro.send(res, 200, result);
|
|
997
|
+
}
|
|
998
|
+
catch (error) {
|
|
999
|
+
ioc.loggerService.log("/api/v1/view/storage_one/:id error", {
|
|
1000
|
+
error: errorData(error),
|
|
1001
|
+
});
|
|
1002
|
+
return await micro.send(res, 200, {
|
|
1003
|
+
status: "error",
|
|
1004
|
+
error: getErrorMessage(error),
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
router$4.post("/api/v1/view/storage_list/live", async (req, res) => {
|
|
1009
|
+
try {
|
|
1010
|
+
const request = await micro.json(req);
|
|
1011
|
+
const { requestId, serviceName } = request;
|
|
1012
|
+
const data = await ioc.storageViewService.listSignalLive();
|
|
1013
|
+
const result = {
|
|
1014
|
+
data,
|
|
1015
|
+
status: "ok",
|
|
1016
|
+
error: "",
|
|
1017
|
+
requestId,
|
|
1018
|
+
serviceName,
|
|
1019
|
+
};
|
|
1020
|
+
ioc.loggerService.log("/api/v1/view/storage_list/live ok", {
|
|
1021
|
+
request,
|
|
1022
|
+
result: omit(result, "data"),
|
|
1023
|
+
});
|
|
1024
|
+
return await micro.send(res, 200, result);
|
|
1025
|
+
}
|
|
1026
|
+
catch (error) {
|
|
1027
|
+
ioc.loggerService.log("/api/v1/view/storage_list/live error", {
|
|
1028
|
+
error: errorData(error),
|
|
1029
|
+
});
|
|
1030
|
+
return await micro.send(res, 200, {
|
|
1031
|
+
status: "error",
|
|
1032
|
+
error: getErrorMessage(error),
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
router$4.post("/api/v1/view/storage_list/backtest", async (req, res) => {
|
|
1037
|
+
try {
|
|
1038
|
+
const request = await micro.json(req);
|
|
1039
|
+
const { requestId, serviceName } = request;
|
|
1040
|
+
const data = await ioc.storageViewService.listSignalBacktest();
|
|
1041
|
+
const result = {
|
|
1042
|
+
data,
|
|
1043
|
+
status: "ok",
|
|
1044
|
+
error: "",
|
|
1045
|
+
requestId,
|
|
1046
|
+
serviceName,
|
|
1047
|
+
};
|
|
1048
|
+
ioc.loggerService.log("/api/v1/view/storage_list/backtest ok", {
|
|
1049
|
+
request,
|
|
1050
|
+
result: omit(result, "data"),
|
|
1051
|
+
});
|
|
1052
|
+
return await micro.send(res, 200, result);
|
|
1053
|
+
}
|
|
1054
|
+
catch (error) {
|
|
1055
|
+
ioc.loggerService.log("/api/v1/view/storage_list/backtest error", {
|
|
1056
|
+
error: errorData(error),
|
|
1057
|
+
});
|
|
1058
|
+
return await micro.send(res, 200, {
|
|
1059
|
+
status: "error",
|
|
1060
|
+
error: getErrorMessage(error),
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
const require$1 = createRequire(import.meta.url);
|
|
1066
|
+
function getModulesPath() {
|
|
1067
|
+
const modulePath = require$1.resolve('@backtest-kit/ui');
|
|
1068
|
+
const basePath = dirname(modulePath);
|
|
1069
|
+
return join(basePath, "../../");
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
const router$3 = Router({
|
|
1073
|
+
params: true,
|
|
1074
|
+
});
|
|
1075
|
+
// getModulesPath
|
|
1076
|
+
const ASSET_SVG = CC_ENABLE_MOCK
|
|
1077
|
+
? join(process.cwd(), "node_modules/cryptocurrency-icons/svg/color")
|
|
1078
|
+
: join(getModulesPath(), "cryptocurrency-icons/svg/color");
|
|
1079
|
+
const ASSET_128 = CC_ENABLE_MOCK
|
|
1080
|
+
? join(process.cwd(), "node_modules/cryptocurrency-icons/128/color")
|
|
1081
|
+
: join(getModulesPath(), "cryptocurrency-icons/128/color");
|
|
1082
|
+
const ASSET_32 = CC_ENABLE_MOCK
|
|
1083
|
+
? join(process.cwd(), "node_modules/cryptocurrency-icons/32/color")
|
|
1084
|
+
: join(getModulesPath(), "cryptocurrency-icons/32/color");
|
|
1085
|
+
// File caches to avoid repeated disk reads
|
|
1086
|
+
const cache128 = new Map();
|
|
1087
|
+
const cache32 = new Map();
|
|
1088
|
+
const cacheSvg = new Map();
|
|
1089
|
+
router$3.get("/icon/128/:filename", async (req, res) => {
|
|
1090
|
+
const filename = req.params.filename;
|
|
1091
|
+
// Check cache first
|
|
1092
|
+
if (cache128.has(filename)) {
|
|
1093
|
+
res.setHeader("Content-Type", "image/png");
|
|
1094
|
+
return await micro.send(res, 200, cache128.get(filename));
|
|
1095
|
+
}
|
|
1096
|
+
const filePath = join(ASSET_128, filename);
|
|
1097
|
+
if (existsSync(filePath)) {
|
|
1098
|
+
const fileBuffer = await readFile(filePath);
|
|
1099
|
+
cache128.set(filename, fileBuffer);
|
|
1100
|
+
res.setHeader("Content-Type", "image/png");
|
|
1101
|
+
return await micro.send(res, 200, fileBuffer);
|
|
1102
|
+
}
|
|
1103
|
+
return await micro.send(res, 404, "File not found (128)");
|
|
1104
|
+
});
|
|
1105
|
+
router$3.get("/icon/32/:filename", async (req, res) => {
|
|
1106
|
+
const filename = req.params.filename;
|
|
1107
|
+
// Check cache first
|
|
1108
|
+
if (cache32.has(filename)) {
|
|
1109
|
+
res.setHeader("Content-Type", "image/png");
|
|
1110
|
+
return await micro.send(res, 200, cache32.get(filename));
|
|
1111
|
+
}
|
|
1112
|
+
const filePath = join(ASSET_32, filename);
|
|
1113
|
+
if (existsSync(filePath)) {
|
|
1114
|
+
const fileBuffer = await readFile(filePath);
|
|
1115
|
+
cache32.set(filename, fileBuffer);
|
|
1116
|
+
res.setHeader("Content-Type", "image/png");
|
|
1117
|
+
return await micro.send(res, 200, fileBuffer);
|
|
1118
|
+
}
|
|
1119
|
+
return await micro.send(res, 404, "File not found (32)");
|
|
1120
|
+
});
|
|
1121
|
+
router$3.get("/icon/svg/:filename", async (req, res) => {
|
|
1122
|
+
const filename = req.params.filename;
|
|
1123
|
+
// Check cache first
|
|
1124
|
+
if (cacheSvg.has(filename)) {
|
|
1125
|
+
res.setHeader("Content-Type", "image/svg+xml");
|
|
1126
|
+
return await micro.send(res, 200, cacheSvg.get(filename));
|
|
1127
|
+
}
|
|
1128
|
+
const filePath = join(ASSET_SVG, filename);
|
|
1129
|
+
if (existsSync(filePath)) {
|
|
1130
|
+
const fileBuffer = await readFile(filePath);
|
|
1131
|
+
cacheSvg.set(filename, fileBuffer);
|
|
1132
|
+
res.setHeader("Content-Type", "image/svg+xml");
|
|
1133
|
+
return await micro.send(res, 200, fileBuffer);
|
|
1134
|
+
}
|
|
1135
|
+
return await micro.send(res, 404, "File not found (svg)");
|
|
1136
|
+
});
|
|
1137
|
+
router$3.get("/icon/:filename", async (req, res) => {
|
|
1138
|
+
const filename = req.params.filename;
|
|
1139
|
+
// Check cache first
|
|
1140
|
+
if (cache32.has(filename)) {
|
|
1141
|
+
res.setHeader("Content-Type", "image/png");
|
|
1142
|
+
return await micro.send(res, 200, cache32.get(filename));
|
|
1143
|
+
}
|
|
1144
|
+
const filePath = join(ASSET_32, filename);
|
|
1145
|
+
if (existsSync(filePath)) {
|
|
1146
|
+
const fileBuffer = await readFile(filePath);
|
|
1147
|
+
cache32.set(filename, fileBuffer);
|
|
1148
|
+
res.setHeader("Content-Type", "image/png");
|
|
1149
|
+
return await micro.send(res, 200, fileBuffer);
|
|
1150
|
+
}
|
|
1151
|
+
return await micro.send(res, 404, "File not found (root)");
|
|
1152
|
+
});
|
|
1153
|
+
|
|
1154
|
+
const router$2 = Router({
|
|
1155
|
+
params: true,
|
|
1156
|
+
});
|
|
1157
|
+
router$2.post("/api/v1/dict/symbol/list", async (req, res) => {
|
|
1158
|
+
try {
|
|
1159
|
+
const request = await micro.json(req);
|
|
1160
|
+
const { requestId, serviceName } = request;
|
|
1161
|
+
const data = await ioc.symbolMetaService.getSymbolList();
|
|
1162
|
+
const result = {
|
|
1163
|
+
data,
|
|
1164
|
+
status: "ok",
|
|
1165
|
+
error: "",
|
|
1166
|
+
requestId,
|
|
1167
|
+
serviceName,
|
|
1168
|
+
};
|
|
1169
|
+
ioc.loggerService.log("/api/v1/dict/symbol/list ok", {
|
|
1170
|
+
request,
|
|
1171
|
+
result: omit(result, "data"),
|
|
1172
|
+
});
|
|
1173
|
+
return await micro.send(res, 200, result);
|
|
1174
|
+
}
|
|
1175
|
+
catch (error) {
|
|
1176
|
+
ioc.loggerService.log("/api/v1/dict/symbol/list error", {
|
|
1177
|
+
error: errorData(error),
|
|
1178
|
+
});
|
|
1179
|
+
return await micro.send(res, 200, {
|
|
1180
|
+
status: "error",
|
|
1181
|
+
error: getErrorMessage(error),
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
});
|
|
1185
|
+
router$2.post("/api/v1/dict/symbol/map", async (req, res) => {
|
|
1186
|
+
try {
|
|
1187
|
+
const request = await micro.json(req);
|
|
1188
|
+
const { requestId, serviceName } = request;
|
|
1189
|
+
const data = await ioc.symbolMetaService.getSymbolMap();
|
|
1190
|
+
const result = {
|
|
1191
|
+
data,
|
|
1192
|
+
status: "ok",
|
|
1193
|
+
error: "",
|
|
1194
|
+
requestId,
|
|
1195
|
+
serviceName,
|
|
1196
|
+
};
|
|
1197
|
+
ioc.loggerService.log("/api/v1/dict/symbol/map ok", {
|
|
1198
|
+
request,
|
|
1199
|
+
result: omit(result, "data"),
|
|
1200
|
+
});
|
|
1201
|
+
return await micro.send(res, 200, result);
|
|
1202
|
+
}
|
|
1203
|
+
catch (error) {
|
|
1204
|
+
ioc.loggerService.log("/api/v1/dict/symbol/map error", {
|
|
1205
|
+
error: errorData(error),
|
|
1206
|
+
});
|
|
1207
|
+
return await micro.send(res, 200, {
|
|
1208
|
+
status: "error",
|
|
1209
|
+
error: getErrorMessage(error),
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
});
|
|
1213
|
+
router$2.post("/api/v1/dict/symbol/one", async (req, res) => {
|
|
1214
|
+
try {
|
|
1215
|
+
const request = await micro.json(req);
|
|
1216
|
+
const { requestId, serviceName, id } = request;
|
|
1217
|
+
const data = await ioc.symbolMetaService.getSymbol(id);
|
|
1218
|
+
const result = {
|
|
1219
|
+
data,
|
|
1220
|
+
status: "ok",
|
|
1221
|
+
error: "",
|
|
1222
|
+
requestId,
|
|
1223
|
+
serviceName,
|
|
1224
|
+
};
|
|
1225
|
+
ioc.loggerService.log("/api/v1/dict/symbol/one ok", {
|
|
1226
|
+
request,
|
|
1227
|
+
result: omit(result, "data"),
|
|
1228
|
+
});
|
|
1229
|
+
return await micro.send(res, 200, result);
|
|
1230
|
+
}
|
|
1231
|
+
catch (error) {
|
|
1232
|
+
ioc.loggerService.log("/api/v1/dict/symbol/one error", {
|
|
1233
|
+
error: errorData(error),
|
|
1234
|
+
});
|
|
1235
|
+
return await micro.send(res, 200, {
|
|
1236
|
+
status: "error",
|
|
1237
|
+
error: getErrorMessage(error),
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
});
|
|
1241
|
+
|
|
1242
|
+
const require = createRequire(import.meta.url);
|
|
1243
|
+
function getPublicPath() {
|
|
1244
|
+
const modulePath = require.resolve('@backtest-kit/ui');
|
|
1245
|
+
const basePath = dirname(modulePath);
|
|
1246
|
+
return join(basePath, "./modules/frontend/build");
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
const router = Router({
|
|
1250
|
+
params: true,
|
|
1251
|
+
});
|
|
1252
|
+
router.all("/api/v1/health/*", (req, res) => {
|
|
1253
|
+
return router$6(req, res, finalhandler(req, res));
|
|
1254
|
+
});
|
|
1255
|
+
router.all("/api/v1/mock/*", (req, res) => {
|
|
1256
|
+
return router$5(req, res, finalhandler(req, res));
|
|
1257
|
+
});
|
|
1258
|
+
router.all("/api/v1/view/*", (req, res) => {
|
|
1259
|
+
return router$4(req, res, finalhandler(req, res));
|
|
1260
|
+
});
|
|
1261
|
+
router.all("/icon/*", (req, res) => {
|
|
1262
|
+
return router$3(req, res, finalhandler(req, res));
|
|
1263
|
+
});
|
|
1264
|
+
router.all("/api/v1/dict/*", (req, res) => {
|
|
1265
|
+
return router$2(req, res, finalhandler(req, res));
|
|
1266
|
+
});
|
|
1267
|
+
router.get("/*", (req, res) => serveHandler(req, res, {
|
|
1268
|
+
public: CC_ENABLE_MOCK
|
|
1269
|
+
? CC_WWWROOT_PATH || "./build/modules/frontend/build"
|
|
1270
|
+
: CC_WWWROOT_PATH || getPublicPath(),
|
|
1271
|
+
}));
|
|
1272
|
+
var router$1 = micro.serve(async (req, res) => {
|
|
1273
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1274
|
+
res.setHeader("Access-Control-Allow-Headers", "*");
|
|
1275
|
+
res.setHeader("Access-Control-Allow-Credentials", "true");
|
|
1276
|
+
res.setHeader("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE");
|
|
1277
|
+
return router(req, res, finalhandler(req, res));
|
|
1278
|
+
});
|
|
1279
|
+
|
|
1280
|
+
const METHOD_NAME_SERVE = "serve.serve";
|
|
1281
|
+
const METHOD_NAME_GET_ROUTER = "serve.getRouter";
|
|
1282
|
+
const MAX_CONNECTIONS = 1000;
|
|
1283
|
+
const SOCKET_TIMEOUT = 60 * 10 * 1000;
|
|
1284
|
+
const serveInternal = singleshot((host = CC_WWWROOT_HOST, port = CC_WWWROOT_PORT) => {
|
|
1285
|
+
const server = new http.Server(router$1);
|
|
1286
|
+
server.listen(port, host).addListener("listening", () => {
|
|
1287
|
+
console.log(`Listening on http://${host}:${port}`);
|
|
1288
|
+
});
|
|
1289
|
+
server.maxConnections = MAX_CONNECTIONS;
|
|
1290
|
+
server.setTimeout(SOCKET_TIMEOUT);
|
|
1291
|
+
return () => {
|
|
1292
|
+
server.close();
|
|
1293
|
+
serveInternal.clear();
|
|
1294
|
+
};
|
|
1295
|
+
});
|
|
1296
|
+
function serve(host, port) {
|
|
1297
|
+
ioc.loggerService.log(METHOD_NAME_SERVE, {
|
|
1298
|
+
host,
|
|
1299
|
+
port,
|
|
1300
|
+
});
|
|
1301
|
+
return serveInternal(host, port);
|
|
1302
|
+
}
|
|
1303
|
+
function getRouter() {
|
|
1304
|
+
ioc.loggerService.log(METHOD_NAME_GET_ROUTER);
|
|
1305
|
+
return router$1;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
const setLogger = (logger) => {
|
|
1309
|
+
ioc.loggerService.setLogger(logger);
|
|
1310
|
+
};
|
|
1311
|
+
|
|
1312
|
+
export { getModulesPath, getPublicPath, getRouter, ioc as lib, serve, setLogger };
|