tradeblocks-mcp 3.0.2 → 3.0.3
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 +82 -68
- package/dist/{chunk-LDKTV7GW.js → chunk-27S67XW3.js} +24 -52
- package/dist/chunk-27S67XW3.js.map +1 -0
- package/dist/{chunk-FGZH632F.js → chunk-2E63THNI.js} +13 -11
- package/dist/chunk-2E63THNI.js.map +1 -0
- package/dist/{chunk-BKQ4PM4Y.js → chunk-6S37CXUA.js} +11 -13
- package/dist/chunk-6S37CXUA.js.map +1 -0
- package/dist/{chunk-PRAYH3RT.js → chunk-AP7IHUUR.js} +29 -12
- package/dist/chunk-AP7IHUUR.js.map +1 -0
- package/dist/{chunk-QTTR7AAW.js → chunk-C6LL746C.js} +67 -84
- package/dist/{chunk-QTTR7AAW.js.map → chunk-C6LL746C.js.map} +1 -1
- package/dist/{chunk-W2PP3LEH.js → chunk-SEUZYQGQ.js} +17 -8
- package/dist/chunk-SEUZYQGQ.js.map +1 -0
- package/dist/{chunk-4BLCXNQ6.js → chunk-UBUC5A66.js} +105 -114
- package/dist/chunk-UBUC5A66.js.map +1 -0
- package/dist/{chunk-XXYOUIZY.js → chunk-VDU25Z6X.js} +63 -80
- package/dist/chunk-VDU25Z6X.js.map +1 -0
- package/dist/daily-log-processor-BY3ISY6K.js +9 -0
- package/dist/{daily-logs-store-D3QC2XPV.js → daily-logs-store-3C3OTGPK.js} +2 -2
- package/dist/iv-solver-worker.js +9 -1
- package/dist/market-provider-H3ARIVZ4.js +16 -0
- package/dist/{proto-3HZTCWK4.js → proto-V23GIZG2.js} +2 -2
- package/dist/{sync-PO4IPCYV.js → sync-EVLKB3ZJ.js} +7 -7
- package/dist/test-exports.js +358 -254
- package/dist/test-exports.js.map +1 -1
- package/dist/trade-processor-EYA3I3XB.js +9 -0
- package/dist/{trades-store-LOL3FQSF.js → trades-store-R3NL25T2.js} +2 -2
- package/manifest.json +3 -11
- package/package.json +1 -1
- package/server/{chunk-NZO6PT64.js → chunk-4P7D7YZP.js} +102 -113
- package/server/chunk-4P7D7YZP.js.map +1 -0
- package/server/{chunk-72GKJE2U.js → chunk-BOPHW5M6.js} +521 -235
- package/server/chunk-BOPHW5M6.js.map +1 -0
- package/server/{chunk-WA5AAPCH.js → chunk-GH2552SE.js} +17 -8
- package/server/chunk-GH2552SE.js.map +1 -0
- package/server/{chunk-5EBXHT6C.js → chunk-OBYKFW2B.js} +11 -13
- package/server/chunk-OBYKFW2B.js.map +1 -0
- package/server/{chunk-FBNDMCT5.js → chunk-T66KH2XH.js} +13 -11
- package/server/chunk-T66KH2XH.js.map +1 -0
- package/server/{chunk-NRFXAJF7.js → chunk-W5E7FHC4.js} +24 -52
- package/server/chunk-W5E7FHC4.js.map +1 -0
- package/server/{chunk-CSDVJPBB.js → chunk-YUCOAJ4Z.js} +29 -12
- package/server/chunk-YUCOAJ4Z.js.map +1 -0
- package/server/{chunk-PNKG7RY7.js → chunk-ZBJCF4ZG.js} +63 -80
- package/server/chunk-ZBJCF4ZG.js.map +1 -0
- package/server/{config-6IZXEFEX.js → config-DK7KOMNL.js} +1 -1
- package/server/config-DK7KOMNL.js.map +1 -0
- package/server/daily-log-processor-ENEUT22A.js +10 -0
- package/server/{daily-logs-store-NU7M4L5C.js → daily-logs-store-YFNXTB7J.js} +2 -2
- package/server/http-server.js +2 -5
- package/server/index.js +1771 -1538
- package/server/index.js.map +1 -1
- package/server/iv-solver-worker.js +9 -1
- package/server/market-provider-B437HKLW.js +17 -0
- package/server/{proto-DWRZJO4E.js → proto-YZ4TLX2N.js} +2 -2
- package/server/{sync-QFI5L7S7.js → sync-V25UQJA3.js} +7 -7
- package/server/trade-processor-L3PIQ5TG.js +10 -0
- package/server/{trades-store-VY26MWER.js → trades-store-RTTEYMXH.js} +2 -2
- package/src/auth/clients-store.ts +4 -4
- package/src/auth/code-store.ts +1 -1
- package/src/auth/config.ts +11 -12
- package/src/auth/login-page.ts +8 -10
- package/src/auth/provider.ts +35 -30
- package/src/auth/token.ts +17 -15
- package/src/db/backtest-schemas.ts +12 -4
- package/src/db/connection.ts +107 -40
- package/src/db/index.ts +37 -5
- package/src/db/json-adapters.ts +22 -31
- package/src/db/json-migration.ts +11 -15
- package/src/db/json-store.ts +5 -3
- package/src/db/market-datasets.ts +48 -15
- package/src/db/market-schemas.ts +11 -5
- package/src/db/market-views.ts +119 -32
- package/src/db/parquet-writer.ts +7 -7
- package/src/db/profile-schemas.ts +39 -22
- package/src/db/schemas.ts +2 -2
- package/src/http-server.ts +7 -12
- package/src/index.ts +8 -9
- package/src/market/ingestor/index.ts +1 -4
- package/src/market/ingestor/market-ingestor.ts +140 -67
- package/src/market/ingestor/types.ts +1 -3
- package/src/market/stores/chain-sql.ts +2 -8
- package/src/market/stores/chain-store.ts +1 -4
- package/src/market/stores/duckdb-chain-store.ts +3 -14
- package/src/market/stores/duckdb-enriched-store.ts +3 -13
- package/src/market/stores/duckdb-quote-store.ts +10 -27
- package/src/market/stores/duckdb-spot-store.ts +4 -20
- package/src/market/stores/enriched-sql.ts +3 -9
- package/src/market/stores/enriched-store.ts +2 -2
- package/src/market/stores/parquet-chain-store.ts +5 -22
- package/src/market/stores/parquet-enriched-store.ts +4 -16
- package/src/market/stores/parquet-oi-daily-store.ts +3 -14
- package/src/market/stores/parquet-quote-store.ts +9 -29
- package/src/market/stores/parquet-spot-store.ts +14 -33
- package/src/market/stores/quote-store.ts +2 -10
- package/src/market/stores/spot-sql.ts +3 -15
- package/src/market/stores/spot-store.ts +1 -1
- package/src/market/tickers/defaults.json +6 -6
- package/src/market/tickers/loader.ts +2 -7
- package/src/market/tickers/registry.ts +5 -15
- package/src/market/tickers/schemas.ts +2 -6
- package/src/models/strategy-profile.ts +39 -39
- package/src/sync/block-sync.ts +44 -65
- package/src/sync/index.ts +1 -4
- package/src/sync/metadata.ts +14 -20
- package/src/test-exports.ts +127 -123
- package/src/tools/analysis.ts +180 -159
- package/src/tools/batch-exit-analysis.ts +100 -59
- package/src/tools/blocks/analysis.ts +116 -101
- package/src/tools/blocks/comparison.ts +281 -364
- package/src/tools/blocks/core.ts +208 -253
- package/src/tools/blocks/health.ts +571 -563
- package/src/tools/blocks/similarity.ts +316 -283
- package/src/tools/edge-decay.ts +124 -197
- package/src/tools/exit-analysis.ts +130 -77
- package/src/tools/greeks-attribution.ts +84 -35
- package/src/tools/guides.ts +4 -6
- package/src/tools/imports.ts +9 -12
- package/src/tools/market-data.ts +1067 -757
- package/src/tools/market-enrichment.ts +3 -3
- package/src/tools/market-fetch.ts +148 -67
- package/src/tools/market-imports.ts +12 -12
- package/src/tools/middleware/sync-middleware.ts +5 -6
- package/src/tools/performance.ts +185 -302
- package/src/tools/profile-analysis.ts +52 -66
- package/src/tools/profiles.ts +106 -69
- package/src/tools/regime-advisor.ts +20 -45
- package/src/tools/replay.ts +81 -77
- package/src/tools/reports/discrepancies.ts +298 -328
- package/src/tools/reports/fields.ts +7 -25
- package/src/tools/reports/helpers.ts +18 -49
- package/src/tools/reports/predictive.ts +27 -70
- package/src/tools/reports/slippage-trends.ts +315 -345
- package/src/tools/reports/slippage.ts +1 -4
- package/src/tools/reports/strategy-matches.ts +399 -441
- package/src/tools/schema.ts +43 -40
- package/src/tools/shared/filters.ts +3 -9
- package/src/tools/snapshot.ts +9 -30
- package/src/tools/sql.ts +15 -14
- package/src/tools/tickers.ts +1 -4
- package/src/utils/batch-exit-analysis.ts +31 -42
- package/src/utils/black-scholes.ts +39 -29
- package/src/utils/block-loader.ts +68 -82
- package/src/utils/calibration-probe.ts +1 -2
- package/src/utils/chain-loader.ts +3 -3
- package/src/utils/csv-discovery.ts +16 -22
- package/src/utils/data-quality.ts +24 -36
- package/src/utils/exit-triggers.ts +91 -96
- package/src/utils/field-timing.ts +94 -79
- package/src/utils/filter-predicates.ts +13 -9
- package/src/utils/flatfile-importer.ts +94 -64
- package/src/utils/greeks-decomposition.ts +152 -100
- package/src/utils/iv-solver-pool.ts +55 -25
- package/src/utils/iv-solver-worker.ts +5 -5
- package/src/utils/market-enricher.ts +528 -497
- package/src/utils/market-importer.ts +31 -12
- package/src/utils/market-provider.ts +21 -23
- package/src/utils/massive-tier.ts +5 -7
- package/src/utils/migrate-option-data-helpers.ts +2 -8
- package/src/utils/option-quote-greeks.ts +25 -31
- package/src/utils/option-time.ts +4 -8
- package/src/utils/output-formatter.ts +1 -4
- package/src/utils/provider-capabilities.ts +1 -4
- package/src/utils/providers/massive.ts +59 -93
- package/src/utils/providers/thetadata/backfill.ts +14 -23
- package/src/utils/providers/thetadata/client.ts +12 -8
- package/src/utils/providers/thetadata/decode.ts +2 -20
- package/src/utils/providers/thetadata/endpoints.ts +32 -40
- package/src/utils/providers/thetadata/join.ts +11 -10
- package/src/utils/providers/thetadata/proto.ts +12 -10
- package/src/utils/providers/thetadata/quote-mid-greeks.ts +5 -5
- package/src/utils/providers/thetadata.ts +11 -10
- package/src/utils/quote-enricher.ts +4 -4
- package/src/utils/quote-parquet-projection.ts +3 -11
- package/src/utils/sample-date-selector.ts +3 -5
- package/src/utils/schema-metadata.ts +102 -70
- package/src/utils/ticker.ts +5 -9
- package/src/utils/trade-replay.ts +77 -68
- package/dist/chunk-4BLCXNQ6.js.map +0 -1
- package/dist/chunk-BKQ4PM4Y.js.map +0 -1
- package/dist/chunk-FGZH632F.js.map +0 -1
- package/dist/chunk-LDKTV7GW.js.map +0 -1
- package/dist/chunk-PRAYH3RT.js.map +0 -1
- package/dist/chunk-W2PP3LEH.js.map +0 -1
- package/dist/chunk-XXYOUIZY.js.map +0 -1
- package/dist/daily-log-processor-6MWJ23JK.js +0 -9
- package/dist/market-provider-VDRJUEF2.js +0 -16
- package/dist/trade-processor-NHU4VWRX.js +0 -9
- package/server/chunk-5EBXHT6C.js.map +0 -1
- package/server/chunk-72GKJE2U.js.map +0 -1
- package/server/chunk-CSDVJPBB.js.map +0 -1
- package/server/chunk-FBNDMCT5.js.map +0 -1
- package/server/chunk-NRFXAJF7.js.map +0 -1
- package/server/chunk-NZO6PT64.js.map +0 -1
- package/server/chunk-PNKG7RY7.js.map +0 -1
- package/server/chunk-WA5AAPCH.js.map +0 -1
- package/server/config-6IZXEFEX.js.map +0 -1
- package/server/daily-log-processor-Y3PVSVBM.js +0 -10
- package/server/market-provider-VOYYVYWT.js +0 -17
- package/server/trade-processor-JWVS37KM.js +0 -10
- /package/dist/{daily-log-processor-6MWJ23JK.js.map → daily-log-processor-BY3ISY6K.js.map} +0 -0
- /package/dist/{daily-logs-store-D3QC2XPV.js.map → daily-logs-store-3C3OTGPK.js.map} +0 -0
- /package/dist/{market-provider-VDRJUEF2.js.map → market-provider-H3ARIVZ4.js.map} +0 -0
- /package/dist/{proto-3HZTCWK4.js.map → proto-V23GIZG2.js.map} +0 -0
- /package/dist/{sync-PO4IPCYV.js.map → sync-EVLKB3ZJ.js.map} +0 -0
- /package/dist/{trade-processor-NHU4VWRX.js.map → trade-processor-EYA3I3XB.js.map} +0 -0
- /package/dist/{trades-store-LOL3FQSF.js.map → trades-store-R3NL25T2.js.map} +0 -0
- /package/server/{daily-log-processor-Y3PVSVBM.js.map → daily-log-processor-ENEUT22A.js.map} +0 -0
- /package/server/{daily-logs-store-NU7M4L5C.js.map → daily-logs-store-YFNXTB7J.js.map} +0 -0
- /package/server/{market-provider-VOYYVYWT.js.map → market-provider-B437HKLW.js.map} +0 -0
- /package/server/{proto-DWRZJO4E.js.map → proto-YZ4TLX2N.js.map} +0 -0
- /package/server/{sync-QFI5L7S7.js.map → sync-V25UQJA3.js.map} +0 -0
- /package/server/{trade-processor-JWVS37KM.js.map → trade-processor-L3PIQ5TG.js.map} +0 -0
- /package/server/{trades-store-VY26MWER.js.map → trades-store-RTTEYMXH.js.map} +0 -0
|
@@ -15420,12 +15420,7 @@ function normalQuantile(p) {
|
|
|
15420
15420
|
4.374664141464968,
|
|
15421
15421
|
2.938163982698783
|
|
15422
15422
|
];
|
|
15423
|
-
const d = [
|
|
15424
|
-
0.007784695709041462,
|
|
15425
|
-
0.3224671290700398,
|
|
15426
|
-
2.445134137142996,
|
|
15427
|
-
3.754408661907416
|
|
15428
|
-
];
|
|
15423
|
+
const d = [0.007784695709041462, 0.3224671290700398, 2.445134137142996, 3.754408661907416];
|
|
15429
15424
|
const pLow = 0.02425;
|
|
15430
15425
|
const pHigh = 1 - pLow;
|
|
15431
15426
|
let q;
|
|
@@ -15589,10 +15584,7 @@ function calculateCorrelationMatrix(trades, options = {}) {
|
|
|
15589
15584
|
}
|
|
15590
15585
|
const strategyReturns = {};
|
|
15591
15586
|
for (const strategy of Object.keys(strategyDailyReturns)) {
|
|
15592
|
-
strategyReturns[strategy] = aggregateByPeriod(
|
|
15593
|
-
strategyDailyReturns[strategy],
|
|
15594
|
-
timePeriod
|
|
15595
|
-
);
|
|
15587
|
+
strategyReturns[strategy] = aggregateByPeriod(strategyDailyReturns[strategy], timePeriod);
|
|
15596
15588
|
}
|
|
15597
15589
|
const allDates = /* @__PURE__ */ new Set();
|
|
15598
15590
|
for (const returns of Object.values(strategyReturns)) {
|
|
@@ -15727,9 +15719,7 @@ function normalizeReturn(trade, mode) {
|
|
|
15727
15719
|
return trade.pl / trade.marginReq;
|
|
15728
15720
|
}
|
|
15729
15721
|
case "notional": {
|
|
15730
|
-
const notional = Math.abs(
|
|
15731
|
-
(trade.openingPrice || 0) * (trade.numContracts || 0)
|
|
15732
|
-
);
|
|
15722
|
+
const notional = Math.abs((trade.openingPrice || 0) * (trade.numContracts || 0));
|
|
15733
15723
|
if (!notional) {
|
|
15734
15724
|
return null;
|
|
15735
15725
|
}
|
|
@@ -15742,9 +15732,7 @@ function normalizeReturn(trade, mode) {
|
|
|
15742
15732
|
function getTradeDateKey(trade, basis) {
|
|
15743
15733
|
const date = basis === "closed" ? trade.dateClosed : trade.dateOpened;
|
|
15744
15734
|
if (!date) {
|
|
15745
|
-
throw new Error(
|
|
15746
|
-
"Trade is missing required date information for correlation calculation"
|
|
15747
|
-
);
|
|
15735
|
+
throw new Error("Trade is missing required date information for correlation calculation");
|
|
15748
15736
|
}
|
|
15749
15737
|
const year = date.getFullYear();
|
|
15750
15738
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
@@ -15761,9 +15749,7 @@ function getIsoWeekKey(dateStr) {
|
|
|
15761
15749
|
const dayOfWeek = thursday.getUTCDay() || 7;
|
|
15762
15750
|
thursday.setUTCDate(thursday.getUTCDate() + (4 - dayOfWeek));
|
|
15763
15751
|
const yearStart = new Date(Date.UTC(thursday.getUTCFullYear(), 0, 1));
|
|
15764
|
-
const weekNum = Math.ceil(
|
|
15765
|
-
((thursday.getTime() - yearStart.getTime()) / 864e5 + 1) / 7
|
|
15766
|
-
);
|
|
15752
|
+
const weekNum = Math.ceil(((thursday.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
15767
15753
|
return `${thursday.getUTCFullYear()}-W${String(weekNum).padStart(2, "0")}`;
|
|
15768
15754
|
}
|
|
15769
15755
|
function getMonthKey(dateStr) {
|
|
@@ -15850,9 +15836,7 @@ function performTailRiskAnalysis(trades, options = {}) {
|
|
|
15850
15836
|
}
|
|
15851
15837
|
if (strategyFilter && strategyFilter.length > 0) {
|
|
15852
15838
|
const filterSet = new Set(strategyFilter);
|
|
15853
|
-
filteredTrades = filteredTrades.filter(
|
|
15854
|
-
(t) => t.strategy && filterSet.has(t.strategy)
|
|
15855
|
-
);
|
|
15839
|
+
filteredTrades = filteredTrades.filter((t) => t.strategy && filterSet.has(t.strategy));
|
|
15856
15840
|
}
|
|
15857
15841
|
if (dateRange?.from || dateRange?.to) {
|
|
15858
15842
|
filteredTrades = filteredTrades.filter((t) => {
|
|
@@ -15867,41 +15851,27 @@ function performTailRiskAnalysis(trades, options = {}) {
|
|
|
15867
15851
|
return true;
|
|
15868
15852
|
});
|
|
15869
15853
|
}
|
|
15870
|
-
const aligned = aggregateAndAlignReturns(
|
|
15871
|
-
filteredTrades,
|
|
15872
|
-
normalization,
|
|
15873
|
-
dateBasis
|
|
15874
|
-
);
|
|
15854
|
+
const aligned = aggregateAndAlignReturns(filteredTrades, normalization, dateBasis);
|
|
15875
15855
|
if (aligned.strategies.length < 2) {
|
|
15876
|
-
return createEmptyResult(
|
|
15877
|
-
aligned,
|
|
15878
|
-
tailThreshold,
|
|
15879
|
-
varianceThreshold,
|
|
15880
|
-
startTime
|
|
15881
|
-
);
|
|
15856
|
+
return createEmptyResult(aligned, tailThreshold, varianceThreshold, startTime);
|
|
15882
15857
|
}
|
|
15883
15858
|
if (aligned.dates.length < minTradingDays) {
|
|
15884
|
-
return createEmptyResult(
|
|
15885
|
-
aligned,
|
|
15886
|
-
tailThreshold,
|
|
15887
|
-
varianceThreshold,
|
|
15888
|
-
startTime
|
|
15889
|
-
);
|
|
15859
|
+
return createEmptyResult(aligned, tailThreshold, varianceThreshold, startTime);
|
|
15890
15860
|
}
|
|
15891
15861
|
const transformedReturns = aligned.returns.map(
|
|
15892
15862
|
(strategyReturns) => probabilityIntegralTransform(strategyReturns)
|
|
15893
15863
|
);
|
|
15894
15864
|
const copulaCorrelationMatrix = computeCorrelationMatrix(transformedReturns);
|
|
15895
|
-
const { eigenvalues, eigenvectors, explainedVariance, effectiveFactors } = performEigenAnalysis(
|
|
15865
|
+
const { eigenvalues, eigenvectors, explainedVariance, effectiveFactors } = performEigenAnalysis(
|
|
15866
|
+
copulaCorrelationMatrix,
|
|
15867
|
+
varianceThreshold
|
|
15868
|
+
);
|
|
15896
15869
|
const jointTailRiskResult = estimateJointTailRisk(
|
|
15897
15870
|
transformedReturns,
|
|
15898
15871
|
aligned.tradedMask,
|
|
15899
15872
|
tailThreshold
|
|
15900
15873
|
);
|
|
15901
|
-
const analytics = calculateTailRiskAnalytics(
|
|
15902
|
-
jointTailRiskResult.matrix,
|
|
15903
|
-
aligned.strategies
|
|
15904
|
-
);
|
|
15874
|
+
const analytics = calculateTailRiskAnalytics(jointTailRiskResult.matrix, aligned.strategies);
|
|
15905
15875
|
const marginalContributions = calculateMarginalContributions(
|
|
15906
15876
|
copulaCorrelationMatrix,
|
|
15907
15877
|
jointTailRiskResult.matrix,
|
|
@@ -15998,9 +15968,7 @@ function normalizeReturn2(trade, mode) {
|
|
|
15998
15968
|
break;
|
|
15999
15969
|
}
|
|
16000
15970
|
case "notional": {
|
|
16001
|
-
const notional = Math.abs(
|
|
16002
|
-
(trade.openingPrice || 0) * (trade.numContracts || 0)
|
|
16003
|
-
);
|
|
15971
|
+
const notional = Math.abs((trade.openingPrice || 0) * (trade.numContracts || 0));
|
|
16004
15972
|
if (!notional || notional === 0) {
|
|
16005
15973
|
return null;
|
|
16006
15974
|
}
|
|
@@ -16084,10 +16052,7 @@ function performEigenAnalysis(correlationMatrix, varianceThreshold = 0.8) {
|
|
|
16084
16052
|
effectiveFactors
|
|
16085
16053
|
};
|
|
16086
16054
|
} catch (error) {
|
|
16087
|
-
console.warn(
|
|
16088
|
-
"Eigenvalue decomposition failed, using identity fallback:",
|
|
16089
|
-
error
|
|
16090
|
-
);
|
|
16055
|
+
console.warn("Eigenvalue decomposition failed, using identity fallback:", error);
|
|
16091
16056
|
return {
|
|
16092
16057
|
eigenvalues: new Array(n).fill(1),
|
|
16093
16058
|
eigenvectors: correlationMatrix.map(
|
|
@@ -16146,10 +16111,7 @@ function estimateJointTailRisk(transformedReturns, tradedMask, tailThreshold) {
|
|
|
16146
16111
|
}
|
|
16147
16112
|
}
|
|
16148
16113
|
}
|
|
16149
|
-
const minTailObs = getMinTailObservations(
|
|
16150
|
-
tailThreshold,
|
|
16151
|
-
sharedTradingDays
|
|
16152
|
-
);
|
|
16114
|
+
const minTailObs = getMinTailObservations(tailThreshold, sharedTradingDays);
|
|
16153
16115
|
if (iInTailAndBothTraded < minTailObs) {
|
|
16154
16116
|
row.push(NaN);
|
|
16155
16117
|
insufficientPairs++;
|
|
@@ -16220,10 +16182,7 @@ function calculateMarginalContributions(_copulaCorrelationMatrix, jointTailRiskM
|
|
|
16220
16182
|
}
|
|
16221
16183
|
const contributions = [];
|
|
16222
16184
|
const firstEigenvector = eigenvectors[0] || new Array(n).fill(0);
|
|
16223
|
-
const sumAbsLoadings = firstEigenvector.reduce(
|
|
16224
|
-
(sum2, val) => sum2 + Math.abs(val),
|
|
16225
|
-
0
|
|
16226
|
-
);
|
|
16185
|
+
const sumAbsLoadings = firstEigenvector.reduce((sum2, val) => sum2 + Math.abs(val), 0);
|
|
16227
16186
|
for (let i = 0; i < n; i++) {
|
|
16228
16187
|
const concentrationScore = sumAbsLoadings > 0 ? Math.abs(firstEigenvector[i]) / sumAbsLoadings : 1 / n;
|
|
16229
16188
|
let sumJointRisk = 0;
|
|
@@ -20837,8 +20796,10 @@ var PortfolioStatsCalculator = class _PortfolioStatsCalculator {
|
|
|
20837
20796
|
if (!trade.dateOpened) return false;
|
|
20838
20797
|
const date = new Date(trade.dateOpened);
|
|
20839
20798
|
if (isNaN(date.getTime())) return false;
|
|
20840
|
-
if (typeof trade.openingCommissionsFees !== "number" || isNaN(trade.openingCommissionsFees))
|
|
20841
|
-
|
|
20799
|
+
if (typeof trade.openingCommissionsFees !== "number" || isNaN(trade.openingCommissionsFees))
|
|
20800
|
+
return false;
|
|
20801
|
+
if (typeof trade.closingCommissionsFees !== "number" || isNaN(trade.closingCommissionsFees))
|
|
20802
|
+
return false;
|
|
20842
20803
|
return true;
|
|
20843
20804
|
} catch {
|
|
20844
20805
|
return false;
|
|
@@ -20877,7 +20838,10 @@ var PortfolioStatsCalculator = class _PortfolioStatsCalculator {
|
|
|
20877
20838
|
const streaks = this.calculateStreaks(validTrades);
|
|
20878
20839
|
const timeInDrawdown = this.calculateTimeInDrawdown(validTrades, adjustedDailyLogs);
|
|
20879
20840
|
const periodicWinRates = this.calculatePeriodicWinRates(validTrades);
|
|
20880
|
-
const initialCapital = _PortfolioStatsCalculator.calculateInitialCapital(
|
|
20841
|
+
const initialCapital = _PortfolioStatsCalculator.calculateInitialCapital(
|
|
20842
|
+
validTrades,
|
|
20843
|
+
adjustedDailyLogs
|
|
20844
|
+
);
|
|
20881
20845
|
return {
|
|
20882
20846
|
totalTrades,
|
|
20883
20847
|
totalPl,
|
|
@@ -20915,14 +20879,17 @@ var PortfolioStatsCalculator = class _PortfolioStatsCalculator {
|
|
|
20915
20879
|
if (trades.length === 0) {
|
|
20916
20880
|
return {};
|
|
20917
20881
|
}
|
|
20918
|
-
const tradesByStrategy = trades.reduce(
|
|
20919
|
-
|
|
20920
|
-
|
|
20921
|
-
acc[strategy]
|
|
20922
|
-
|
|
20923
|
-
|
|
20924
|
-
|
|
20925
|
-
|
|
20882
|
+
const tradesByStrategy = trades.reduce(
|
|
20883
|
+
(acc, trade) => {
|
|
20884
|
+
const strategy = trade.strategy || "Unknown";
|
|
20885
|
+
if (!acc[strategy]) {
|
|
20886
|
+
acc[strategy] = [];
|
|
20887
|
+
}
|
|
20888
|
+
acc[strategy].push(trade);
|
|
20889
|
+
return acc;
|
|
20890
|
+
},
|
|
20891
|
+
{}
|
|
20892
|
+
);
|
|
20926
20893
|
const strategyStats = {};
|
|
20927
20894
|
Object.entries(tradesByStrategy).forEach(([strategyName, strategyTrades]) => {
|
|
20928
20895
|
const portfolioStats = this.calculatePortfolioStats(strategyTrades);
|
|
@@ -20957,7 +20924,9 @@ var PortfolioStatsCalculator = class _PortfolioStatsCalculator {
|
|
|
20957
20924
|
return maxDrawdown2;
|
|
20958
20925
|
}
|
|
20959
20926
|
if (trades.length === 0) return 0;
|
|
20960
|
-
const closedTrades = trades.filter(
|
|
20927
|
+
const closedTrades = trades.filter(
|
|
20928
|
+
(trade) => trade.dateClosed && trade.fundsAtClose !== void 0
|
|
20929
|
+
);
|
|
20961
20930
|
if (closedTrades.length === 0) return 0;
|
|
20962
20931
|
const sortedTrades = [...closedTrades].sort((a, b) => {
|
|
20963
20932
|
try {
|
|
@@ -21058,9 +21027,7 @@ var PortfolioStatsCalculator = class _PortfolioStatsCalculator {
|
|
|
21058
21027
|
* Calculate average days to expiration (DTE)
|
|
21059
21028
|
*/
|
|
21060
21029
|
calculateAvgDTE(trades) {
|
|
21061
|
-
const tradesWithDTE = trades.filter(
|
|
21062
|
-
(trade) => trade.dateClosed && trade.dateOpened
|
|
21063
|
-
);
|
|
21030
|
+
const tradesWithDTE = trades.filter((trade) => trade.dateClosed && trade.dateOpened);
|
|
21064
21031
|
if (tradesWithDTE.length === 0) return void 0;
|
|
21065
21032
|
const totalDTE = tradesWithDTE.reduce((sum2, trade) => {
|
|
21066
21033
|
const openDate = new Date(trade.dateOpened);
|
|
@@ -21081,7 +21048,9 @@ var PortfolioStatsCalculator = class _PortfolioStatsCalculator {
|
|
|
21081
21048
|
return a.timeOpened.localeCompare(b.timeOpened);
|
|
21082
21049
|
});
|
|
21083
21050
|
const startDate = new Date(sortedTrades[0].dateOpened);
|
|
21084
|
-
const endDate = new Date(
|
|
21051
|
+
const endDate = new Date(
|
|
21052
|
+
sortedTrades[sortedTrades.length - 1].dateClosed || sortedTrades[sortedTrades.length - 1].dateOpened
|
|
21053
|
+
);
|
|
21085
21054
|
const totalYears = (endDate.getTime() - startDate.getTime()) / (1e3 * 60 * 60 * 24 * 365.25);
|
|
21086
21055
|
if (totalYears <= 0) return void 0;
|
|
21087
21056
|
const initialCapital = _PortfolioStatsCalculator.calculateInitialCapital(trades);
|
|
@@ -21148,7 +21117,9 @@ var PortfolioStatsCalculator = class _PortfolioStatsCalculator {
|
|
|
21148
21117
|
if (winningTrades.length === 0 || losingTrades.length === 0) return void 0;
|
|
21149
21118
|
const winRate = winningTrades.length / trades.length;
|
|
21150
21119
|
const avgWin = winningTrades.reduce((sum2, trade) => sum2 + trade.pl, 0) / winningTrades.length;
|
|
21151
|
-
const avgLoss = Math.abs(
|
|
21120
|
+
const avgLoss = Math.abs(
|
|
21121
|
+
losingTrades.reduce((sum2, trade) => sum2 + trade.pl, 0) / losingTrades.length
|
|
21122
|
+
);
|
|
21152
21123
|
if (avgLoss === 0) return void 0;
|
|
21153
21124
|
const winLossRatio = avgWin / avgLoss;
|
|
21154
21125
|
const kellyPercentage = (winRate * winLossRatio - (1 - winRate)) / winLossRatio;
|
|
@@ -21194,7 +21165,9 @@ var PortfolioStatsCalculator = class _PortfolioStatsCalculator {
|
|
|
21194
21165
|
return daysInDrawdown / dailyLogEntries.length * 100;
|
|
21195
21166
|
}
|
|
21196
21167
|
if (trades.length === 0) return void 0;
|
|
21197
|
-
const closedTrades = trades.filter(
|
|
21168
|
+
const closedTrades = trades.filter(
|
|
21169
|
+
(trade) => trade.dateClosed && trade.fundsAtClose !== void 0
|
|
21170
|
+
);
|
|
21198
21171
|
if (closedTrades.length === 0) return void 0;
|
|
21199
21172
|
const sortedTrades = [...closedTrades].sort((a, b) => {
|
|
21200
21173
|
try {
|
|
@@ -21243,7 +21216,9 @@ var PortfolioStatsCalculator = class _PortfolioStatsCalculator {
|
|
|
21243
21216
|
}
|
|
21244
21217
|
monthlyTrades.get(monthKey).push(trade);
|
|
21245
21218
|
const startOfYear = new Date(date.getFullYear(), 0, 1);
|
|
21246
|
-
const weekNumber = Math.ceil(
|
|
21219
|
+
const weekNumber = Math.ceil(
|
|
21220
|
+
((date.getTime() - startOfYear.getTime()) / 864e5 + startOfYear.getDay() + 1) / 7
|
|
21221
|
+
);
|
|
21247
21222
|
const weekKey = `${date.getFullYear()}-${String(weekNumber).padStart(2, "0")}`;
|
|
21248
21223
|
if (!weeklyTrades.has(weekKey)) {
|
|
21249
21224
|
weeklyTrades.set(weekKey, []);
|
|
@@ -21420,7 +21395,14 @@ var WalkForwardAnalyzer = class {
|
|
|
21420
21395
|
const calculator = new PortfolioStatsCalculator();
|
|
21421
21396
|
const startedAt = /* @__PURE__ */ new Date();
|
|
21422
21397
|
if (sortedTrades.length === 0) {
|
|
21423
|
-
const emptyResults = this.buildResults(
|
|
21398
|
+
const emptyResults = this.buildResults(
|
|
21399
|
+
[],
|
|
21400
|
+
options.config,
|
|
21401
|
+
0,
|
|
21402
|
+
0,
|
|
21403
|
+
sortedTrades.length,
|
|
21404
|
+
startedAt
|
|
21405
|
+
);
|
|
21424
21406
|
return {
|
|
21425
21407
|
config: options.config,
|
|
21426
21408
|
results: emptyResults,
|
|
@@ -21441,8 +21423,16 @@ var WalkForwardAnalyzer = class {
|
|
|
21441
21423
|
for (let index = 0; index < windows.length; index++) {
|
|
21442
21424
|
this.throwIfAborted(options.signal);
|
|
21443
21425
|
const window2 = windows[index];
|
|
21444
|
-
const inSampleTrades = this.filterTrades(
|
|
21445
|
-
|
|
21426
|
+
const inSampleTrades = this.filterTrades(
|
|
21427
|
+
sortedTrades,
|
|
21428
|
+
window2.inSampleStart,
|
|
21429
|
+
window2.inSampleEnd
|
|
21430
|
+
);
|
|
21431
|
+
const outSampleTrades = this.filterTrades(
|
|
21432
|
+
sortedTrades,
|
|
21433
|
+
window2.outOfSampleStart,
|
|
21434
|
+
window2.outOfSampleEnd
|
|
21435
|
+
);
|
|
21446
21436
|
const minInSample = options.config.minInSampleTrades ?? DEFAULT_MIN_IN_SAMPLE_TRADES;
|
|
21447
21437
|
const minOutSample = options.config.minOutOfSampleTrades ?? DEFAULT_MIN_OUT_SAMPLE_TRADES;
|
|
21448
21438
|
if (inSampleTrades.length < minInSample || outSampleTrades.length < minOutSample) {
|
|
@@ -21451,7 +21441,9 @@ var WalkForwardAnalyzer = class {
|
|
|
21451
21441
|
skippedWindows.push({ ...window2, reason, detail });
|
|
21452
21442
|
continue;
|
|
21453
21443
|
}
|
|
21454
|
-
const combinationIterator = this.buildCombinationIterator(
|
|
21444
|
+
const combinationIterator = this.buildCombinationIterator(
|
|
21445
|
+
options.config.parameterRanges || {}
|
|
21446
|
+
);
|
|
21455
21447
|
if (combinationIterator.count > MAX_PARAMETER_COMBINATIONS) {
|
|
21456
21448
|
throw new Error(
|
|
21457
21449
|
`Walk-forward parameter grid too large (${combinationIterator.count.toLocaleString()} combinations). Reduce ranges or increase step sizes.`
|
|
@@ -21482,7 +21474,12 @@ var WalkForwardAnalyzer = class {
|
|
|
21482
21474
|
inSampleInitialCapital
|
|
21483
21475
|
);
|
|
21484
21476
|
const inSampleStats = calculator.calculatePortfolioStats(scaledInSampleTrades);
|
|
21485
|
-
if (!this.isRiskAcceptable(
|
|
21477
|
+
if (!this.isRiskAcceptable(
|
|
21478
|
+
params,
|
|
21479
|
+
inSampleStats,
|
|
21480
|
+
scaledInSampleTrades,
|
|
21481
|
+
options.config.performanceFloor
|
|
21482
|
+
)) {
|
|
21486
21483
|
continue;
|
|
21487
21484
|
}
|
|
21488
21485
|
if (enforceDiversificationConstraints && diversificationConfig) {
|
|
@@ -21494,7 +21491,10 @@ var WalkForwardAnalyzer = class {
|
|
|
21494
21491
|
continue;
|
|
21495
21492
|
}
|
|
21496
21493
|
}
|
|
21497
|
-
const targetValue = this.getTargetMetricValue(
|
|
21494
|
+
const targetValue = this.getTargetMetricValue(
|
|
21495
|
+
inSampleStats,
|
|
21496
|
+
options.config.optimizationTarget
|
|
21497
|
+
);
|
|
21498
21498
|
if (!Number.isFinite(targetValue)) {
|
|
21499
21499
|
continue;
|
|
21500
21500
|
}
|
|
@@ -21549,7 +21549,10 @@ var WalkForwardAnalyzer = class {
|
|
|
21549
21549
|
inSampleMetrics: bestCombo.inSampleStats,
|
|
21550
21550
|
outOfSampleMetrics: outSampleStats,
|
|
21551
21551
|
targetMetricInSample: bestCombo.score,
|
|
21552
|
-
targetMetricOutOfSample: this.getTargetMetricValue(
|
|
21552
|
+
targetMetricOutOfSample: this.getTargetMetricValue(
|
|
21553
|
+
outSampleStats,
|
|
21554
|
+
options.config.optimizationTarget
|
|
21555
|
+
),
|
|
21553
21556
|
diversificationMetrics
|
|
21554
21557
|
};
|
|
21555
21558
|
periods.push(period);
|
|
@@ -21623,7 +21626,9 @@ var WalkForwardAnalyzer = class {
|
|
|
21623
21626
|
const inSampleStart = new Date(cursor);
|
|
21624
21627
|
const inSampleEnd = new Date(cursor + (config4.inSampleDays - 1) * DAY_MS);
|
|
21625
21628
|
const outOfSampleStart = new Date(inSampleEnd.getTime() + DAY_MS);
|
|
21626
|
-
const outOfSampleEnd = new Date(
|
|
21629
|
+
const outOfSampleEnd = new Date(
|
|
21630
|
+
outOfSampleStart.getTime() + (config4.outOfSampleDays - 1) * DAY_MS
|
|
21631
|
+
);
|
|
21627
21632
|
if (outOfSampleStart > lastDate) {
|
|
21628
21633
|
break;
|
|
21629
21634
|
}
|
|
@@ -21638,7 +21643,9 @@ var WalkForwardAnalyzer = class {
|
|
|
21638
21643
|
return windows;
|
|
21639
21644
|
}
|
|
21640
21645
|
floorToUTCDate(date) {
|
|
21641
|
-
const floored = new Date(
|
|
21646
|
+
const floored = new Date(
|
|
21647
|
+
Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())
|
|
21648
|
+
);
|
|
21642
21649
|
return floored;
|
|
21643
21650
|
}
|
|
21644
21651
|
buildCombinationIterator(parameterRanges) {
|
|
@@ -21824,10 +21831,7 @@ var WalkForwardAnalyzer = class {
|
|
|
21824
21831
|
maxCorrelationPair,
|
|
21825
21832
|
avgTailDependence: tailRiskResult?.analytics.averageJointTailRisk ?? 0,
|
|
21826
21833
|
maxTailDependence: tailRiskResult?.analytics.highestJointTailRisk.value ?? 0,
|
|
21827
|
-
maxTailDependencePair: tailRiskResult?.analytics.highestJointTailRisk.pair ?? [
|
|
21828
|
-
"",
|
|
21829
|
-
""
|
|
21830
|
-
],
|
|
21834
|
+
maxTailDependencePair: tailRiskResult?.analytics.highestJointTailRisk.pair ?? ["", ""],
|
|
21831
21835
|
effectiveFactors: tailRiskResult?.effectiveFactors ?? correlationMatrix.strategies.length,
|
|
21832
21836
|
highRiskPairsPct: tailRiskResult?.analytics.highRiskPairsPct ?? 0,
|
|
21833
21837
|
// Track insufficient tail data for UI display
|
|
@@ -22198,10 +22202,7 @@ function createSyntheticMaxLossTrades(trades, percentage, simulationLength, base
|
|
|
22198
22202
|
return [];
|
|
22199
22203
|
}
|
|
22200
22204
|
const requestedBudget = Math.ceil(simulationLength * percentage / 100);
|
|
22201
|
-
const cappedBudget = Math.min(
|
|
22202
|
-
simulationLength,
|
|
22203
|
-
Math.max(1, requestedBudget)
|
|
22204
|
-
);
|
|
22205
|
+
const cappedBudget = Math.min(simulationLength, Math.max(1, requestedBudget));
|
|
22205
22206
|
if (cappedBudget <= 0) {
|
|
22206
22207
|
return [];
|
|
22207
22208
|
}
|
|
@@ -22397,9 +22398,7 @@ function calculatePercentageReturns(trades, normalizeTo1Lot, initialCapital) {
|
|
|
22397
22398
|
if (trades.length === 0) {
|
|
22398
22399
|
return [];
|
|
22399
22400
|
}
|
|
22400
|
-
const sortedTrades = [...trades].sort(
|
|
22401
|
-
(a, b) => a.dateOpened.getTime() - b.dateOpened.getTime()
|
|
22402
|
-
);
|
|
22401
|
+
const sortedTrades = [...trades].sort((a, b) => a.dateOpened.getTime() - b.dateOpened.getTime());
|
|
22403
22402
|
const percentageReturns = [];
|
|
22404
22403
|
let capital;
|
|
22405
22404
|
if (initialCapital !== void 0 && initialCapital > 0) {
|
|
@@ -22424,9 +22423,7 @@ function calculateMarginReturns(trades) {
|
|
|
22424
22423
|
if (trades.length === 0) {
|
|
22425
22424
|
return [];
|
|
22426
22425
|
}
|
|
22427
|
-
const sortedTrades = [...trades].sort(
|
|
22428
|
-
(a, b) => a.dateOpened.getTime() - b.dateOpened.getTime()
|
|
22429
|
-
);
|
|
22426
|
+
const sortedTrades = [...trades].sort((a, b) => a.dateOpened.getTime() - b.dateOpened.getTime());
|
|
22430
22427
|
const returns = [];
|
|
22431
22428
|
for (const trade of sortedTrades) {
|
|
22432
22429
|
if (trade.marginReq > 0) {
|
|
@@ -22520,25 +22517,13 @@ function runMonteCarloSimulation(trades, params) {
|
|
|
22520
22517
|
let actualResamplePoolSize;
|
|
22521
22518
|
const isPercentageMode = params.resampleMethod === "percentage";
|
|
22522
22519
|
if (params.resampleMethod === "trades") {
|
|
22523
|
-
const tradePool = getTradeResamplePool(
|
|
22524
|
-
trades,
|
|
22525
|
-
params.resampleWindow,
|
|
22526
|
-
params.strategy
|
|
22527
|
-
);
|
|
22520
|
+
const tradePool = getTradeResamplePool(trades, params.resampleWindow, params.strategy);
|
|
22528
22521
|
actualResamplePoolSize = tradePool.length;
|
|
22529
|
-
resamplePool = tradePool.map(
|
|
22530
|
-
(t) => params.normalizeTo1Lot ? scaleTradeToOneLot(t) : t.pl
|
|
22531
|
-
);
|
|
22522
|
+
resamplePool = tradePool.map((t) => params.normalizeTo1Lot ? scaleTradeToOneLot(t) : t.pl);
|
|
22532
22523
|
} else if (params.resampleMethod === "daily") {
|
|
22533
22524
|
const filteredTrades = params.strategy && params.strategy !== "all" ? trades.filter((t) => t.strategy === params.strategy) : trades;
|
|
22534
|
-
const dailyReturns = calculateDailyReturns(
|
|
22535
|
-
|
|
22536
|
-
params.normalizeTo1Lot
|
|
22537
|
-
);
|
|
22538
|
-
const dailyPLs = getDailyResamplePool(
|
|
22539
|
-
dailyReturns,
|
|
22540
|
-
params.resampleWindow
|
|
22541
|
-
);
|
|
22525
|
+
const dailyReturns = calculateDailyReturns(filteredTrades, params.normalizeTo1Lot);
|
|
22526
|
+
const dailyPLs = getDailyResamplePool(dailyReturns, params.resampleWindow);
|
|
22542
22527
|
actualResamplePoolSize = dailyPLs.length;
|
|
22543
22528
|
resamplePool = dailyPLs;
|
|
22544
22529
|
} else {
|
|
@@ -22557,10 +22542,7 @@ function runMonteCarloSimulation(trades, params) {
|
|
|
22557
22542
|
params.historicalInitialCapital
|
|
22558
22543
|
// Use historical capital (if provided) to reconstruct trajectory
|
|
22559
22544
|
);
|
|
22560
|
-
const percentagePool = getPercentageResamplePool(
|
|
22561
|
-
percentageReturns,
|
|
22562
|
-
params.resampleWindow
|
|
22563
|
-
);
|
|
22545
|
+
const percentagePool = getPercentageResamplePool(percentageReturns, params.resampleWindow);
|
|
22564
22546
|
actualResamplePoolSize = percentagePool.length;
|
|
22565
22547
|
resamplePool = percentagePool;
|
|
22566
22548
|
}
|
|
@@ -22611,20 +22593,13 @@ function runMonteCarloSimulation(trades, params) {
|
|
|
22611
22593
|
resamplePool = [...resamplePool, ...worstCaseTrades];
|
|
22612
22594
|
}
|
|
22613
22595
|
}
|
|
22614
|
-
const enforcedGuaranteeTrades = params.worstCaseEnabled && params.worstCaseMode === "guarantee" && params.simulationLength > 0 ? worstCaseTrades.slice(
|
|
22615
|
-
0,
|
|
22616
|
-
Math.min(worstCaseTrades.length, params.simulationLength)
|
|
22617
|
-
) : [];
|
|
22596
|
+
const enforcedGuaranteeTrades = params.worstCaseEnabled && params.worstCaseMode === "guarantee" && params.simulationLength > 0 ? worstCaseTrades.slice(0, Math.min(worstCaseTrades.length, params.simulationLength)) : [];
|
|
22618
22597
|
const simulations = [];
|
|
22619
22598
|
for (let i = 0; i < params.numSimulations; i++) {
|
|
22620
22599
|
const seed = params.randomSeed !== void 0 ? params.randomSeed + i : void 0;
|
|
22621
22600
|
const guaranteeActive = enforcedGuaranteeTrades.length > 0;
|
|
22622
22601
|
const baselineSampleSize = guaranteeActive ? Math.max(0, params.simulationLength - enforcedGuaranteeTrades.length) : params.simulationLength;
|
|
22623
|
-
let resampledPLs = resampleWithReplacement(
|
|
22624
|
-
resamplePool,
|
|
22625
|
-
baselineSampleSize,
|
|
22626
|
-
seed
|
|
22627
|
-
);
|
|
22602
|
+
let resampledPLs = resampleWithReplacement(resamplePool, baselineSampleSize, seed);
|
|
22628
22603
|
if (guaranteeActive) {
|
|
22629
22604
|
const combined = [...resampledPLs];
|
|
22630
22605
|
const rng = seed !== void 0 ? createSeededRandom(seed + 999999) : Math.random;
|
|
@@ -22714,10 +22689,7 @@ function calculateStatistics(simulations) {
|
|
|
22714
22689
|
50
|
|
22715
22690
|
);
|
|
22716
22691
|
const meanSharpeRatio = sharpeRatios.reduce((sum2, s) => sum2 + s, 0) / sharpeRatios.length;
|
|
22717
|
-
const variance2 = finalValues.reduce(
|
|
22718
|
-
(sum2, v) => sum2 + Math.pow(v - meanFinalValue, 2),
|
|
22719
|
-
0
|
|
22720
|
-
) / (finalValues.length - 1);
|
|
22692
|
+
const variance2 = finalValues.reduce((sum2, v) => sum2 + Math.pow(v - meanFinalValue, 2), 0) / (finalValues.length - 1);
|
|
22721
22693
|
const stdFinalValue = Math.sqrt(variance2);
|
|
22722
22694
|
const profitableSimulations = totalReturns.filter((r) => r > 0).length;
|
|
22723
22695
|
const probabilityOfProfit = profitableSimulations / totalReturns.length;
|
|
@@ -22908,10 +22880,7 @@ function linearRegression(y) {
|
|
|
22908
22880
|
const slope = sumX2 > 0 ? sumXY / sumX2 : 0;
|
|
22909
22881
|
const intercept = meanY - slope * meanX;
|
|
22910
22882
|
const predicted = x.map((xi) => slope * xi + intercept);
|
|
22911
|
-
const ssRes = y.reduce(
|
|
22912
|
-
(sum2, yi, i) => sum2 + (yi - predicted[i]) ** 2,
|
|
22913
|
-
0
|
|
22914
|
-
);
|
|
22883
|
+
const ssRes = y.reduce((sum2, yi, i) => sum2 + (yi - predicted[i]) ** 2, 0);
|
|
22915
22884
|
const ssTot = y.reduce((sum2, yi) => sum2 + (yi - meanY) ** 2, 0);
|
|
22916
22885
|
const rSquared = ssTot > 0 ? 1 - ssRes / ssTot : 0;
|
|
22917
22886
|
const mse = n > 2 ? ssRes / (n - 2) : 0;
|
|
@@ -23152,7 +23121,13 @@ function segmentByPeriod(trades) {
|
|
|
23152
23121
|
const monthDate = parseMonthKey(m.periodKey);
|
|
23153
23122
|
return monthDate !== null && getQuarterKey(monthDate) === qKey;
|
|
23154
23123
|
}).map((m) => m.avgMonthlyReturnPct);
|
|
23155
|
-
return computePeriodMetrics(
|
|
23124
|
+
return computePeriodMetrics(
|
|
23125
|
+
qKey,
|
|
23126
|
+
quarterTrades,
|
|
23127
|
+
isPartial,
|
|
23128
|
+
partialNote,
|
|
23129
|
+
constituentMonthlyReturns
|
|
23130
|
+
);
|
|
23156
23131
|
});
|
|
23157
23132
|
const yearlyGroups = groupTradesByKey(sortedTrades, getYearKey);
|
|
23158
23133
|
const yearKeys = Array.from(yearlyGroups.keys()).sort();
|
|
@@ -23165,7 +23140,13 @@ function segmentByPeriod(trades) {
|
|
|
23165
23140
|
index === yearKeys.length - 1
|
|
23166
23141
|
);
|
|
23167
23142
|
const constituentMonthlyReturns = monthlyMetrics.filter((m) => m.periodKey.startsWith(yKey + "-")).map((m) => m.avgMonthlyReturnPct);
|
|
23168
|
-
return computePeriodMetrics(
|
|
23143
|
+
return computePeriodMetrics(
|
|
23144
|
+
yKey,
|
|
23145
|
+
yearTrades,
|
|
23146
|
+
isPartial,
|
|
23147
|
+
partialNote,
|
|
23148
|
+
constituentMonthlyReturns
|
|
23149
|
+
);
|
|
23169
23150
|
});
|
|
23170
23151
|
const yearlyTrends = computePeriodTrends(yearlyMetrics);
|
|
23171
23152
|
const quarterlyTrends = computePeriodTrends(quarterlyMetrics);
|
|
@@ -23362,7 +23343,14 @@ function computeRollingMetrics(trades, options) {
|
|
|
23362
23343
|
};
|
|
23363
23344
|
}
|
|
23364
23345
|
function computeSeasonalAverages(series) {
|
|
23365
|
-
const metricNames = [
|
|
23346
|
+
const metricNames = [
|
|
23347
|
+
"winRate",
|
|
23348
|
+
"profitFactor",
|
|
23349
|
+
"kellyPercent",
|
|
23350
|
+
"sharpeRatio",
|
|
23351
|
+
"avgReturn",
|
|
23352
|
+
"netPl"
|
|
23353
|
+
];
|
|
23366
23354
|
const buckets = {};
|
|
23367
23355
|
for (const metric of metricNames) {
|
|
23368
23356
|
buckets[metric] = { Q1: [], Q2: [], Q3: [], Q4: [] };
|
|
@@ -23598,9 +23586,17 @@ function runRegimeComparison(trades, options) {
|
|
|
23598
23586
|
};
|
|
23599
23587
|
const recentResult = runMonteCarloSimulation(recentPool, recentParams);
|
|
23600
23588
|
const metricPairs = [
|
|
23601
|
-
[
|
|
23589
|
+
[
|
|
23590
|
+
"probabilityOfProfit",
|
|
23591
|
+
fullResult.statistics.probabilityOfProfit,
|
|
23592
|
+
recentResult.statistics.probabilityOfProfit
|
|
23593
|
+
],
|
|
23602
23594
|
["sharpeRatio", fullResult.statistics.meanSharpeRatio, recentResult.statistics.meanSharpeRatio],
|
|
23603
|
-
[
|
|
23595
|
+
[
|
|
23596
|
+
"medianMaxDrawdown",
|
|
23597
|
+
fullResult.statistics.medianMaxDrawdown,
|
|
23598
|
+
recentResult.statistics.medianMaxDrawdown
|
|
23599
|
+
]
|
|
23604
23600
|
];
|
|
23605
23601
|
const comparison = metricPairs.map(([metric, fullValue, recentValue]) => {
|
|
23606
23602
|
const delta = recentValue - fullValue;
|
|
@@ -23773,7 +23769,6 @@ function analyzeWalkForwardDegradation(trades, options) {
|
|
|
23773
23769
|
if (options) {
|
|
23774
23770
|
for (const [key, value] of Object.entries(options)) {
|
|
23775
23771
|
if (value !== void 0) {
|
|
23776
|
-
;
|
|
23777
23772
|
definedOptions[key] = value;
|
|
23778
23773
|
}
|
|
23779
23774
|
}
|
|
@@ -23862,7 +23857,11 @@ function analyzeWalkForwardDegradation(trades, options) {
|
|
|
23862
23857
|
const oosMetrics = computeMetrics(oosTrades);
|
|
23863
23858
|
const sharpeEfficiency = computeEfficiency(oosMetrics.sharpe, isMetrics.sharpe, "sharpe");
|
|
23864
23859
|
const winRateEfficiency = computeEfficiency(oosMetrics.winRate, isMetrics.winRate, "winRate");
|
|
23865
|
-
const pfEfficiency = computeEfficiency(
|
|
23860
|
+
const pfEfficiency = computeEfficiency(
|
|
23861
|
+
oosMetrics.profitFactor,
|
|
23862
|
+
isMetrics.profitFactor,
|
|
23863
|
+
"profitFactor"
|
|
23864
|
+
);
|
|
23866
23865
|
if (isMetrics.sharpe !== null && isMetrics.sharpe < 0) {
|
|
23867
23866
|
warnings.push(
|
|
23868
23867
|
`Negative IS Sharpe in period ${window2.periodIndex} -- efficiency ratio may be misleading`
|
|
@@ -23871,9 +23870,21 @@ function analyzeWalkForwardDegradation(trades, options) {
|
|
|
23871
23870
|
periods.push({
|
|
23872
23871
|
window: window2,
|
|
23873
23872
|
metrics: {
|
|
23874
|
-
sharpe: {
|
|
23875
|
-
|
|
23876
|
-
|
|
23873
|
+
sharpe: {
|
|
23874
|
+
inSample: isMetrics.sharpe,
|
|
23875
|
+
outOfSample: oosMetrics.sharpe,
|
|
23876
|
+
efficiency: sharpeEfficiency
|
|
23877
|
+
},
|
|
23878
|
+
winRate: {
|
|
23879
|
+
inSample: isMetrics.winRate,
|
|
23880
|
+
outOfSample: oosMetrics.winRate,
|
|
23881
|
+
efficiency: winRateEfficiency
|
|
23882
|
+
},
|
|
23883
|
+
profitFactor: {
|
|
23884
|
+
inSample: isMetrics.profitFactor,
|
|
23885
|
+
outOfSample: oosMetrics.profitFactor,
|
|
23886
|
+
efficiency: pfEfficiency
|
|
23887
|
+
}
|
|
23877
23888
|
},
|
|
23878
23889
|
sufficient: true,
|
|
23879
23890
|
warnings
|
|
@@ -23917,7 +23928,10 @@ function analyzeWalkForwardDegradation(trades, options) {
|
|
|
23917
23928
|
const qualifyingCount = qualifyingPeriods.length;
|
|
23918
23929
|
const recentCount = Math.min(config4.recentPeriodCount, qualifyingCount);
|
|
23919
23930
|
const recentPeriodSlice = qualifyingPeriods.slice(-recentCount);
|
|
23920
|
-
const historicalPeriodSlice = qualifyingPeriods.slice(
|
|
23931
|
+
const historicalPeriodSlice = qualifyingPeriods.slice(
|
|
23932
|
+
0,
|
|
23933
|
+
Math.max(0, qualifyingCount - recentCount)
|
|
23934
|
+
);
|
|
23921
23935
|
const recentData = getEfficiencyData(recentPeriodSlice);
|
|
23922
23936
|
const historicalData = getEfficiencyData(historicalPeriodSlice);
|
|
23923
23937
|
const recentAvgEfficiency = {
|
|
@@ -24004,9 +24018,7 @@ function getIsoWeekKey2(dateStr) {
|
|
|
24004
24018
|
const dayOfWeek = thursday.getUTCDay() || 7;
|
|
24005
24019
|
thursday.setUTCDate(thursday.getUTCDate() + (4 - dayOfWeek));
|
|
24006
24020
|
const yearStart = new Date(Date.UTC(thursday.getUTCFullYear(), 0, 1));
|
|
24007
|
-
const weekNum = Math.ceil(
|
|
24008
|
-
((thursday.getTime() - yearStart.getTime()) / 864e5 + 1) / 7
|
|
24009
|
-
);
|
|
24021
|
+
const weekNum = Math.ceil(((thursday.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
24010
24022
|
return `${thursday.getUTCFullYear()}-W${String(weekNum).padStart(2, "0")}`;
|
|
24011
24023
|
}
|
|
24012
24024
|
function getMonthKey3(dateStr) {
|
|
@@ -24025,9 +24037,7 @@ function applyDateRangeFilter(trades, dateRange) {
|
|
|
24025
24037
|
}
|
|
24026
24038
|
function applyStrategyFilter(trades, strategy) {
|
|
24027
24039
|
if (!strategy) return trades;
|
|
24028
|
-
return trades.filter(
|
|
24029
|
-
(t) => t.strategy.toLowerCase() === strategy.toLowerCase()
|
|
24030
|
-
);
|
|
24040
|
+
return trades.filter((t) => t.strategy.toLowerCase() === strategy.toLowerCase());
|
|
24031
24041
|
}
|
|
24032
24042
|
function calculateScaledPl(btPl, actualPl, btContracts, actualContracts, scaling) {
|
|
24033
24043
|
let scaledBtPl = btPl;
|
|
@@ -24206,14 +24216,20 @@ function analyzeLiveAlignment(backtestTrades, actualTrades, options) {
|
|
|
24206
24216
|
if (actualTrades.length === 0) {
|
|
24207
24217
|
warnings.push("No actual trades provided");
|
|
24208
24218
|
}
|
|
24209
|
-
const { pairs, unmatchedBacktestByStrategy, unmatchedActualByStrategy } = matchTradesWithScaledPl(
|
|
24219
|
+
const { pairs, unmatchedBacktestByStrategy, unmatchedActualByStrategy } = matchTradesWithScaledPl(
|
|
24220
|
+
filteredBacktest,
|
|
24221
|
+
filteredActual,
|
|
24222
|
+
scaling
|
|
24223
|
+
);
|
|
24210
24224
|
if (pairs.length === 0 && (backtestTrades.length > 0 || actualTrades.length > 0)) {
|
|
24211
24225
|
warnings.push("No matched trade pairs found");
|
|
24212
24226
|
}
|
|
24213
24227
|
const minOverlapTrades = Math.min(filteredBacktest.length, filteredActual.length);
|
|
24214
24228
|
const matchRate = minOverlapTrades > 0 ? pairs.length / minOverlapTrades : 0;
|
|
24215
24229
|
if (matchRate > 0 && matchRate < 0.5) {
|
|
24216
|
-
warnings.push(
|
|
24230
|
+
warnings.push(
|
|
24231
|
+
`Low match rate (${(matchRate * 100).toFixed(1)}%) - trade matching may be unreliable`
|
|
24232
|
+
);
|
|
24217
24233
|
}
|
|
24218
24234
|
const dayStrategyMap = /* @__PURE__ */ new Map();
|
|
24219
24235
|
for (const pair of pairs) {
|
|
@@ -24236,12 +24252,14 @@ function analyzeLiveAlignment(backtestTrades, actualTrades, options) {
|
|
|
24236
24252
|
if (agreed) stratData.agreed++;
|
|
24237
24253
|
strategyDayAgreement.set(strategy, stratData);
|
|
24238
24254
|
}
|
|
24239
|
-
const directionByStrategy = Array.from(strategyDayAgreement.entries()).map(
|
|
24240
|
-
strategy,
|
|
24241
|
-
|
|
24242
|
-
|
|
24243
|
-
|
|
24244
|
-
|
|
24255
|
+
const directionByStrategy = Array.from(strategyDayAgreement.entries()).map(
|
|
24256
|
+
([strategy, data]) => ({
|
|
24257
|
+
strategy,
|
|
24258
|
+
rate: data.total > 0 ? data.agreed / data.total : 0,
|
|
24259
|
+
totalDays: data.total,
|
|
24260
|
+
agreementDays: data.agreed
|
|
24261
|
+
})
|
|
24262
|
+
);
|
|
24245
24263
|
const directionAgreement = {
|
|
24246
24264
|
overallRate: totalDays > 0 ? agreementDays / totalDays : 0,
|
|
24247
24265
|
totalDays,
|
|
@@ -24395,20 +24413,9 @@ function safePercentChange(current, comparison) {
|
|
|
24395
24413
|
if (comparison === 0) return null;
|
|
24396
24414
|
return (current - comparison) / Math.abs(comparison) * 100;
|
|
24397
24415
|
}
|
|
24398
|
-
var DOLLAR_METRICS = /* @__PURE__ */ new Set([
|
|
24399
|
-
|
|
24400
|
-
|
|
24401
|
-
"avgReturn",
|
|
24402
|
-
"netPl"
|
|
24403
|
-
]);
|
|
24404
|
-
var DOLLAR_TREND_METRICS = /* @__PURE__ */ new Set([
|
|
24405
|
-
"netPl",
|
|
24406
|
-
"tradeCount"
|
|
24407
|
-
]);
|
|
24408
|
-
var HIGHER_IS_WORSE_METRICS = /* @__PURE__ */ new Set([
|
|
24409
|
-
"maxDrawdownPercent",
|
|
24410
|
-
"medianMaxDrawdown"
|
|
24411
|
-
]);
|
|
24416
|
+
var DOLLAR_METRICS = /* @__PURE__ */ new Set(["avgWin", "avgLoss", "avgReturn", "netPl"]);
|
|
24417
|
+
var DOLLAR_TREND_METRICS = /* @__PURE__ */ new Set(["netPl", "tradeCount"]);
|
|
24418
|
+
var HIGHER_IS_WORSE_METRICS = /* @__PURE__ */ new Set(["maxDrawdownPercent", "medianMaxDrawdown"]);
|
|
24412
24419
|
function extractObservations(periodResult, rollingResult, regimeResult, wfResult, liveResult) {
|
|
24413
24420
|
const observations = [];
|
|
24414
24421
|
for (const m of rollingResult.recentVsHistorical.metrics) {
|
|
@@ -24666,14 +24673,18 @@ function synthesizeEdgeDecay(trades, actualTrades, options) {
|
|
|
24666
24673
|
const winRateComp = findMetric(rollingResult.recentVsHistorical.metrics, "winRate");
|
|
24667
24674
|
const pfComp = findMetric(rollingResult.recentVsHistorical.metrics, "profitFactor");
|
|
24668
24675
|
const sharpeComp = findMetric(rollingResult.recentVsHistorical.metrics, "sharpeRatio");
|
|
24669
|
-
const rateObsWithPct = observations.filter(
|
|
24676
|
+
const rateObsWithPct = observations.filter(
|
|
24677
|
+
(o) => o.percentChange !== null && o.metricType === "rate"
|
|
24678
|
+
);
|
|
24670
24679
|
let decayCount = 0;
|
|
24671
24680
|
for (const o of rateObsWithPct) {
|
|
24672
24681
|
const isDecay = HIGHER_IS_WORSE_METRICS.has(o.metric) ? o.percentChange > 0 : o.percentChange < 0;
|
|
24673
24682
|
if (isDecay) decayCount++;
|
|
24674
24683
|
}
|
|
24675
24684
|
const decayFraction = rateObsWithPct.length > 0 ? decayCount / rateObsWithPct.length : 0;
|
|
24676
|
-
const rateObsWithAbsPct = observations.filter(
|
|
24685
|
+
const rateObsWithAbsPct = observations.filter(
|
|
24686
|
+
(o) => o.absPercentChange !== null && o.metricType === "rate"
|
|
24687
|
+
);
|
|
24677
24688
|
const meanAbsPctValue = rateObsWithAbsPct.length > 0 ? rateObsWithAbsPct.reduce((sum2, o) => sum2 + o.absPercentChange, 0) / rateObsWithAbsPct.length : 0;
|
|
24678
24689
|
const meanAbsPctNormalized = Math.min(meanAbsPctValue / 50, 1) * decayFraction;
|
|
24679
24690
|
const mcDivergenceValue = regimeResult?.divergence.compositeScore ?? null;
|
|
@@ -24708,15 +24719,35 @@ function synthesizeEdgeDecay(trades, actualTrades, options) {
|
|
|
24708
24719
|
structuralFlagRatio: BASE_WEIGHTS.structuralFlagRatio / otherSum
|
|
24709
24720
|
};
|
|
24710
24721
|
}
|
|
24711
|
-
const compositeDecayScore = Math.max(
|
|
24712
|
-
|
|
24713
|
-
|
|
24714
|
-
|
|
24722
|
+
const compositeDecayScore = Math.max(
|
|
24723
|
+
0,
|
|
24724
|
+
Math.min(
|
|
24725
|
+
1,
|
|
24726
|
+
meanAbsPctNormalized * weights.meanAbsPercentChange + mcDivergenceNormalized * mcDecayDirection * weights.mcRegimeDivergence + wfEffDeltaNormalized * weights.wfEfficiencyDelta + structuralFlagRatioNormalized * weights.structuralFlagRatio
|
|
24727
|
+
)
|
|
24728
|
+
);
|
|
24715
24729
|
const compositeDecayScoreComponents = {
|
|
24716
|
-
meanAbsPercentChange: {
|
|
24717
|
-
|
|
24718
|
-
|
|
24719
|
-
|
|
24730
|
+
meanAbsPercentChange: {
|
|
24731
|
+
value: meanAbsPctValue,
|
|
24732
|
+
normalized: meanAbsPctNormalized,
|
|
24733
|
+
weight: weights.meanAbsPercentChange,
|
|
24734
|
+
decayFraction
|
|
24735
|
+
},
|
|
24736
|
+
mcRegimeDivergence: {
|
|
24737
|
+
value: mcDivergenceValue,
|
|
24738
|
+
normalized: mcDivergenceNormalized * mcDecayDirection,
|
|
24739
|
+
weight: weights.mcRegimeDivergence
|
|
24740
|
+
},
|
|
24741
|
+
wfEfficiencyDelta: {
|
|
24742
|
+
value: wfEffDeltaValue,
|
|
24743
|
+
normalized: wfEffDeltaNormalized,
|
|
24744
|
+
weight: weights.wfEfficiencyDelta
|
|
24745
|
+
},
|
|
24746
|
+
structuralFlagRatio: {
|
|
24747
|
+
value: structuralFlagRatioValue,
|
|
24748
|
+
normalized: structuralFlagRatioNormalized,
|
|
24749
|
+
weight: weights.structuralFlagRatio
|
|
24750
|
+
}
|
|
24720
24751
|
};
|
|
24721
24752
|
const summary = {
|
|
24722
24753
|
totalTrades,
|
|
@@ -24969,7 +25000,9 @@ var PerformanceCalculator = class {
|
|
|
24969
25000
|
static calculateMonthlyReturns(trades, initialCapital) {
|
|
24970
25001
|
if (trades.length === 0) return {};
|
|
24971
25002
|
if (!initialCapital) {
|
|
24972
|
-
const firstTrade = trades.sort(
|
|
25003
|
+
const firstTrade = trades.sort(
|
|
25004
|
+
(a, b) => new Date(a.dateOpened).getTime() - new Date(b.dateOpened).getTime()
|
|
25005
|
+
)[0];
|
|
24973
25006
|
initialCapital = firstTrade.fundsAtClose - firstTrade.pl;
|
|
24974
25007
|
}
|
|
24975
25008
|
const monthlyPl = this.aggregatePLByPeriod(trades, "monthly");
|
|
@@ -25102,53 +25135,300 @@ var PerformanceCalculator = class {
|
|
|
25102
25135
|
// ../lib/models/report-config.ts
|
|
25103
25136
|
var REPORT_FIELDS = [
|
|
25104
25137
|
// Market conditions
|
|
25105
|
-
{
|
|
25106
|
-
|
|
25107
|
-
|
|
25108
|
-
|
|
25109
|
-
|
|
25110
|
-
|
|
25111
|
-
{
|
|
25112
|
-
|
|
25113
|
-
|
|
25114
|
-
|
|
25138
|
+
{
|
|
25139
|
+
field: "openingVix",
|
|
25140
|
+
label: "Opening VIX",
|
|
25141
|
+
category: "market",
|
|
25142
|
+
description: "VIX level when the trade was opened"
|
|
25143
|
+
},
|
|
25144
|
+
{
|
|
25145
|
+
field: "closingVix",
|
|
25146
|
+
label: "Closing VIX",
|
|
25147
|
+
category: "market",
|
|
25148
|
+
description: "VIX level when the trade was closed"
|
|
25149
|
+
},
|
|
25150
|
+
{
|
|
25151
|
+
field: "vixChange",
|
|
25152
|
+
label: "VIX Change",
|
|
25153
|
+
category: "market",
|
|
25154
|
+
description: "How much VIX moved during the trade",
|
|
25155
|
+
formula: "Closing VIX - Opening VIX"
|
|
25156
|
+
},
|
|
25157
|
+
{
|
|
25158
|
+
field: "vixChangePct",
|
|
25159
|
+
label: "VIX Change %",
|
|
25160
|
+
category: "market",
|
|
25161
|
+
unit: "%",
|
|
25162
|
+
description: "Percentage change in VIX during the trade",
|
|
25163
|
+
formula: "((Closing VIX - Opening VIX) / Opening VIX) \xD7 100"
|
|
25164
|
+
},
|
|
25165
|
+
{
|
|
25166
|
+
field: "openingShortLongRatio",
|
|
25167
|
+
label: "Opening S/L Ratio",
|
|
25168
|
+
category: "market",
|
|
25169
|
+
description: "Short/Long ratio at trade entry - measures market sentiment"
|
|
25170
|
+
},
|
|
25171
|
+
{
|
|
25172
|
+
field: "closingShortLongRatio",
|
|
25173
|
+
label: "Closing S/L Ratio",
|
|
25174
|
+
category: "market",
|
|
25175
|
+
description: "Short/Long ratio at trade exit"
|
|
25176
|
+
},
|
|
25177
|
+
{
|
|
25178
|
+
field: "shortLongRatioChange",
|
|
25179
|
+
label: "S/L Ratio Change",
|
|
25180
|
+
category: "market",
|
|
25181
|
+
description: "How S/L ratio changed during the trade",
|
|
25182
|
+
formula: "Closing S/L Ratio / Opening S/L Ratio"
|
|
25183
|
+
},
|
|
25184
|
+
{
|
|
25185
|
+
field: "shortLongRatioChangePct",
|
|
25186
|
+
label: "S/L Ratio Change %",
|
|
25187
|
+
category: "market",
|
|
25188
|
+
unit: "%",
|
|
25189
|
+
description: "Percentage change in S/L ratio",
|
|
25190
|
+
formula: "((Closing - Opening) / Opening) \xD7 100"
|
|
25191
|
+
},
|
|
25192
|
+
{
|
|
25193
|
+
field: "gap",
|
|
25194
|
+
label: "Gap",
|
|
25195
|
+
category: "market",
|
|
25196
|
+
description: "Opening gap in points from previous close"
|
|
25197
|
+
},
|
|
25198
|
+
{
|
|
25199
|
+
field: "movement",
|
|
25200
|
+
label: "Movement",
|
|
25201
|
+
category: "market",
|
|
25202
|
+
description: "Underlying price movement during the trade"
|
|
25203
|
+
},
|
|
25115
25204
|
// Return metrics
|
|
25116
|
-
{
|
|
25117
|
-
|
|
25118
|
-
|
|
25119
|
-
|
|
25120
|
-
|
|
25121
|
-
|
|
25205
|
+
{
|
|
25206
|
+
field: "pl",
|
|
25207
|
+
label: "Profit/Loss",
|
|
25208
|
+
category: "returns",
|
|
25209
|
+
unit: "$",
|
|
25210
|
+
description: "Trade profit or loss in dollars (before fees)"
|
|
25211
|
+
},
|
|
25212
|
+
{
|
|
25213
|
+
field: "netPl",
|
|
25214
|
+
label: "Net P/L",
|
|
25215
|
+
category: "returns",
|
|
25216
|
+
unit: "$",
|
|
25217
|
+
description: "Profit/loss after subtracting all fees",
|
|
25218
|
+
formula: "P/L - Total Fees"
|
|
25219
|
+
},
|
|
25220
|
+
{
|
|
25221
|
+
field: "plPct",
|
|
25222
|
+
label: "P/L %",
|
|
25223
|
+
category: "returns",
|
|
25224
|
+
unit: "%",
|
|
25225
|
+
description: "Return as a percentage of premium collected",
|
|
25226
|
+
formula: "(P/L / Premium) \xD7 100"
|
|
25227
|
+
},
|
|
25228
|
+
{
|
|
25229
|
+
field: "netPlPct",
|
|
25230
|
+
label: "Net P/L %",
|
|
25231
|
+
category: "returns",
|
|
25232
|
+
unit: "%",
|
|
25233
|
+
description: "Net return as a percentage of premium",
|
|
25234
|
+
formula: "(Net P/L / Premium) \xD7 100"
|
|
25235
|
+
},
|
|
25236
|
+
{
|
|
25237
|
+
field: "rom",
|
|
25238
|
+
label: "Return on Margin",
|
|
25239
|
+
category: "returns",
|
|
25240
|
+
unit: "%",
|
|
25241
|
+
description: "Return relative to margin required - measures capital efficiency",
|
|
25242
|
+
formula: "(P/L / Margin Required) \xD7 100"
|
|
25243
|
+
},
|
|
25244
|
+
{
|
|
25245
|
+
field: "isWinner",
|
|
25246
|
+
label: "Is Winner",
|
|
25247
|
+
category: "returns",
|
|
25248
|
+
description: "Binary flag: 1 if trade was profitable, 0 if it was a loss"
|
|
25249
|
+
},
|
|
25122
25250
|
// Risk metrics (MFE/MAE)
|
|
25123
|
-
{
|
|
25124
|
-
|
|
25125
|
-
|
|
25126
|
-
|
|
25127
|
-
|
|
25251
|
+
{
|
|
25252
|
+
field: "mfePercent",
|
|
25253
|
+
label: "MFE %",
|
|
25254
|
+
category: "risk",
|
|
25255
|
+
unit: "%",
|
|
25256
|
+
description: "Maximum Favorable Excursion - the best unrealized profit during the trade as % of premium",
|
|
25257
|
+
formula: "(Max Unrealized Profit / Premium) \xD7 100"
|
|
25258
|
+
},
|
|
25259
|
+
{
|
|
25260
|
+
field: "maePercent",
|
|
25261
|
+
label: "MAE %",
|
|
25262
|
+
category: "risk",
|
|
25263
|
+
unit: "%",
|
|
25264
|
+
description: "Maximum Adverse Excursion - the worst unrealized loss during the trade as % of premium",
|
|
25265
|
+
formula: "(Max Unrealized Loss / Premium) \xD7 100"
|
|
25266
|
+
},
|
|
25267
|
+
{
|
|
25268
|
+
field: "profitCapturePercent",
|
|
25269
|
+
label: "Profit Capture %",
|
|
25270
|
+
category: "risk",
|
|
25271
|
+
unit: "%",
|
|
25272
|
+
description: "How much of the peak profit was captured at exit",
|
|
25273
|
+
formula: "(P/L / MFE) \xD7 100"
|
|
25274
|
+
},
|
|
25275
|
+
{
|
|
25276
|
+
field: "excursionRatio",
|
|
25277
|
+
label: "Excursion Ratio",
|
|
25278
|
+
category: "risk",
|
|
25279
|
+
description: "Reward/risk ratio - how much upside vs downside the trade experienced",
|
|
25280
|
+
formula: "MFE / MAE"
|
|
25281
|
+
},
|
|
25282
|
+
{
|
|
25283
|
+
field: "rMultiple",
|
|
25284
|
+
label: "R-Multiple",
|
|
25285
|
+
category: "risk",
|
|
25286
|
+
description: 'Risk-adjusted return - how many "R" (risk units) were won or lost',
|
|
25287
|
+
formula: "P/L / MAE"
|
|
25288
|
+
},
|
|
25128
25289
|
// Trade details
|
|
25129
|
-
{
|
|
25130
|
-
|
|
25131
|
-
|
|
25132
|
-
|
|
25133
|
-
|
|
25134
|
-
|
|
25135
|
-
|
|
25136
|
-
{
|
|
25137
|
-
|
|
25138
|
-
|
|
25290
|
+
{
|
|
25291
|
+
field: "premium",
|
|
25292
|
+
label: "Premium",
|
|
25293
|
+
category: "trade",
|
|
25294
|
+
unit: "$",
|
|
25295
|
+
description: "Premium collected per contract when opening the trade"
|
|
25296
|
+
},
|
|
25297
|
+
{
|
|
25298
|
+
field: "marginReq",
|
|
25299
|
+
label: "Margin Required",
|
|
25300
|
+
category: "trade",
|
|
25301
|
+
unit: "$",
|
|
25302
|
+
description: "Margin/buying power required to hold the position"
|
|
25303
|
+
},
|
|
25304
|
+
{
|
|
25305
|
+
field: "openingPrice",
|
|
25306
|
+
label: "Opening Price",
|
|
25307
|
+
category: "trade",
|
|
25308
|
+
unit: "$",
|
|
25309
|
+
description: "Price of the position when opened"
|
|
25310
|
+
},
|
|
25311
|
+
{
|
|
25312
|
+
field: "closingPrice",
|
|
25313
|
+
label: "Closing Price",
|
|
25314
|
+
category: "trade",
|
|
25315
|
+
unit: "$",
|
|
25316
|
+
description: "Price of the position when closed"
|
|
25317
|
+
},
|
|
25318
|
+
{
|
|
25319
|
+
field: "numContracts",
|
|
25320
|
+
label: "Contracts",
|
|
25321
|
+
category: "trade",
|
|
25322
|
+
description: "Number of contracts traded"
|
|
25323
|
+
},
|
|
25324
|
+
{
|
|
25325
|
+
field: "totalFees",
|
|
25326
|
+
label: "Total Fees",
|
|
25327
|
+
category: "trade",
|
|
25328
|
+
unit: "$",
|
|
25329
|
+
description: "All commissions and fees paid",
|
|
25330
|
+
formula: "Opening Fees + Closing Fees"
|
|
25331
|
+
},
|
|
25332
|
+
{
|
|
25333
|
+
field: "openingCommissionsFees",
|
|
25334
|
+
label: "Opening Fees",
|
|
25335
|
+
category: "trade",
|
|
25336
|
+
unit: "$",
|
|
25337
|
+
description: "Commissions and fees paid when opening"
|
|
25338
|
+
},
|
|
25339
|
+
{
|
|
25340
|
+
field: "closingCommissionsFees",
|
|
25341
|
+
label: "Closing Fees",
|
|
25342
|
+
category: "trade",
|
|
25343
|
+
unit: "$",
|
|
25344
|
+
description: "Commissions and fees paid when closing"
|
|
25345
|
+
},
|
|
25346
|
+
{
|
|
25347
|
+
field: "maxProfit",
|
|
25348
|
+
label: "Max Profit",
|
|
25349
|
+
category: "trade",
|
|
25350
|
+
unit: "%",
|
|
25351
|
+
description: "Maximum unrealized profit as % of premium during the trade"
|
|
25352
|
+
},
|
|
25353
|
+
{
|
|
25354
|
+
field: "maxLoss",
|
|
25355
|
+
label: "Max Loss",
|
|
25356
|
+
category: "trade",
|
|
25357
|
+
unit: "%",
|
|
25358
|
+
description: "Maximum unrealized loss as % of premium during the trade"
|
|
25359
|
+
},
|
|
25139
25360
|
// Timing
|
|
25140
|
-
{
|
|
25141
|
-
|
|
25142
|
-
|
|
25143
|
-
|
|
25144
|
-
|
|
25145
|
-
|
|
25146
|
-
{
|
|
25147
|
-
|
|
25148
|
-
|
|
25361
|
+
{
|
|
25362
|
+
field: "tradeNumber",
|
|
25363
|
+
label: "Trade #",
|
|
25364
|
+
category: "timing",
|
|
25365
|
+
description: "Sequential trade number (1 = first trade)"
|
|
25366
|
+
},
|
|
25367
|
+
{
|
|
25368
|
+
field: "dateOpenedTimestamp",
|
|
25369
|
+
label: "Date Opened",
|
|
25370
|
+
category: "timing",
|
|
25371
|
+
description: "When the trade was opened (useful for time-series charts)"
|
|
25372
|
+
},
|
|
25373
|
+
{
|
|
25374
|
+
field: "durationHours",
|
|
25375
|
+
label: "Duration (hrs)",
|
|
25376
|
+
category: "timing",
|
|
25377
|
+
unit: "hrs",
|
|
25378
|
+
description: "How long the position was held",
|
|
25379
|
+
formula: "Close Time - Open Time"
|
|
25380
|
+
},
|
|
25381
|
+
{
|
|
25382
|
+
field: "dayOfWeek",
|
|
25383
|
+
label: "Day of Week",
|
|
25384
|
+
category: "timing",
|
|
25385
|
+
description: "Day of week when opened: 0=Sunday through 6=Saturday"
|
|
25386
|
+
},
|
|
25387
|
+
{
|
|
25388
|
+
field: "hourOfDay",
|
|
25389
|
+
label: "Hour of Day",
|
|
25390
|
+
category: "timing",
|
|
25391
|
+
description: "Hour of day when opened (0-23 in Eastern Time)"
|
|
25392
|
+
},
|
|
25393
|
+
{
|
|
25394
|
+
field: "timeOfDayMinutes",
|
|
25395
|
+
label: "Time of Day",
|
|
25396
|
+
category: "timing",
|
|
25397
|
+
description: "Exact time when opened as minutes since midnight (e.g., 11:45 = 705). Useful for scatter plots to analyze floating-time or multiple-entry trades"
|
|
25398
|
+
},
|
|
25399
|
+
{
|
|
25400
|
+
field: "dayOfMonth",
|
|
25401
|
+
label: "Day of Month",
|
|
25402
|
+
category: "timing",
|
|
25403
|
+
description: "Day of month when opened (1-31)"
|
|
25404
|
+
},
|
|
25405
|
+
{
|
|
25406
|
+
field: "monthOfYear",
|
|
25407
|
+
label: "Month of Year",
|
|
25408
|
+
category: "timing",
|
|
25409
|
+
description: "Month when opened (1=January through 12=December)"
|
|
25410
|
+
},
|
|
25411
|
+
{
|
|
25412
|
+
field: "weekOfYear",
|
|
25413
|
+
label: "Week of Year",
|
|
25414
|
+
category: "timing",
|
|
25415
|
+
description: "ISO week number when opened (1-52)"
|
|
25416
|
+
},
|
|
25149
25417
|
// Portfolio context
|
|
25150
|
-
{
|
|
25151
|
-
|
|
25418
|
+
{
|
|
25419
|
+
field: "exposureOnOpen",
|
|
25420
|
+
label: "Portfolio Exposure %",
|
|
25421
|
+
category: "risk",
|
|
25422
|
+
unit: "%",
|
|
25423
|
+
description: "Portfolio margin exposure as % of equity at the exact moment this trade opened - shows how much risk was deployed when entering"
|
|
25424
|
+
},
|
|
25425
|
+
{
|
|
25426
|
+
field: "exposureOnOpenDollars",
|
|
25427
|
+
label: "Portfolio Exposure $",
|
|
25428
|
+
category: "risk",
|
|
25429
|
+
unit: "$",
|
|
25430
|
+
description: "Portfolio margin exposure in dollars at the exact moment this trade opened - shows total margin at risk when entering"
|
|
25431
|
+
}
|
|
25152
25432
|
];
|
|
25153
25433
|
|
|
25154
25434
|
// ../lib/calculations/index.ts
|
|
@@ -25179,9 +25459,15 @@ var CalculationOrchestrator = class {
|
|
|
25179
25459
|
};
|
|
25180
25460
|
}
|
|
25181
25461
|
const calculator = new PortfolioStatsCalculator(config4);
|
|
25182
|
-
const portfolioStats = calculator.calculatePortfolioStats(
|
|
25462
|
+
const portfolioStats = calculator.calculatePortfolioStats(
|
|
25463
|
+
trades,
|
|
25464
|
+
dailyLogs
|
|
25465
|
+
);
|
|
25183
25466
|
const strategyStats = calculator.calculateStrategyStats(trades);
|
|
25184
|
-
const performanceMetrics = PerformanceCalculator.calculatePerformanceMetrics(
|
|
25467
|
+
const performanceMetrics = PerformanceCalculator.calculatePerformanceMetrics(
|
|
25468
|
+
trades,
|
|
25469
|
+
dailyLogs
|
|
25470
|
+
);
|
|
25185
25471
|
this.cache.set(blockId, {
|
|
25186
25472
|
portfolioStats,
|
|
25187
25473
|
strategyStats,
|
|
@@ -25221,9 +25507,9 @@ var calculationOrchestrator = new CalculationOrchestrator();
|
|
|
25221
25507
|
var REPORTING_TRADE_COLUMN_ALIASES = {
|
|
25222
25508
|
"Initial Premium ($)": "Initial Premium",
|
|
25223
25509
|
"Initial Credit": "Initial Premium",
|
|
25224
|
-
|
|
25510
|
+
Contracts: "No. of Contracts",
|
|
25225
25511
|
"Contracts Traded": "No. of Contracts",
|
|
25226
|
-
|
|
25512
|
+
PL: "P/L"
|
|
25227
25513
|
};
|
|
25228
25514
|
|
|
25229
25515
|
// ../lib/processing/tat-adapter.ts
|
|
@@ -25409,4 +25695,4 @@ decimal.js/decimal.mjs:
|
|
|
25409
25695
|
* MIT Licence
|
|
25410
25696
|
*)
|
|
25411
25697
|
*/
|
|
25412
|
-
//# sourceMappingURL=chunk-
|
|
25698
|
+
//# sourceMappingURL=chunk-BOPHW5M6.js.map
|