tradeblocks-mcp 0.4.2 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/test-exports.js +186 -19
- package/dist/test-exports.js.map +1 -1
- package/package.json +1 -1
- package/server/index.js +1835 -141
- package/server/index.js.map +1 -1
package/dist/test-exports.js
CHANGED
|
@@ -20454,8 +20454,17 @@ async function discoverCsvFiles(folderPath) {
|
|
|
20454
20454
|
if (csvFiles.includes("reportinglog.csv")) {
|
|
20455
20455
|
mappings.reportinglog = "reportinglog.csv";
|
|
20456
20456
|
}
|
|
20457
|
+
if (!mappings.reportinglog) {
|
|
20458
|
+
const strategyLogFile = csvFiles.find((f) => {
|
|
20459
|
+
const lower = f.toLowerCase();
|
|
20460
|
+
return lower.includes("strategy-trade-log") || lower.includes("strategylog") || lower.startsWith("strategy-log");
|
|
20461
|
+
});
|
|
20462
|
+
if (strategyLogFile) {
|
|
20463
|
+
mappings.reportinglog = strategyLogFile;
|
|
20464
|
+
}
|
|
20465
|
+
}
|
|
20457
20466
|
for (const csvFile of csvFiles) {
|
|
20458
|
-
if (csvFile === "tradelog.csv" || csvFile === "dailylog.csv" || csvFile === "reportinglog.csv") {
|
|
20467
|
+
if (csvFile === "tradelog.csv" || csvFile === "dailylog.csv" || csvFile === "reportinglog.csv" || csvFile === mappings.reportinglog) {
|
|
20459
20468
|
continue;
|
|
20460
20469
|
}
|
|
20461
20470
|
const csvPath = path.join(folderPath, csvFile);
|
|
@@ -20761,6 +20770,19 @@ async function listBlocks(baseDir) {
|
|
|
20761
20770
|
continue;
|
|
20762
20771
|
}
|
|
20763
20772
|
const hasDailyLog = !!dailylogFilename;
|
|
20773
|
+
const reportingLogFilename = await findReportingLogFile(blockPath, metadata);
|
|
20774
|
+
const hasReportingLog = !!reportingLogFilename;
|
|
20775
|
+
let reportingLogSummary;
|
|
20776
|
+
if (hasReportingLog && metadata?.reportingLogStats) {
|
|
20777
|
+
const reportingLogStale = reportingLogFilename ? !await isReportingLogCacheValid(blockPath, metadata, reportingLogFilename) : false;
|
|
20778
|
+
reportingLogSummary = {
|
|
20779
|
+
tradeCount: metadata.reportingLogStats.totalTrades,
|
|
20780
|
+
strategyCount: metadata.reportingLogStats.strategies.length,
|
|
20781
|
+
totalPL: metadata.reportingLogStats.totalPL,
|
|
20782
|
+
dateRange: metadata.reportingLogStats.dateRange,
|
|
20783
|
+
stale: reportingLogStale
|
|
20784
|
+
};
|
|
20785
|
+
}
|
|
20764
20786
|
const cacheValid = metadata?.cachedStats && !needsMetadataUpdate && await isCacheValid(blockPath, metadata);
|
|
20765
20787
|
if (cacheValid && metadata?.cachedStats) {
|
|
20766
20788
|
blocks.push({
|
|
@@ -20768,13 +20790,15 @@ async function listBlocks(baseDir) {
|
|
|
20768
20790
|
name: metadata.name || entry.name,
|
|
20769
20791
|
tradeCount: metadata.tradeCount,
|
|
20770
20792
|
hasDailyLog,
|
|
20793
|
+
hasReportingLog,
|
|
20771
20794
|
dateRange: {
|
|
20772
20795
|
start: metadata.dateRange.start ? new Date(metadata.dateRange.start) : null,
|
|
20773
20796
|
end: metadata.dateRange.end ? new Date(metadata.dateRange.end) : null
|
|
20774
20797
|
},
|
|
20775
20798
|
strategies: metadata.strategies,
|
|
20776
20799
|
totalPl: metadata.cachedStats.totalPl,
|
|
20777
|
-
netPl: metadata.cachedStats.netPl
|
|
20800
|
+
netPl: metadata.cachedStats.netPl,
|
|
20801
|
+
reportingLog: reportingLogSummary
|
|
20778
20802
|
});
|
|
20779
20803
|
} else {
|
|
20780
20804
|
try {
|
|
@@ -20800,13 +20824,15 @@ async function listBlocks(baseDir) {
|
|
|
20800
20824
|
name: updatedMetadata.name,
|
|
20801
20825
|
tradeCount: trades.length,
|
|
20802
20826
|
hasDailyLog,
|
|
20827
|
+
hasReportingLog,
|
|
20803
20828
|
dateRange: {
|
|
20804
20829
|
start: updatedMetadata.dateRange.start ? new Date(updatedMetadata.dateRange.start) : null,
|
|
20805
20830
|
end: updatedMetadata.dateRange.end ? new Date(updatedMetadata.dateRange.end) : null
|
|
20806
20831
|
},
|
|
20807
20832
|
strategies: updatedMetadata.strategies,
|
|
20808
20833
|
totalPl,
|
|
20809
|
-
netPl: totalPl - totalCommissions
|
|
20834
|
+
netPl: totalPl - totalCommissions,
|
|
20835
|
+
reportingLog: reportingLogSummary
|
|
20810
20836
|
});
|
|
20811
20837
|
await saveMetadata(blockPath, updatedMetadata);
|
|
20812
20838
|
} catch (error) {
|
|
@@ -20866,22 +20892,20 @@ async function loadReportingLog(baseDir, blockId) {
|
|
|
20866
20892
|
try {
|
|
20867
20893
|
await fs.access(reportingLogPath);
|
|
20868
20894
|
} catch {
|
|
20869
|
-
|
|
20870
|
-
|
|
20871
|
-
|
|
20872
|
-
|
|
20873
|
-
|
|
20874
|
-
|
|
20875
|
-
|
|
20876
|
-
|
|
20877
|
-
|
|
20878
|
-
|
|
20879
|
-
|
|
20880
|
-
|
|
20881
|
-
|
|
20882
|
-
|
|
20883
|
-
return trades2;
|
|
20884
|
-
}
|
|
20895
|
+
const discovered = await discoverCsvFiles(blockPath);
|
|
20896
|
+
if (discovered.mappings.reportinglog) {
|
|
20897
|
+
const altPath = path.join(blockPath, discovered.mappings.reportinglog);
|
|
20898
|
+
const content2 = await fs.readFile(altPath, "utf-8");
|
|
20899
|
+
const records2 = parseCSV(content2);
|
|
20900
|
+
const trades2 = [];
|
|
20901
|
+
for (const record of records2) {
|
|
20902
|
+
const trade = convertToReportingTrade(record);
|
|
20903
|
+
if (trade) trades2.push(trade);
|
|
20904
|
+
}
|
|
20905
|
+
trades2.sort(
|
|
20906
|
+
(a, b) => new Date(a.dateOpened).getTime() - new Date(b.dateOpened).getTime()
|
|
20907
|
+
);
|
|
20908
|
+
return trades2;
|
|
20885
20909
|
}
|
|
20886
20910
|
throw new Error(`reportinglog.csv not found in block: ${blockId}`);
|
|
20887
20911
|
}
|
|
@@ -20899,6 +20923,148 @@ async function loadReportingLog(baseDir, blockId) {
|
|
|
20899
20923
|
);
|
|
20900
20924
|
return trades;
|
|
20901
20925
|
}
|
|
20926
|
+
function computeReportingLogStats(trades, invalidCount = 0) {
|
|
20927
|
+
const byStrategy = {};
|
|
20928
|
+
const strategyTrades = /* @__PURE__ */ new Map();
|
|
20929
|
+
for (const trade of trades) {
|
|
20930
|
+
const strategyKey = trade.strategy.trim();
|
|
20931
|
+
if (!strategyTrades.has(strategyKey)) {
|
|
20932
|
+
strategyTrades.set(strategyKey, []);
|
|
20933
|
+
}
|
|
20934
|
+
strategyTrades.get(strategyKey).push(trade);
|
|
20935
|
+
}
|
|
20936
|
+
for (const [strategy, strategyTradeList] of strategyTrades) {
|
|
20937
|
+
const tradeCount = strategyTradeList.length;
|
|
20938
|
+
const winningTrades = strategyTradeList.filter((t) => t.pl > 0).length;
|
|
20939
|
+
const winRate = tradeCount > 0 ? winningTrades / tradeCount : 0;
|
|
20940
|
+
const totalPL = strategyTradeList.reduce((sum2, t) => sum2 + t.pl, 0);
|
|
20941
|
+
const avgPL = tradeCount > 0 ? totalPL / tradeCount : 0;
|
|
20942
|
+
const contractCount = strategyTradeList.reduce(
|
|
20943
|
+
(sum2, t) => sum2 + t.numContracts,
|
|
20944
|
+
0
|
|
20945
|
+
);
|
|
20946
|
+
byStrategy[strategy] = {
|
|
20947
|
+
tradeCount,
|
|
20948
|
+
winRate,
|
|
20949
|
+
totalPL,
|
|
20950
|
+
avgPL,
|
|
20951
|
+
contractCount
|
|
20952
|
+
};
|
|
20953
|
+
}
|
|
20954
|
+
const dates = trades.map((t) => new Date(t.dateOpened).getTime());
|
|
20955
|
+
const dateRange = {
|
|
20956
|
+
start: dates.length > 0 ? new Date(Math.min(...dates)).toISOString() : null,
|
|
20957
|
+
end: dates.length > 0 ? new Date(Math.max(...dates)).toISOString() : null
|
|
20958
|
+
};
|
|
20959
|
+
return {
|
|
20960
|
+
totalTrades: trades.length,
|
|
20961
|
+
totalPL: trades.reduce((sum2, t) => sum2 + t.pl, 0),
|
|
20962
|
+
dateRange,
|
|
20963
|
+
strategies: Array.from(strategyTrades.keys()).sort(),
|
|
20964
|
+
byStrategy,
|
|
20965
|
+
invalidTrades: invalidCount,
|
|
20966
|
+
calculatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
20967
|
+
};
|
|
20968
|
+
}
|
|
20969
|
+
async function findReportingLogFile(blockPath, metadata) {
|
|
20970
|
+
if (metadata?.csvMappings?.reportinglog) {
|
|
20971
|
+
const mappedPath = path.join(blockPath, metadata.csvMappings.reportinglog);
|
|
20972
|
+
try {
|
|
20973
|
+
await fs.access(mappedPath);
|
|
20974
|
+
return metadata.csvMappings.reportinglog;
|
|
20975
|
+
} catch {
|
|
20976
|
+
}
|
|
20977
|
+
}
|
|
20978
|
+
const standardPath = path.join(blockPath, "reportinglog.csv");
|
|
20979
|
+
try {
|
|
20980
|
+
await fs.access(standardPath);
|
|
20981
|
+
return "reportinglog.csv";
|
|
20982
|
+
} catch {
|
|
20983
|
+
}
|
|
20984
|
+
const discovered = await discoverCsvFiles(blockPath);
|
|
20985
|
+
return discovered.mappings.reportinglog;
|
|
20986
|
+
}
|
|
20987
|
+
async function isReportingLogCacheValid(blockPath, metadata, reportingLogFilename) {
|
|
20988
|
+
if (!metadata.reportingLogStats) {
|
|
20989
|
+
return false;
|
|
20990
|
+
}
|
|
20991
|
+
if (!metadata.csvFileMtimes?.reportinglog) {
|
|
20992
|
+
return false;
|
|
20993
|
+
}
|
|
20994
|
+
try {
|
|
20995
|
+
const filePath = path.join(blockPath, reportingLogFilename);
|
|
20996
|
+
const stat2 = await fs.stat(filePath);
|
|
20997
|
+
return stat2.mtimeMs === metadata.csvFileMtimes.reportinglog;
|
|
20998
|
+
} catch {
|
|
20999
|
+
return false;
|
|
21000
|
+
}
|
|
21001
|
+
}
|
|
21002
|
+
async function loadReportingLogStats(baseDir, blockId) {
|
|
21003
|
+
const blockPath = path.join(baseDir, blockId);
|
|
21004
|
+
const metadata = await loadMetadata(blockPath);
|
|
21005
|
+
const reportingLogFilename = await findReportingLogFile(blockPath, metadata);
|
|
21006
|
+
if (!reportingLogFilename) {
|
|
21007
|
+
return void 0;
|
|
21008
|
+
}
|
|
21009
|
+
if (metadata && await isReportingLogCacheValid(blockPath, metadata, reportingLogFilename)) {
|
|
21010
|
+
return {
|
|
21011
|
+
stats: metadata.reportingLogStats,
|
|
21012
|
+
stale: false
|
|
21013
|
+
};
|
|
21014
|
+
}
|
|
21015
|
+
try {
|
|
21016
|
+
const reportingLogPath = path.join(blockPath, reportingLogFilename);
|
|
21017
|
+
const content = await fs.readFile(reportingLogPath, "utf-8");
|
|
21018
|
+
const records = parseCSV(content);
|
|
21019
|
+
const trades = [];
|
|
21020
|
+
let invalidCount = 0;
|
|
21021
|
+
for (const record of records) {
|
|
21022
|
+
const trade = convertToReportingTrade(record);
|
|
21023
|
+
if (trade) {
|
|
21024
|
+
trades.push(trade);
|
|
21025
|
+
} else {
|
|
21026
|
+
invalidCount++;
|
|
21027
|
+
}
|
|
21028
|
+
}
|
|
21029
|
+
const stats = computeReportingLogStats(trades, invalidCount);
|
|
21030
|
+
const fileStat = await fs.stat(reportingLogPath);
|
|
21031
|
+
const updatedMetadata = {
|
|
21032
|
+
...metadata || {
|
|
21033
|
+
blockId,
|
|
21034
|
+
name: blockId,
|
|
21035
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
21036
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
21037
|
+
tradeCount: 0,
|
|
21038
|
+
dailyLogCount: 0,
|
|
21039
|
+
dateRange: { start: null, end: null },
|
|
21040
|
+
strategies: []
|
|
21041
|
+
},
|
|
21042
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
21043
|
+
csvMappings: {
|
|
21044
|
+
...metadata?.csvMappings || {},
|
|
21045
|
+
reportinglog: reportingLogFilename
|
|
21046
|
+
},
|
|
21047
|
+
csvFileMtimes: {
|
|
21048
|
+
...metadata?.csvFileMtimes || {},
|
|
21049
|
+
reportinglog: fileStat.mtimeMs
|
|
21050
|
+
},
|
|
21051
|
+
reportingLogStats: stats
|
|
21052
|
+
};
|
|
21053
|
+
await saveMetadata(blockPath, updatedMetadata);
|
|
21054
|
+
return {
|
|
21055
|
+
stats,
|
|
21056
|
+
stale: false
|
|
21057
|
+
};
|
|
21058
|
+
} catch (error) {
|
|
21059
|
+
if (metadata?.reportingLogStats) {
|
|
21060
|
+
return {
|
|
21061
|
+
stats: metadata.reportingLogStats,
|
|
21062
|
+
stale: true
|
|
21063
|
+
};
|
|
21064
|
+
}
|
|
21065
|
+
throw error;
|
|
21066
|
+
}
|
|
21067
|
+
}
|
|
20902
21068
|
function toKebabCase(str) {
|
|
20903
21069
|
return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").replace(/[^a-zA-Z0-9-]/g, "").toLowerCase().replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
20904
21070
|
}
|
|
@@ -21055,6 +21221,7 @@ export {
|
|
|
21055
21221
|
loadBlock,
|
|
21056
21222
|
loadMetadata,
|
|
21057
21223
|
loadReportingLog,
|
|
21224
|
+
loadReportingLogStats,
|
|
21058
21225
|
performTailRiskAnalysis,
|
|
21059
21226
|
saveMetadata
|
|
21060
21227
|
};
|