@tradejs/infra 2.0.18 → 2.0.20
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/dist/ai.js +39 -74
- package/dist/ai.mjs +37 -74
- package/dist/chunk-2CZREG43.mjs +112 -0
- package/dist/chunk-DFMKDB2R.mjs +1285 -0
- package/dist/chunk-I2J6YDBD.mjs +910 -0
- package/dist/chunk-NWXFWTWU.mjs +1114 -0
- package/dist/chunk-SZQB7ER5.mjs +492 -0
- package/dist/chunk-YVIHTUV5.mjs +286 -0
- package/dist/coreResearch.d.mts +20 -0
- package/dist/coreResearch.d.ts +20 -0
- package/dist/coreResearch.js +89 -0
- package/dist/coreResearch.mjs +48 -0
- package/dist/internal-2coHaaos.d.mts +26 -0
- package/dist/internal-2coHaaos.d.ts +26 -0
- package/dist/ml.mjs +3 -3
- package/dist/runtimeDeployments.d.mts +10 -0
- package/dist/runtimeDeployments.d.ts +10 -0
- package/dist/runtimeDeployments.js +447 -0
- package/dist/runtimeDeployments.mjs +81 -0
- package/dist/runtimeStrategyConfigs.d.mts +28 -0
- package/dist/runtimeStrategyConfigs.d.ts +28 -0
- package/dist/runtimeStrategyConfigs.js +425 -0
- package/dist/runtimeStrategyConfigs.mjs +89 -0
- package/dist/strategyReleaseEvidence.d.mts +12 -0
- package/dist/strategyReleaseEvidence.d.ts +12 -0
- package/dist/strategyReleaseEvidence.js +120 -0
- package/dist/strategyReleaseEvidence.mjs +90 -0
- package/dist/timescale/candles.d.mts +38 -0
- package/dist/timescale/candles.d.ts +38 -0
- package/dist/timescale/candles.js +408 -0
- package/dist/timescale/candles.mjs +21 -0
- package/dist/timescale/client.d.mts +4 -0
- package/dist/timescale/client.d.ts +4 -0
- package/dist/timescale/client.js +109 -0
- package/dist/timescale/client.mjs +12 -0
- package/dist/timescale/derivatives.d.mts +90 -0
- package/dist/timescale/derivatives.d.ts +90 -0
- package/dist/timescale/derivatives.js +1270 -0
- package/dist/timescale/derivatives.mjs +26 -0
- package/dist/timescale/hyperliquidWhales.d.mts +149 -0
- package/dist/timescale/hyperliquidWhales.d.ts +149 -0
- package/dist/timescale/hyperliquidWhales.js +1893 -0
- package/dist/timescale/hyperliquidWhales.mjs +30 -0
- package/dist/timescale/marketContext.d.mts +188 -0
- package/dist/timescale/marketContext.d.ts +188 -0
- package/dist/timescale/marketContext.js +2091 -0
- package/dist/timescale/marketContext.mjs +60 -0
- package/dist/timescale/spread.d.mts +11 -0
- package/dist/timescale/spread.d.ts +11 -0
- package/dist/timescale/spread.js +215 -0
- package/dist/timescale/spread.mjs +11 -0
- package/dist/timescale.d.mts +9 -476
- package/dist/timescale.d.ts +9 -476
- package/dist/timescale.js +2121 -2112
- package/dist/timescale.mjs +73 -4070
- package/dist/tradingAccounts.d.mts +2 -8
- package/dist/tradingAccounts.d.ts +2 -8
- package/dist/tradingAccounts.js +0 -71
- package/dist/tradingAccounts.mjs +0 -65
- package/dist/values-BrvcmnfM.d.mts +6 -0
- package/dist/values-BrvcmnfM.d.ts +6 -0
- package/package.json +53 -2
package/dist/ai.js
CHANGED
|
@@ -55,58 +55,7 @@ var import_promises2 = __toESM(require("fs/promises"));
|
|
|
55
55
|
var import_path2 = __toESM(require("path"));
|
|
56
56
|
var import_node_fs = require("fs");
|
|
57
57
|
var import_node_readline2 = __toESM(require("readline"));
|
|
58
|
-
|
|
59
|
-
// ../../node_modules/date-fns/toDate.mjs
|
|
60
|
-
function toDate(argument) {
|
|
61
|
-
const argStr = Object.prototype.toString.call(argument);
|
|
62
|
-
if (argument instanceof Date || typeof argument === "object" && argStr === "[object Date]") {
|
|
63
|
-
return new argument.constructor(+argument);
|
|
64
|
-
} else if (typeof argument === "number" || argStr === "[object Number]" || typeof argument === "string" || argStr === "[object String]") {
|
|
65
|
-
return new Date(argument);
|
|
66
|
-
} else {
|
|
67
|
-
return /* @__PURE__ */ new Date(NaN);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ../../node_modules/date-fns/constructFrom.mjs
|
|
72
|
-
function constructFrom(date, value) {
|
|
73
|
-
if (date instanceof Date) {
|
|
74
|
-
return new date.constructor(value);
|
|
75
|
-
} else {
|
|
76
|
-
return new Date(value);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// ../../node_modules/date-fns/addMonths.mjs
|
|
81
|
-
function addMonths(date, amount) {
|
|
82
|
-
const _date = toDate(date);
|
|
83
|
-
if (isNaN(amount)) return constructFrom(date, NaN);
|
|
84
|
-
if (!amount) {
|
|
85
|
-
return _date;
|
|
86
|
-
}
|
|
87
|
-
const dayOfMonth = _date.getDate();
|
|
88
|
-
const endOfDesiredMonth = constructFrom(date, _date.getTime());
|
|
89
|
-
endOfDesiredMonth.setMonth(_date.getMonth() + amount + 1, 0);
|
|
90
|
-
const daysInMonth = endOfDesiredMonth.getDate();
|
|
91
|
-
if (dayOfMonth >= daysInMonth) {
|
|
92
|
-
return endOfDesiredMonth;
|
|
93
|
-
} else {
|
|
94
|
-
_date.setFullYear(
|
|
95
|
-
endOfDesiredMonth.getFullYear(),
|
|
96
|
-
endOfDesiredMonth.getMonth(),
|
|
97
|
-
dayOfMonth
|
|
98
|
-
);
|
|
99
|
-
return _date;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// ../../node_modules/date-fns/startOfMonth.mjs
|
|
104
|
-
function startOfMonth(date) {
|
|
105
|
-
const _date = toDate(date);
|
|
106
|
-
_date.setDate(1);
|
|
107
|
-
_date.setHours(0, 0, 0, 0);
|
|
108
|
-
return _date;
|
|
109
|
-
}
|
|
58
|
+
var import_date_fns = require("date-fns");
|
|
110
59
|
|
|
111
60
|
// src/mlDatasetFile.ts
|
|
112
61
|
var import_events = require("events");
|
|
@@ -185,21 +134,32 @@ var getRunIdFromAiChunkFileName = (strategyName, fileName) => {
|
|
|
185
134
|
var appendAiDatasetRow = async (params) => {
|
|
186
135
|
const { strategyName, chunkId, row, outDir = DEFAULT_DIR } = params;
|
|
187
136
|
const filePath = getAiChunkFilePath(strategyName, chunkId, outDir);
|
|
188
|
-
let
|
|
189
|
-
if (!
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
137
|
+
let statePromise = writerByPath.get(filePath);
|
|
138
|
+
if (!statePromise) {
|
|
139
|
+
statePromise = (async () => {
|
|
140
|
+
await import_promises2.default.mkdir(outDir, { recursive: true });
|
|
141
|
+
const stream = (0, import_fs2.createWriteStream)(filePath, {
|
|
142
|
+
encoding: "utf8",
|
|
143
|
+
flags: "a"
|
|
144
|
+
});
|
|
145
|
+
return {
|
|
146
|
+
filePath,
|
|
147
|
+
stream,
|
|
148
|
+
buffer: [],
|
|
149
|
+
writeQueue: Promise.resolve(),
|
|
150
|
+
closed: false
|
|
151
|
+
};
|
|
152
|
+
})();
|
|
153
|
+
writerByPath.set(filePath, statePromise);
|
|
154
|
+
}
|
|
155
|
+
let state;
|
|
156
|
+
try {
|
|
157
|
+
state = await statePromise;
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (writerByPath.get(filePath) === statePromise) {
|
|
160
|
+
writerByPath.delete(filePath);
|
|
161
|
+
}
|
|
162
|
+
throw error;
|
|
203
163
|
}
|
|
204
164
|
if (state.closed) {
|
|
205
165
|
throw new Error(`AI dataset writer is closed: ${filePath}`);
|
|
@@ -222,10 +182,12 @@ var flushState = async (state) => {
|
|
|
222
182
|
}
|
|
223
183
|
};
|
|
224
184
|
var flushAiDatasetWriter = async (filePath) => {
|
|
225
|
-
const
|
|
226
|
-
if (!
|
|
185
|
+
const statePromise = writerByPath.get(filePath);
|
|
186
|
+
if (!statePromise) {
|
|
227
187
|
return;
|
|
228
188
|
}
|
|
189
|
+
const state = await statePromise;
|
|
190
|
+
if (state.closed) return;
|
|
229
191
|
state.writeQueue = state.writeQueue.then(() => flushState(state));
|
|
230
192
|
await state.writeQueue;
|
|
231
193
|
};
|
|
@@ -242,13 +204,16 @@ var closeState = async (state) => {
|
|
|
242
204
|
]);
|
|
243
205
|
};
|
|
244
206
|
var closeAiDatasetWriter = async (filePath) => {
|
|
245
|
-
const
|
|
246
|
-
if (!
|
|
207
|
+
const statePromise = writerByPath.get(filePath);
|
|
208
|
+
if (!statePromise) {
|
|
247
209
|
return;
|
|
248
210
|
}
|
|
211
|
+
const state = await statePromise;
|
|
249
212
|
state.writeQueue = state.writeQueue.then(() => closeState(state));
|
|
250
213
|
await state.writeQueue;
|
|
251
|
-
writerByPath.
|
|
214
|
+
if (writerByPath.get(filePath) === statePromise) {
|
|
215
|
+
writerByPath.delete(filePath);
|
|
216
|
+
}
|
|
252
217
|
};
|
|
253
218
|
var closeAllAiDatasetWriters = async () => {
|
|
254
219
|
const filePaths = [...writerByPath.keys()];
|
|
@@ -570,8 +535,8 @@ var getAiDatasetPartPath = (filePath, partIndex) => {
|
|
|
570
535
|
return import_path2.default.join(parsed.dir, `${parsed.name}-part${partIndex}${parsed.ext}`);
|
|
571
536
|
};
|
|
572
537
|
var getPartWindowEndExclusive = (timestamp, monthsPerPart) => {
|
|
573
|
-
const partStart = startOfMonth(new Date(timestamp));
|
|
574
|
-
return addMonths(partStart, Math.max(1, monthsPerPart)).getTime();
|
|
538
|
+
const partStart = (0, import_date_fns.startOfMonth)(new Date(timestamp));
|
|
539
|
+
return (0, import_date_fns.addMonths)(partStart, Math.max(1, monthsPerPart)).getTime();
|
|
575
540
|
};
|
|
576
541
|
var splitAiMergedDatasetFile = async (params) => {
|
|
577
542
|
const { filePath, monthsPerPart = 2 } = params;
|
package/dist/ai.mjs
CHANGED
|
@@ -10,60 +10,7 @@ import fs from "fs/promises";
|
|
|
10
10
|
import path from "path";
|
|
11
11
|
import { createReadStream } from "fs";
|
|
12
12
|
import readline from "readline";
|
|
13
|
-
|
|
14
|
-
// ../../node_modules/date-fns/toDate.mjs
|
|
15
|
-
function toDate(argument) {
|
|
16
|
-
const argStr = Object.prototype.toString.call(argument);
|
|
17
|
-
if (argument instanceof Date || typeof argument === "object" && argStr === "[object Date]") {
|
|
18
|
-
return new argument.constructor(+argument);
|
|
19
|
-
} else if (typeof argument === "number" || argStr === "[object Number]" || typeof argument === "string" || argStr === "[object String]") {
|
|
20
|
-
return new Date(argument);
|
|
21
|
-
} else {
|
|
22
|
-
return /* @__PURE__ */ new Date(NaN);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ../../node_modules/date-fns/constructFrom.mjs
|
|
27
|
-
function constructFrom(date, value) {
|
|
28
|
-
if (date instanceof Date) {
|
|
29
|
-
return new date.constructor(value);
|
|
30
|
-
} else {
|
|
31
|
-
return new Date(value);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// ../../node_modules/date-fns/addMonths.mjs
|
|
36
|
-
function addMonths(date, amount) {
|
|
37
|
-
const _date = toDate(date);
|
|
38
|
-
if (isNaN(amount)) return constructFrom(date, NaN);
|
|
39
|
-
if (!amount) {
|
|
40
|
-
return _date;
|
|
41
|
-
}
|
|
42
|
-
const dayOfMonth = _date.getDate();
|
|
43
|
-
const endOfDesiredMonth = constructFrom(date, _date.getTime());
|
|
44
|
-
endOfDesiredMonth.setMonth(_date.getMonth() + amount + 1, 0);
|
|
45
|
-
const daysInMonth = endOfDesiredMonth.getDate();
|
|
46
|
-
if (dayOfMonth >= daysInMonth) {
|
|
47
|
-
return endOfDesiredMonth;
|
|
48
|
-
} else {
|
|
49
|
-
_date.setFullYear(
|
|
50
|
-
endOfDesiredMonth.getFullYear(),
|
|
51
|
-
endOfDesiredMonth.getMonth(),
|
|
52
|
-
dayOfMonth
|
|
53
|
-
);
|
|
54
|
-
return _date;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ../../node_modules/date-fns/startOfMonth.mjs
|
|
59
|
-
function startOfMonth(date) {
|
|
60
|
-
const _date = toDate(date);
|
|
61
|
-
_date.setDate(1);
|
|
62
|
-
_date.setHours(0, 0, 0, 0);
|
|
63
|
-
return _date;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// src/aiDatasetFile.ts
|
|
13
|
+
import { addMonths, startOfMonth } from "date-fns";
|
|
67
14
|
var DEFAULT_DIR = "data/ai/export";
|
|
68
15
|
var AI_DATASET_WRITE_BATCH_SIZE = 100;
|
|
69
16
|
var AI_MERGE_SORT_RUN_MAX_ROWS = 2e3;
|
|
@@ -88,21 +35,32 @@ var getRunIdFromAiChunkFileName = (strategyName, fileName) => {
|
|
|
88
35
|
var appendAiDatasetRow = async (params) => {
|
|
89
36
|
const { strategyName, chunkId, row, outDir = DEFAULT_DIR } = params;
|
|
90
37
|
const filePath = getAiChunkFilePath(strategyName, chunkId, outDir);
|
|
91
|
-
let
|
|
92
|
-
if (!
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
38
|
+
let statePromise = writerByPath.get(filePath);
|
|
39
|
+
if (!statePromise) {
|
|
40
|
+
statePromise = (async () => {
|
|
41
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
42
|
+
const stream = createWriteStream(filePath, {
|
|
43
|
+
encoding: "utf8",
|
|
44
|
+
flags: "a"
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
filePath,
|
|
48
|
+
stream,
|
|
49
|
+
buffer: [],
|
|
50
|
+
writeQueue: Promise.resolve(),
|
|
51
|
+
closed: false
|
|
52
|
+
};
|
|
53
|
+
})();
|
|
54
|
+
writerByPath.set(filePath, statePromise);
|
|
55
|
+
}
|
|
56
|
+
let state;
|
|
57
|
+
try {
|
|
58
|
+
state = await statePromise;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (writerByPath.get(filePath) === statePromise) {
|
|
61
|
+
writerByPath.delete(filePath);
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
106
64
|
}
|
|
107
65
|
if (state.closed) {
|
|
108
66
|
throw new Error(`AI dataset writer is closed: ${filePath}`);
|
|
@@ -125,10 +83,12 @@ var flushState = async (state) => {
|
|
|
125
83
|
}
|
|
126
84
|
};
|
|
127
85
|
var flushAiDatasetWriter = async (filePath) => {
|
|
128
|
-
const
|
|
129
|
-
if (!
|
|
86
|
+
const statePromise = writerByPath.get(filePath);
|
|
87
|
+
if (!statePromise) {
|
|
130
88
|
return;
|
|
131
89
|
}
|
|
90
|
+
const state = await statePromise;
|
|
91
|
+
if (state.closed) return;
|
|
132
92
|
state.writeQueue = state.writeQueue.then(() => flushState(state));
|
|
133
93
|
await state.writeQueue;
|
|
134
94
|
};
|
|
@@ -145,13 +105,16 @@ var closeState = async (state) => {
|
|
|
145
105
|
]);
|
|
146
106
|
};
|
|
147
107
|
var closeAiDatasetWriter = async (filePath) => {
|
|
148
|
-
const
|
|
149
|
-
if (!
|
|
108
|
+
const statePromise = writerByPath.get(filePath);
|
|
109
|
+
if (!statePromise) {
|
|
150
110
|
return;
|
|
151
111
|
}
|
|
112
|
+
const state = await statePromise;
|
|
152
113
|
state.writeQueue = state.writeQueue.then(() => closeState(state));
|
|
153
114
|
await state.writeQueue;
|
|
154
|
-
writerByPath.
|
|
115
|
+
if (writerByPath.get(filePath) === statePromise) {
|
|
116
|
+
writerByPath.delete(filePath);
|
|
117
|
+
}
|
|
155
118
|
};
|
|
156
119
|
var closeAllAiDatasetWriters = async () => {
|
|
157
120
|
const filePaths = [...writerByPath.keys()];
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureSpreadSchema,
|
|
3
|
+
getPool
|
|
4
|
+
} from "./chunk-I2J6YDBD.mjs";
|
|
5
|
+
|
|
6
|
+
// src/timescale/spread.ts
|
|
7
|
+
async function upsertSpreadRows(rows) {
|
|
8
|
+
if (!rows.length) return;
|
|
9
|
+
await ensureSpreadSchema();
|
|
10
|
+
const pool = getPool();
|
|
11
|
+
const cols = [
|
|
12
|
+
"symbol",
|
|
13
|
+
"interval",
|
|
14
|
+
"ts",
|
|
15
|
+
"binance_price",
|
|
16
|
+
"coinbase_price",
|
|
17
|
+
"spread",
|
|
18
|
+
"source"
|
|
19
|
+
];
|
|
20
|
+
const maxRows = Math.floor(65535 / cols.length);
|
|
21
|
+
if (rows.length > maxRows) {
|
|
22
|
+
for (let i = 0; i < rows.length; i += maxRows) {
|
|
23
|
+
await upsertSpreadRows(rows.slice(i, i + maxRows));
|
|
24
|
+
}
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const valuesSql = rows.map(
|
|
28
|
+
(_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
|
|
29
|
+
).join(",");
|
|
30
|
+
const flat = rows.flatMap((row) => [
|
|
31
|
+
row.symbol,
|
|
32
|
+
row.interval,
|
|
33
|
+
row.ts,
|
|
34
|
+
row.binancePrice ?? null,
|
|
35
|
+
row.coinbasePrice ?? null,
|
|
36
|
+
row.spread ?? null,
|
|
37
|
+
row.source ?? null
|
|
38
|
+
]);
|
|
39
|
+
const sql = `
|
|
40
|
+
INSERT INTO market_spread (${cols.join(",")})
|
|
41
|
+
VALUES ${valuesSql}
|
|
42
|
+
ON CONFLICT (symbol, interval, ts) DO UPDATE SET
|
|
43
|
+
binance_price = COALESCE(EXCLUDED.binance_price, market_spread.binance_price),
|
|
44
|
+
coinbase_price = COALESCE(EXCLUDED.coinbase_price, market_spread.coinbase_price),
|
|
45
|
+
spread = COALESCE(EXCLUDED.spread, market_spread.spread),
|
|
46
|
+
source = COALESCE(EXCLUDED.source, market_spread.source),
|
|
47
|
+
ingested_at = now()
|
|
48
|
+
`;
|
|
49
|
+
await pool.query(sql, flat);
|
|
50
|
+
}
|
|
51
|
+
async function getSpreadRangeForSymbols(symbols, interval, startMs, endMs) {
|
|
52
|
+
if (!symbols.length) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
await ensureSpreadSchema();
|
|
56
|
+
const pool = getPool();
|
|
57
|
+
const sql = `
|
|
58
|
+
SELECT symbol, interval, ts, binance_price, coinbase_price, spread
|
|
59
|
+
FROM market_spread
|
|
60
|
+
WHERE symbol = ANY($1)
|
|
61
|
+
AND interval = $2
|
|
62
|
+
AND ts >= to_timestamp($3/1000.0)
|
|
63
|
+
AND ts <= to_timestamp($4/1000.0)
|
|
64
|
+
ORDER BY symbol ASC, ts ASC
|
|
65
|
+
`;
|
|
66
|
+
const res = await pool.query(sql, [symbols, interval, startMs, endMs]);
|
|
67
|
+
return res.rows;
|
|
68
|
+
}
|
|
69
|
+
async function getSpreadSummary(hours = 24, limit = 500) {
|
|
70
|
+
await ensureSpreadSchema();
|
|
71
|
+
const pool = getPool();
|
|
72
|
+
const cappedHours = Math.max(1, Math.min(24 * 30, hours));
|
|
73
|
+
const cappedLimit = Math.max(50, Math.min(5e3, limit));
|
|
74
|
+
const rowsQ = await pool.query(
|
|
75
|
+
`
|
|
76
|
+
SELECT symbol, interval, ts, binance_price, coinbase_price, spread
|
|
77
|
+
FROM market_spread
|
|
78
|
+
WHERE ts >= now() - ($1 || ' hours')::interval
|
|
79
|
+
ORDER BY ts DESC
|
|
80
|
+
LIMIT $2
|
|
81
|
+
`,
|
|
82
|
+
[String(cappedHours), cappedLimit]
|
|
83
|
+
);
|
|
84
|
+
const aggQ = await pool.query(
|
|
85
|
+
`
|
|
86
|
+
SELECT
|
|
87
|
+
symbol,
|
|
88
|
+
interval,
|
|
89
|
+
COUNT(*)::int AS points,
|
|
90
|
+
MAX(ts) AS last_ts,
|
|
91
|
+
AVG(spread) AS avg_spread,
|
|
92
|
+
STDDEV_POP(spread) AS std_spread
|
|
93
|
+
FROM market_spread
|
|
94
|
+
WHERE ts >= now() - ($1 || ' hours')::interval
|
|
95
|
+
GROUP BY symbol, interval
|
|
96
|
+
ORDER BY points DESC, symbol ASC
|
|
97
|
+
LIMIT 500
|
|
98
|
+
`,
|
|
99
|
+
[String(cappedHours)]
|
|
100
|
+
);
|
|
101
|
+
return {
|
|
102
|
+
rows: rowsQ.rows,
|
|
103
|
+
aggregates: aggQ.rows,
|
|
104
|
+
hours: cappedHours
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
upsertSpreadRows,
|
|
110
|
+
getSpreadRangeForSymbols,
|
|
111
|
+
getSpreadSummary
|
|
112
|
+
};
|