@tradejs/cli 3.1.29-beta.261 → 3.1.29-beta.262
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 +47 -8
- package/dist/cli.js +126 -24
- package/dist/lib/aiPocketSearch.js +16 -0
- package/dist/lib/aiPocketSearchCli.js +3 -3
- package/dist/lib/jevMicroReferences.js +73 -0
- package/dist/lib/jevResearch.js +11 -0
- package/dist/scripts/aiPocketSearch.js +101 -23
- package/dist/scripts/backtest.js +4 -0
- package/dist/scripts/jev.js +11 -0
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -102,8 +102,10 @@ yarn exec tradejs backtest -c TrendFollow:base -d 30 --cacheOnly --jev --jevReco
|
|
|
102
102
|
Without `--jev`, ordinary backtests do not evaluate Jev, even if a configuration
|
|
103
103
|
contains `JEV`. Use `--ai --jev` to save completed trades with Jev features for
|
|
104
104
|
AI training. `--jev` alone records Jev assessments without enabling AI export.
|
|
105
|
-
Jev
|
|
106
|
-
|
|
105
|
+
Jev uses one current set of nine questions: directional agreement, correction
|
|
106
|
+
containment, current volume expansion, setup volume contraction, entry proximity,
|
|
107
|
+
entry candle conviction, impulse persistence, boundary regularity, and overall
|
|
108
|
+
signal strength.
|
|
107
109
|
It asks each question only when the corresponding signal-time facts exist and
|
|
108
110
|
marks missing facts explicitly. The shared projection
|
|
109
111
|
selects a few signal-time trend, swing, volume, delta, entry-distance and
|
|
@@ -114,6 +116,38 @@ which is available to the strategy's AI gate and AI export. Jev scores do not
|
|
|
114
116
|
approve or reject entries. Backtests do not apply the AI gate; `ai-train`
|
|
115
117
|
evaluates it on exported outcomes, while runtime follows `AI_MODE`.
|
|
116
118
|
|
|
119
|
+
`--jev` and runtime `JEV` always use the current compact nine-question set.
|
|
120
|
+
There is no question-set selector or fallback to older rubrics. The recorded
|
|
121
|
+
`micro-v3` marker and question hash identify the current meanings; old or
|
|
122
|
+
unversioned recordings and local models are rejected. Use a new recording
|
|
123
|
+
directory for the new backtest and retrain local models from current answers.
|
|
124
|
+
|
|
125
|
+
The first eight questions describe separate signal-time facts. The ninth asks
|
|
126
|
+
how strong and actionable the signal is: do the available facts support opening
|
|
127
|
+
a trade now? It weighs direction, setup, confirmation, placement and the
|
|
128
|
+
stop/target plan together. Essential missing facts and unresolved conflicts
|
|
129
|
+
reduce support. All eligible questions share the same compact state in one
|
|
130
|
+
request; outcomes and other Jev answers are excluded.
|
|
131
|
+
|
|
132
|
+
Its ten rubric levels appear as
|
|
133
|
+
`additionalIndicators.jev.scores.signalStrength`, from **1 to 10** (decimal
|
|
134
|
+
expected scores are retained). Confidence remains 0–1. The API returns a
|
|
135
|
+
zero-based 0–9 score; recordings and local teacher models normalize it to 0–1,
|
|
136
|
+
and AI features convert it to 1–10. This is an entry assessment, not a calibrated
|
|
137
|
+
probability of profit, and never automatically approves or blocks a trade.
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
yarn exec tradejs backtest -c <Strategy>:<config> -d 365 --cacheOnly --ai --jev \
|
|
141
|
+
--jevRecordsDir data/ai/jev/<new-study>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Use `--ai` to retain completed outcomes for comparing the score with realized
|
|
145
|
+
PnL. Freeze score thresholds on the first 60% with three internal stability
|
|
146
|
+
blocks before checking the last 40%. Compare with the current gate and ordinary
|
|
147
|
+
features on the same rows, include independent event support and costs, and do
|
|
148
|
+
not choose thresholds on the outer test. A fresh run does not make previously
|
|
149
|
+
inspected historical outcomes untouched.
|
|
150
|
+
|
|
117
151
|
Any strategy can optionally attach `jevEvidence` through its `StrategyAPI.entry`
|
|
118
152
|
`additionalIndicators` with a version, `knownAt` timestamp, up to 16 scalar
|
|
119
153
|
setup facts and 16 scalar geometry facts:
|
|
@@ -147,9 +181,12 @@ Provider responses and decisions are saved under `data/ai/jev`, or the directory
|
|
|
147
181
|
selected with `--jevRecordsDir`. Input, selected question version and provider
|
|
148
182
|
identity determine each signal record. Equivalent facts reuse one provider
|
|
149
183
|
response even when signal timestamps or symbols differ. Input v4 records
|
|
150
|
-
explicitly store missing facts and their sources.
|
|
151
|
-
|
|
152
|
-
|
|
184
|
+
explicitly store missing facts and their sources. Paid state groups fact paths
|
|
185
|
+
by namespace and retains exact values, units and missing reasons; signal identity,
|
|
186
|
+
internal versions and source metadata remain local. The current questions use
|
|
187
|
+
short rubrics with explicit thresholds and unknown-data rules. Recordings and
|
|
188
|
+
local models must match the current question marker and hash. Older versions
|
|
189
|
+
are not migrated or used as substitutes for missing current answers.
|
|
153
190
|
`--jevRecorded` requires the same signal-time input facts, question set and
|
|
154
191
|
provider as the recording run. A shorter backtest may warm up indicators from
|
|
155
192
|
a different history or reach the same date with a different trade state, so its
|
|
@@ -179,9 +216,11 @@ path for each run.
|
|
|
179
216
|
|
|
180
217
|
Training fits shallow trees to Jev scores, without trade outcomes as features or
|
|
181
218
|
labels. It requires at least 30 samples across 10 distinct signal times, selects
|
|
182
|
-
one strategy and one teacher/provider lineage, and
|
|
183
|
-
chronologically
|
|
184
|
-
|
|
219
|
+
one strategy and one teacher/provider lineage, and splits timestamp groups
|
|
220
|
+
chronologically into 60% development and 40% test. Three expanding-window folds
|
|
221
|
+
fit earlier development data and check later development data, entirely inside
|
|
222
|
+
the first 60%. The final teacher model uses the full development partition.
|
|
223
|
+
The outer test never fits or selects the model. The report measures teacher agreement, score error
|
|
185
224
|
and matched-outcome economics. These summaries are not sequential portfolio
|
|
186
225
|
backtests and do not establish profitability. Validate a frozen gate on a later
|
|
187
226
|
period with `--jevModelFile` before runtime use. Studies are bounded to 20,000
|
package/dist/cli.js
CHANGED
|
@@ -1564,7 +1564,7 @@ var init_aiExport2 = __esm({
|
|
|
1564
1564
|
});
|
|
1565
1565
|
|
|
1566
1566
|
// src/lib/aiPocketSearchCli.ts
|
|
1567
|
-
var DAY_MS, DEFAULT_MIN_SUPPORT, LOW_CADENCE_EVENT_LIMIT, LOW_CADENCE_MAX_EVENT_SHARE, countIndependentEvents, getPeriodDays, clampInt, resolveAiPocketCadenceProfile, AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS, splitAiPocketResearchRowsByTimestamp,
|
|
1567
|
+
var DAY_MS, DEFAULT_MIN_SUPPORT, LOW_CADENCE_EVENT_LIMIT, LOW_CADENCE_MAX_EVENT_SHARE, countIndependentEvents, getPeriodDays, clampInt, resolveAiPocketCadenceProfile, AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS, splitAiPocketResearchRowsByTimestamp, sealAiPocketTestPartition, readAiPocketSearchCliOption;
|
|
1568
1568
|
var init_aiPocketSearchCli = __esm({
|
|
1569
1569
|
"src/lib/aiPocketSearchCli.ts"() {
|
|
1570
1570
|
"use strict";
|
|
@@ -1606,7 +1606,7 @@ var init_aiPocketSearchCli = __esm({
|
|
|
1606
1606
|
validationRows = [],
|
|
1607
1607
|
testRows = [],
|
|
1608
1608
|
mode = "auto",
|
|
1609
|
-
validationSplit = 0
|
|
1609
|
+
validationSplit = 0,
|
|
1610
1610
|
minSupport,
|
|
1611
1611
|
minEvents,
|
|
1612
1612
|
minValidationSupport,
|
|
@@ -1656,8 +1656,8 @@ var init_aiPocketSearchCli = __esm({
|
|
|
1656
1656
|
minProfitFactor: "1.2",
|
|
1657
1657
|
minWinRate: "0",
|
|
1658
1658
|
minTotalProfit: "0",
|
|
1659
|
-
validationSplit: "0
|
|
1660
|
-
testSplit: "0"
|
|
1659
|
+
validationSplit: "0",
|
|
1660
|
+
testSplit: "0.4"
|
|
1661
1661
|
};
|
|
1662
1662
|
splitAiPocketResearchRowsByTimestamp = (rows, validationSplit, testSplit = 0) => {
|
|
1663
1663
|
const timestamps = [
|
|
@@ -1698,11 +1698,6 @@ var init_aiPocketSearchCli = __esm({
|
|
|
1698
1698
|
testRows: rows.filter((row) => isIn(testTimestamps, row.timestamp))
|
|
1699
1699
|
};
|
|
1700
1700
|
};
|
|
1701
|
-
splitAiPocketCoverageRowsByTimestamp = (rows, family, validationSplit, testSplit = 0) => splitAiPocketResearchRowsByTimestamp(
|
|
1702
|
-
rows.filter((row) => row.featureCoverage?.[family] === true),
|
|
1703
|
-
validationSplit,
|
|
1704
|
-
testSplit
|
|
1705
|
-
);
|
|
1706
1701
|
sealAiPocketTestPartition = (split, sealed) => {
|
|
1707
1702
|
const finiteTimestamps = split.testRows.map((row) => row.timestamp).filter((timestamp) => Number.isFinite(timestamp));
|
|
1708
1703
|
return {
|
|
@@ -2033,17 +2028,17 @@ var init_commandOptions = __esm({
|
|
|
2033
2028
|
longName: "validationSplit",
|
|
2034
2029
|
shortName: "V"
|
|
2035
2030
|
}) ?? flags35.validationSplit,
|
|
2036
|
-
0
|
|
2037
|
-
) : 0
|
|
2031
|
+
0
|
|
2032
|
+
) : 0;
|
|
2038
2033
|
const testSplit = hasCliOption2(argv2, "testSplit", "T") ? normalizeRatio(
|
|
2039
2034
|
readAiPocketSearchCliOption({
|
|
2040
2035
|
argv: argv2,
|
|
2041
2036
|
longName: "testSplit",
|
|
2042
2037
|
shortName: "T"
|
|
2043
2038
|
}) ?? flags35.testSplit,
|
|
2044
|
-
0
|
|
2045
|
-
) : 0;
|
|
2046
|
-
const sealTest = Boolean(flags35.sealTest);
|
|
2039
|
+
0.4
|
|
2040
|
+
) : 0.4;
|
|
2041
|
+
const sealTest = flags35.sealTest == null ? true : Boolean(flags35.sealTest);
|
|
2047
2042
|
if (sealTest && testSplit <= 0) {
|
|
2048
2043
|
throw new Error("--sealTest requires a positive --testSplit");
|
|
2049
2044
|
}
|
|
@@ -4275,6 +4270,22 @@ var init_markdownReport = __esm({
|
|
|
4275
4270
|
"",
|
|
4276
4271
|
markdownTable(pocketTableHeaders, pocketRows(pocketSearch.positivePockets)),
|
|
4277
4272
|
"",
|
|
4273
|
+
"## Development Stability (inside discovery, not independent validation)",
|
|
4274
|
+
"",
|
|
4275
|
+
markdownTable(
|
|
4276
|
+
["Condition", "Status", "Block", "Events", "PnL", "PF"],
|
|
4277
|
+
pocketSearch.positivePockets.flatMap(
|
|
4278
|
+
(pocket) => (pocket.stability?.folds ?? []).map((fold, index) => [
|
|
4279
|
+
pocket.condition,
|
|
4280
|
+
pocket.stability?.status,
|
|
4281
|
+
index + 1,
|
|
4282
|
+
fold.summary.events,
|
|
4283
|
+
formatMdNumber(fold.summary.totalProfit),
|
|
4284
|
+
formatMdNumber(fold.summary.profitFactor)
|
|
4285
|
+
])
|
|
4286
|
+
)
|
|
4287
|
+
),
|
|
4288
|
+
"",
|
|
4278
4289
|
"## Top Loss Pockets",
|
|
4279
4290
|
"",
|
|
4280
4291
|
markdownTable(pocketTableHeaders, pocketRows(pocketSearch.negativePockets)),
|
|
@@ -5815,6 +5826,70 @@ var init_aiTrainMetrics = __esm({
|
|
|
5815
5826
|
}
|
|
5816
5827
|
});
|
|
5817
5828
|
|
|
5829
|
+
// src/lib/aiPocketSearch/stability.ts
|
|
5830
|
+
var assessDevelopmentStability;
|
|
5831
|
+
var init_stability = __esm({
|
|
5832
|
+
"src/lib/aiPocketSearch/stability.ts"() {
|
|
5833
|
+
"use strict";
|
|
5834
|
+
init_predicates();
|
|
5835
|
+
init_summary();
|
|
5836
|
+
assessDevelopmentStability = (result, options) => {
|
|
5837
|
+
const minEvents = options.minEventsPerFold ?? 5;
|
|
5838
|
+
const timestamps = [...new Set(options.timestamps)].sort((a, b) => a - b);
|
|
5839
|
+
const boundaries = [0, 1, 2, 3].map(
|
|
5840
|
+
(index) => index === 3 ? timestamps.at(-1) == null ? Number.POSITIVE_INFINITY : timestamps.at(-1) + 1 : timestamps[Math.floor(timestamps.length * index / 3)] ?? Number.POSITIVE_INFINITY
|
|
5841
|
+
);
|
|
5842
|
+
const blocks = [0, 1, 2].map((index) => ({
|
|
5843
|
+
start: boundaries[index],
|
|
5844
|
+
end: boundaries[index + 1],
|
|
5845
|
+
rows: options.developmentRows.filter(
|
|
5846
|
+
(row) => row.timestamp != null && row.timestamp >= boundaries[index] && row.timestamp < boundaries[index + 1]
|
|
5847
|
+
),
|
|
5848
|
+
baseline: options.baselineRows.filter(
|
|
5849
|
+
(row) => row.timestamp != null && row.timestamp >= boundaries[index] && row.timestamp < boundaries[index + 1]
|
|
5850
|
+
)
|
|
5851
|
+
}));
|
|
5852
|
+
for (const pocket of result.positivePockets) {
|
|
5853
|
+
const folds = blocks.map((block) => {
|
|
5854
|
+
const mask = buildPredicateListMask(block.rows, pocket.predicates).mask;
|
|
5855
|
+
const selected = block.rows.filter((_3, index) => mask[index] === 1);
|
|
5856
|
+
const summary = summarizeAiPocketRows(selected);
|
|
5857
|
+
const baseline = summarizeAiPocketRows(block.baseline);
|
|
5858
|
+
const objectiveSummary = result.objective === "add-to-gate" ? summarizeAiPocketRows([
|
|
5859
|
+
.../* @__PURE__ */ new Set([...block.baseline, ...selected])
|
|
5860
|
+
]) : summary;
|
|
5861
|
+
return {
|
|
5862
|
+
startTimestamp: Number.isFinite(block.start) ? block.start : null,
|
|
5863
|
+
endTimestamp: Number.isFinite(block.end) ? block.end : null,
|
|
5864
|
+
summary,
|
|
5865
|
+
objectiveSummary,
|
|
5866
|
+
baseline
|
|
5867
|
+
};
|
|
5868
|
+
});
|
|
5869
|
+
const supported = folds.filter((fold) => fold.summary.events >= minEvents);
|
|
5870
|
+
const unstable = supported.some(
|
|
5871
|
+
(fold) => fold.objectiveSummary.totalProfit < (result.objective === "add-to-gate" ? fold.baseline.totalProfit : 0) || (fold.objectiveSummary.profitFactor ?? (fold.objectiveSummary.grossLoss === 0 ? Infinity : 0)) < 1
|
|
5872
|
+
);
|
|
5873
|
+
const status = unstable ? "unstable" : supported.length < 3 ? "insufficient-support" : "stable";
|
|
5874
|
+
pocket.stability = {
|
|
5875
|
+
status,
|
|
5876
|
+
role: "development-only diagnostic",
|
|
5877
|
+
minEventsPerFold: minEvents,
|
|
5878
|
+
folds
|
|
5879
|
+
};
|
|
5880
|
+
if (status !== "stable") {
|
|
5881
|
+
pocket.readiness = "research-only";
|
|
5882
|
+
pocket.readinessReasons.push(`development stability: ${status}`);
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
const rank = { stable: 0, "insufficient-support": 1, unstable: 2 };
|
|
5886
|
+
result.positivePockets.sort(
|
|
5887
|
+
(a, b) => rank[a.stability.status] - rank[b.stability.status]
|
|
5888
|
+
);
|
|
5889
|
+
};
|
|
5890
|
+
}
|
|
5891
|
+
});
|
|
5892
|
+
|
|
5818
5893
|
// src/lib/aiPocketSearch/research.ts
|
|
5819
5894
|
var resolveScopeRows, runAiPocketSearchResearch;
|
|
5820
5895
|
var init_research = __esm({
|
|
@@ -5823,6 +5898,7 @@ var init_research = __esm({
|
|
|
5823
5898
|
init_aiTrainMetrics();
|
|
5824
5899
|
init_aiPocketSearch();
|
|
5825
5900
|
init_aiPocketSearchCli();
|
|
5901
|
+
init_stability();
|
|
5826
5902
|
resolveScopeRows = (rows, scope) => {
|
|
5827
5903
|
if (scope === "approved") {
|
|
5828
5904
|
return rows.filter((row) => row.aiApproved);
|
|
@@ -5917,7 +5993,7 @@ var init_research = __esm({
|
|
|
5917
5993
|
maxEventCountShare,
|
|
5918
5994
|
explicitMaxEventCountShare
|
|
5919
5995
|
});
|
|
5920
|
-
|
|
5996
|
+
const result = searchAiPockets(train, {
|
|
5921
5997
|
minSupport: cadenceProfile.minSupport,
|
|
5922
5998
|
minProfitFactor,
|
|
5923
5999
|
minTotalProfit,
|
|
@@ -5947,6 +6023,17 @@ var init_research = __esm({
|
|
|
5947
6023
|
progressInterval: 250,
|
|
5948
6024
|
onProgress: (progress) => onProgress?.({ label, progress })
|
|
5949
6025
|
});
|
|
6026
|
+
assessDevelopmentStability(result, {
|
|
6027
|
+
developmentRows: [...train, ...validation],
|
|
6028
|
+
baselineRows: [...trainBaseline, ...validationBaseline],
|
|
6029
|
+
// Every provider/scope uses the same outer development timestamps.
|
|
6030
|
+
timestamps: [
|
|
6031
|
+
...new Set(
|
|
6032
|
+
fullSplit.trainRows.concat(fullSplit.validationRows).map((row) => row.timestamp)
|
|
6033
|
+
)
|
|
6034
|
+
].sort((a, b) => a - b)
|
|
6035
|
+
});
|
|
6036
|
+
return result;
|
|
5950
6037
|
};
|
|
5951
6038
|
const search = runSearch({
|
|
5952
6039
|
label: "full",
|
|
@@ -5966,12 +6053,12 @@ var init_research = __esm({
|
|
|
5966
6053
|
const coverageSearches = [];
|
|
5967
6054
|
if (coverageMode === "auto") {
|
|
5968
6055
|
for (const family of ["cmc", "coinalyze"]) {
|
|
5969
|
-
const
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
6056
|
+
const covered = (row) => row.featureCoverage?.[family] === true;
|
|
6057
|
+
const cohortSplit = {
|
|
6058
|
+
trainRows: fullSplit.trainRows.filter(covered),
|
|
6059
|
+
validationRows: fullSplit.validationRows.filter(covered),
|
|
6060
|
+
testRows: fullSplit.testRows.filter(covered)
|
|
6061
|
+
};
|
|
5975
6062
|
const sealedCohortSplit = sealAiPocketTestPartition(
|
|
5976
6063
|
cohortSplit,
|
|
5977
6064
|
sealTest
|
|
@@ -6327,18 +6414,18 @@ var init_aiPocketSearch2 = __esm({
|
|
|
6327
6414
|
);
|
|
6328
6415
|
import_args3.default.option(
|
|
6329
6416
|
["V", "validationSplit"],
|
|
6330
|
-
"
|
|
6417
|
+
"Optional legacy tuning share; default 0, stability is checked inside development",
|
|
6331
6418
|
AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS.validationSplit
|
|
6332
6419
|
);
|
|
6333
6420
|
import_args3.default.option(
|
|
6334
6421
|
["T", "testSplit"],
|
|
6335
|
-
"Trailing timestamp-grouped
|
|
6422
|
+
"Trailing timestamp-grouped test share; default 0.4 (outer 60/40)",
|
|
6336
6423
|
AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS.testSplit
|
|
6337
6424
|
);
|
|
6338
6425
|
import_args3.default.option(
|
|
6339
6426
|
["w", "sealTest"],
|
|
6340
6427
|
"Reserve test bounds without exposing its rows or economics to discovery",
|
|
6341
|
-
|
|
6428
|
+
true
|
|
6342
6429
|
);
|
|
6343
6430
|
import_args3.default.option(
|
|
6344
6431
|
["N", "minValidationSupport"],
|
|
@@ -8823,6 +8910,17 @@ var init_jevResearch = __esm({
|
|
|
8823
8910
|
throw new Error(
|
|
8824
8911
|
"Export exceeds 20000 samples; select a bounded recording directory"
|
|
8825
8912
|
);
|
|
8913
|
+
await (0, import_jev2.evaluateJevInput)({
|
|
8914
|
+
input: record.input,
|
|
8915
|
+
config: {
|
|
8916
|
+
source: "recorded",
|
|
8917
|
+
mode: "observe",
|
|
8918
|
+
provider: record.provider,
|
|
8919
|
+
recordsDir: options.recordsDir
|
|
8920
|
+
},
|
|
8921
|
+
userName: options.userName,
|
|
8922
|
+
projectRoot: options.projectRoot
|
|
8923
|
+
});
|
|
8826
8924
|
const outcome = byRecord.get(record.id);
|
|
8827
8925
|
rows2.push({
|
|
8828
8926
|
schema: "tradejs-jev-study/v4",
|
|
@@ -8942,6 +9040,10 @@ var init_jevOptions = __esm({
|
|
|
8942
9040
|
import_jev5 = require("@tradejs/node/jev");
|
|
8943
9041
|
resolveBacktestAiExportEnabled = (flags35) => Boolean(flags35.ai);
|
|
8944
9042
|
resolveBacktestJev = async (flags35, userName3, projectRoot18) => {
|
|
9043
|
+
if (flags35.jevQuestionSet != null)
|
|
9044
|
+
throw new Error(
|
|
9045
|
+
"--jevQuestionSet was removed; --jev always uses the current nine-question set"
|
|
9046
|
+
);
|
|
8945
9047
|
if (!flags35.jev) {
|
|
8946
9048
|
if (flags35.jevRecorded || flags35.jevModelFile)
|
|
8947
9049
|
throw new Error("Jev options require --jev");
|
|
@@ -2158,6 +2158,22 @@ var buildAiPocketMarkdownReport = ({
|
|
|
2158
2158
|
"",
|
|
2159
2159
|
markdownTable(pocketTableHeaders, pocketRows(pocketSearch.positivePockets)),
|
|
2160
2160
|
"",
|
|
2161
|
+
"## Development Stability (inside discovery, not independent validation)",
|
|
2162
|
+
"",
|
|
2163
|
+
markdownTable(
|
|
2164
|
+
["Condition", "Status", "Block", "Events", "PnL", "PF"],
|
|
2165
|
+
pocketSearch.positivePockets.flatMap(
|
|
2166
|
+
(pocket) => (pocket.stability?.folds ?? []).map((fold, index) => [
|
|
2167
|
+
pocket.condition,
|
|
2168
|
+
pocket.stability?.status,
|
|
2169
|
+
index + 1,
|
|
2170
|
+
fold.summary.events,
|
|
2171
|
+
formatMdNumber(fold.summary.totalProfit),
|
|
2172
|
+
formatMdNumber(fold.summary.profitFactor)
|
|
2173
|
+
])
|
|
2174
|
+
)
|
|
2175
|
+
),
|
|
2176
|
+
"",
|
|
2161
2177
|
"## Top Loss Pockets",
|
|
2162
2178
|
"",
|
|
2163
2179
|
markdownTable(pocketTableHeaders, pocketRows(pocketSearch.negativePockets)),
|
|
@@ -66,7 +66,7 @@ var resolveAiPocketCadenceProfile = ({
|
|
|
66
66
|
validationRows = [],
|
|
67
67
|
testRows = [],
|
|
68
68
|
mode = "auto",
|
|
69
|
-
validationSplit = 0
|
|
69
|
+
validationSplit = 0,
|
|
70
70
|
minSupport,
|
|
71
71
|
minEvents,
|
|
72
72
|
minValidationSupport,
|
|
@@ -116,8 +116,8 @@ var AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS = {
|
|
|
116
116
|
minProfitFactor: "1.2",
|
|
117
117
|
minWinRate: "0",
|
|
118
118
|
minTotalProfit: "0",
|
|
119
|
-
validationSplit: "0
|
|
120
|
-
testSplit: "0"
|
|
119
|
+
validationSplit: "0",
|
|
120
|
+
testSplit: "0.4"
|
|
121
121
|
};
|
|
122
122
|
var splitAiPocketResearchRowsByTimestamp = (rows, validationSplit, testSplit = 0) => {
|
|
123
123
|
const timestamps = [
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/lib/jevMicroReferences.ts
|
|
21
|
+
var jevMicroReferences_exports = {};
|
|
22
|
+
__export(jevMicroReferences_exports, {
|
|
23
|
+
microReferenceFeatures: () => microReferenceFeatures
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(jevMicroReferences_exports);
|
|
26
|
+
var microReferenceFeatures = (input) => {
|
|
27
|
+
const number = (...keys) => {
|
|
28
|
+
for (const key of keys) {
|
|
29
|
+
const value = input.features[key];
|
|
30
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
const bands = (value, cuts) => {
|
|
35
|
+
if (value == null || value < 0) return null;
|
|
36
|
+
const level = cuts.filter((cut) => value >= cut).length;
|
|
37
|
+
return level / 4;
|
|
38
|
+
};
|
|
39
|
+
const ratio = number("setup.retracementRatio");
|
|
40
|
+
const contraction = number(
|
|
41
|
+
"setup.consolidationToImpulseVolumeRatio",
|
|
42
|
+
"setup.flagToPoleVolumeRatio"
|
|
43
|
+
);
|
|
44
|
+
const extension = number("setup.breakoutDistanceAtr");
|
|
45
|
+
const efficiency = number(
|
|
46
|
+
"setup.impulseEfficiencyRatio",
|
|
47
|
+
"setup.poleEfficiencyRatio"
|
|
48
|
+
);
|
|
49
|
+
const body = number("entry.directionalBodyAtr");
|
|
50
|
+
const close = number("entry.directionalCloseLocation");
|
|
51
|
+
const upper = number("geometry.upperR2"), lower = number("geometry.lowerR2"), divergence = number("geometry.slopeDivergenceRatio");
|
|
52
|
+
const fit = upper != null && lower != null ? Math.min(upper, lower) : null;
|
|
53
|
+
const bias = (key) => {
|
|
54
|
+
const value = input.features[key];
|
|
55
|
+
if (!["bull", "bear"].includes(String(value))) return 0;
|
|
56
|
+
return value === (input.direction === "LONG" ? "bull" : "bear") ? 1 : -1;
|
|
57
|
+
};
|
|
58
|
+
const trend = bias("market.trendBias"), swing = bias("market.swingBias");
|
|
59
|
+
return {
|
|
60
|
+
directionalAgreement: input.features["market.trendBias"] == null && input.features["market.swingBias"] == null ? null : (trend + swing + 2) / 4,
|
|
61
|
+
correctionContainment: ratio == null || ratio < 0 ? null : (ratio <= 0.2 ? 4 : ratio <= 0.4 ? 3 : ratio <= 0.65 ? 2 : ratio <= 1 ? 1 : 0) / 4,
|
|
62
|
+
volumeExpansion: bands(number("market.volumeRel20"), [0.5, 0.8, 1.2, 2]),
|
|
63
|
+
volumeContraction: contraction == null || contraction < 0 ? null : (contraction <= 0.4 ? 4 : contraction <= 0.7 ? 3 : contraction <= 0.9 ? 2 : contraction <= 1.2 ? 1 : 0) / 4,
|
|
64
|
+
entryProximity: extension == null || extension < 0 ? null : (extension <= 0.2 ? 4 : extension <= 0.5 ? 3 : extension <= 1 ? 2 : extension <= 2 ? 1 : 0) / 4,
|
|
65
|
+
entryConviction: body == null || close == null || close < 0 || close > 1 ? null : (body < 0 && close < 0.25 ? 0 : body < 0 || close < 0.5 ? 1 : body < 0.2 ? 2 : body >= 0.5 && close >= 0.75 ? 4 : 3) / 4,
|
|
66
|
+
impulsePersistence: efficiency == null || efficiency > 1 ? null : bands(efficiency, [0.2, 0.4, 0.6, 0.8]),
|
|
67
|
+
boundaryRegularity: fit == null || divergence == null || fit < 0 || fit > 1 || divergence < 0 ? null : (fit < 0.5 || divergence > 0.5 ? 0 : fit < 0.7 || divergence > 0.3 ? 1 : fit < 0.85 || divergence > 0.15 ? 2 : fit >= 0.95 && divergence <= 0.05 ? 4 : 3) / 4
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
71
|
+
0 && (module.exports = {
|
|
72
|
+
microReferenceFeatures
|
|
73
|
+
});
|
package/dist/lib/jevResearch.js
CHANGED
|
@@ -170,6 +170,17 @@ var runJevResearch = async (options) => {
|
|
|
170
170
|
throw new Error(
|
|
171
171
|
"Export exceeds 20000 samples; select a bounded recording directory"
|
|
172
172
|
);
|
|
173
|
+
await (0, import_jev2.evaluateJevInput)({
|
|
174
|
+
input: record.input,
|
|
175
|
+
config: {
|
|
176
|
+
source: "recorded",
|
|
177
|
+
mode: "observe",
|
|
178
|
+
provider: record.provider,
|
|
179
|
+
recordsDir: options.recordsDir
|
|
180
|
+
},
|
|
181
|
+
userName: options.userName,
|
|
182
|
+
projectRoot: options.projectRoot
|
|
183
|
+
});
|
|
173
184
|
const outcome = byRecord.get(record.id);
|
|
174
185
|
rows2.push({
|
|
175
186
|
schema: "tradejs-jev-study/v4",
|
|
@@ -74,7 +74,7 @@ var resolveAiPocketCadenceProfile = ({
|
|
|
74
74
|
validationRows = [],
|
|
75
75
|
testRows = [],
|
|
76
76
|
mode = "auto",
|
|
77
|
-
validationSplit = 0
|
|
77
|
+
validationSplit = 0,
|
|
78
78
|
minSupport,
|
|
79
79
|
minEvents,
|
|
80
80
|
minValidationSupport,
|
|
@@ -124,8 +124,8 @@ var AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS = {
|
|
|
124
124
|
minProfitFactor: "1.2",
|
|
125
125
|
minWinRate: "0",
|
|
126
126
|
minTotalProfit: "0",
|
|
127
|
-
validationSplit: "0
|
|
128
|
-
testSplit: "0"
|
|
127
|
+
validationSplit: "0",
|
|
128
|
+
testSplit: "0.4"
|
|
129
129
|
};
|
|
130
130
|
var splitAiPocketResearchRowsByTimestamp = (rows, validationSplit, testSplit = 0) => {
|
|
131
131
|
const timestamps = [
|
|
@@ -166,11 +166,6 @@ var splitAiPocketResearchRowsByTimestamp = (rows, validationSplit, testSplit = 0
|
|
|
166
166
|
testRows: rows.filter((row) => isIn(testTimestamps, row.timestamp))
|
|
167
167
|
};
|
|
168
168
|
};
|
|
169
|
-
var splitAiPocketCoverageRowsByTimestamp = (rows, family, validationSplit, testSplit = 0) => splitAiPocketResearchRowsByTimestamp(
|
|
170
|
-
rows.filter((row) => row.featureCoverage?.[family] === true),
|
|
171
|
-
validationSplit,
|
|
172
|
-
testSplit
|
|
173
|
-
);
|
|
174
169
|
var sealAiPocketTestPartition = (split, sealed) => {
|
|
175
170
|
const finiteTimestamps = split.testRows.map((row) => row.timestamp).filter((timestamp) => Number.isFinite(timestamp));
|
|
176
171
|
return {
|
|
@@ -436,17 +431,17 @@ var resolveAiPocketSearchCommandOptions = ({
|
|
|
436
431
|
longName: "validationSplit",
|
|
437
432
|
shortName: "V"
|
|
438
433
|
}) ?? flags2.validationSplit,
|
|
439
|
-
0
|
|
440
|
-
) : 0
|
|
434
|
+
0
|
|
435
|
+
) : 0;
|
|
441
436
|
const testSplit = hasCliOption2(argv, "testSplit", "T") ? normalizeRatio(
|
|
442
437
|
readAiPocketSearchCliOption({
|
|
443
438
|
argv,
|
|
444
439
|
longName: "testSplit",
|
|
445
440
|
shortName: "T"
|
|
446
441
|
}) ?? flags2.testSplit,
|
|
447
|
-
0
|
|
448
|
-
) : 0;
|
|
449
|
-
const sealTest = Boolean(flags2.sealTest);
|
|
442
|
+
0.4
|
|
443
|
+
) : 0.4;
|
|
444
|
+
const sealTest = flags2.sealTest == null ? true : Boolean(flags2.sealTest);
|
|
450
445
|
if (sealTest && testSplit <= 0) {
|
|
451
446
|
throw new Error("--sealTest requires a positive --testSplit");
|
|
452
447
|
}
|
|
@@ -2649,6 +2644,22 @@ var buildAiPocketMarkdownReport = ({
|
|
|
2649
2644
|
"",
|
|
2650
2645
|
markdownTable(pocketTableHeaders, pocketRows(pocketSearch.positivePockets)),
|
|
2651
2646
|
"",
|
|
2647
|
+
"## Development Stability (inside discovery, not independent validation)",
|
|
2648
|
+
"",
|
|
2649
|
+
markdownTable(
|
|
2650
|
+
["Condition", "Status", "Block", "Events", "PnL", "PF"],
|
|
2651
|
+
pocketSearch.positivePockets.flatMap(
|
|
2652
|
+
(pocket) => (pocket.stability?.folds ?? []).map((fold, index) => [
|
|
2653
|
+
pocket.condition,
|
|
2654
|
+
pocket.stability?.status,
|
|
2655
|
+
index + 1,
|
|
2656
|
+
fold.summary.events,
|
|
2657
|
+
formatMdNumber(fold.summary.totalProfit),
|
|
2658
|
+
formatMdNumber(fold.summary.profitFactor)
|
|
2659
|
+
])
|
|
2660
|
+
)
|
|
2661
|
+
),
|
|
2662
|
+
"",
|
|
2652
2663
|
"## Top Loss Pockets",
|
|
2653
2664
|
"",
|
|
2654
2665
|
markdownTable(pocketTableHeaders, pocketRows(pocketSearch.negativePockets)),
|
|
@@ -4099,6 +4110,62 @@ var summarizeAiTrainEvaluationsByQualityThreshold = (evaluations, thresholds = [
|
|
|
4099
4110
|
)
|
|
4100
4111
|
}));
|
|
4101
4112
|
|
|
4113
|
+
// src/lib/aiPocketSearch/stability.ts
|
|
4114
|
+
var assessDevelopmentStability = (result, options) => {
|
|
4115
|
+
const minEvents = options.minEventsPerFold ?? 5;
|
|
4116
|
+
const timestamps = [...new Set(options.timestamps)].sort((a, b) => a - b);
|
|
4117
|
+
const boundaries = [0, 1, 2, 3].map(
|
|
4118
|
+
(index) => index === 3 ? timestamps.at(-1) == null ? Number.POSITIVE_INFINITY : timestamps.at(-1) + 1 : timestamps[Math.floor(timestamps.length * index / 3)] ?? Number.POSITIVE_INFINITY
|
|
4119
|
+
);
|
|
4120
|
+
const blocks = [0, 1, 2].map((index) => ({
|
|
4121
|
+
start: boundaries[index],
|
|
4122
|
+
end: boundaries[index + 1],
|
|
4123
|
+
rows: options.developmentRows.filter(
|
|
4124
|
+
(row) => row.timestamp != null && row.timestamp >= boundaries[index] && row.timestamp < boundaries[index + 1]
|
|
4125
|
+
),
|
|
4126
|
+
baseline: options.baselineRows.filter(
|
|
4127
|
+
(row) => row.timestamp != null && row.timestamp >= boundaries[index] && row.timestamp < boundaries[index + 1]
|
|
4128
|
+
)
|
|
4129
|
+
}));
|
|
4130
|
+
for (const pocket of result.positivePockets) {
|
|
4131
|
+
const folds = blocks.map((block) => {
|
|
4132
|
+
const mask = buildPredicateListMask(block.rows, pocket.predicates).mask;
|
|
4133
|
+
const selected = block.rows.filter((_, index) => mask[index] === 1);
|
|
4134
|
+
const summary = summarizeAiPocketRows(selected);
|
|
4135
|
+
const baseline = summarizeAiPocketRows(block.baseline);
|
|
4136
|
+
const objectiveSummary = result.objective === "add-to-gate" ? summarizeAiPocketRows([
|
|
4137
|
+
.../* @__PURE__ */ new Set([...block.baseline, ...selected])
|
|
4138
|
+
]) : summary;
|
|
4139
|
+
return {
|
|
4140
|
+
startTimestamp: Number.isFinite(block.start) ? block.start : null,
|
|
4141
|
+
endTimestamp: Number.isFinite(block.end) ? block.end : null,
|
|
4142
|
+
summary,
|
|
4143
|
+
objectiveSummary,
|
|
4144
|
+
baseline
|
|
4145
|
+
};
|
|
4146
|
+
});
|
|
4147
|
+
const supported = folds.filter((fold) => fold.summary.events >= minEvents);
|
|
4148
|
+
const unstable = supported.some(
|
|
4149
|
+
(fold) => fold.objectiveSummary.totalProfit < (result.objective === "add-to-gate" ? fold.baseline.totalProfit : 0) || (fold.objectiveSummary.profitFactor ?? (fold.objectiveSummary.grossLoss === 0 ? Infinity : 0)) < 1
|
|
4150
|
+
);
|
|
4151
|
+
const status = unstable ? "unstable" : supported.length < 3 ? "insufficient-support" : "stable";
|
|
4152
|
+
pocket.stability = {
|
|
4153
|
+
status,
|
|
4154
|
+
role: "development-only diagnostic",
|
|
4155
|
+
minEventsPerFold: minEvents,
|
|
4156
|
+
folds
|
|
4157
|
+
};
|
|
4158
|
+
if (status !== "stable") {
|
|
4159
|
+
pocket.readiness = "research-only";
|
|
4160
|
+
pocket.readinessReasons.push(`development stability: ${status}`);
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
const rank = { stable: 0, "insufficient-support": 1, unstable: 2 };
|
|
4164
|
+
result.positivePockets.sort(
|
|
4165
|
+
(a, b) => rank[a.stability.status] - rank[b.stability.status]
|
|
4166
|
+
);
|
|
4167
|
+
};
|
|
4168
|
+
|
|
4102
4169
|
// src/lib/aiPocketSearch/research.ts
|
|
4103
4170
|
var resolveScopeRows = (rows, scope) => {
|
|
4104
4171
|
if (scope === "approved") {
|
|
@@ -4194,7 +4261,7 @@ var runAiPocketSearchResearch = ({
|
|
|
4194
4261
|
maxEventCountShare,
|
|
4195
4262
|
explicitMaxEventCountShare
|
|
4196
4263
|
});
|
|
4197
|
-
|
|
4264
|
+
const result = searchAiPockets(train, {
|
|
4198
4265
|
minSupport: cadenceProfile.minSupport,
|
|
4199
4266
|
minProfitFactor,
|
|
4200
4267
|
minTotalProfit,
|
|
@@ -4224,6 +4291,17 @@ var runAiPocketSearchResearch = ({
|
|
|
4224
4291
|
progressInterval: 250,
|
|
4225
4292
|
onProgress: (progress) => onProgress?.({ label, progress })
|
|
4226
4293
|
});
|
|
4294
|
+
assessDevelopmentStability(result, {
|
|
4295
|
+
developmentRows: [...train, ...validation],
|
|
4296
|
+
baselineRows: [...trainBaseline, ...validationBaseline],
|
|
4297
|
+
// Every provider/scope uses the same outer development timestamps.
|
|
4298
|
+
timestamps: [
|
|
4299
|
+
...new Set(
|
|
4300
|
+
fullSplit.trainRows.concat(fullSplit.validationRows).map((row) => row.timestamp)
|
|
4301
|
+
)
|
|
4302
|
+
].sort((a, b) => a - b)
|
|
4303
|
+
});
|
|
4304
|
+
return result;
|
|
4227
4305
|
};
|
|
4228
4306
|
const search = runSearch({
|
|
4229
4307
|
label: "full",
|
|
@@ -4243,12 +4321,12 @@ var runAiPocketSearchResearch = ({
|
|
|
4243
4321
|
const coverageSearches = [];
|
|
4244
4322
|
if (coverageMode === "auto") {
|
|
4245
4323
|
for (const family of ["cmc", "coinalyze"]) {
|
|
4246
|
-
const
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4324
|
+
const covered = (row) => row.featureCoverage?.[family] === true;
|
|
4325
|
+
const cohortSplit = {
|
|
4326
|
+
trainRows: fullSplit.trainRows.filter(covered),
|
|
4327
|
+
validationRows: fullSplit.validationRows.filter(covered),
|
|
4328
|
+
testRows: fullSplit.testRows.filter(covered)
|
|
4329
|
+
};
|
|
4252
4330
|
const sealedCohortSplit = sealAiPocketTestPartition(
|
|
4253
4331
|
cohortSplit,
|
|
4254
4332
|
sealTest
|
|
@@ -4581,18 +4659,18 @@ import_args.default.option(
|
|
|
4581
4659
|
);
|
|
4582
4660
|
import_args.default.option(
|
|
4583
4661
|
["V", "validationSplit"],
|
|
4584
|
-
"
|
|
4662
|
+
"Optional legacy tuning share; default 0, stability is checked inside development",
|
|
4585
4663
|
AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS.validationSplit
|
|
4586
4664
|
);
|
|
4587
4665
|
import_args.default.option(
|
|
4588
4666
|
["T", "testSplit"],
|
|
4589
|
-
"Trailing timestamp-grouped
|
|
4667
|
+
"Trailing timestamp-grouped test share; default 0.4 (outer 60/40)",
|
|
4590
4668
|
AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS.testSplit
|
|
4591
4669
|
);
|
|
4592
4670
|
import_args.default.option(
|
|
4593
4671
|
["w", "sealTest"],
|
|
4594
4672
|
"Reserve test bounds without exposing its rows or economics to discovery",
|
|
4595
|
-
|
|
4673
|
+
true
|
|
4596
4674
|
);
|
|
4597
4675
|
import_args.default.option(
|
|
4598
4676
|
["N", "minValidationSupport"],
|
package/dist/scripts/backtest.js
CHANGED
|
@@ -74,6 +74,10 @@ var import_userSettings = require("@tradejs/infra/userSettings");
|
|
|
74
74
|
var import_jev3 = require("@tradejs/node/jev");
|
|
75
75
|
var resolveBacktestAiExportEnabled = (flags2) => Boolean(flags2.ai);
|
|
76
76
|
var resolveBacktestJev = async (flags2, userName2, projectRoot2) => {
|
|
77
|
+
if (flags2.jevQuestionSet != null)
|
|
78
|
+
throw new Error(
|
|
79
|
+
"--jevQuestionSet was removed; --jev always uses the current nine-question set"
|
|
80
|
+
);
|
|
77
81
|
if (!flags2.jev) {
|
|
78
82
|
if (flags2.jevRecorded || flags2.jevModelFile)
|
|
79
83
|
throw new Error("Jev options require --jev");
|
package/dist/scripts/jev.js
CHANGED
|
@@ -172,6 +172,17 @@ var runJevResearch = async (options) => {
|
|
|
172
172
|
throw new Error(
|
|
173
173
|
"Export exceeds 20000 samples; select a bounded recording directory"
|
|
174
174
|
);
|
|
175
|
+
await (0, import_jev2.evaluateJevInput)({
|
|
176
|
+
input: record.input,
|
|
177
|
+
config: {
|
|
178
|
+
source: "recorded",
|
|
179
|
+
mode: "observe",
|
|
180
|
+
provider: record.provider,
|
|
181
|
+
recordsDir: options.recordsDir
|
|
182
|
+
},
|
|
183
|
+
userName: options.userName,
|
|
184
|
+
projectRoot: options.projectRoot
|
|
185
|
+
});
|
|
175
186
|
const outcome = byRecord.get(record.id);
|
|
176
187
|
rows2.push({
|
|
177
188
|
schema: "tradejs-jev-study/v4",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradejs/cli",
|
|
3
|
-
"version": "3.1.29-beta.
|
|
3
|
+
"version": "3.1.29-beta.262",
|
|
4
4
|
"description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tradejs",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"coinbase",
|
|
17
17
|
"ai",
|
|
18
18
|
"claude",
|
|
19
|
-
"codex"
|
|
19
|
+
"codex",
|
|
20
|
+
"jev"
|
|
20
21
|
],
|
|
21
22
|
"homepage": "https://tradejs.dev",
|
|
22
23
|
"repository": {
|
|
@@ -36,13 +37,13 @@
|
|
|
36
37
|
"tradejs": "dist/cli.js"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"@tradejs/connectors": "^3.1.29-beta.
|
|
40
|
-
"@tradejs/core": "^3.1.29-beta.
|
|
41
|
-
"@tradejs/indicators": "^3.1.29-beta.
|
|
42
|
-
"@tradejs/infra": "^3.1.29-beta.
|
|
43
|
-
"@tradejs/node": "^3.1.29-beta.
|
|
40
|
+
"@tradejs/connectors": "^3.1.29-beta.262",
|
|
41
|
+
"@tradejs/core": "^3.1.29-beta.262",
|
|
42
|
+
"@tradejs/indicators": "^3.1.29-beta.262",
|
|
43
|
+
"@tradejs/infra": "^3.1.29-beta.262",
|
|
44
|
+
"@tradejs/node": "^3.1.29-beta.262",
|
|
44
45
|
"@tradejs/strategy-kit": "3.0.6",
|
|
45
|
-
"@tradejs/types": "^3.1.29-beta.
|
|
46
|
+
"@tradejs/types": "^3.1.29-beta.262",
|
|
46
47
|
"args": "^5.0.3",
|
|
47
48
|
"bcryptjs": "^2.4.3",
|
|
48
49
|
"chalk": "4.1.2",
|