@tradejs/infra 1.0.5 → 1.0.8
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.d.mts +36 -0
- package/dist/ai.d.ts +36 -0
- package/dist/ai.js +456 -0
- package/dist/ai.mjs +386 -0
- package/dist/aiEndpoints.d.mts +10 -0
- package/dist/aiEndpoints.d.ts +10 -0
- package/dist/aiEndpoints.js +159 -0
- package/dist/aiEndpoints.mjs +12 -0
- package/dist/aiLanguages.d.mts +11 -0
- package/dist/aiLanguages.d.ts +11 -0
- package/dist/aiLanguages.js +71 -0
- package/dist/aiLanguages.mjs +12 -0
- package/dist/aiModels.d.mts +12 -0
- package/dist/aiModels.d.ts +12 -0
- package/dist/aiModels.js +272 -0
- package/dist/aiModels.mjs +17 -0
- package/dist/chunk-CCC7DX2T.mjs +44 -0
- package/dist/chunk-DSTV67R5.mjs +345 -0
- package/dist/chunk-DTCLZIBM.mjs +163 -0
- package/dist/chunk-KVEZORMS.mjs +142 -0
- package/dist/chunk-XQ3YBULV.mjs +132 -0
- package/dist/ml.d.mts +2 -20
- package/dist/ml.d.ts +2 -20
- package/dist/ml.js +136 -0
- package/dist/ml.mjs +139 -124
- package/dist/mlDatasetFile-sRGWgR_o.d.mts +24 -0
- package/dist/mlDatasetFile-sRGWgR_o.d.ts +24 -0
- package/dist/redis.d.mts +17 -1
- package/dist/redis.d.ts +17 -1
- package/dist/redis.js +50 -0
- package/dist/redis.mjs +13 -287
- package/dist/timescale.d.mts +18 -7
- package/dist/timescale.d.ts +18 -7
- package/dist/timescale.js +142 -31
- package/dist/timescale.mjs +140 -31
- package/dist/userSettings.d.mts +31 -0
- package/dist/userSettings.d.ts +31 -0
- package/dist/userSettings.js +636 -0
- package/dist/userSettings.mjs +70 -0
- package/package.json +28 -3
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// src/aiEndpoints.ts
|
|
2
|
+
var AI_CUSTOM_ENDPOINT_VALUE = "__custom__";
|
|
3
|
+
var AI_ENDPOINT_OPTIONS = [
|
|
4
|
+
{
|
|
5
|
+
label: "OpenAI",
|
|
6
|
+
value: "https://api.openai.com/v1"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
label: "Claude",
|
|
10
|
+
value: "https://api.anthropic.com/v1"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
label: "OpenRouter",
|
|
14
|
+
value: "https://openrouter.ai/api/v1"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
label: "Gemini",
|
|
18
|
+
value: "https://generativelanguage.googleapis.com/v1beta/openai"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: "Together AI",
|
|
22
|
+
value: "https://api.together.xyz/v1"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: "Groq",
|
|
26
|
+
value: "https://api.groq.com/openai/v1"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: "DeepInfra",
|
|
30
|
+
value: "https://api.deepinfra.com/v1/openai"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: "xAI",
|
|
34
|
+
value: "https://api.x.ai/v1"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
label: "Qwen (DashScope Intl)",
|
|
38
|
+
value: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: "Qwen (DashScope CN)",
|
|
42
|
+
value: "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
label: "Qwen (DashScope US)",
|
|
46
|
+
value: "https://dashscope-us.aliyuncs.com/compatible-mode/v1"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
label: "Perplexity",
|
|
50
|
+
value: "https://api.perplexity.ai"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
label: "Fireworks",
|
|
54
|
+
value: "https://api.fireworks.ai/inference/v1"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
label: "SambaNova",
|
|
58
|
+
value: "https://api.sambanova.ai/v1"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
label: "Hyperbolic",
|
|
62
|
+
value: "https://api.hyperbolic.xyz/v1"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
label: "Kimi",
|
|
66
|
+
value: "https://api.moonshot.ai/v1"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
label: "ProxyAPI",
|
|
70
|
+
value: "https://openai.api.proxyapi.ru/v1"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
label: "Custom",
|
|
74
|
+
value: AI_CUSTOM_ENDPOINT_VALUE
|
|
75
|
+
}
|
|
76
|
+
];
|
|
77
|
+
var KNOWN_AI_ENDPOINTS = new Set(
|
|
78
|
+
AI_ENDPOINT_OPTIONS.map((option) => option.value).filter(
|
|
79
|
+
(value) => value !== AI_CUSTOM_ENDPOINT_VALUE
|
|
80
|
+
)
|
|
81
|
+
);
|
|
82
|
+
var normalizeUrl = (value) => value.replace(/\/+$/, "");
|
|
83
|
+
var isIpv4Address = (value) => /^(?:\d{1,3}\.){3}\d{1,3}$/.test(value.trim());
|
|
84
|
+
var parseIpv4Address = (value) => value.trim().split(".").map((part) => Number(part));
|
|
85
|
+
var isPrivateIpv4Address = (value) => {
|
|
86
|
+
if (!isIpv4Address(value)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const [a, b, c, d] = parseIpv4Address(value);
|
|
90
|
+
if ([a, b, c, d].some(
|
|
91
|
+
(part) => !Number.isInteger(part) || part < 0 || part > 255
|
|
92
|
+
)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
return a === 10 || a === 127 || a === 0 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168;
|
|
96
|
+
};
|
|
97
|
+
var isPrivateHostname = (hostname) => {
|
|
98
|
+
const normalized = hostname.trim().toLowerCase();
|
|
99
|
+
if (!normalized) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || normalized.endsWith(".internal") || normalized.endsWith(".lan") || normalized === "::1" || normalized === "[::1]" || isPrivateIpv4Address(normalized);
|
|
103
|
+
};
|
|
104
|
+
var isValidAiEndpointUrl = (value) => {
|
|
105
|
+
try {
|
|
106
|
+
const url = new URL(value);
|
|
107
|
+
return url.protocol === "https:" && !isPrivateHostname(url.hostname);
|
|
108
|
+
} catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
var normalizeAiEndpoint = (value) => {
|
|
113
|
+
if (typeof value !== "string") {
|
|
114
|
+
return "";
|
|
115
|
+
}
|
|
116
|
+
const trimmed = normalizeUrl(value.trim());
|
|
117
|
+
if (!trimmed) {
|
|
118
|
+
return "";
|
|
119
|
+
}
|
|
120
|
+
if (KNOWN_AI_ENDPOINTS.has(trimmed)) {
|
|
121
|
+
return trimmed;
|
|
122
|
+
}
|
|
123
|
+
return isValidAiEndpointUrl(trimmed) ? trimmed : "";
|
|
124
|
+
};
|
|
125
|
+
var isKnownAiEndpoint = (value) => KNOWN_AI_ENDPOINTS.has(normalizeAiEndpoint(value));
|
|
126
|
+
|
|
127
|
+
export {
|
|
128
|
+
AI_CUSTOM_ENDPOINT_VALUE,
|
|
129
|
+
AI_ENDPOINT_OPTIONS,
|
|
130
|
+
normalizeAiEndpoint,
|
|
131
|
+
isKnownAiEndpoint
|
|
132
|
+
};
|
package/dist/ml.d.mts
CHANGED
|
@@ -1,22 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
declare const getMlChunkFilePath: (strategyName: string, chunkId: string, outDir?: string) => string;
|
|
3
|
-
declare const appendMlDatasetRow: (params: {
|
|
4
|
-
strategyName: string;
|
|
5
|
-
chunkId: string;
|
|
6
|
-
row: Record<string, number | string | null>;
|
|
7
|
-
outDir?: string;
|
|
8
|
-
}) => Promise<string>;
|
|
9
|
-
declare const flushMlDatasetWriter: (filePath: string) => Promise<void>;
|
|
10
|
-
declare const closeMlDatasetWriter: (filePath: string) => Promise<void>;
|
|
11
|
-
declare const closeAllMlDatasetWriters: () => Promise<void>;
|
|
12
|
-
declare const listMlChunkFiles: (params: {
|
|
13
|
-
strategyName: string;
|
|
14
|
-
outDir?: string;
|
|
15
|
-
}) => Promise<string[]>;
|
|
16
|
-
declare const mergeJsonlFiles: (params: {
|
|
17
|
-
filePaths: string[];
|
|
18
|
-
outPath: string;
|
|
19
|
-
}) => Promise<void>;
|
|
1
|
+
export { a as appendMlDatasetRow, c as closeAllMlDatasetWriters, b as closeMlDatasetWriter, f as flushMlDatasetWriter, g as getMlChunkFilePath, l as listMlChunkFiles, d as listMlChunkStrategies, m as mergeJsonlFiles, t as toFileToken } from './mlDatasetFile-sRGWgR_o.mjs';
|
|
20
2
|
|
|
21
3
|
type MlPredictResponse = {
|
|
22
4
|
probability: number;
|
|
@@ -134,4 +116,4 @@ type MlSeriesAnalysisSummary = Record<string, number>;
|
|
|
134
116
|
declare const analyzeMlSeriesWindow: (input: MlSeriesAnalysisInput) => MlSeriesAnalysisSummary;
|
|
135
117
|
declare const buildMlSeriesAlignment: (left: MlSeriesAnalysisSummary | undefined, right: MlSeriesAnalysisSummary | undefined) => MlSeriesAnalysisSummary;
|
|
136
118
|
|
|
137
|
-
export { type LookaheadViolation, type MlPredictParams, type MlPredictResponse, type MlResultRecord, type MlSeriesAnalysisCandle, type MlSeriesAnalysisSummary, type MlSignalRecord, analyzeMlSeriesWindow,
|
|
119
|
+
export { type LookaheadViolation, type MlPredictParams, type MlPredictResponse, type MlResultRecord, type MlSeriesAnalysisCandle, type MlSeriesAnalysisSummary, type MlSignalRecord, analyzeMlSeriesWindow, buildMlFeatures, buildMlSeriesAlignment, buildMlTrainingRow, computeWindowBoundaries, fetchMlThreshold, findLookaheadViolations, isDerivedDatasetFileName, isTimestampFeatureKey, toIsoUtcOrNull, trimMlTrainingRowWindows };
|
package/dist/ml.d.ts
CHANGED
|
@@ -1,22 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
declare const getMlChunkFilePath: (strategyName: string, chunkId: string, outDir?: string) => string;
|
|
3
|
-
declare const appendMlDatasetRow: (params: {
|
|
4
|
-
strategyName: string;
|
|
5
|
-
chunkId: string;
|
|
6
|
-
row: Record<string, number | string | null>;
|
|
7
|
-
outDir?: string;
|
|
8
|
-
}) => Promise<string>;
|
|
9
|
-
declare const flushMlDatasetWriter: (filePath: string) => Promise<void>;
|
|
10
|
-
declare const closeMlDatasetWriter: (filePath: string) => Promise<void>;
|
|
11
|
-
declare const closeAllMlDatasetWriters: () => Promise<void>;
|
|
12
|
-
declare const listMlChunkFiles: (params: {
|
|
13
|
-
strategyName: string;
|
|
14
|
-
outDir?: string;
|
|
15
|
-
}) => Promise<string[]>;
|
|
16
|
-
declare const mergeJsonlFiles: (params: {
|
|
17
|
-
filePaths: string[];
|
|
18
|
-
outPath: string;
|
|
19
|
-
}) => Promise<void>;
|
|
1
|
+
export { a as appendMlDatasetRow, c as closeAllMlDatasetWriters, b as closeMlDatasetWriter, f as flushMlDatasetWriter, g as getMlChunkFilePath, l as listMlChunkFiles, d as listMlChunkStrategies, m as mergeJsonlFiles, t as toFileToken } from './mlDatasetFile-sRGWgR_o.js';
|
|
20
2
|
|
|
21
3
|
type MlPredictResponse = {
|
|
22
4
|
probability: number;
|
|
@@ -134,4 +116,4 @@ type MlSeriesAnalysisSummary = Record<string, number>;
|
|
|
134
116
|
declare const analyzeMlSeriesWindow: (input: MlSeriesAnalysisInput) => MlSeriesAnalysisSummary;
|
|
135
117
|
declare const buildMlSeriesAlignment: (left: MlSeriesAnalysisSummary | undefined, right: MlSeriesAnalysisSummary | undefined) => MlSeriesAnalysisSummary;
|
|
136
118
|
|
|
137
|
-
export { type LookaheadViolation, type MlPredictParams, type MlPredictResponse, type MlResultRecord, type MlSeriesAnalysisCandle, type MlSeriesAnalysisSummary, type MlSignalRecord, analyzeMlSeriesWindow,
|
|
119
|
+
export { type LookaheadViolation, type MlPredictParams, type MlPredictResponse, type MlResultRecord, type MlSeriesAnalysisCandle, type MlSeriesAnalysisSummary, type MlSignalRecord, analyzeMlSeriesWindow, buildMlFeatures, buildMlSeriesAlignment, buildMlTrainingRow, computeWindowBoundaries, fetchMlThreshold, findLookaheadViolations, isDerivedDatasetFileName, isTimestampFeatureKey, toIsoUtcOrNull, trimMlTrainingRowWindows };
|
package/dist/ml.js
CHANGED
|
@@ -45,6 +45,7 @@ __export(ml_exports, {
|
|
|
45
45
|
isDerivedDatasetFileName: () => isDerivedDatasetFileName,
|
|
46
46
|
isTimestampFeatureKey: () => isTimestampFeatureKey,
|
|
47
47
|
listMlChunkFiles: () => listMlChunkFiles,
|
|
48
|
+
listMlChunkStrategies: () => listMlChunkStrategies,
|
|
48
49
|
mergeJsonlFiles: () => mergeJsonlFiles,
|
|
49
50
|
toFileToken: () => toFileToken,
|
|
50
51
|
toIsoUtcOrNull: () => toIsoUtcOrNull,
|
|
@@ -59,6 +60,7 @@ var import_promises = __toESM(require("fs/promises"));
|
|
|
59
60
|
var import_path = __toESM(require("path"));
|
|
60
61
|
var DEFAULT_DIR = "data/ml/export";
|
|
61
62
|
var ML_DATASET_WRITE_BATCH_SIZE = 200;
|
|
63
|
+
var ML_CHUNK_FILE_RE = /^ml-dataset-(.+)-chunk-[^.]+\.jsonl$/;
|
|
62
64
|
var writerByPath = /* @__PURE__ */ new Map();
|
|
63
65
|
var toFileToken = (value) => value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "_").replace(/^_+|_+$/g, "") || "any";
|
|
64
66
|
var getMlChunkFilePath = (strategyName, chunkId, outDir = DEFAULT_DIR) => import_path.default.join(
|
|
@@ -148,6 +150,20 @@ var listMlChunkFiles = async (params) => {
|
|
|
148
150
|
}
|
|
149
151
|
return entries.filter((name) => name.startsWith(prefix) && name.endsWith(".jsonl")).map((name) => import_path.default.join(outDir, name)).sort();
|
|
150
152
|
};
|
|
153
|
+
var listMlChunkStrategies = async (params) => {
|
|
154
|
+
const outDir = params?.outDir ?? DEFAULT_DIR;
|
|
155
|
+
let entries = [];
|
|
156
|
+
try {
|
|
157
|
+
entries = await import_promises.default.readdir(outDir);
|
|
158
|
+
} catch {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
return [
|
|
162
|
+
...new Set(
|
|
163
|
+
entries.map((name) => name.match(ML_CHUNK_FILE_RE)?.[1] || "").filter(Boolean)
|
|
164
|
+
)
|
|
165
|
+
].sort();
|
|
166
|
+
};
|
|
151
167
|
var mergeJsonlFiles = async (params) => {
|
|
152
168
|
const { filePaths, outPath } = params;
|
|
153
169
|
await import_promises.default.mkdir(import_path.default.dirname(outPath), { recursive: true });
|
|
@@ -621,6 +637,44 @@ var INDICATOR_TIMEFRAMES = [
|
|
|
621
637
|
{ label: "TF4H", suffix: "4h" },
|
|
622
638
|
{ label: "TF1D", suffix: "1d" }
|
|
623
639
|
];
|
|
640
|
+
var DERIVATIVES_INTERVALS = [
|
|
641
|
+
{ label: "15M", key: "15m" },
|
|
642
|
+
{ label: "1H", key: "1h" }
|
|
643
|
+
];
|
|
644
|
+
var DERIVATIVES_PRESSURES = [
|
|
645
|
+
"neutral",
|
|
646
|
+
"crowded_long",
|
|
647
|
+
"crowded_short",
|
|
648
|
+
"long_flush",
|
|
649
|
+
"short_flush"
|
|
650
|
+
];
|
|
651
|
+
var DERIVATIVES_PRESSURE_LABELS = {
|
|
652
|
+
neutral: "Neutral",
|
|
653
|
+
crowded_long: "CrowdedLong",
|
|
654
|
+
crowded_short: "CrowdedShort",
|
|
655
|
+
long_flush: "LongFlush",
|
|
656
|
+
short_flush: "ShortFlush"
|
|
657
|
+
};
|
|
658
|
+
var DERIVATIVES_RISK_FLAGS = [
|
|
659
|
+
"missing_derivatives",
|
|
660
|
+
"stale_derivatives",
|
|
661
|
+
"crowded_long",
|
|
662
|
+
"crowded_short",
|
|
663
|
+
"oi_falling",
|
|
664
|
+
"oi_not_confirming",
|
|
665
|
+
"long_liquidation_spike",
|
|
666
|
+
"short_liquidation_spike"
|
|
667
|
+
];
|
|
668
|
+
var DERIVATIVES_RISK_FLAG_LABELS = {
|
|
669
|
+
missing_derivatives: "Missing",
|
|
670
|
+
stale_derivatives: "Stale",
|
|
671
|
+
crowded_long: "CrowdedLong",
|
|
672
|
+
crowded_short: "CrowdedShort",
|
|
673
|
+
oi_falling: "OiFalling",
|
|
674
|
+
oi_not_confirming: "OiNotConfirming",
|
|
675
|
+
long_liquidation_spike: "LongLiqSpike",
|
|
676
|
+
short_liquidation_spike: "ShortLiqSpike"
|
|
677
|
+
};
|
|
624
678
|
var toNumber = (value, fallback = 0) => {
|
|
625
679
|
if (typeof value === "number") {
|
|
626
680
|
return Number.isFinite(value) ? value : fallback;
|
|
@@ -741,6 +795,7 @@ var sliceWindow = (values, endIndex, windowSize) => {
|
|
|
741
795
|
return values.slice(start, endIndex + 1);
|
|
742
796
|
};
|
|
743
797
|
var asArray = (value) => Array.isArray(value) ? value : [];
|
|
798
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
744
799
|
var dropLastFromIndicatorSeries = (indicators) => {
|
|
745
800
|
if (!ML_WINDOW_POLICY.dropLastIndicatorElement) {
|
|
746
801
|
return indicators;
|
|
@@ -1462,6 +1517,85 @@ var buildMlTrainingRow = (signalRecord, resultRecord) => {
|
|
|
1462
1517
|
row.TrendLine_Slope = null;
|
|
1463
1518
|
}
|
|
1464
1519
|
};
|
|
1520
|
+
const applyDerivativesPhase = () => {
|
|
1521
|
+
const context2 = asRecord(signal?.additionalIndicators?.derivativesContext);
|
|
1522
|
+
const summary = asRecord(context2?.summary);
|
|
1523
|
+
const intervals = asRecord(context2?.intervals);
|
|
1524
|
+
const pressure = typeof summary?.pressure === "string" ? summary.pressure : "";
|
|
1525
|
+
const riskFlags = new Set(
|
|
1526
|
+
asArray(summary?.riskFlags).map((flag) => String(flag))
|
|
1527
|
+
);
|
|
1528
|
+
row.Deriv_HasContext = context2 ? 1 : 0;
|
|
1529
|
+
row.Deriv_Source_Coinalyze = context2?.source === "coinalyze" ? 1 : 0;
|
|
1530
|
+
row.Deriv_DirectionAligned = summary?.directionAligned === true ? 1 : summary?.directionAligned === false ? -1 : 0;
|
|
1531
|
+
for (const pressureName of DERIVATIVES_PRESSURES) {
|
|
1532
|
+
row[`Deriv_Pressure_${DERIVATIVES_PRESSURE_LABELS[pressureName]}`] = pressure === pressureName ? 1 : 0;
|
|
1533
|
+
}
|
|
1534
|
+
for (const riskFlag of DERIVATIVES_RISK_FLAGS) {
|
|
1535
|
+
row[`Deriv_Flag_${DERIVATIVES_RISK_FLAG_LABELS[riskFlag]}`] = riskFlags.has(riskFlag) ? 1 : 0;
|
|
1536
|
+
}
|
|
1537
|
+
for (const { label, key: intervalKey } of DERIVATIVES_INTERVALS) {
|
|
1538
|
+
const intervalContext = asRecord(intervals?.[intervalKey]);
|
|
1539
|
+
const prefix = `Deriv_${label}`;
|
|
1540
|
+
const asOfTs = toNumber(intervalContext?.asOfTs, entryTimestamp);
|
|
1541
|
+
const ageHours = intervalContext ? Math.max(0, safeDiv2(entryTimestamp - asOfTs, 60 * 60 * 1e3)) : 0;
|
|
1542
|
+
row[`${prefix}_Present`] = intervalContext ? 1 : 0;
|
|
1543
|
+
row[`${prefix}_Stale`] = intervalContext?.stale === true ? 1 : 0;
|
|
1544
|
+
row[`${prefix}_AgeHours_Log`] = safeLog1pPositive(ageHours);
|
|
1545
|
+
row[`${prefix}_Points_Log`] = safeLog1pPositive(
|
|
1546
|
+
toNumber(intervalContext?.points, 0)
|
|
1547
|
+
);
|
|
1548
|
+
row[`${prefix}_OpenInterest_Log`] = safeLog1pPositive(
|
|
1549
|
+
toNumber(intervalContext?.openInterest, 0)
|
|
1550
|
+
);
|
|
1551
|
+
row[`${prefix}_OiChange1h`] = clamp2(
|
|
1552
|
+
toNumber(intervalContext?.oiChangePct1h, 0) / 100,
|
|
1553
|
+
-5,
|
|
1554
|
+
5
|
|
1555
|
+
);
|
|
1556
|
+
row[`${prefix}_OiChange4h`] = clamp2(
|
|
1557
|
+
toNumber(intervalContext?.oiChangePct4h, 0) / 100,
|
|
1558
|
+
-5,
|
|
1559
|
+
5
|
|
1560
|
+
);
|
|
1561
|
+
row[`${prefix}_OiChange24h`] = clamp2(
|
|
1562
|
+
toNumber(intervalContext?.oiChangePct24h, 0) / 100,
|
|
1563
|
+
-5,
|
|
1564
|
+
5
|
|
1565
|
+
);
|
|
1566
|
+
row[`${prefix}_FundingBps`] = clamp2(
|
|
1567
|
+
toNumber(intervalContext?.fundingRate, 0) * 1e4,
|
|
1568
|
+
-100,
|
|
1569
|
+
100
|
|
1570
|
+
);
|
|
1571
|
+
row[`${prefix}_FundingZ`] = clamp2(
|
|
1572
|
+
toNumber(intervalContext?.fundingZScore, 0),
|
|
1573
|
+
-8,
|
|
1574
|
+
8
|
|
1575
|
+
);
|
|
1576
|
+
row[`${prefix}_LiqLong_Log`] = safeLog1pPositive(
|
|
1577
|
+
toNumber(intervalContext?.liqLong, 0)
|
|
1578
|
+
);
|
|
1579
|
+
row[`${prefix}_LiqShort_Log`] = safeLog1pPositive(
|
|
1580
|
+
toNumber(intervalContext?.liqShort, 0)
|
|
1581
|
+
);
|
|
1582
|
+
row[`${prefix}_LiqTotal_Log`] = safeLog1pPositive(
|
|
1583
|
+
toNumber(intervalContext?.liqTotal, 0)
|
|
1584
|
+
);
|
|
1585
|
+
row[`${prefix}_LiqImbalance`] = clamp2(
|
|
1586
|
+
toNumber(intervalContext?.liqImbalance, 0),
|
|
1587
|
+
-1,
|
|
1588
|
+
1
|
|
1589
|
+
);
|
|
1590
|
+
row[`${prefix}_LiqSpike_Log`] = safeLog1pPositive(
|
|
1591
|
+
toNumber(intervalContext?.liqSpikeRatio, 0)
|
|
1592
|
+
);
|
|
1593
|
+
row[`${prefix}_LiqSpike_Squashed`] = squash(
|
|
1594
|
+
toNumber(intervalContext?.liqSpikeRatio, 0),
|
|
1595
|
+
3
|
|
1596
|
+
);
|
|
1597
|
+
}
|
|
1598
|
+
};
|
|
1465
1599
|
const applyLabelPhase = () => {
|
|
1466
1600
|
const profit = toNumber(resultRecord?.profit, NaN);
|
|
1467
1601
|
row.label = Number.isFinite(profit) ? profit > 0 ? 1 : 0 : null;
|
|
@@ -1471,6 +1605,7 @@ var buildMlTrainingRow = (signalRecord, resultRecord) => {
|
|
|
1471
1605
|
applyRegimePhase();
|
|
1472
1606
|
applySeriesAnalysisPhase();
|
|
1473
1607
|
applyTrendlinePhase();
|
|
1608
|
+
applyDerivativesPhase();
|
|
1474
1609
|
applyLabelPhase();
|
|
1475
1610
|
return row;
|
|
1476
1611
|
};
|
|
@@ -1605,6 +1740,7 @@ var findLookaheadViolations = (row) => {
|
|
|
1605
1740
|
isDerivedDatasetFileName,
|
|
1606
1741
|
isTimestampFeatureKey,
|
|
1607
1742
|
listMlChunkFiles,
|
|
1743
|
+
listMlChunkStrategies,
|
|
1608
1744
|
mergeJsonlFiles,
|
|
1609
1745
|
toFileToken,
|
|
1610
1746
|
toIsoUtcOrNull,
|
package/dist/ml.mjs
CHANGED
|
@@ -1,142 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
appendMlDatasetRow,
|
|
3
|
+
closeAllMlDatasetWriters,
|
|
4
|
+
closeMlDatasetWriter,
|
|
5
|
+
flushMlDatasetWriter,
|
|
6
|
+
getMlChunkFilePath,
|
|
7
|
+
listMlChunkFiles,
|
|
8
|
+
listMlChunkStrategies,
|
|
9
|
+
mergeJsonlFiles,
|
|
10
|
+
toFileToken
|
|
11
|
+
} from "./chunk-KVEZORMS.mjs";
|
|
1
12
|
import {
|
|
2
13
|
logger
|
|
3
14
|
} from "./chunk-LNFUOXDW.mjs";
|
|
4
15
|
|
|
5
|
-
// src/mlDatasetFile.ts
|
|
6
|
-
import { once } from "events";
|
|
7
|
-
import { createReadStream, createWriteStream } from "fs";
|
|
8
|
-
import fs from "fs/promises";
|
|
9
|
-
import path from "path";
|
|
10
|
-
var DEFAULT_DIR = "data/ml/export";
|
|
11
|
-
var ML_DATASET_WRITE_BATCH_SIZE = 200;
|
|
12
|
-
var writerByPath = /* @__PURE__ */ new Map();
|
|
13
|
-
var toFileToken = (value) => value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "_").replace(/^_+|_+$/g, "") || "any";
|
|
14
|
-
var getMlChunkFilePath = (strategyName, chunkId, outDir = DEFAULT_DIR) => path.join(
|
|
15
|
-
outDir,
|
|
16
|
-
`ml-dataset-${toFileToken(strategyName)}-chunk-${toFileToken(chunkId)}.jsonl`
|
|
17
|
-
);
|
|
18
|
-
var appendMlDatasetRow = async (params) => {
|
|
19
|
-
const { strategyName, chunkId, row, outDir = DEFAULT_DIR } = params;
|
|
20
|
-
const filePath = getMlChunkFilePath(strategyName, chunkId, outDir);
|
|
21
|
-
let state = writerByPath.get(filePath);
|
|
22
|
-
if (!state) {
|
|
23
|
-
await fs.mkdir(outDir, { recursive: true });
|
|
24
|
-
const stream = createWriteStream(filePath, {
|
|
25
|
-
encoding: "utf8",
|
|
26
|
-
flags: "a"
|
|
27
|
-
});
|
|
28
|
-
state = {
|
|
29
|
-
filePath,
|
|
30
|
-
stream,
|
|
31
|
-
buffer: [],
|
|
32
|
-
writeQueue: Promise.resolve(),
|
|
33
|
-
closed: false
|
|
34
|
-
};
|
|
35
|
-
writerByPath.set(filePath, state);
|
|
36
|
-
}
|
|
37
|
-
if (state.closed) {
|
|
38
|
-
throw new Error(`ML dataset writer is closed: ${filePath}`);
|
|
39
|
-
}
|
|
40
|
-
state.buffer.push(`${JSON.stringify(row)}
|
|
41
|
-
`);
|
|
42
|
-
if (state.buffer.length >= ML_DATASET_WRITE_BATCH_SIZE) {
|
|
43
|
-
await flushMlDatasetWriter(filePath);
|
|
44
|
-
}
|
|
45
|
-
return filePath;
|
|
46
|
-
};
|
|
47
|
-
var flushState = async (state) => {
|
|
48
|
-
if (state.closed || state.buffer.length === 0) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
const chunk = state.buffer.join("");
|
|
52
|
-
state.buffer = [];
|
|
53
|
-
if (!state.stream.write(chunk)) {
|
|
54
|
-
await once(state.stream, "drain");
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
var flushMlDatasetWriter = async (filePath) => {
|
|
58
|
-
const state = writerByPath.get(filePath);
|
|
59
|
-
if (!state || state.closed) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
state.writeQueue = state.writeQueue.then(() => flushState(state));
|
|
63
|
-
await state.writeQueue;
|
|
64
|
-
};
|
|
65
|
-
var closeState = async (state) => {
|
|
66
|
-
if (state.closed) {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
await flushState(state);
|
|
70
|
-
state.closed = true;
|
|
71
|
-
state.stream.end();
|
|
72
|
-
await Promise.all([
|
|
73
|
-
once(state.stream, "finish"),
|
|
74
|
-
once(state.stream, "close")
|
|
75
|
-
]);
|
|
76
|
-
};
|
|
77
|
-
var closeMlDatasetWriter = async (filePath) => {
|
|
78
|
-
const state = writerByPath.get(filePath);
|
|
79
|
-
if (!state) return;
|
|
80
|
-
state.writeQueue = state.writeQueue.then(() => closeState(state));
|
|
81
|
-
await state.writeQueue;
|
|
82
|
-
writerByPath.delete(filePath);
|
|
83
|
-
};
|
|
84
|
-
var closeAllMlDatasetWriters = async () => {
|
|
85
|
-
const filePaths = [...writerByPath.keys()];
|
|
86
|
-
for (const filePath of filePaths) {
|
|
87
|
-
await closeMlDatasetWriter(filePath);
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
var listMlChunkFiles = async (params) => {
|
|
91
|
-
const { strategyName, outDir = DEFAULT_DIR } = params;
|
|
92
|
-
const prefix = `ml-dataset-${toFileToken(strategyName)}-chunk-`;
|
|
93
|
-
let entries = [];
|
|
94
|
-
try {
|
|
95
|
-
entries = await fs.readdir(outDir);
|
|
96
|
-
} catch (error) {
|
|
97
|
-
return [];
|
|
98
|
-
}
|
|
99
|
-
return entries.filter((name) => name.startsWith(prefix) && name.endsWith(".jsonl")).map((name) => path.join(outDir, name)).sort();
|
|
100
|
-
};
|
|
101
|
-
var mergeJsonlFiles = async (params) => {
|
|
102
|
-
const { filePaths, outPath } = params;
|
|
103
|
-
await fs.mkdir(path.dirname(outPath), { recursive: true });
|
|
104
|
-
const stream = createWriteStream(outPath, { encoding: "utf8" });
|
|
105
|
-
const done = Promise.all([once(stream, "finish"), once(stream, "close")]);
|
|
106
|
-
try {
|
|
107
|
-
for (const filePath of filePaths) {
|
|
108
|
-
const reader = createReadStream(filePath, { encoding: "utf8" });
|
|
109
|
-
for await (const chunk of reader) {
|
|
110
|
-
if (!stream.write(chunk)) {
|
|
111
|
-
await once(stream, "drain");
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
} finally {
|
|
116
|
-
stream.end();
|
|
117
|
-
await done;
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
|
|
121
16
|
// src/mlGrpc.ts
|
|
122
|
-
import
|
|
17
|
+
import path from "path";
|
|
123
18
|
import * as grpc from "@grpc/grpc-js";
|
|
124
19
|
import * as protoLoader from "@grpc/proto-loader";
|
|
125
|
-
import
|
|
20
|
+
import fs from "fs";
|
|
126
21
|
var clientCache = /* @__PURE__ */ new Map();
|
|
127
22
|
var resolveProtoPath = (projectRoot, protoPath) => {
|
|
128
|
-
if (protoPath &&
|
|
23
|
+
if (protoPath && fs.existsSync(protoPath)) {
|
|
129
24
|
return protoPath;
|
|
130
25
|
}
|
|
131
26
|
const explicitRoot = String(projectRoot || "").trim();
|
|
132
|
-
const root = explicitRoot ?
|
|
27
|
+
const root = explicitRoot ? path.resolve(explicitRoot) : String(process.env.PROJECT_CWD || "").trim() ? path.resolve(String(process.env.PROJECT_CWD || "").trim()) : process.cwd();
|
|
133
28
|
const candidates = [
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
29
|
+
path.resolve(__dirname, "../proto/ml_infer.proto"),
|
|
30
|
+
path.resolve(__dirname, "../../proto/ml_infer.proto"),
|
|
31
|
+
path.resolve(root, "proto/ml_infer.proto")
|
|
137
32
|
];
|
|
138
33
|
for (const candidate of candidates) {
|
|
139
|
-
if (
|
|
34
|
+
if (fs.existsSync(candidate)) {
|
|
140
35
|
return candidate;
|
|
141
36
|
}
|
|
142
37
|
}
|
|
@@ -530,6 +425,44 @@ var INDICATOR_TIMEFRAMES = [
|
|
|
530
425
|
{ label: "TF4H", suffix: "4h" },
|
|
531
426
|
{ label: "TF1D", suffix: "1d" }
|
|
532
427
|
];
|
|
428
|
+
var DERIVATIVES_INTERVALS = [
|
|
429
|
+
{ label: "15M", key: "15m" },
|
|
430
|
+
{ label: "1H", key: "1h" }
|
|
431
|
+
];
|
|
432
|
+
var DERIVATIVES_PRESSURES = [
|
|
433
|
+
"neutral",
|
|
434
|
+
"crowded_long",
|
|
435
|
+
"crowded_short",
|
|
436
|
+
"long_flush",
|
|
437
|
+
"short_flush"
|
|
438
|
+
];
|
|
439
|
+
var DERIVATIVES_PRESSURE_LABELS = {
|
|
440
|
+
neutral: "Neutral",
|
|
441
|
+
crowded_long: "CrowdedLong",
|
|
442
|
+
crowded_short: "CrowdedShort",
|
|
443
|
+
long_flush: "LongFlush",
|
|
444
|
+
short_flush: "ShortFlush"
|
|
445
|
+
};
|
|
446
|
+
var DERIVATIVES_RISK_FLAGS = [
|
|
447
|
+
"missing_derivatives",
|
|
448
|
+
"stale_derivatives",
|
|
449
|
+
"crowded_long",
|
|
450
|
+
"crowded_short",
|
|
451
|
+
"oi_falling",
|
|
452
|
+
"oi_not_confirming",
|
|
453
|
+
"long_liquidation_spike",
|
|
454
|
+
"short_liquidation_spike"
|
|
455
|
+
];
|
|
456
|
+
var DERIVATIVES_RISK_FLAG_LABELS = {
|
|
457
|
+
missing_derivatives: "Missing",
|
|
458
|
+
stale_derivatives: "Stale",
|
|
459
|
+
crowded_long: "CrowdedLong",
|
|
460
|
+
crowded_short: "CrowdedShort",
|
|
461
|
+
oi_falling: "OiFalling",
|
|
462
|
+
oi_not_confirming: "OiNotConfirming",
|
|
463
|
+
long_liquidation_spike: "LongLiqSpike",
|
|
464
|
+
short_liquidation_spike: "ShortLiqSpike"
|
|
465
|
+
};
|
|
533
466
|
var toNumber = (value, fallback = 0) => {
|
|
534
467
|
if (typeof value === "number") {
|
|
535
468
|
return Number.isFinite(value) ? value : fallback;
|
|
@@ -650,6 +583,7 @@ var sliceWindow = (values, endIndex, windowSize) => {
|
|
|
650
583
|
return values.slice(start, endIndex + 1);
|
|
651
584
|
};
|
|
652
585
|
var asArray = (value) => Array.isArray(value) ? value : [];
|
|
586
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
653
587
|
var dropLastFromIndicatorSeries = (indicators) => {
|
|
654
588
|
if (!ML_WINDOW_POLICY.dropLastIndicatorElement) {
|
|
655
589
|
return indicators;
|
|
@@ -1371,6 +1305,85 @@ var buildMlTrainingRow = (signalRecord, resultRecord) => {
|
|
|
1371
1305
|
row.TrendLine_Slope = null;
|
|
1372
1306
|
}
|
|
1373
1307
|
};
|
|
1308
|
+
const applyDerivativesPhase = () => {
|
|
1309
|
+
const context2 = asRecord(signal?.additionalIndicators?.derivativesContext);
|
|
1310
|
+
const summary = asRecord(context2?.summary);
|
|
1311
|
+
const intervals = asRecord(context2?.intervals);
|
|
1312
|
+
const pressure = typeof summary?.pressure === "string" ? summary.pressure : "";
|
|
1313
|
+
const riskFlags = new Set(
|
|
1314
|
+
asArray(summary?.riskFlags).map((flag) => String(flag))
|
|
1315
|
+
);
|
|
1316
|
+
row.Deriv_HasContext = context2 ? 1 : 0;
|
|
1317
|
+
row.Deriv_Source_Coinalyze = context2?.source === "coinalyze" ? 1 : 0;
|
|
1318
|
+
row.Deriv_DirectionAligned = summary?.directionAligned === true ? 1 : summary?.directionAligned === false ? -1 : 0;
|
|
1319
|
+
for (const pressureName of DERIVATIVES_PRESSURES) {
|
|
1320
|
+
row[`Deriv_Pressure_${DERIVATIVES_PRESSURE_LABELS[pressureName]}`] = pressure === pressureName ? 1 : 0;
|
|
1321
|
+
}
|
|
1322
|
+
for (const riskFlag of DERIVATIVES_RISK_FLAGS) {
|
|
1323
|
+
row[`Deriv_Flag_${DERIVATIVES_RISK_FLAG_LABELS[riskFlag]}`] = riskFlags.has(riskFlag) ? 1 : 0;
|
|
1324
|
+
}
|
|
1325
|
+
for (const { label, key: intervalKey } of DERIVATIVES_INTERVALS) {
|
|
1326
|
+
const intervalContext = asRecord(intervals?.[intervalKey]);
|
|
1327
|
+
const prefix = `Deriv_${label}`;
|
|
1328
|
+
const asOfTs = toNumber(intervalContext?.asOfTs, entryTimestamp);
|
|
1329
|
+
const ageHours = intervalContext ? Math.max(0, safeDiv2(entryTimestamp - asOfTs, 60 * 60 * 1e3)) : 0;
|
|
1330
|
+
row[`${prefix}_Present`] = intervalContext ? 1 : 0;
|
|
1331
|
+
row[`${prefix}_Stale`] = intervalContext?.stale === true ? 1 : 0;
|
|
1332
|
+
row[`${prefix}_AgeHours_Log`] = safeLog1pPositive(ageHours);
|
|
1333
|
+
row[`${prefix}_Points_Log`] = safeLog1pPositive(
|
|
1334
|
+
toNumber(intervalContext?.points, 0)
|
|
1335
|
+
);
|
|
1336
|
+
row[`${prefix}_OpenInterest_Log`] = safeLog1pPositive(
|
|
1337
|
+
toNumber(intervalContext?.openInterest, 0)
|
|
1338
|
+
);
|
|
1339
|
+
row[`${prefix}_OiChange1h`] = clamp2(
|
|
1340
|
+
toNumber(intervalContext?.oiChangePct1h, 0) / 100,
|
|
1341
|
+
-5,
|
|
1342
|
+
5
|
|
1343
|
+
);
|
|
1344
|
+
row[`${prefix}_OiChange4h`] = clamp2(
|
|
1345
|
+
toNumber(intervalContext?.oiChangePct4h, 0) / 100,
|
|
1346
|
+
-5,
|
|
1347
|
+
5
|
|
1348
|
+
);
|
|
1349
|
+
row[`${prefix}_OiChange24h`] = clamp2(
|
|
1350
|
+
toNumber(intervalContext?.oiChangePct24h, 0) / 100,
|
|
1351
|
+
-5,
|
|
1352
|
+
5
|
|
1353
|
+
);
|
|
1354
|
+
row[`${prefix}_FundingBps`] = clamp2(
|
|
1355
|
+
toNumber(intervalContext?.fundingRate, 0) * 1e4,
|
|
1356
|
+
-100,
|
|
1357
|
+
100
|
|
1358
|
+
);
|
|
1359
|
+
row[`${prefix}_FundingZ`] = clamp2(
|
|
1360
|
+
toNumber(intervalContext?.fundingZScore, 0),
|
|
1361
|
+
-8,
|
|
1362
|
+
8
|
|
1363
|
+
);
|
|
1364
|
+
row[`${prefix}_LiqLong_Log`] = safeLog1pPositive(
|
|
1365
|
+
toNumber(intervalContext?.liqLong, 0)
|
|
1366
|
+
);
|
|
1367
|
+
row[`${prefix}_LiqShort_Log`] = safeLog1pPositive(
|
|
1368
|
+
toNumber(intervalContext?.liqShort, 0)
|
|
1369
|
+
);
|
|
1370
|
+
row[`${prefix}_LiqTotal_Log`] = safeLog1pPositive(
|
|
1371
|
+
toNumber(intervalContext?.liqTotal, 0)
|
|
1372
|
+
);
|
|
1373
|
+
row[`${prefix}_LiqImbalance`] = clamp2(
|
|
1374
|
+
toNumber(intervalContext?.liqImbalance, 0),
|
|
1375
|
+
-1,
|
|
1376
|
+
1
|
|
1377
|
+
);
|
|
1378
|
+
row[`${prefix}_LiqSpike_Log`] = safeLog1pPositive(
|
|
1379
|
+
toNumber(intervalContext?.liqSpikeRatio, 0)
|
|
1380
|
+
);
|
|
1381
|
+
row[`${prefix}_LiqSpike_Squashed`] = squash(
|
|
1382
|
+
toNumber(intervalContext?.liqSpikeRatio, 0),
|
|
1383
|
+
3
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
};
|
|
1374
1387
|
const applyLabelPhase = () => {
|
|
1375
1388
|
const profit = toNumber(resultRecord?.profit, NaN);
|
|
1376
1389
|
row.label = Number.isFinite(profit) ? profit > 0 ? 1 : 0 : null;
|
|
@@ -1380,6 +1393,7 @@ var buildMlTrainingRow = (signalRecord, resultRecord) => {
|
|
|
1380
1393
|
applyRegimePhase();
|
|
1381
1394
|
applySeriesAnalysisPhase();
|
|
1382
1395
|
applyTrendlinePhase();
|
|
1396
|
+
applyDerivativesPhase();
|
|
1383
1397
|
applyLabelPhase();
|
|
1384
1398
|
return row;
|
|
1385
1399
|
};
|
|
@@ -1513,6 +1527,7 @@ export {
|
|
|
1513
1527
|
isDerivedDatasetFileName,
|
|
1514
1528
|
isTimestampFeatureKey,
|
|
1515
1529
|
listMlChunkFiles,
|
|
1530
|
+
listMlChunkStrategies,
|
|
1516
1531
|
mergeJsonlFiles,
|
|
1517
1532
|
toFileToken,
|
|
1518
1533
|
toIsoUtcOrNull,
|