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
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
dailyLogEntrySchema,
|
|
5
5
|
findMissingHeaders,
|
|
6
6
|
rawDailyLogDataSchema
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-OBYKFW2B.js";
|
|
8
8
|
|
|
9
9
|
// ../lib/models/daily-log.ts
|
|
10
10
|
var DAILY_LOG_COLUMN_MAPPING = {
|
|
11
|
-
|
|
11
|
+
Date: "date",
|
|
12
12
|
"Net Liquidity": "netLiquidity",
|
|
13
13
|
"Current Funds": "currentFunds",
|
|
14
|
-
|
|
14
|
+
Withdrawn: "withdrawn",
|
|
15
15
|
"Trading Funds": "tradingFunds",
|
|
16
16
|
"P/L": "dailyPl",
|
|
17
17
|
"P/L %": "dailyPlPct",
|
|
@@ -211,7 +211,10 @@ var DailyLogProcessor = class {
|
|
|
211
211
|
rawDailyLogDataSchema.parse(normalizedRow);
|
|
212
212
|
return normalizedRow;
|
|
213
213
|
} catch (error) {
|
|
214
|
-
console.warn(
|
|
214
|
+
console.warn(
|
|
215
|
+
`[DailyLogProcessor] Row ${rowIndex + 2} validation failed:`,
|
|
216
|
+
error instanceof Error ? error.message : error
|
|
217
|
+
);
|
|
215
218
|
return null;
|
|
216
219
|
}
|
|
217
220
|
}
|
|
@@ -277,7 +280,9 @@ var DailyLogProcessor = class {
|
|
|
277
280
|
const validatedEntry = dailyLogEntrySchema.parse(entry);
|
|
278
281
|
return validatedEntry;
|
|
279
282
|
} catch (error) {
|
|
280
|
-
throw new Error(
|
|
283
|
+
throw new Error(
|
|
284
|
+
`Daily log entry conversion failed: ${error instanceof Error ? error.message : String(error)}`
|
|
285
|
+
);
|
|
281
286
|
}
|
|
282
287
|
}
|
|
283
288
|
/**
|
|
@@ -294,13 +299,17 @@ var DailyLogProcessor = class {
|
|
|
294
299
|
static validateDataConsistency(entries) {
|
|
295
300
|
const warnings = [];
|
|
296
301
|
if (entries.length === 0) return warnings;
|
|
297
|
-
const sortedEntries = [...entries].sort(
|
|
302
|
+
const sortedEntries = [...entries].sort(
|
|
303
|
+
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()
|
|
304
|
+
);
|
|
298
305
|
for (let i = 1; i < sortedEntries.length; i++) {
|
|
299
306
|
const prevDate = new Date(sortedEntries[i - 1].date);
|
|
300
307
|
const currentDate = new Date(sortedEntries[i].date);
|
|
301
308
|
const daysDiff = (currentDate.getTime() - prevDate.getTime()) / (1e3 * 60 * 60 * 24);
|
|
302
309
|
if (daysDiff > 7) {
|
|
303
|
-
warnings.push(
|
|
310
|
+
warnings.push(
|
|
311
|
+
`Large date gap detected: ${daysDiff.toFixed(0)} days between ${prevDate.toISOString().split("T")[0]} and ${currentDate.toISOString().split("T")[0]}`
|
|
312
|
+
);
|
|
304
313
|
}
|
|
305
314
|
}
|
|
306
315
|
const negativeEntries = sortedEntries.filter((entry) => entry.netLiquidity < 0);
|
|
@@ -318,4 +327,4 @@ var DailyLogProcessor = class {
|
|
|
318
327
|
export {
|
|
319
328
|
DailyLogProcessor
|
|
320
329
|
};
|
|
321
|
-
//# sourceMappingURL=chunk-
|
|
330
|
+
//# sourceMappingURL=chunk-GH2552SE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../lib/models/daily-log.ts","../../lib/processing/daily-log-processor.ts"],"sourcesContent":["/**\n * Daily log model based on legacy Python DailyLogEntry class\n * Represents daily portfolio performance data from OptionOmega\n */\nexport interface DailyLogEntry {\n date: Date;\n netLiquidity: number;\n currentFunds: number;\n withdrawn: number;\n tradingFunds: number;\n dailyPl: number; // P/L for the day\n dailyPlPct: number; // P/L percentage\n drawdownPct: number; // Drawdown percentage\n blockId?: string; // Optional block ID for linking to trades\n\n /**\n * Custom fields from extra columns in the daily log CSV\n * Keys are the original column names, values are auto-detected as number or string\n * These fields can be joined to trades by date for analysis (e.g., dayOpenVix, spyOpen)\n */\n customFields?: Record<string, number | string>;\n}\n\n/**\n * Raw daily log data as it comes from CSV before processing\n */\nexport interface RawDailyLogData {\n Date: string;\n \"Net Liquidity\": string;\n \"Current Funds\": string;\n Withdrawn: string;\n \"Trading Funds\": string;\n \"P/L\": string;\n \"P/L %\": string;\n \"Drawdown %\": string;\n}\n\n/**\n * Processed daily log collection with metadata\n */\nexport interface DailyLog {\n entries: DailyLogEntry[];\n uploadTimestamp: Date;\n filename: string;\n totalEntries: number;\n dateRangeStart: Date;\n dateRangeEnd: Date;\n finalPortfolioValue: number;\n maxDrawdown: number;\n}\n\n/**\n * Column mapping from CSV headers to DailyLogEntry interface properties\n */\nexport const DAILY_LOG_COLUMN_MAPPING = {\n Date: \"date\",\n \"Net Liquidity\": \"netLiquidity\",\n \"Current Funds\": \"currentFunds\",\n Withdrawn: \"withdrawn\",\n \"Trading Funds\": \"tradingFunds\",\n \"P/L\": \"dailyPl\",\n \"P/L %\": \"dailyPlPct\",\n \"Drawdown %\": \"drawdownPct\",\n} as const;\n\n/**\n * Required columns for daily log processing\n */\nexport const REQUIRED_DAILY_LOG_COLUMNS = [\n \"Date\",\n \"Net Liquidity\",\n \"Current Funds\",\n \"Trading Funds\",\n \"P/L\",\n \"P/L %\",\n \"Drawdown %\",\n] as const;\n","/**\n * Daily Log Processor\n *\n * Handles parsing and processing of daily log CSV files from OptionOmega.\n * Converts raw CSV data to validated DailyLogEntry objects.\n */\n\nimport {\n type DailyLogEntry,\n REQUIRED_DAILY_LOG_COLUMNS,\n DAILY_LOG_COLUMN_MAPPING,\n} from \"../models/daily-log.ts\";\n\n/**\n * Set of known daily log column names (canonical names from DAILY_LOG_COLUMN_MAPPING)\n * Used to identify custom columns that should be preserved\n */\nconst KNOWN_DAILY_LOG_COLUMNS = new Set([\n ...Object.keys(DAILY_LOG_COLUMN_MAPPING),\n \"Withdrawn\", // Optional column that may not be in REQUIRED but is known\n]);\nimport type { ValidationError, ProcessingError } from \"../models/index.ts\";\nimport { rawDailyLogDataSchema, dailyLogEntrySchema } from \"../models/validators.ts\";\nimport { CSVParser, type ParseProgress } from \"./csv-parser.ts\";\nimport { findMissingHeaders } from \"../utils/csv-headers.ts\";\n// import { CSVParseResult } from './csv-parser.ts'\n\n/**\n * Daily log processing configuration\n */\nexport interface DailyLogProcessingConfig {\n maxEntries?: number;\n strictValidation?: boolean;\n progressCallback?: (progress: DailyLogProcessingProgress) => void;\n}\n\n/**\n * Daily log processing progress\n */\nexport interface DailyLogProcessingProgress extends ParseProgress {\n stage: \"reading\" | \"parsing\" | \"validating\" | \"converting\" | \"completed\";\n validEntries: number;\n invalidEntries: number;\n}\n\n/**\n * Daily log processing result\n */\nexport interface DailyLogProcessingResult {\n entries: DailyLogEntry[];\n totalRows: number;\n validEntries: number;\n invalidEntries: number;\n errors: ProcessingError[];\n warnings: string[];\n stats: {\n processingTimeMs: number;\n dateRange: { start: Date | null; end: Date | null };\n finalPortfolioValue: number;\n maxDrawdown: number;\n totalPL: number;\n };\n}\n\n/**\n * Daily log processor class\n */\nexport class DailyLogProcessor {\n private config: Required<DailyLogProcessingConfig>;\n\n constructor(config: DailyLogProcessingConfig = {}) {\n this.config = {\n maxEntries: 10000, // Reasonable limit for daily entries\n strictValidation: false,\n progressCallback: () => {},\n ...config,\n };\n }\n\n /**\n * Process daily log file\n */\n async processFile(file: File, blockId?: string): Promise<DailyLogProcessingResult> {\n const startTime = Date.now();\n const errors: ProcessingError[] = [];\n const warnings: string[] = [];\n\n try {\n // Validate file\n const fileValidation = CSVParser.validateCSVFile(file);\n if (!fileValidation.valid) {\n throw new Error(fileValidation.error);\n }\n\n // Configure CSV parser\n const csvParser = new CSVParser({\n maxRows: this.config.maxEntries,\n progressCallback: (progress, rowsProcessed) => {\n this.config.progressCallback({\n stage: \"parsing\",\n progress,\n rowsProcessed,\n totalRows: 0,\n errors: 0,\n validEntries: 0,\n invalidEntries: 0,\n });\n },\n });\n\n // Parse CSV with validation\n const parseResult = await csvParser.parseFileObject(\n file,\n (row, rowIndex) => this.validateRawDailyLogData(row, rowIndex),\n (progress) => {\n this.config.progressCallback({\n ...progress,\n validEntries: 0,\n invalidEntries: 0,\n });\n },\n );\n\n // Collect parsing errors\n errors.push(...parseResult.errors);\n warnings.push(...parseResult.warnings);\n\n // Check for required columns\n const missingColumns = findMissingHeaders(parseResult.headers, REQUIRED_DAILY_LOG_COLUMNS);\n if (missingColumns.length > 0) {\n throw new Error(`Missing required columns: ${missingColumns.join(\", \")}`);\n }\n\n // Update progress for conversion stage\n this.config.progressCallback({\n stage: \"converting\",\n progress: 0,\n rowsProcessed: 0,\n totalRows: parseResult.data.length,\n errors: errors.length,\n validEntries: 0,\n invalidEntries: 0,\n });\n\n // Convert validated data to DailyLogEntry objects\n const entries: DailyLogEntry[] = [];\n let validEntries = 0;\n let invalidEntries = 0;\n\n for (let i = 0; i < parseResult.data.length; i++) {\n try {\n const entry = this.convertToDailyLogEntry(parseResult.data[i], blockId);\n entries.push(entry);\n validEntries++;\n } catch (error) {\n invalidEntries++;\n const errorMessage = `Daily log entry conversion failed at row ${i + 2}: ${error instanceof Error ? error.message : String(error)}`;\n\n // Log conversion errors to console for debugging\n console.warn(`[DailyLogProcessor] ${errorMessage}`);\n\n const validationError: ValidationError = {\n type: \"validation\",\n message: errorMessage,\n details: { row: parseResult.data[i], rowIndex: i + 2 },\n field: \"unknown\",\n value: parseResult.data[i],\n expected: \"Valid daily log entry data\",\n };\n errors.push(validationError);\n\n if (!this.config.strictValidation) {\n continue; // Skip invalid row in non-strict mode\n } else {\n throw error; // Fail fast in strict mode\n }\n }\n\n // Update progress\n if (i % 50 === 0 || i === parseResult.data.length - 1) {\n const progress = Math.round((i / parseResult.data.length) * 100);\n this.config.progressCallback({\n stage: \"converting\",\n progress,\n rowsProcessed: i + 1,\n totalRows: parseResult.data.length,\n errors: errors.length,\n validEntries,\n invalidEntries,\n });\n }\n }\n\n // Sort entries by date\n entries.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());\n\n // Calculate statistics\n const processingTimeMs = Date.now() - startTime;\n const dates = entries.map((e) => new Date(e.date));\n const dateRange = {\n start: dates.length > 0 ? new Date(Math.min(...dates.map((d) => d.getTime()))) : null,\n end: dates.length > 0 ? new Date(Math.max(...dates.map((d) => d.getTime()))) : null,\n };\n const finalPortfolioValue = entries.length > 0 ? entries[entries.length - 1].netLiquidity : 0;\n const maxDrawdown = entries.length > 0 ? Math.min(...entries.map((e) => e.drawdownPct)) : 0;\n const totalPL = entries.reduce((sum, e) => sum + e.dailyPl, 0);\n\n // Final progress update\n this.config.progressCallback({\n stage: \"completed\",\n progress: 100,\n rowsProcessed: parseResult.data.length,\n totalRows: parseResult.data.length,\n errors: errors.length,\n validEntries,\n invalidEntries,\n });\n\n return {\n entries,\n totalRows: parseResult.totalRows,\n validEntries,\n invalidEntries,\n errors,\n warnings,\n stats: {\n processingTimeMs,\n dateRange,\n finalPortfolioValue,\n maxDrawdown,\n totalPL,\n },\n };\n } catch (error) {\n const processingError: ProcessingError = {\n type: \"parsing\",\n message: `Daily log processing failed: ${error instanceof Error ? error.message : String(error)}`,\n details: { fileName: file.name, fileSize: file.size },\n };\n\n return {\n entries: [],\n totalRows: 0,\n validEntries: 0,\n invalidEntries: 0,\n errors: [processingError, ...errors],\n warnings,\n stats: {\n processingTimeMs: Date.now() - startTime,\n dateRange: { start: null, end: null },\n finalPortfolioValue: 0,\n maxDrawdown: 0,\n totalPL: 0,\n },\n };\n }\n }\n\n /**\n * Validate raw daily log data from CSV\n */\n private validateRawDailyLogData(\n row: Record<string, string>,\n rowIndex: number,\n ): Record<string, string> | null {\n try {\n // Set default values for missing optional fields\n const normalizedRow = { ...row };\n if (!normalizedRow[\"Withdrawn\"]) {\n normalizedRow[\"Withdrawn\"] = \"0\";\n }\n\n // Ensure required columns have values\n for (const field of REQUIRED_DAILY_LOG_COLUMNS) {\n if (!normalizedRow[field] || normalizedRow[field].trim() === \"\") {\n throw new Error(`Missing required field: ${field}`);\n }\n }\n\n // Basic format validation (detailed validation happens in conversion)\n rawDailyLogDataSchema.parse(normalizedRow);\n\n return normalizedRow;\n } catch (error) {\n // Log validation errors to console for debugging\n console.warn(\n `[DailyLogProcessor] Row ${rowIndex + 2} validation failed:`,\n error instanceof Error ? error.message : error,\n );\n // Return null for invalid rows - they'll be counted as invalid\n return null;\n }\n }\n\n /**\n * Parse a YYYY-MM-DD date string preserving the calendar date.\n * Same approach as trade-processor.ts for consistency.\n */\n private parseDatePreservingCalendarDay(dateStr: string): Date {\n const match = dateStr.match(/^(\\d{4})-(\\d{2})-(\\d{2})$/);\n if (match) {\n const [, year, month, day] = match;\n // Create date at midnight local time - this preserves the calendar date\n return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));\n }\n // Fall back to default parsing for other formats\n return new Date(dateStr);\n }\n\n /**\n * Convert validated CSV row to DailyLogEntry object\n */\n private convertToDailyLogEntry(rawData: Record<string, string>, blockId?: string): DailyLogEntry {\n try {\n // Parse date preserving calendar day (same as trade processor)\n const date = this.parseDatePreservingCalendarDay(rawData[\"Date\"]);\n if (isNaN(date.getTime())) {\n throw new Error(`Invalid Date: ${rawData[\"Date\"]}`);\n }\n\n // Parse numeric values with error handling\n const parseNumber = (\n value: string | undefined,\n fieldName: string,\n defaultValue?: number,\n ): number => {\n if (!value || value.trim() === \"\") {\n if (defaultValue !== undefined) return defaultValue;\n throw new Error(`Missing required numeric field: ${fieldName}`);\n }\n\n // Remove currency symbols, commas, and percentage signs\n const cleaned = value.replace(/[$,%]/g, \"\").trim();\n const parsed = parseFloat(cleaned);\n\n if (isNaN(parsed)) {\n throw new Error(`Invalid numeric value for ${fieldName}: ${value}`);\n }\n\n return parsed;\n };\n\n // Build daily log entry object\n const entry: DailyLogEntry = {\n date,\n netLiquidity: parseNumber(rawData[\"Net Liquidity\"], \"Net Liquidity\"),\n currentFunds: parseNumber(rawData[\"Current Funds\"], \"Current Funds\"),\n withdrawn: parseNumber(rawData[\"Withdrawn\"], \"Withdrawn\", 0),\n tradingFunds: parseNumber(rawData[\"Trading Funds\"], \"Trading Funds\"),\n dailyPl: parseNumber(rawData[\"P/L\"], \"P/L\"),\n dailyPlPct: parseNumber(rawData[\"P/L %\"], \"P/L %\"),\n drawdownPct: parseNumber(rawData[\"Drawdown %\"], \"Drawdown %\"),\n blockId,\n };\n\n // Keep percentage values as they are from CSV to match legacy behavior\n // Legacy Python expects percentage values (e.g., -5.55), not decimals (e.g., -0.0555)\n\n // Extract custom fields (columns not in KNOWN_DAILY_LOG_COLUMNS)\n const customFields: Record<string, number | string> = {};\n for (const [key, value] of Object.entries(rawData)) {\n if (!KNOWN_DAILY_LOG_COLUMNS.has(key) && value !== undefined && value.trim() !== \"\") {\n // Auto-detect type: try to parse as number\n const cleaned = value.replace(/[$,%]/g, \"\").trim();\n const parsed = parseFloat(cleaned);\n if (!isNaN(parsed) && isFinite(parsed)) {\n customFields[key] = parsed;\n } else {\n customFields[key] = value.trim();\n }\n }\n }\n\n // Only add customFields if there are any\n if (Object.keys(customFields).length > 0) {\n entry.customFields = customFields;\n }\n\n // Final validation with Zod schema\n const validatedEntry = dailyLogEntrySchema.parse(entry);\n return validatedEntry;\n } catch (error) {\n throw new Error(\n `Daily log entry conversion failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n /**\n * Process CSV content directly (for testing)\n */\n async processCSVContent(content: string, blockId?: string): Promise<DailyLogProcessingResult> {\n // Create a mock File object for testing\n const blob = new Blob([content], { type: \"text/csv\" });\n const file = new File([blob], \"test-daily.csv\", { type: \"text/csv\" });\n return this.processFile(file, blockId);\n }\n\n /**\n * Validate daily log data consistency\n */\n static validateDataConsistency(entries: DailyLogEntry[]): string[] {\n const warnings: string[] = [];\n\n if (entries.length === 0) return warnings;\n\n // Sort by date for chronological validation\n const sortedEntries = [...entries].sort(\n (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(),\n );\n\n // Check for gaps in dates (more than 7 days)\n for (let i = 1; i < sortedEntries.length; i++) {\n const prevDate = new Date(sortedEntries[i - 1].date);\n const currentDate = new Date(sortedEntries[i].date);\n const daysDiff = (currentDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24);\n\n if (daysDiff > 7) {\n warnings.push(\n `Large date gap detected: ${daysDiff.toFixed(0)} days between ${prevDate.toISOString().split(\"T\")[0]} and ${currentDate.toISOString().split(\"T\")[0]}`,\n );\n }\n }\n\n // Check for negative net liquidity\n const negativeEntries = sortedEntries.filter((entry) => entry.netLiquidity < 0);\n if (negativeEntries.length > 0) {\n warnings.push(`${negativeEntries.length} entries have negative net liquidity`);\n }\n\n // Check for extreme drawdowns (> 50%)\n const extremeDrawdowns = sortedEntries.filter((entry) => entry.drawdownPct < -0.5);\n if (extremeDrawdowns.length > 0) {\n warnings.push(`${extremeDrawdowns.length} entries have extreme drawdowns (> 50%)`);\n }\n\n return warnings;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -38,9 +38,7 @@ var CSVParser = class _CSVParser {
|
|
|
38
38
|
if (headers.length === 0) {
|
|
39
39
|
throw new Error("No valid headers found");
|
|
40
40
|
}
|
|
41
|
-
const cleanHeaders = headers.map(
|
|
42
|
-
(header) => header.replace(/^\ufeff/, "").trim()
|
|
43
|
-
);
|
|
41
|
+
const cleanHeaders = headers.map((header) => header.replace(/^\ufeff/, "").trim());
|
|
44
42
|
let validRows = 0;
|
|
45
43
|
let processedRows = 0;
|
|
46
44
|
for (let i = 1; i < lines.length && processedRows < this.config.maxRows; i++) {
|
|
@@ -262,8 +260,8 @@ var rawTradeDataSchema = z.object({
|
|
|
262
260
|
"Date Opened": z.string().min(1, "Date Opened is required"),
|
|
263
261
|
"Time Opened": z.string().min(1, "Time Opened is required"),
|
|
264
262
|
"Opening Price": z.string().min(1, "Opening Price is required"),
|
|
265
|
-
|
|
266
|
-
|
|
263
|
+
Legs: z.string().min(1, "Legs description is required"),
|
|
264
|
+
Premium: z.string().min(1, "Premium is required"),
|
|
267
265
|
"Closing Price": z.string().optional(),
|
|
268
266
|
"Date Closed": z.string().optional(),
|
|
269
267
|
"Time Closed": z.string().optional(),
|
|
@@ -273,15 +271,15 @@ var rawTradeDataSchema = z.object({
|
|
|
273
271
|
"No. of Contracts": z.string().min(1, "Number of Contracts is required"),
|
|
274
272
|
"Funds at Close": z.string().min(1, "Funds at Close is required"),
|
|
275
273
|
"Margin Req.": z.string().min(1, "Margin Requirement is required"),
|
|
276
|
-
|
|
274
|
+
Strategy: z.string().min(1, "Strategy is required"),
|
|
277
275
|
"Opening Commissions + Fees": z.string().default("0"),
|
|
278
276
|
"Closing Commissions + Fees": z.string().optional(),
|
|
279
277
|
"Opening Short/Long Ratio": z.string().default("0"),
|
|
280
278
|
"Closing Short/Long Ratio": z.string().optional(),
|
|
281
279
|
"Opening VIX": z.string().optional(),
|
|
282
280
|
"Closing VIX": z.string().optional(),
|
|
283
|
-
|
|
284
|
-
|
|
281
|
+
Gap: z.string().optional(),
|
|
282
|
+
Movement: z.string().optional(),
|
|
285
283
|
"Max Profit": z.string().optional(),
|
|
286
284
|
"Max Loss": z.string().optional()
|
|
287
285
|
}).passthrough();
|
|
@@ -316,11 +314,11 @@ var tradeSchema = z.object({
|
|
|
316
314
|
customFields: z.record(z.string(), z.union([z.number(), z.string()])).optional()
|
|
317
315
|
});
|
|
318
316
|
var rawReportingTradeDataSchema = z.object({
|
|
319
|
-
|
|
317
|
+
Strategy: z.string().min(1, "Strategy is required"),
|
|
320
318
|
"Date Opened": z.string().min(1, "Date Opened is required"),
|
|
321
319
|
"Time Opened": z.string().optional(),
|
|
322
320
|
"Opening Price": z.string().min(1, "Opening Price is required"),
|
|
323
|
-
|
|
321
|
+
Legs: z.string().min(1, "Legs description is required"),
|
|
324
322
|
"Initial Premium": z.string().min(1, "Initial Premium is required"),
|
|
325
323
|
"No. of Contracts": z.string().min(1, "Number of Contracts is required"),
|
|
326
324
|
"P/L": z.string().min(1, "P/L is required"),
|
|
@@ -346,10 +344,10 @@ var reportingTradeSchema = z.object({
|
|
|
346
344
|
reasonForClose: z.string().optional()
|
|
347
345
|
});
|
|
348
346
|
var rawDailyLogDataSchema = z.object({
|
|
349
|
-
|
|
347
|
+
Date: z.string().min(1, "Date is required"),
|
|
350
348
|
"Net Liquidity": z.string().min(1, "Net Liquidity is required"),
|
|
351
349
|
"Current Funds": z.string().min(1, "Current Funds is required"),
|
|
352
|
-
|
|
350
|
+
Withdrawn: z.string().default("0"),
|
|
353
351
|
"Trading Funds": z.string().min(1, "Trading Funds is required"),
|
|
354
352
|
"P/L": z.string().min(1, "P/L is required"),
|
|
355
353
|
"P/L %": z.string().min(1, "P/L % is required"),
|
|
@@ -441,4 +439,4 @@ export {
|
|
|
441
439
|
normalizeHeaders,
|
|
442
440
|
findMissingHeaders
|
|
443
441
|
};
|
|
444
|
-
//# sourceMappingURL=chunk-
|
|
442
|
+
//# sourceMappingURL=chunk-OBYKFW2B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../lib/processing/csv-parser.ts","../../lib/models/validators.ts","../../lib/utils/csv-headers.ts"],"sourcesContent":["/**\n * CSV Parser Service\n *\n * Handles CSV file parsing with progress tracking, error handling,\n * and validation for TradeBlocks data.\n */\n\nimport type { ParsingError } from \"../models/index.ts\";\n// import { ProcessingError } from '../models/index.ts'\n\n/**\n * CSV parsing configuration\n */\nexport interface CSVParseConfig {\n delimiter?: string;\n quote?: string;\n escape?: string;\n skipEmptyLines?: boolean;\n trimValues?: boolean;\n maxRows?: number;\n progressCallback?: (progress: number, rowsProcessed: number) => void;\n}\n\n/**\n * CSV parsing result\n */\nexport interface CSVParseResult<T = Record<string, string>> {\n data: T[];\n headers: string[];\n totalRows: number;\n validRows: number;\n errors: ParsingError[];\n warnings: string[];\n}\n\n/**\n * CSV parsing progress info\n */\nexport interface ParseProgress {\n stage: \"reading\" | \"parsing\" | \"validating\" | \"converting\" | \"completed\";\n progress: number; // 0-100\n rowsProcessed: number;\n totalRows: number;\n errors: number;\n}\n\n/**\n * Base CSV parser class with streaming support for large files\n */\nexport class CSVParser {\n private config: Required<CSVParseConfig>;\n\n constructor(config: CSVParseConfig = {}) {\n this.config = {\n delimiter: \",\",\n quote: '\"',\n escape: '\"',\n skipEmptyLines: true,\n trimValues: true,\n maxRows: 100000, // Safety limit\n progressCallback: () => {},\n ...config,\n };\n }\n\n /**\n * Parse CSV file content\n */\n async parseFile<T = Record<string, string>>(\n fileContent: string,\n validator?: (row: Record<string, string>, rowIndex: number) => T | null,\n ): Promise<CSVParseResult<T>> {\n const errors: ParsingError[] = [];\n const warnings: string[] = [];\n const data: T[] = [];\n\n try {\n // Split into lines and handle different line endings\n const lines = fileContent.split(/\\r?\\n/);\n const totalRows = lines.length;\n\n if (totalRows === 0) {\n throw new Error(\"Empty CSV file\");\n }\n\n // Parse headers\n const headerLine = lines[0];\n if (!headerLine || headerLine.trim() === \"\") {\n throw new Error(\"Missing CSV headers\");\n }\n\n const headers = this.parseLine(headerLine);\n if (headers.length === 0) {\n throw new Error(\"No valid headers found\");\n }\n\n // Clean headers (remove BOM, trim whitespace)\n const cleanHeaders = headers.map((header) => header.replace(/^\\ufeff/, \"\").trim());\n\n let validRows = 0;\n let processedRows = 0;\n\n // Process data rows\n for (let i = 1; i < lines.length && processedRows < this.config.maxRows; i++) {\n const line = lines[i];\n\n // Skip empty lines if configured\n if (this.config.skipEmptyLines && (!line || line.trim() === \"\")) {\n continue;\n }\n\n processedRows++;\n\n try {\n const values = this.parseLine(line);\n\n // Create row object\n const row: Record<string, string> = {};\n cleanHeaders.forEach((header, index) => {\n const value = values[index] || \"\";\n row[header] = this.config.trimValues ? value.trim() : value;\n });\n\n // Validate row if validator provided\n if (validator) {\n const validatedRow = validator(row, i);\n if (validatedRow) {\n data.push(validatedRow);\n validRows++;\n }\n } else {\n data.push(row as T);\n validRows++;\n }\n } catch (error) {\n const parsingError: ParsingError = {\n type: \"parsing\",\n message: `Error parsing line ${i + 1}: ${error instanceof Error ? error.message : String(error)}`,\n details: { lineNumber: i + 1, line },\n line: i + 1,\n raw: line,\n };\n errors.push(parsingError);\n }\n\n // Report progress\n if (processedRows % 100 === 0 || processedRows === totalRows - 1) {\n const progress = Math.round((processedRows / (totalRows - 1)) * 100);\n this.config.progressCallback(progress, processedRows);\n }\n }\n\n // Check for truncation\n if (processedRows >= this.config.maxRows && lines.length > this.config.maxRows + 1) {\n warnings.push(`File truncated at ${this.config.maxRows} rows for performance`);\n }\n\n return {\n data,\n headers: cleanHeaders,\n totalRows: totalRows - 1, // Excluding header\n validRows,\n errors,\n warnings,\n };\n } catch (error) {\n const parsingError: ParsingError = {\n type: \"parsing\",\n message: `CSV parsing failed: ${error instanceof Error ? error.message : String(error)}`,\n details: { fileContent: fileContent.substring(0, 500) + \"...\" },\n line: 0,\n raw: \"\",\n };\n\n return {\n data: [],\n headers: [],\n totalRows: 0,\n validRows: 0,\n errors: [parsingError],\n warnings,\n };\n }\n }\n\n /**\n * Parse CSV from File object with progress tracking\n */\n async parseFileObject<T = Record<string, string>>(\n file: File,\n validator?: (row: Record<string, string>, rowIndex: number) => T | null,\n progressCallback?: (progress: ParseProgress) => void,\n ): Promise<CSVParseResult<T>> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n\n reader.onprogress = (event) => {\n if (event.lengthComputable && progressCallback) {\n const progress = Math.round((event.loaded / event.total) * 100);\n progressCallback({\n stage: \"reading\",\n progress,\n rowsProcessed: 0,\n totalRows: 0,\n errors: 0,\n });\n }\n };\n\n reader.onload = async (event) => {\n try {\n const content = event.target?.result as string;\n if (!content) {\n throw new Error(\"Failed to read file content\");\n }\n\n // Update progress callback for parsing stage\n const parseConfig = {\n ...this.config,\n progressCallback: (progress: number, rowsProcessed: number) => {\n if (progressCallback) {\n progressCallback({\n stage: \"parsing\",\n progress,\n rowsProcessed,\n totalRows: content.split(/\\r?\\n/).length - 1,\n errors: 0,\n });\n }\n },\n };\n\n const parser = new CSVParser(parseConfig);\n const result = await parser.parseFile(content, validator);\n\n if (progressCallback) {\n progressCallback({\n stage: \"completed\",\n progress: 100,\n rowsProcessed: result.validRows,\n totalRows: result.totalRows,\n errors: result.errors.length,\n });\n }\n\n resolve(result);\n } catch (error) {\n reject(error);\n }\n };\n\n reader.onerror = () => {\n reject(new Error(\"Failed to read file\"));\n };\n\n reader.readAsText(file);\n });\n }\n\n /**\n * Parse a single CSV line, handling quoted values and escapes\n */\n private parseLine(line: string): string[] {\n const result: string[] = [];\n const { delimiter, quote, escape } = this.config;\n\n let current = \"\";\n let inQuotes = false;\n let i = 0;\n\n while (i < line.length) {\n const char = line[i];\n const nextChar = line[i + 1];\n\n if (!inQuotes) {\n if (char === delimiter) {\n result.push(current);\n current = \"\";\n } else if (char === quote) {\n inQuotes = true;\n } else {\n current += char;\n }\n } else {\n if (char === escape && nextChar === quote) {\n // Escaped quote\n current += quote;\n i++; // Skip next character\n } else if (char === quote) {\n inQuotes = false;\n } else {\n current += char;\n }\n }\n\n i++;\n }\n\n // Add the last field\n result.push(current);\n\n return result;\n }\n\n /**\n * Validate CSV file format before parsing\n */\n static validateCSVFile(file: File): { valid: boolean; error?: string } {\n // Check file type\n const validTypes = [\"text/csv\", \"application/vnd.ms-excel\", \"text/plain\"];\n if (!validTypes.includes(file.type) && !file.name.toLowerCase().endsWith(\".csv\")) {\n return { valid: false, error: \"File must be a CSV file (.csv extension)\" };\n }\n\n // Check file size (50MB limit)\n const maxSize = 50 * 1024 * 1024;\n if (file.size > maxSize) {\n return { valid: false, error: \"File size must be less than 50MB\" };\n }\n\n // Check for empty file\n if (file.size === 0) {\n return { valid: false, error: \"File is empty\" };\n }\n\n return { valid: true };\n }\n\n /**\n * Detect CSV delimiter from sample content\n */\n static detectDelimiter(sampleContent: string): string {\n const delimiters = [\",\", \";\", \"\\t\", \"|\"];\n const lines = sampleContent.split(/\\r?\\n/).slice(0, 5); // Check first 5 lines\n\n let bestDelimiter = \",\";\n let maxScore = 0;\n\n for (const delimiter of delimiters) {\n let score = 0;\n let consistentCounts = true;\n let expectedCount = -1;\n\n for (const line of lines) {\n if (!line.trim()) continue;\n\n const count = line.split(delimiter).length - 1;\n if (expectedCount === -1) {\n expectedCount = count;\n } else if (count !== expectedCount) {\n consistentCounts = false;\n break;\n }\n score += count;\n }\n\n if (consistentCounts && score > maxScore) {\n maxScore = score;\n bestDelimiter = delimiter;\n }\n }\n\n return bestDelimiter;\n }\n}\n","import { z } from \"zod\";\n\n/**\n * Zod schema for validating raw trade data from CSV\n */\nexport const rawTradeDataSchema = z\n .object({\n \"Date Opened\": z.string().min(1, \"Date Opened is required\"),\n \"Time Opened\": z.string().min(1, \"Time Opened is required\"),\n \"Opening Price\": z.string().min(1, \"Opening Price is required\"),\n Legs: z.string().min(1, \"Legs description is required\"),\n Premium: z.string().min(1, \"Premium is required\"),\n \"Closing Price\": z.string().optional(),\n \"Date Closed\": z.string().optional(),\n \"Time Closed\": z.string().optional(),\n \"Avg. Closing Cost\": z.string().optional(),\n \"Reason For Close\": z.string().optional(),\n \"P/L\": z.string().min(1, \"P/L is required\"),\n \"No. of Contracts\": z.string().min(1, \"Number of Contracts is required\"),\n \"Funds at Close\": z.string().min(1, \"Funds at Close is required\"),\n \"Margin Req.\": z.string().min(1, \"Margin Requirement is required\"),\n Strategy: z.string().min(1, \"Strategy is required\"),\n \"Opening Commissions + Fees\": z.string().default(\"0\"),\n \"Closing Commissions + Fees\": z.string().optional(),\n \"Opening Short/Long Ratio\": z.string().default(\"0\"),\n \"Closing Short/Long Ratio\": z.string().optional(),\n \"Opening VIX\": z.string().optional(),\n \"Closing VIX\": z.string().optional(),\n Gap: z.string().optional(),\n Movement: z.string().optional(),\n \"Max Profit\": z.string().optional(),\n \"Max Loss\": z.string().optional(),\n })\n .passthrough(); // Allow custom columns to pass through validation\n\n/**\n * Zod schema for validating processed trade data\n */\nexport const tradeSchema = z.object({\n dateOpened: z.date(),\n timeOpened: z\n .string()\n .regex(/^\\d{1,2}:\\d{2}:\\d{2}$/, \"Time must be in H:mm:ss or HH:mm:ss format\"),\n openingPrice: z.number().finite(),\n legs: z.string().min(1),\n premium: z.number().finite(),\n premiumPrecision: z.enum([\"dollars\", \"cents\"]).optional(),\n closingPrice: z.number().finite().optional(),\n dateClosed: z.date().optional(),\n timeClosed: z\n .string()\n .regex(/^\\d{1,2}:\\d{2}:\\d{2}$/)\n .optional(),\n avgClosingCost: z.number().finite().optional(),\n reasonForClose: z.string().optional(),\n pl: z.number().finite(),\n numContracts: z.number().int().positive(),\n fundsAtClose: z.number().finite(),\n marginReq: z.number().finite().min(0),\n strategy: z.string().min(1),\n openingCommissionsFees: z.number().finite().min(0),\n closingCommissionsFees: z.number().finite().min(0),\n openingShortLongRatio: z.number().finite().min(0),\n closingShortLongRatio: z.number().finite().min(0).optional(),\n openingVix: z.number().finite().min(0).optional(),\n closingVix: z.number().finite().min(0).optional(),\n gap: z.number().finite().optional(),\n movement: z.number().finite().optional(),\n maxProfit: z.number().finite().optional(),\n maxLoss: z.number().finite().optional(),\n syntheticCapitalRatio: z.number().finite().optional(),\n customFields: z.record(z.string(), z.union([z.number(), z.string()])).optional(),\n});\n\n/**\n * Zod schema for validating raw reporting trade data from strategy logs\n */\nexport const rawReportingTradeDataSchema = z.object({\n Strategy: z.string().min(1, \"Strategy is required\"),\n \"Date Opened\": z.string().min(1, \"Date Opened is required\"),\n \"Time Opened\": z.string().optional(),\n \"Opening Price\": z.string().min(1, \"Opening Price is required\"),\n Legs: z.string().min(1, \"Legs description is required\"),\n \"Initial Premium\": z.string().min(1, \"Initial Premium is required\"),\n \"No. of Contracts\": z.string().min(1, \"Number of Contracts is required\"),\n \"P/L\": z.string().min(1, \"P/L is required\"),\n \"Closing Price\": z.string().optional(),\n \"Date Closed\": z.string().optional(),\n \"Time Closed\": z.string().optional(),\n \"Avg. Closing Cost\": z.string().optional(),\n \"Reason For Close\": z.string().optional(),\n});\n\n/**\n * Zod schema for validating processed reporting trade data\n */\nexport const reportingTradeSchema = z.object({\n strategy: z.string().min(1),\n dateOpened: z.date(),\n timeOpened: z.string().optional(),\n openingPrice: z.number().finite(),\n legs: z.string().min(1),\n initialPremium: z.number().finite(),\n numContracts: z.number().finite(),\n pl: z.number().finite(),\n closingPrice: z.number().finite().optional(),\n dateClosed: z.date().optional(),\n timeClosed: z.string().optional(),\n avgClosingCost: z.number().finite().optional(),\n reasonForClose: z.string().optional(),\n});\n\n/**\n * Zod schema for validating raw daily log data from CSV\n */\nexport const rawDailyLogDataSchema = z\n .object({\n Date: z.string().min(1, \"Date is required\"),\n \"Net Liquidity\": z.string().min(1, \"Net Liquidity is required\"),\n \"Current Funds\": z.string().min(1, \"Current Funds is required\"),\n Withdrawn: z.string().default(\"0\"),\n \"Trading Funds\": z.string().min(1, \"Trading Funds is required\"),\n \"P/L\": z.string().min(1, \"P/L is required\"),\n \"P/L %\": z.string().min(1, \"P/L % is required\"),\n \"Drawdown %\": z.string().min(1, \"Drawdown % is required\"),\n })\n .passthrough(); // Allow custom columns to pass through validation\n\n/**\n * Zod schema for validating processed daily log entry\n */\nexport const dailyLogEntrySchema = z.object({\n date: z.date(),\n netLiquidity: z.number().finite(),\n currentFunds: z.number().finite(),\n withdrawn: z.number().finite().min(0),\n tradingFunds: z.number().finite().min(0),\n dailyPl: z.number().finite(),\n dailyPlPct: z.number().finite(),\n drawdownPct: z.number().finite().max(0), // Drawdown should be negative or zero\n blockId: z.string().optional(),\n customFields: z.record(z.string(), z.union([z.number(), z.string()])).optional(),\n});\n\n/**\n * Zod schema for portfolio statistics\n */\nexport const portfolioStatsSchema = z.object({\n totalTrades: z.number().int().min(0),\n totalPl: z.number().finite(),\n winRate: z.number().min(0).max(1),\n avgWin: z.number().finite().min(0),\n avgLoss: z.number().finite().max(0),\n maxWin: z.number().finite().min(0),\n maxLoss: z.number().finite().max(0),\n sharpeRatio: z.number().finite().optional(),\n maxDrawdown: z.number().finite().max(0),\n avgDailyPl: z.number().finite(),\n totalCommissions: z.number().finite().min(0),\n netPl: z.number().finite(),\n profitFactor: z.number().finite().min(0),\n});\n\n/**\n * Zod schema for strategy statistics\n */\nexport const strategyStatsSchema = z.object({\n strategyName: z.string().min(1),\n tradeCount: z.number().int().min(0),\n totalPl: z.number().finite(),\n winRate: z.number().min(0).max(1),\n avgWin: z.number().finite().min(0),\n avgLoss: z.number().finite().max(0),\n maxWin: z.number().finite().min(0),\n maxLoss: z.number().finite().max(0),\n avgDte: z.number().finite().min(0).optional(),\n successRate: z.number().min(0).max(1),\n profitFactor: z.number().finite().min(0),\n});\n\n/**\n * Zod schema for analysis configuration\n */\nexport const analysisConfigSchema = z.object({\n useBusinessDaysOnly: z.boolean(),\n annualizationFactor: z.number().int().min(200).max(365),\n confidenceLevel: z.number().min(0.8).max(0.99),\n});\n\n/**\n * Zod schema for file validation\n */\nexport const fileSchema = z.object({\n name: z.string().min(1),\n size: z.number().int().positive(),\n type: z\n .string()\n .refine(\n (type) => type === \"text/csv\" || type === \"application/vnd.ms-excel\",\n \"File must be a CSV file\",\n ),\n});\n\n/**\n * Zod schema for block creation request\n */\nexport const createBlockRequestSchema = z.object({\n name: z.string().min(1).max(100),\n description: z.string().max(500).optional(),\n analysisConfig: analysisConfigSchema.partial().optional(),\n});\n\n/**\n * Type exports for use with TypeScript\n */\nexport type RawTradeData = z.infer<typeof rawTradeDataSchema>;\nexport type ValidatedTrade = z.infer<typeof tradeSchema>;\nexport type RawReportingTradeData = z.infer<typeof rawReportingTradeDataSchema>;\nexport type ValidatedReportingTrade = z.infer<typeof reportingTradeSchema>;\nexport type RawDailyLogData = z.infer<typeof rawDailyLogDataSchema>;\nexport type ValidatedDailyLogEntry = z.infer<typeof dailyLogEntrySchema>;\nexport type ValidatedPortfolioStats = z.infer<typeof portfolioStatsSchema>;\nexport type ValidatedStrategyStats = z.infer<typeof strategyStatsSchema>;\nexport type ValidatedAnalysisConfig = z.infer<typeof analysisConfigSchema>;\nexport type ValidatedFile = z.infer<typeof fileSchema>;\nexport type ValidatedCreateBlockRequest = z.infer<typeof createBlockRequestSchema>;\n","/**\n * CSV header utilities\n */\n\nexport interface HeaderValidationOptions {\n /** Optional map of alternate header names to canonical names */\n aliases?: Record<string, string> | Readonly<Record<string, string>>;\n /** Human-readable label used in error messages */\n contextLabel?: string;\n}\n\nconst BOM = \"\\uFEFF\";\n\n/**\n * Remove UTF-8 byte order mark from a string if present\n */\nexport function stripBom(value: string): string {\n return value.startsWith(BOM) ? value.slice(1) : value;\n}\n\n/**\n * Parse a single CSV line into values, handling quoted fields and commas\n */\nexport function parseCsvLine(line: string): string[] {\n const result: string[] = [];\n let current = \"\";\n let inQuotes = false;\n\n for (let i = 0; i < line.length; i++) {\n const char = line[i];\n\n if (char === '\"') {\n const nextChar = line[i + 1];\n if (inQuotes && nextChar === '\"') {\n // Escaped quote inside quoted value\n current += '\"';\n i++;\n } else {\n inQuotes = !inQuotes;\n }\n continue;\n }\n\n if (char === \",\" && !inQuotes) {\n result.push(current.trim());\n current = \"\";\n } else {\n current += char;\n }\n }\n\n result.push(current.trim());\n\n if (result.length > 0) {\n result[0] = stripBom(result[0]);\n }\n\n return result;\n}\n\n/**\n * Normalize a CSV header by trimming whitespace, stripping BOM, and applying aliases\n */\nexport function normalizeHeader(\n header: string,\n aliases?: Record<string, string> | Readonly<Record<string, string>>,\n): string {\n const trimmed = stripBom(header).trim();\n return aliases?.[trimmed] ?? trimmed;\n}\n\n/**\n * Normalize an array of headers\n */\nexport function normalizeHeaders(\n headers: string[],\n aliases?: Record<string, string> | Readonly<Record<string, string>>,\n): string[] {\n return headers.map((header) => normalizeHeader(header, aliases));\n}\n\n/**\n * Validate that required headers are present. Returns the missing headers without throwing.\n */\nexport function findMissingHeaders(headers: string[], required: readonly string[]): string[] {\n const headerSet = new Set(headers);\n return required.filter((requiredHeader) => !headerSet.has(requiredHeader));\n}\n\n/**\n * Ensure required headers are present, throwing an Error with a helpful message when missing.\n */\nexport function assertRequiredHeaders(\n headers: string[],\n required: readonly string[],\n options: HeaderValidationOptions = {},\n): void {\n const missing = findMissingHeaders(headers, required);\n if (missing.length === 0) {\n return;\n }\n\n const label = options.contextLabel ? `${options.contextLabel} ` : \"\";\n throw new Error(`Missing required ${label}columns: ${missing.join(\", \")}`);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -9,16 +9,18 @@ import * as protoLoader from "@grpc/proto-loader";
|
|
|
9
9
|
import protobuf from "protobufjs";
|
|
10
10
|
var moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
11
11
|
function protoCandidates() {
|
|
12
|
-
return Array.from(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
return Array.from(
|
|
13
|
+
/* @__PURE__ */ new Set([
|
|
14
|
+
resolve(moduleDir, "mdds.proto"),
|
|
15
|
+
resolve(moduleDir, "../../../mdds.proto"),
|
|
16
|
+
resolve(moduleDir, "../../../../server/mdds.proto"),
|
|
17
|
+
resolve(moduleDir, "../../../../dist/mdds.proto"),
|
|
18
|
+
resolve(process.cwd(), "packages/mcp-server/src/utils/providers/thetadata/mdds.proto"),
|
|
19
|
+
resolve(process.cwd(), "src/utils/providers/thetadata/mdds.proto"),
|
|
20
|
+
resolve(process.cwd(), "server/mdds.proto"),
|
|
21
|
+
resolve(process.cwd(), "dist/mdds.proto")
|
|
22
|
+
])
|
|
23
|
+
);
|
|
22
24
|
}
|
|
23
25
|
function resolveMddsProtoPath() {
|
|
24
26
|
const candidates = protoCandidates();
|
|
@@ -53,4 +55,4 @@ export {
|
|
|
53
55
|
loadMddsGrpcPackage,
|
|
54
56
|
loadMddsProtoRoot
|
|
55
57
|
};
|
|
56
|
-
//# sourceMappingURL=chunk-
|
|
58
|
+
//# sourceMappingURL=chunk-T66KH2XH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/providers/thetadata/proto.ts"],"sourcesContent":["import { existsSync } from \"fs\";\nimport { dirname, resolve } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport * as grpc from \"@grpc/grpc-js\";\nimport * as protoLoader from \"@grpc/proto-loader\";\nimport protobuf from \"protobufjs\";\nimport type { Root } from \"protobufjs\";\n\nconst moduleDir = dirname(fileURLToPath(import.meta.url));\n\nfunction protoCandidates(): string[] {\n return Array.from(\n new Set([\n resolve(moduleDir, \"mdds.proto\"),\n resolve(moduleDir, \"../../../mdds.proto\"),\n resolve(moduleDir, \"../../../../server/mdds.proto\"),\n resolve(moduleDir, \"../../../../dist/mdds.proto\"),\n resolve(process.cwd(), \"packages/mcp-server/src/utils/providers/thetadata/mdds.proto\"),\n resolve(process.cwd(), \"src/utils/providers/thetadata/mdds.proto\"),\n resolve(process.cwd(), \"server/mdds.proto\"),\n resolve(process.cwd(), \"dist/mdds.proto\"),\n ]),\n );\n}\n\nexport function resolveMddsProtoPath(): string {\n const candidates = protoCandidates();\n const found = candidates.find((candidate) => existsSync(candidate));\n if (!found) {\n throw new Error(`ThetaData MDDS proto not found. Checked: ${candidates.join(\", \")}`);\n }\n return found;\n}\n\nlet grpcPackage: unknown | null = null;\nlet protobufRoot: Root | null = null;\n\nexport function loadMddsGrpcPackage(): unknown {\n if (!grpcPackage) {\n const definition = protoLoader.loadSync(resolveMddsProtoPath(), {\n longs: Number,\n enums: String,\n defaults: false,\n oneofs: true,\n bytes: Buffer,\n });\n grpcPackage = grpc.loadPackageDefinition(definition);\n }\n return grpcPackage;\n}\n\nexport function loadMddsProtoRoot(): Root {\n if (!protobufRoot) protobufRoot = protobuf.loadSync(resolveMddsProtoPath());\n return protobufRoot;\n}\n"],"mappings":";;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,SAAS,eAAe;AACjC,SAAS,qBAAqB;AAC9B,YAAY,UAAU;AACtB,YAAY,iBAAiB;AAC7B,OAAO,cAAc;AAGrB,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAExD,SAAS,kBAA4B;AACnC,SAAO,MAAM;AAAA,IACX,oBAAI,IAAI;AAAA,MACN,QAAQ,WAAW,YAAY;AAAA,MAC/B,QAAQ,WAAW,qBAAqB;AAAA,MACxC,QAAQ,WAAW,+BAA+B;AAAA,MAClD,QAAQ,WAAW,6BAA6B;AAAA,MAChD,QAAQ,QAAQ,IAAI,GAAG,8DAA8D;AAAA,MACrF,QAAQ,QAAQ,IAAI,GAAG,0CAA0C;AAAA,MACjE,QAAQ,QAAQ,IAAI,GAAG,mBAAmB;AAAA,MAC1C,QAAQ,QAAQ,IAAI,GAAG,iBAAiB;AAAA,IAC1C,CAAC;AAAA,EACH;AACF;AAEO,SAAS,uBAA+B;AAC7C,QAAM,aAAa,gBAAgB;AACnC,QAAM,QAAQ,WAAW,KAAK,CAAC,cAAc,WAAW,SAAS,CAAC;AAClE,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,4CAA4C,WAAW,KAAK,IAAI,CAAC,EAAE;AAAA,EACrF;AACA,SAAO;AACT;AAEA,IAAI,cAA8B;AAClC,IAAI,eAA4B;AAEzB,SAAS,sBAA+B;AAC7C,MAAI,CAAC,aAAa;AAChB,UAAM,aAAyB,qBAAS,qBAAqB,GAAG;AAAA,MAC9D,OAAO;AAAA,MACP,OAAO;AAAA,MACP,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,OAAO;AAAA,IACT,CAAC;AACD,kBAAmB,2BAAsB,UAAU;AAAA,EACrD;AACA,SAAO;AACT;AAEO,SAAS,oBAA0B;AACxC,MAAI,CAAC,aAAc,gBAAe,SAAS,SAAS,qBAAqB,CAAC;AAC1E,SAAO;AACT;","names":[]}
|
|
@@ -256,12 +256,7 @@ async function getTradesByDateRange(blockId, startDate, endDate) {
|
|
|
256
256
|
return withReadTransaction(STORES.TRADES, async (transaction) => {
|
|
257
257
|
const store = transaction.objectStore(STORES.TRADES);
|
|
258
258
|
const index = store.index("composite_block_date");
|
|
259
|
-
const range = IDBKeyRange.bound(
|
|
260
|
-
[blockId, startDate],
|
|
261
|
-
[blockId, endDate],
|
|
262
|
-
false,
|
|
263
|
-
false
|
|
264
|
-
);
|
|
259
|
+
const range = IDBKeyRange.bound([blockId, startDate], [blockId, endDate], false, false);
|
|
265
260
|
const result = await promisifyRequest(index.getAll(range));
|
|
266
261
|
return result;
|
|
267
262
|
});
|
|
@@ -358,9 +353,7 @@ async function getTradeStatistics(blockId) {
|
|
|
358
353
|
const dates = trades.map((trade) => new Date(trade.dateOpened));
|
|
359
354
|
const start = new Date(Math.min(...dates.map((d) => d.getTime())));
|
|
360
355
|
const end = new Date(Math.max(...dates.map((d) => d.getTime())));
|
|
361
|
-
const strategies = Array.from(
|
|
362
|
-
new Set(trades.map((trade) => trade.strategy))
|
|
363
|
-
).sort();
|
|
356
|
+
const strategies = Array.from(new Set(trades.map((trade) => trade.strategy))).sort();
|
|
364
357
|
return {
|
|
365
358
|
totalTrades,
|
|
366
359
|
totalPl,
|
|
@@ -519,11 +512,9 @@ async function initializeDatabase() {
|
|
|
519
512
|
unique: false
|
|
520
513
|
});
|
|
521
514
|
tradesStore.createIndex("pl", "pl", { unique: false });
|
|
522
|
-
tradesStore.createIndex(
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
{ unique: false }
|
|
526
|
-
);
|
|
515
|
+
tradesStore.createIndex("composite_block_date", ["blockId", "dateOpened"], {
|
|
516
|
+
unique: false
|
|
517
|
+
});
|
|
527
518
|
}
|
|
528
519
|
if (!db.objectStoreNames.contains(STORES.DAILY_LOGS)) {
|
|
529
520
|
const dailyLogsStore = db.createObjectStore(STORES.DAILY_LOGS, {
|
|
@@ -535,11 +526,7 @@ async function initializeDatabase() {
|
|
|
535
526
|
dailyLogsStore.createIndex(INDEXES.DAILY_LOGS_BY_DATE, "date", {
|
|
536
527
|
unique: false
|
|
537
528
|
});
|
|
538
|
-
dailyLogsStore.createIndex(
|
|
539
|
-
"composite_block_date",
|
|
540
|
-
["blockId", "date"],
|
|
541
|
-
{ unique: false }
|
|
542
|
-
);
|
|
529
|
+
dailyLogsStore.createIndex("composite_block_date", ["blockId", "date"], { unique: false });
|
|
543
530
|
}
|
|
544
531
|
if (!db.objectStoreNames.contains(STORES.REPORTING_LOGS)) {
|
|
545
532
|
const reportingStore = db.createObjectStore(STORES.REPORTING_LOGS, {
|
|
@@ -548,26 +535,18 @@ async function initializeDatabase() {
|
|
|
548
535
|
reportingStore.createIndex(INDEXES.REPORTING_LOGS_BY_BLOCK, "blockId", {
|
|
549
536
|
unique: false
|
|
550
537
|
});
|
|
551
|
-
reportingStore.createIndex(
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
"composite_block_date",
|
|
558
|
-
["blockId", "dateOpened"],
|
|
559
|
-
{ unique: false }
|
|
560
|
-
);
|
|
538
|
+
reportingStore.createIndex(INDEXES.REPORTING_LOGS_BY_STRATEGY, "strategy", {
|
|
539
|
+
unique: false
|
|
540
|
+
});
|
|
541
|
+
reportingStore.createIndex("composite_block_date", ["blockId", "dateOpened"], {
|
|
542
|
+
unique: false
|
|
543
|
+
});
|
|
561
544
|
}
|
|
562
545
|
if (!db.objectStoreNames.contains(STORES.CALCULATIONS)) {
|
|
563
546
|
const calculationsStore = db.createObjectStore(STORES.CALCULATIONS, {
|
|
564
547
|
keyPath: "id"
|
|
565
548
|
});
|
|
566
|
-
calculationsStore.createIndex(
|
|
567
|
-
INDEXES.CALCULATIONS_BY_BLOCK,
|
|
568
|
-
"blockId",
|
|
569
|
-
{ unique: false }
|
|
570
|
-
);
|
|
549
|
+
calculationsStore.createIndex(INDEXES.CALCULATIONS_BY_BLOCK, "blockId", { unique: false });
|
|
571
550
|
calculationsStore.createIndex("calculationType", "calculationType", {
|
|
572
551
|
unique: false
|
|
573
552
|
});
|
|
@@ -595,16 +574,12 @@ async function initializeDatabase() {
|
|
|
595
574
|
const staticDatasetRowsStore = db.createObjectStore(STORES.STATIC_DATASET_ROWS, {
|
|
596
575
|
autoIncrement: true
|
|
597
576
|
});
|
|
598
|
-
staticDatasetRowsStore.createIndex(
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
INDEXES.STATIC_DATASET_ROWS_BY_TIMESTAMP,
|
|
605
|
-
"timestamp",
|
|
606
|
-
{ unique: false }
|
|
607
|
-
);
|
|
577
|
+
staticDatasetRowsStore.createIndex(INDEXES.STATIC_DATASET_ROWS_BY_DATASET, "datasetId", {
|
|
578
|
+
unique: false
|
|
579
|
+
});
|
|
580
|
+
staticDatasetRowsStore.createIndex(INDEXES.STATIC_DATASET_ROWS_BY_TIMESTAMP, "timestamp", {
|
|
581
|
+
unique: false
|
|
582
|
+
});
|
|
608
583
|
staticDatasetRowsStore.createIndex(
|
|
609
584
|
"composite_dataset_timestamp",
|
|
610
585
|
["datasetId", "timestamp"],
|
|
@@ -671,12 +646,7 @@ async function getDailyLogsByDateRange(blockId, startDate, endDate) {
|
|
|
671
646
|
return withReadTransaction(STORES.DAILY_LOGS, async (transaction) => {
|
|
672
647
|
const store = transaction.objectStore(STORES.DAILY_LOGS);
|
|
673
648
|
const index = store.index("composite_block_date");
|
|
674
|
-
const range = IDBKeyRange.bound(
|
|
675
|
-
[blockId, startDate],
|
|
676
|
-
[blockId, endDate],
|
|
677
|
-
false,
|
|
678
|
-
false
|
|
679
|
-
);
|
|
649
|
+
const range = IDBKeyRange.bound([blockId, startDate], [blockId, endDate], false, false);
|
|
680
650
|
const result = await promisifyRequest(index.getAll(range));
|
|
681
651
|
return result;
|
|
682
652
|
});
|
|
@@ -752,7 +722,9 @@ async function getDailyLogStatistics(blockId) {
|
|
|
752
722
|
const dates = entries.map((entry) => new Date(entry.date));
|
|
753
723
|
const start = new Date(Math.min(...dates.map((d) => d.getTime())));
|
|
754
724
|
const end = new Date(Math.max(...dates.map((d) => d.getTime())));
|
|
755
|
-
const sortedEntries = entries.sort(
|
|
725
|
+
const sortedEntries = entries.sort(
|
|
726
|
+
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
|
|
727
|
+
);
|
|
756
728
|
const finalPortfolioValue = sortedEntries[0]?.netLiquidity || 0;
|
|
757
729
|
const maxDrawdown = Math.min(...entries.map((entry) => entry.drawdownPct));
|
|
758
730
|
const totalPl = entries.reduce((sum, entry) => sum + entry.dailyPl, 0);
|
|
@@ -866,4 +838,4 @@ export {
|
|
|
866
838
|
getTradesPage,
|
|
867
839
|
exportTradesToCSV
|
|
868
840
|
};
|
|
869
|
-
//# sourceMappingURL=chunk-
|
|
841
|
+
//# sourceMappingURL=chunk-W5E7FHC4.js.map
|