tradeblocks-mcp 3.0.2 → 3.3.1

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.
Files changed (218) hide show
  1. package/README.md +85 -80
  2. package/dist/{chunk-LDKTV7GW.js → chunk-27S67XW3.js} +24 -52
  3. package/dist/chunk-27S67XW3.js.map +1 -0
  4. package/dist/{chunk-FGZH632F.js → chunk-2E63THNI.js} +13 -11
  5. package/dist/chunk-2E63THNI.js.map +1 -0
  6. package/dist/{chunk-QTTR7AAW.js → chunk-2I3S2ZLT.js} +325 -84
  7. package/dist/chunk-2I3S2ZLT.js.map +1 -0
  8. package/dist/{chunk-4BLCXNQ6.js → chunk-4HZLVUOZ.js} +179 -183
  9. package/dist/chunk-4HZLVUOZ.js.map +1 -0
  10. package/dist/{chunk-XXYOUIZY.js → chunk-NWNSKALN.js} +63 -80
  11. package/dist/chunk-NWNSKALN.js.map +1 -0
  12. package/dist/{chunk-PRAYH3RT.js → chunk-WNTYA7ER.js} +29 -12
  13. package/dist/chunk-WNTYA7ER.js.map +1 -0
  14. package/dist/{chunk-BKQ4PM4Y.js → chunk-YZA4RS76.js} +24 -18
  15. package/dist/chunk-YZA4RS76.js.map +1 -0
  16. package/dist/{chunk-W2PP3LEH.js → chunk-ZTJXC3LS.js} +17 -8
  17. package/dist/chunk-ZTJXC3LS.js.map +1 -0
  18. package/dist/daily-log-processor-MZW2XBZY.js +9 -0
  19. package/dist/{daily-logs-store-D3QC2XPV.js → daily-logs-store-3C3OTGPK.js} +2 -2
  20. package/dist/iv-solver-worker.js +9 -1
  21. package/dist/market-provider-6U33TQUT.js +16 -0
  22. package/dist/{proto-3HZTCWK4.js → proto-V23GIZG2.js} +2 -2
  23. package/dist/{sync-PO4IPCYV.js → sync-3UBC37VW.js} +7 -7
  24. package/dist/test-exports.js +876 -483
  25. package/dist/test-exports.js.map +1 -1
  26. package/dist/trade-processor-DKDCWGKD.js +9 -0
  27. package/dist/{trades-store-LOL3FQSF.js → trades-store-R3NL25T2.js} +2 -2
  28. package/package.json +2 -6
  29. package/server/{chunk-CSDVJPBB.js → chunk-536NSFED.js} +29 -12
  30. package/server/chunk-536NSFED.js.map +1 -0
  31. package/server/{chunk-WA5AAPCH.js → chunk-A3UKD64A.js} +17 -8
  32. package/server/chunk-A3UKD64A.js.map +1 -0
  33. package/server/{chunk-PNKG7RY7.js → chunk-OMUDNJBJ.js} +63 -80
  34. package/server/chunk-OMUDNJBJ.js.map +1 -0
  35. package/server/{chunk-72GKJE2U.js → chunk-RJDJHZ5Y.js} +934 -335
  36. package/server/chunk-RJDJHZ5Y.js.map +1 -0
  37. package/server/{chunk-NZO6PT64.js → chunk-RM4B2VKS.js} +167 -177
  38. package/server/chunk-RM4B2VKS.js.map +1 -0
  39. package/server/{chunk-5EBXHT6C.js → chunk-SY6GDNVI.js} +24 -18
  40. package/server/chunk-SY6GDNVI.js.map +1 -0
  41. package/server/{chunk-FBNDMCT5.js → chunk-T66KH2XH.js} +13 -11
  42. package/server/chunk-T66KH2XH.js.map +1 -0
  43. package/server/{chunk-NRFXAJF7.js → chunk-W5E7FHC4.js} +24 -52
  44. package/server/chunk-W5E7FHC4.js.map +1 -0
  45. package/server/{config-6IZXEFEX.js → config-DK7KOMNL.js} +1 -1
  46. package/server/config-DK7KOMNL.js.map +1 -0
  47. package/server/daily-log-processor-B2VPU2FQ.js +10 -0
  48. package/server/{daily-logs-store-NU7M4L5C.js → daily-logs-store-YFNXTB7J.js} +2 -2
  49. package/server/http-server.js +2 -5
  50. package/server/index.js +2497 -2029
  51. package/server/index.js.map +1 -1
  52. package/server/iv-solver-worker.js +9 -1
  53. package/server/market-provider-L56RFHAC.js +17 -0
  54. package/server/{proto-DWRZJO4E.js → proto-YZ4TLX2N.js} +2 -2
  55. package/server/{sync-QFI5L7S7.js → sync-CENKDN53.js} +7 -7
  56. package/server/trade-processor-3PI4LHZI.js +10 -0
  57. package/server/{trades-store-VY26MWER.js → trades-store-RTTEYMXH.js} +2 -2
  58. package/src/auth/clients-store.ts +4 -4
  59. package/src/auth/code-store.ts +1 -1
  60. package/src/auth/config.ts +11 -12
  61. package/src/auth/login-page.ts +8 -10
  62. package/src/auth/provider.ts +35 -30
  63. package/src/auth/token.ts +17 -15
  64. package/src/db/backtest-schemas.ts +38 -8
  65. package/src/db/connection.ts +107 -40
  66. package/src/db/data-root.ts +22 -0
  67. package/src/db/index.ts +37 -5
  68. package/src/db/json-adapters.ts +22 -31
  69. package/src/db/json-migration.ts +11 -15
  70. package/src/db/json-store.ts +5 -3
  71. package/src/db/market-datasets.ts +48 -15
  72. package/src/db/market-schemas.ts +11 -5
  73. package/src/db/market-views.ts +119 -32
  74. package/src/db/parquet-writer.ts +7 -7
  75. package/src/db/profile-schemas.ts +39 -22
  76. package/src/db/schemas.ts +2 -2
  77. package/src/http-server.ts +7 -12
  78. package/src/index.ts +8 -9
  79. package/src/market/ingestor/index.ts +1 -4
  80. package/src/market/ingestor/market-ingestor.ts +140 -67
  81. package/src/market/ingestor/types.ts +1 -3
  82. package/src/market/stores/chain-sql.ts +2 -8
  83. package/src/market/stores/chain-store.ts +1 -4
  84. package/src/market/stores/duckdb-chain-store.ts +3 -14
  85. package/src/market/stores/duckdb-enriched-store.ts +3 -13
  86. package/src/market/stores/duckdb-quote-store.ts +10 -27
  87. package/src/market/stores/duckdb-spot-store.ts +4 -20
  88. package/src/market/stores/enriched-sql.ts +3 -9
  89. package/src/market/stores/enriched-store.ts +2 -2
  90. package/src/market/stores/parquet-chain-store.ts +5 -22
  91. package/src/market/stores/parquet-enriched-store.ts +4 -16
  92. package/src/market/stores/parquet-oi-daily-store.ts +3 -14
  93. package/src/market/stores/parquet-quote-store.ts +9 -29
  94. package/src/market/stores/parquet-spot-store.ts +14 -33
  95. package/src/market/stores/quote-store.ts +2 -10
  96. package/src/market/stores/spot-sql.ts +3 -15
  97. package/src/market/stores/spot-store.ts +1 -1
  98. package/src/market/tickers/defaults.json +6 -6
  99. package/src/market/tickers/loader.ts +2 -7
  100. package/src/market/tickers/registry.ts +5 -15
  101. package/src/market/tickers/schemas.ts +2 -6
  102. package/src/models/strategy-profile.ts +39 -39
  103. package/src/sync/block-sync.ts +44 -65
  104. package/src/sync/index.ts +1 -4
  105. package/src/sync/metadata.ts +14 -20
  106. package/src/test-exports.ts +139 -123
  107. package/src/tools/analysis.ts +180 -159
  108. package/src/tools/batch-exit-analysis.ts +100 -59
  109. package/src/tools/blocks/analysis.ts +116 -101
  110. package/src/tools/blocks/comparison.ts +281 -364
  111. package/src/tools/blocks/core.ts +208 -253
  112. package/src/tools/blocks/health.ts +571 -563
  113. package/src/tools/blocks/index.ts +2 -0
  114. package/src/tools/blocks/paired-comparison.ts +425 -0
  115. package/src/tools/blocks/similarity.ts +316 -283
  116. package/src/tools/edge-decay.ts +124 -197
  117. package/src/tools/exit-analysis.ts +130 -77
  118. package/src/tools/greeks-attribution.ts +84 -35
  119. package/src/tools/guides.ts +4 -6
  120. package/src/tools/imports.ts +11 -14
  121. package/src/tools/market-data.ts +1067 -757
  122. package/src/tools/market-enrichment.ts +3 -3
  123. package/src/tools/market-fetch.ts +148 -67
  124. package/src/tools/market-imports.ts +12 -12
  125. package/src/tools/middleware/sync-middleware.ts +5 -6
  126. package/src/tools/performance.ts +185 -302
  127. package/src/tools/profile-analysis.ts +52 -66
  128. package/src/tools/profiles.ts +106 -69
  129. package/src/tools/regime-advisor.ts +20 -45
  130. package/src/tools/replay.ts +81 -77
  131. package/src/tools/reports/discrepancies.ts +298 -328
  132. package/src/tools/reports/fields.ts +7 -25
  133. package/src/tools/reports/helpers.ts +18 -49
  134. package/src/tools/reports/predictive.ts +27 -70
  135. package/src/tools/reports/slippage-trends.ts +315 -345
  136. package/src/tools/reports/slippage.ts +1 -4
  137. package/src/tools/reports/strategy-matches.ts +399 -441
  138. package/src/tools/schema.ts +43 -40
  139. package/src/tools/shared/filters.ts +3 -9
  140. package/src/tools/snapshot.ts +9 -30
  141. package/src/tools/sql.ts +15 -14
  142. package/src/tools/tickers.ts +1 -4
  143. package/src/utils/batch-exit-analysis.ts +31 -42
  144. package/src/utils/black-scholes.ts +39 -29
  145. package/src/utils/block-loader.ts +69 -83
  146. package/src/utils/calibration-probe.ts +3 -4
  147. package/src/utils/chain-loader.ts +3 -3
  148. package/src/utils/csv-discovery.ts +16 -22
  149. package/src/utils/data-quality.ts +24 -36
  150. package/src/utils/exit-triggers.ts +91 -96
  151. package/src/utils/field-timing.ts +94 -79
  152. package/src/utils/filter-predicates.ts +13 -9
  153. package/src/utils/flatfile-importer.ts +94 -64
  154. package/src/utils/greeks-decomposition.ts +152 -100
  155. package/src/utils/iv-solver-pool.ts +55 -25
  156. package/src/utils/iv-solver-worker.ts +5 -5
  157. package/src/utils/market-enricher.ts +528 -497
  158. package/src/utils/market-importer.ts +31 -12
  159. package/src/utils/market-provider.ts +21 -23
  160. package/src/utils/massive-tier.ts +5 -7
  161. package/src/utils/migrate-option-data-helpers.ts +2 -8
  162. package/src/utils/option-quote-greeks.ts +25 -31
  163. package/src/utils/option-time.ts +4 -8
  164. package/src/utils/output-formatter.ts +1 -4
  165. package/src/utils/provider-capabilities.ts +1 -4
  166. package/src/utils/providers/massive.ts +59 -93
  167. package/src/utils/providers/thetadata/backfill.ts +14 -23
  168. package/src/utils/providers/thetadata/client.ts +12 -8
  169. package/src/utils/providers/thetadata/decode.ts +2 -20
  170. package/src/utils/providers/thetadata/endpoints.ts +32 -40
  171. package/src/utils/providers/thetadata/join.ts +11 -10
  172. package/src/utils/providers/thetadata/proto.ts +12 -10
  173. package/src/utils/providers/thetadata/quote-mid-greeks.ts +5 -5
  174. package/src/utils/providers/thetadata.ts +11 -10
  175. package/src/utils/quote-enricher.ts +4 -4
  176. package/src/utils/quote-parquet-projection.ts +3 -11
  177. package/src/utils/sample-date-selector.ts +3 -5
  178. package/src/utils/schema-metadata.ts +102 -70
  179. package/src/utils/ticker.ts +5 -9
  180. package/src/utils/trade-replay.ts +77 -68
  181. package/dist/chunk-4BLCXNQ6.js.map +0 -1
  182. package/dist/chunk-BKQ4PM4Y.js.map +0 -1
  183. package/dist/chunk-FGZH632F.js.map +0 -1
  184. package/dist/chunk-LDKTV7GW.js.map +0 -1
  185. package/dist/chunk-PRAYH3RT.js.map +0 -1
  186. package/dist/chunk-QTTR7AAW.js.map +0 -1
  187. package/dist/chunk-W2PP3LEH.js.map +0 -1
  188. package/dist/chunk-XXYOUIZY.js.map +0 -1
  189. package/dist/daily-log-processor-6MWJ23JK.js +0 -9
  190. package/dist/market-provider-VDRJUEF2.js +0 -16
  191. package/dist/trade-processor-NHU4VWRX.js +0 -9
  192. package/manifest.json +0 -141
  193. package/server/chunk-5EBXHT6C.js.map +0 -1
  194. package/server/chunk-72GKJE2U.js.map +0 -1
  195. package/server/chunk-CSDVJPBB.js.map +0 -1
  196. package/server/chunk-FBNDMCT5.js.map +0 -1
  197. package/server/chunk-NRFXAJF7.js.map +0 -1
  198. package/server/chunk-NZO6PT64.js.map +0 -1
  199. package/server/chunk-PNKG7RY7.js.map +0 -1
  200. package/server/chunk-WA5AAPCH.js.map +0 -1
  201. package/server/config-6IZXEFEX.js.map +0 -1
  202. package/server/daily-log-processor-Y3PVSVBM.js +0 -10
  203. package/server/market-provider-VOYYVYWT.js +0 -17
  204. package/server/trade-processor-JWVS37KM.js +0 -10
  205. /package/dist/{daily-log-processor-6MWJ23JK.js.map → daily-log-processor-MZW2XBZY.js.map} +0 -0
  206. /package/dist/{daily-logs-store-D3QC2XPV.js.map → daily-logs-store-3C3OTGPK.js.map} +0 -0
  207. /package/dist/{market-provider-VDRJUEF2.js.map → market-provider-6U33TQUT.js.map} +0 -0
  208. /package/dist/{proto-3HZTCWK4.js.map → proto-V23GIZG2.js.map} +0 -0
  209. /package/dist/{sync-PO4IPCYV.js.map → sync-3UBC37VW.js.map} +0 -0
  210. /package/dist/{trade-processor-NHU4VWRX.js.map → trade-processor-DKDCWGKD.js.map} +0 -0
  211. /package/dist/{trades-store-LOL3FQSF.js.map → trades-store-R3NL25T2.js.map} +0 -0
  212. /package/server/{daily-log-processor-Y3PVSVBM.js.map → daily-log-processor-B2VPU2FQ.js.map} +0 -0
  213. /package/server/{daily-logs-store-NU7M4L5C.js.map → daily-logs-store-YFNXTB7J.js.map} +0 -0
  214. /package/server/{market-provider-VOYYVYWT.js.map → market-provider-L56RFHAC.js.map} +0 -0
  215. /package/server/{proto-DWRZJO4E.js.map → proto-YZ4TLX2N.js.map} +0 -0
  216. /package/server/{sync-QFI5L7S7.js.map → sync-CENKDN53.js.map} +0 -0
  217. /package/server/{trade-processor-JWVS37KM.js.map → trade-processor-3PI4LHZI.js.map} +0 -0
  218. /package/server/{trades-store-VY26MWER.js.map → trades-store-RTTEYMXH.js.map} +0 -0
@@ -4,15 +4,15 @@ import {
4
4
  normalizeHeaders,
5
5
  rawTradeDataSchema,
6
6
  tradeSchema
7
- } from "./chunk-BKQ4PM4Y.js";
7
+ } from "./chunk-YZA4RS76.js";
8
8
 
9
9
  // ../lib/models/trade.ts
10
10
  var TRADE_COLUMN_MAPPING = {
11
11
  "Date Opened": "dateOpened",
12
12
  "Time Opened": "timeOpened",
13
13
  "Opening Price": "openingPrice",
14
- "Legs": "legs",
15
- "Premium": "premium",
14
+ Legs: "legs",
15
+ Premium: "premium",
16
16
  "Closing Price": "closingPrice",
17
17
  "Date Closed": "dateClosed",
18
18
  "Time Closed": "timeClosed",
@@ -22,15 +22,15 @@ var TRADE_COLUMN_MAPPING = {
22
22
  "No. of Contracts": "numContracts",
23
23
  "Funds at Close": "fundsAtClose",
24
24
  "Margin Req.": "marginReq",
25
- "Strategy": "strategy",
25
+ Strategy: "strategy",
26
26
  "Opening Commissions + Fees": "openingCommissionsFees",
27
27
  "Closing Commissions + Fees": "closingCommissionsFees",
28
28
  "Opening Short/Long Ratio": "openingShortLongRatio",
29
29
  "Closing Short/Long Ratio": "closingShortLongRatio",
30
30
  "Opening VIX": "openingVix",
31
31
  "Closing VIX": "closingVix",
32
- "Gap": "gap",
33
- "Movement": "movement",
32
+ Gap: "gap",
33
+ Movement: "movement",
34
34
  "Max Profit": "maxProfit",
35
35
  "Max Loss": "maxLoss"
36
36
  };
@@ -273,7 +273,10 @@ var TradeProcessor = class {
273
273
  rawTradeDataSchema.parse(normalizedRow);
274
274
  return normalizedRow;
275
275
  } catch (error) {
276
- console.warn(`[TradeProcessor] Row ${rowIndex + 2} validation failed:`, error instanceof Error ? error.message : error);
276
+ console.warn(
277
+ `[TradeProcessor] Row ${rowIndex + 2} validation failed:`,
278
+ error instanceof Error ? error.message : error
279
+ );
277
280
  return null;
278
281
  }
279
282
  }
@@ -341,9 +344,21 @@ var TradeProcessor = class {
341
344
  fundsAtClose: parseNumber(rawData["Funds at Close"], "Funds at Close"),
342
345
  marginReq: parseNumber(rawData["Margin Req."], "Margin Req."),
343
346
  strategy,
344
- openingCommissionsFees: parseNumber(rawData["Opening Commissions + Fees"], "Opening Commissions", 0),
345
- closingCommissionsFees: parseNumber(rawData["Closing Commissions + Fees"], "Closing Commissions", 0),
346
- openingShortLongRatio: parseNumber(rawData["Opening Short/Long Ratio"], "Opening Short/Long Ratio", 0),
347
+ openingCommissionsFees: parseNumber(
348
+ rawData["Opening Commissions + Fees"],
349
+ "Opening Commissions",
350
+ 0
351
+ ),
352
+ closingCommissionsFees: parseNumber(
353
+ rawData["Closing Commissions + Fees"],
354
+ "Closing Commissions",
355
+ 0
356
+ ),
357
+ openingShortLongRatio: parseNumber(
358
+ rawData["Opening Short/Long Ratio"],
359
+ "Opening Short/Long Ratio",
360
+ 0
361
+ ),
347
362
  closingShortLongRatio: rawData["Closing Short/Long Ratio"] ? parseNumber(rawData["Closing Short/Long Ratio"], "Closing Short/Long Ratio") : void 0,
348
363
  openingVix: rawData["Opening VIX"] ? parseNumber(rawData["Opening VIX"], "Opening VIX") : void 0,
349
364
  closingVix: rawData["Closing VIX"] ? parseNumber(rawData["Closing VIX"], "Closing VIX") : void 0,
@@ -370,7 +385,9 @@ var TradeProcessor = class {
370
385
  const validatedTrade = tradeSchema.parse(trade);
371
386
  return validatedTrade;
372
387
  } catch (error) {
373
- throw new Error(`Trade conversion failed: ${error instanceof Error ? error.message : String(error)}`);
388
+ throw new Error(
389
+ `Trade conversion failed: ${error instanceof Error ? error.message : String(error)}`
390
+ );
374
391
  }
375
392
  }
376
393
  /**
@@ -386,4 +403,4 @@ var TradeProcessor = class {
386
403
  export {
387
404
  TradeProcessor
388
405
  };
389
- //# sourceMappingURL=chunk-PRAYH3RT.js.map
406
+ //# sourceMappingURL=chunk-WNTYA7ER.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../lib/models/trade.ts","../../lib/processing/trade-processor.ts"],"sourcesContent":["/**\n * Trade model based on legacy Python Trade class\n * Represents individual trade record from portfolio CSV\n */\nexport interface Trade {\n // Core trade identification\n dateOpened: Date;\n timeOpened: string; // HH:mm:ss format\n openingPrice: number;\n legs: string; // Option legs description\n premium: number;\n /**\n * Records how the premium value was encoded in the source CSV.\n * Some exports (OptionOmega) provide cents as whole numbers without decimals.\n */\n premiumPrecision?: \"dollars\" | \"cents\";\n\n // Closing information (optional for open trades)\n closingPrice?: number;\n dateClosed?: Date;\n timeClosed?: string;\n avgClosingCost?: number;\n reasonForClose?: string;\n\n // Financial metrics\n pl: number; // Profit/Loss\n numContracts: number;\n fundsAtClose: number;\n marginReq: number;\n\n // Trade metadata\n strategy: string;\n openingCommissionsFees: number;\n closingCommissionsFees: number;\n\n // Ratios and market data\n openingShortLongRatio: number;\n closingShortLongRatio?: number;\n openingVix?: number;\n closingVix?: number;\n\n // Additional metrics\n gap?: number;\n movement?: number;\n maxProfit?: number;\n maxLoss?: number;\n /**\n * Synthetic-only: ratio of the worst observed loss to account capital at the time\n * Used to scale synthetic losses relative to current account size\n */\n syntheticCapitalRatio?: number;\n\n /**\n * Custom fields from extra columns in the trade CSV\n * Keys are the original column names, values are auto-detected as number or string\n */\n customFields?: Record<string, number | string>;\n}\n\n/**\n * Raw trade data as it comes from CSV before processing\n */\nexport interface RawTradeData {\n \"Date Opened\": string;\n \"Time Opened\": string;\n \"Opening Price\": string;\n Legs: string;\n Premium: string;\n \"Closing Price\"?: string;\n \"Date Closed\"?: string;\n \"Time Closed\"?: string;\n \"Avg. Closing Cost\"?: string;\n \"Reason For Close\"?: string;\n \"P/L\": string;\n \"No. of Contracts\": string;\n \"Funds at Close\": string;\n \"Margin Req.\": string;\n Strategy: string;\n \"Opening Commissions + Fees\": string;\n \"Closing Commissions + Fees\"?: string;\n \"Opening Short/Long Ratio\": string;\n \"Closing Short/Long Ratio\"?: string;\n \"Opening VIX\"?: string;\n \"Closing VIX\"?: string;\n Gap?: string;\n Movement?: string;\n \"Max Profit\"?: string;\n \"Max Loss\"?: string;\n}\n\n/**\n * Column mapping from CSV headers to Trade interface properties\n */\nexport const TRADE_COLUMN_MAPPING = {\n \"Date Opened\": \"dateOpened\",\n \"Time Opened\": \"timeOpened\",\n \"Opening Price\": \"openingPrice\",\n Legs: \"legs\",\n Premium: \"premium\",\n \"Closing Price\": \"closingPrice\",\n \"Date Closed\": \"dateClosed\",\n \"Time Closed\": \"timeClosed\",\n \"Avg. Closing Cost\": \"avgClosingCost\",\n \"Reason For Close\": \"reasonForClose\",\n \"P/L\": \"pl\",\n \"No. of Contracts\": \"numContracts\",\n \"Funds at Close\": \"fundsAtClose\",\n \"Margin Req.\": \"marginReq\",\n Strategy: \"strategy\",\n \"Opening Commissions + Fees\": \"openingCommissionsFees\",\n \"Closing Commissions + Fees\": \"closingCommissionsFees\",\n \"Opening Short/Long Ratio\": \"openingShortLongRatio\",\n \"Closing Short/Long Ratio\": \"closingShortLongRatio\",\n \"Opening VIX\": \"openingVix\",\n \"Closing VIX\": \"closingVix\",\n Gap: \"gap\",\n Movement: \"movement\",\n \"Max Profit\": \"maxProfit\",\n \"Max Loss\": \"maxLoss\",\n} as const;\n\n/**\n * Column aliases for different CSV export variations\n */\nexport const TRADE_COLUMN_ALIASES = {\n \"Opening comms & fees\": \"Opening Commissions + Fees\",\n \"Opening Commissions & Fees\": \"Opening Commissions + Fees\",\n \"Closing comms & fees\": \"Closing Commissions + Fees\",\n \"Closing Commissions & Fees\": \"Closing Commissions + Fees\",\n \"P/L %\": \"P/L %\", // Recognized but ignored (we calculate our own plPct)\n} as const;\n\n/**\n * Minimum required columns for a valid trade log\n */\nexport const REQUIRED_TRADE_COLUMNS = [\n \"Date Opened\",\n \"Time Opened\",\n \"Opening Price\",\n \"Legs\",\n \"Premium\",\n \"Closing Price\",\n \"Date Closed\",\n \"Time Closed\",\n \"Avg. Closing Cost\",\n \"Reason For Close\",\n \"P/L\",\n \"No. of Contracts\",\n \"Funds at Close\",\n \"Margin Req.\",\n \"Strategy\",\n] as const;\n","/**\n * Trade Processor\n *\n * Handles parsing and processing of trade log CSV files from OptionOmega.\n * Converts raw CSV data to validated Trade objects.\n */\n\nimport {\n type Trade,\n TRADE_COLUMN_ALIASES,\n REQUIRED_TRADE_COLUMNS,\n TRADE_COLUMN_MAPPING,\n} from \"../models/trade.ts\";\nimport type { ValidationError, ProcessingError } from \"../models/index.ts\";\nimport { rawTradeDataSchema, tradeSchema } from \"../models/validators.ts\";\nimport { CSVParser, type ParseProgress } from \"./csv-parser.ts\";\nimport { findMissingHeaders, normalizeHeaders } from \"../utils/csv-headers.ts\";\n\n/**\n * Set of known trade column names (canonical names from TRADE_COLUMN_MAPPING)\n * Used to identify custom columns that should be preserved\n */\nconst KNOWN_TRADE_COLUMNS = new Set([\n ...Object.keys(TRADE_COLUMN_MAPPING),\n ...Object.keys(TRADE_COLUMN_ALIASES),\n]);\n\n/**\n * Trade processing configuration\n */\nexport interface TradeProcessingConfig {\n maxTrades?: number;\n strictValidation?: boolean;\n progressCallback?: (progress: TradeProcessingProgress) => void;\n}\n\n/**\n * Trade processing progress\n */\nexport interface TradeProcessingProgress extends ParseProgress {\n stage: \"reading\" | \"parsing\" | \"validating\" | \"converting\" | \"completed\";\n validTrades: number;\n invalidTrades: number;\n}\n\n/**\n * Trade processing result\n */\nexport interface TradeProcessingResult {\n trades: Trade[];\n totalRows: number;\n validTrades: number;\n invalidTrades: number;\n errors: ProcessingError[];\n warnings: string[];\n stats: {\n processingTimeMs: number;\n strategies: string[];\n dateRange: { start: Date | null; end: Date | null };\n totalPL: number;\n };\n}\n\n/**\n * Trade processor class\n */\nexport class TradeProcessor {\n private config: Required<TradeProcessingConfig>;\n\n constructor(config: TradeProcessingConfig = {}) {\n this.config = {\n maxTrades: 50000,\n strictValidation: false,\n progressCallback: () => {},\n ...config,\n };\n }\n\n /**\n * Process trade log file\n */\n async processFile(file: File): Promise<TradeProcessingResult> {\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.maxTrades,\n progressCallback: (progress, rowsProcessed) => {\n this.config.progressCallback({\n stage: \"parsing\",\n progress,\n rowsProcessed,\n totalRows: 0,\n errors: 0,\n validTrades: 0,\n invalidTrades: 0,\n });\n },\n });\n\n // Parse CSV with validation\n const parseResult = await csvParser.parseFileObject(\n file,\n (row, rowIndex) => this.validateRawTradeData(row, rowIndex),\n (progress) => {\n this.config.progressCallback({\n ...progress,\n validTrades: 0,\n invalidTrades: 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 normalizedHeaders = normalizeHeaders(parseResult.headers, TRADE_COLUMN_ALIASES);\n const missingColumns = findMissingHeaders(normalizedHeaders, REQUIRED_TRADE_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 validTrades: 0,\n invalidTrades: 0,\n });\n\n // Convert validated data to Trade objects\n const trades: Trade[] = [];\n let validTrades = 0;\n let invalidTrades = 0;\n\n for (let i = 0; i < parseResult.data.length; i++) {\n try {\n const trade = this.convertToTrade(parseResult.data[i]);\n trades.push(trade);\n validTrades++;\n } catch (error) {\n invalidTrades++;\n const errorMessage = `Trade 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(`[TradeProcessor] ${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 trade 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 % 100 === 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 validTrades,\n invalidTrades,\n });\n }\n }\n\n // Sort trades for consistent ordering (handles simultaneous trades)\n trades.sort((a, b) => {\n const dateCompare = new Date(a.dateOpened).getTime() - new Date(b.dateOpened).getTime();\n if (dateCompare !== 0) return dateCompare;\n\n // Secondary sort by time\n const timeCompare = a.timeOpened.localeCompare(b.timeOpened);\n if (timeCompare !== 0) return timeCompare;\n\n // Tertiary sort by funds_at_close (lower first for simultaneous trades)\n return a.fundsAtClose - b.fundsAtClose;\n });\n\n // Calculate statistics\n const processingTimeMs = Date.now() - startTime;\n const strategies = Array.from(new Set(trades.map((t) => t.strategy))).sort();\n const dates = trades.map((t) => new Date(t.dateOpened));\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 totalPL = trades.reduce((sum, t) => sum + t.pl, 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 validTrades,\n invalidTrades,\n });\n\n return {\n trades,\n totalRows: parseResult.totalRows,\n validTrades,\n invalidTrades,\n errors,\n warnings,\n stats: {\n processingTimeMs,\n strategies,\n dateRange,\n totalPL,\n },\n };\n } catch (error) {\n const processingError: ProcessingError = {\n type: \"parsing\",\n message: `Trade processing failed: ${error instanceof Error ? error.message : String(error)}`,\n details: { fileName: file.name, fileSize: file.size },\n };\n\n return {\n trades: [],\n totalRows: 0,\n validTrades: 0,\n invalidTrades: 0,\n errors: [processingError, ...errors],\n warnings,\n stats: {\n processingTimeMs: Date.now() - startTime,\n strategies: [],\n dateRange: { start: null, end: null },\n totalPL: 0,\n },\n };\n }\n }\n\n /**\n * Validate raw trade data from CSV\n */\n private validateRawTradeData(\n row: Record<string, string>,\n rowIndex: number,\n ): Record<string, string> | null {\n try {\n // Apply column aliases to normalize variations\n const normalizedRow = { ...row };\n Object.entries(TRADE_COLUMN_ALIASES).forEach(([alias, canonical]) => {\n if (normalizedRow[alias] !== undefined) {\n normalizedRow[canonical] = normalizedRow[alias];\n delete normalizedRow[alias];\n }\n });\n\n // OptionOmega sometimes leaves strategy blank; default to Unknown so downstream stats still work\n if (!normalizedRow[\"Strategy\"] || normalizedRow[\"Strategy\"].trim() === \"\") {\n normalizedRow[\"Strategy\"] = \"Unknown\";\n }\n\n // Ensure required columns have values\n const requiredFields = [\"Date Opened\", \"P/L\", \"Strategy\"];\n for (const field of requiredFields) {\n if (!normalizedRow[field] || normalizedRow[field].trim() === \"\") {\n throw new Error(`Missing required field: ${field}`);\n }\n }\n\n // Set default values for missing optional fields\n if (!normalizedRow[\"Opening Commissions + Fees\"]) {\n normalizedRow[\"Opening Commissions + Fees\"] = \"0\";\n }\n if (!normalizedRow[\"Closing Commissions + Fees\"]) {\n normalizedRow[\"Closing Commissions + Fees\"] = \"0\";\n }\n if (!normalizedRow[\"Opening Short/Long Ratio\"]) {\n normalizedRow[\"Opening Short/Long Ratio\"] = \"0\";\n }\n\n const optionalNumericFieldsWithDefaultZero = [\n \"Opening VIX\",\n \"Closing VIX\",\n \"Gap\",\n \"Movement\",\n ] as const;\n optionalNumericFieldsWithDefaultZero.forEach((field) => {\n if (!normalizedRow[field] || normalizedRow[field].trim() === \"\") {\n normalizedRow[field] = \"0\";\n }\n });\n\n // Basic format validation (detailed validation happens in conversion)\n rawTradeDataSchema.parse(normalizedRow);\n\n return normalizedRow;\n } catch (error) {\n // Log validation errors to console for debugging\n console.warn(\n `[TradeProcessor] 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 *\n * Option Omega exports dates in Eastern time. JavaScript's new Date('YYYY-MM-DD')\n * parses as UTC midnight, which when converted to local time can shift to the\n * previous day (e.g., Dec 11 UTC → Dec 10 7pm EST).\n *\n * This method creates a Date representing midnight local time on the specified\n * calendar date, so Dec 11 in the CSV becomes Dec 11 in the app regardless of timezone.\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 Trade object\n */\n private convertToTrade(rawData: Record<string, string>): Trade {\n try {\n // Parse dates preserving calendar day\n const dateOpened = this.parseDatePreservingCalendarDay(rawData[\"Date Opened\"]);\n if (isNaN(dateOpened.getTime())) {\n throw new Error(`Invalid Date Opened: ${rawData[\"Date Opened\"]}`);\n }\n\n const dateClosed = rawData[\"Date Closed\"]\n ? this.parseDatePreservingCalendarDay(rawData[\"Date Closed\"])\n : undefined;\n if (dateClosed && isNaN(dateClosed.getTime())) {\n throw new Error(`Invalid Date Closed: ${rawData[\"Date Closed\"]}`);\n }\n\n // Normalize strategy name (handle empty strings)\n const strategy = (rawData[\"Strategy\"] || \"\").trim() || \"Unknown\";\n\n // Parse numeric values with error handling and NaN handling\n const parseNumber = (\n value: string | undefined,\n fieldName: string,\n defaultValue?: number,\n ): number => {\n if (!value || value.trim() === \"\" || value.toLowerCase() === \"nan\") {\n if (defaultValue !== undefined) return defaultValue;\n throw new Error(`Missing required numeric field: ${fieldName}`);\n }\n\n // Remove currency symbols and commas\n const cleaned = value.replace(/[$,]/g, \"\").trim();\n const parsed = parseFloat(cleaned);\n\n if (isNaN(parsed)) {\n if (defaultValue !== undefined) return defaultValue;\n throw new Error(`Invalid numeric value for ${fieldName}: ${value}`);\n }\n\n return parsed;\n };\n\n // Build trade object\n const rawPremiumString = (rawData[\"Premium\"] || \"\").replace(/[$,]/g, \"\").trim();\n const premiumPrecision: Trade[\"premiumPrecision\"] =\n rawPremiumString && !rawPremiumString.includes(\".\") ? \"cents\" : \"dollars\";\n\n const trade: Trade = {\n dateOpened,\n timeOpened: rawData[\"Time Opened\"] || \"00:00:00\",\n openingPrice: parseNumber(rawData[\"Opening Price\"], \"Opening Price\"),\n legs: rawData[\"Legs\"] || \"\",\n premium: parseNumber(rawData[\"Premium\"], \"Premium\"),\n premiumPrecision,\n closingPrice: rawData[\"Closing Price\"]\n ? parseNumber(rawData[\"Closing Price\"], \"Closing Price\")\n : undefined,\n dateClosed,\n timeClosed: rawData[\"Time Closed\"] || undefined,\n avgClosingCost: rawData[\"Avg. Closing Cost\"]\n ? parseNumber(rawData[\"Avg. Closing Cost\"], \"Avg. Closing Cost\")\n : undefined,\n reasonForClose: rawData[\"Reason For Close\"] || undefined,\n pl: parseNumber(rawData[\"P/L\"], \"P/L\"),\n numContracts: Math.round(parseNumber(rawData[\"No. of Contracts\"], \"No. of Contracts\")),\n fundsAtClose: parseNumber(rawData[\"Funds at Close\"], \"Funds at Close\"),\n marginReq: parseNumber(rawData[\"Margin Req.\"], \"Margin Req.\"),\n strategy,\n openingCommissionsFees: parseNumber(\n rawData[\"Opening Commissions + Fees\"],\n \"Opening Commissions\",\n 0,\n ),\n closingCommissionsFees: parseNumber(\n rawData[\"Closing Commissions + Fees\"],\n \"Closing Commissions\",\n 0,\n ),\n openingShortLongRatio: parseNumber(\n rawData[\"Opening Short/Long Ratio\"],\n \"Opening Short/Long Ratio\",\n 0,\n ),\n closingShortLongRatio: rawData[\"Closing Short/Long Ratio\"]\n ? parseNumber(rawData[\"Closing Short/Long Ratio\"], \"Closing Short/Long Ratio\")\n : undefined,\n openingVix: rawData[\"Opening VIX\"]\n ? parseNumber(rawData[\"Opening VIX\"], \"Opening VIX\")\n : undefined,\n closingVix: rawData[\"Closing VIX\"]\n ? parseNumber(rawData[\"Closing VIX\"], \"Closing VIX\")\n : undefined,\n gap: rawData[\"Gap\"] ? parseNumber(rawData[\"Gap\"], \"Gap\") : undefined,\n movement: rawData[\"Movement\"] ? parseNumber(rawData[\"Movement\"], \"Movement\") : undefined,\n maxProfit: rawData[\"Max Profit\"]\n ? parseNumber(rawData[\"Max Profit\"], \"Max Profit\")\n : undefined,\n maxLoss: rawData[\"Max Loss\"] ? parseNumber(rawData[\"Max Loss\"], \"Max Loss\") : undefined,\n };\n\n // Extract custom fields (columns not in KNOWN_TRADE_COLUMNS)\n const customFields: Record<string, number | string> = {};\n for (const [key, value] of Object.entries(rawData)) {\n if (!KNOWN_TRADE_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 trade.customFields = customFields;\n }\n\n // Final validation with Zod schema\n const validatedTrade = tradeSchema.parse(trade);\n return validatedTrade;\n } catch (error) {\n throw new Error(\n `Trade 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): Promise<TradeProcessingResult> {\n // Create a mock File object for testing\n const blob = new Blob([content], { type: \"text/csv\" });\n const file = new File([blob], \"test.csv\", { type: \"text/csv\" });\n return this.processFile(file);\n }\n}\n"],"mappings":"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","names":[]}
@@ -36,9 +36,7 @@ var CSVParser = class _CSVParser {
36
36
  if (headers.length === 0) {
37
37
  throw new Error("No valid headers found");
38
38
  }
39
- const cleanHeaders = headers.map(
40
- (header) => header.replace(/^\ufeff/, "").trim()
41
- );
39
+ const cleanHeaders = headers.map((header) => header.replace(/^\ufeff/, "").trim());
42
40
  let validRows = 0;
43
41
  let processedRows = 0;
44
42
  for (let i = 1; i < lines.length && processedRows < this.config.maxRows; i++) {
@@ -85,8 +83,9 @@ var CSVParser = class _CSVParser {
85
83
  return {
86
84
  data,
87
85
  headers: cleanHeaders,
88
- totalRows: totalRows - 1,
89
- // Excluding header
86
+ // Count data records actually encountered. Physical line count includes
87
+ // trailing blank lines, which are not CSV records when skipEmptyLines is on.
88
+ totalRows: processedRows,
90
89
  validRows,
91
90
  errors,
92
91
  warnings
@@ -260,8 +259,8 @@ var rawTradeDataSchema = z.object({
260
259
  "Date Opened": z.string().min(1, "Date Opened is required"),
261
260
  "Time Opened": z.string().min(1, "Time Opened is required"),
262
261
  "Opening Price": z.string().min(1, "Opening Price is required"),
263
- "Legs": z.string().min(1, "Legs description is required"),
264
- "Premium": z.string().min(1, "Premium is required"),
262
+ Legs: z.string().min(1, "Legs description is required"),
263
+ Premium: z.string().min(1, "Premium is required"),
265
264
  "Closing Price": z.string().optional(),
266
265
  "Date Closed": z.string().optional(),
267
266
  "Time Closed": z.string().optional(),
@@ -271,15 +270,15 @@ var rawTradeDataSchema = z.object({
271
270
  "No. of Contracts": z.string().min(1, "Number of Contracts is required"),
272
271
  "Funds at Close": z.string().min(1, "Funds at Close is required"),
273
272
  "Margin Req.": z.string().min(1, "Margin Requirement is required"),
274
- "Strategy": z.string().min(1, "Strategy is required"),
273
+ Strategy: z.string().min(1, "Strategy is required"),
275
274
  "Opening Commissions + Fees": z.string().default("0"),
276
275
  "Closing Commissions + Fees": z.string().optional(),
277
276
  "Opening Short/Long Ratio": z.string().default("0"),
278
277
  "Closing Short/Long Ratio": z.string().optional(),
279
278
  "Opening VIX": z.string().optional(),
280
279
  "Closing VIX": z.string().optional(),
281
- "Gap": z.string().optional(),
282
- "Movement": z.string().optional(),
280
+ Gap: z.string().optional(),
281
+ Movement: z.string().optional(),
283
282
  "Max Profit": z.string().optional(),
284
283
  "Max Loss": z.string().optional()
285
284
  }).passthrough();
@@ -314,11 +313,12 @@ var tradeSchema = z.object({
314
313
  customFields: z.record(z.string(), z.union([z.number(), z.string()])).optional()
315
314
  });
316
315
  var rawReportingTradeDataSchema = z.object({
317
- "Strategy": z.string().min(1, "Strategy is required"),
316
+ Strategy: z.string().min(1, "Strategy is required"),
317
+ Account: z.string().optional(),
318
318
  "Date Opened": z.string().min(1, "Date Opened is required"),
319
319
  "Time Opened": z.string().optional(),
320
320
  "Opening Price": z.string().min(1, "Opening Price is required"),
321
- "Legs": z.string().min(1, "Legs description is required"),
321
+ Legs: z.string().min(1, "Legs description is required"),
322
322
  "Initial Premium": z.string().min(1, "Initial Premium is required"),
323
323
  "No. of Contracts": z.string().min(1, "Number of Contracts is required"),
324
324
  "P/L": z.string().min(1, "P/L is required"),
@@ -326,12 +326,15 @@ var rawReportingTradeDataSchema = z.object({
326
326
  "Date Closed": z.string().optional(),
327
327
  "Time Closed": z.string().optional(),
328
328
  "Avg. Closing Cost": z.string().optional(),
329
- "Reason For Close": z.string().optional()
330
- });
329
+ "Reason For Close": z.string().optional(),
330
+ "Days in Trade": z.string().optional()
331
+ }).catchall(z.string());
331
332
  var reportingTradeSchema = z.object({
332
333
  strategy: z.string().min(1),
334
+ account: z.string().optional(),
333
335
  dateOpened: z.date(),
334
336
  timeOpened: z.string().optional(),
337
+ rawTimeOpened: z.string().optional(),
335
338
  openingPrice: z.number().finite(),
336
339
  legs: z.string().min(1),
337
340
  initialPremium: z.number().finite(),
@@ -340,14 +343,17 @@ var reportingTradeSchema = z.object({
340
343
  closingPrice: z.number().finite().optional(),
341
344
  dateClosed: z.date().optional(),
342
345
  timeClosed: z.string().optional(),
346
+ rawTimeClosed: z.string().optional(),
347
+ daysInTrade: z.number().finite().nonnegative().optional(),
343
348
  avgClosingCost: z.number().finite().optional(),
344
- reasonForClose: z.string().optional()
349
+ reasonForClose: z.string().optional(),
350
+ sourceFields: z.record(z.string(), z.string()).optional()
345
351
  });
346
352
  var rawDailyLogDataSchema = z.object({
347
- "Date": z.string().min(1, "Date is required"),
353
+ Date: z.string().min(1, "Date is required"),
348
354
  "Net Liquidity": z.string().min(1, "Net Liquidity is required"),
349
355
  "Current Funds": z.string().min(1, "Current Funds is required"),
350
- "Withdrawn": z.string().default("0"),
356
+ Withdrawn: z.string().default("0"),
351
357
  "Trading Funds": z.string().min(1, "Trading Funds is required"),
352
358
  "P/L": z.string().min(1, "P/L is required"),
353
359
  "P/L %": z.string().min(1, "P/L % is required"),
@@ -439,4 +445,4 @@ export {
439
445
  normalizeHeaders,
440
446
  findMissingHeaders
441
447
  };
442
- //# sourceMappingURL=chunk-BKQ4PM4Y.js.map
448
+ //# sourceMappingURL=chunk-YZA4RS76.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 // Count data records actually encountered. Physical line count includes\n // trailing blank lines, which are not CSV records when skipEmptyLines is on.\n totalRows: processedRows,\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\n .object({\n Strategy: z.string().min(1, \"Strategy is required\"),\n Account: z.string().optional(),\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 \"Days in Trade\": z.string().optional(),\n })\n .catchall(z.string());\n\n/**\n * Zod schema for validating processed reporting trade data\n */\nexport const reportingTradeSchema = z.object({\n strategy: z.string().min(1),\n account: z.string().optional(),\n dateOpened: z.date(),\n timeOpened: z.string().optional(),\n rawTimeOpened: 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 rawTimeClosed: z.string().optional(),\n daysInTrade: z.number().finite().nonnegative().optional(),\n avgClosingCost: z.number().finite().optional(),\n reasonForClose: z.string().optional(),\n sourceFields: z.record(z.string(), 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":[]}
@@ -3,14 +3,14 @@ import {
3
3
  dailyLogEntrySchema,
4
4
  findMissingHeaders,
5
5
  rawDailyLogDataSchema
6
- } from "./chunk-BKQ4PM4Y.js";
6
+ } from "./chunk-YZA4RS76.js";
7
7
 
8
8
  // ../lib/models/daily-log.ts
9
9
  var DAILY_LOG_COLUMN_MAPPING = {
10
- "Date": "date",
10
+ Date: "date",
11
11
  "Net Liquidity": "netLiquidity",
12
12
  "Current Funds": "currentFunds",
13
- "Withdrawn": "withdrawn",
13
+ Withdrawn: "withdrawn",
14
14
  "Trading Funds": "tradingFunds",
15
15
  "P/L": "dailyPl",
16
16
  "P/L %": "dailyPlPct",
@@ -210,7 +210,10 @@ var DailyLogProcessor = class {
210
210
  rawDailyLogDataSchema.parse(normalizedRow);
211
211
  return normalizedRow;
212
212
  } catch (error) {
213
- console.warn(`[DailyLogProcessor] Row ${rowIndex + 2} validation failed:`, error instanceof Error ? error.message : error);
213
+ console.warn(
214
+ `[DailyLogProcessor] Row ${rowIndex + 2} validation failed:`,
215
+ error instanceof Error ? error.message : error
216
+ );
214
217
  return null;
215
218
  }
216
219
  }
@@ -276,7 +279,9 @@ var DailyLogProcessor = class {
276
279
  const validatedEntry = dailyLogEntrySchema.parse(entry);
277
280
  return validatedEntry;
278
281
  } catch (error) {
279
- throw new Error(`Daily log entry conversion failed: ${error instanceof Error ? error.message : String(error)}`);
282
+ throw new Error(
283
+ `Daily log entry conversion failed: ${error instanceof Error ? error.message : String(error)}`
284
+ );
280
285
  }
281
286
  }
282
287
  /**
@@ -293,13 +298,17 @@ var DailyLogProcessor = class {
293
298
  static validateDataConsistency(entries) {
294
299
  const warnings = [];
295
300
  if (entries.length === 0) return warnings;
296
- const sortedEntries = [...entries].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
301
+ const sortedEntries = [...entries].sort(
302
+ (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()
303
+ );
297
304
  for (let i = 1; i < sortedEntries.length; i++) {
298
305
  const prevDate = new Date(sortedEntries[i - 1].date);
299
306
  const currentDate = new Date(sortedEntries[i].date);
300
307
  const daysDiff = (currentDate.getTime() - prevDate.getTime()) / (1e3 * 60 * 60 * 24);
301
308
  if (daysDiff > 7) {
302
- warnings.push(`Large date gap detected: ${daysDiff.toFixed(0)} days between ${prevDate.toISOString().split("T")[0]} and ${currentDate.toISOString().split("T")[0]}`);
309
+ warnings.push(
310
+ `Large date gap detected: ${daysDiff.toFixed(0)} days between ${prevDate.toISOString().split("T")[0]} and ${currentDate.toISOString().split("T")[0]}`
311
+ );
303
312
  }
304
313
  }
305
314
  const negativeEntries = sortedEntries.filter((entry) => entry.netLiquidity < 0);
@@ -317,4 +326,4 @@ var DailyLogProcessor = class {
317
326
  export {
318
327
  DailyLogProcessor
319
328
  };
320
- //# sourceMappingURL=chunk-W2PP3LEH.js.map
329
+ //# sourceMappingURL=chunk-ZTJXC3LS.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":[]}
@@ -0,0 +1,9 @@
1
+ import {
2
+ DailyLogProcessor
3
+ } from "./chunk-ZTJXC3LS.js";
4
+ import "./chunk-YZA4RS76.js";
5
+ import "./chunk-5WRI5ZAA.js";
6
+ export {
7
+ DailyLogProcessor
8
+ };
9
+ //# sourceMappingURL=daily-log-processor-MZW2XBZY.js.map
@@ -11,7 +11,7 @@ import {
11
11
  getPortfolioValueTimeSeries,
12
12
  getWeeklyPl,
13
13
  updateDailyLogsForBlock
14
- } from "./chunk-LDKTV7GW.js";
14
+ } from "./chunk-27S67XW3.js";
15
15
  import "./chunk-5WRI5ZAA.js";
16
16
  export {
17
17
  addDailyLogEntries,
@@ -27,4 +27,4 @@ export {
27
27
  getWeeklyPl,
28
28
  updateDailyLogsForBlock
29
29
  };
30
- //# sourceMappingURL=daily-logs-store-D3QC2XPV.js.map
30
+ //# sourceMappingURL=daily-logs-store-3C3OTGPK.js.map
@@ -218,7 +218,15 @@ function computeLegGreeks(optionPrice, underlyingPrice, strike, dte, type, riskF
218
218
  const bsType = type === "C" ? "call" : "put";
219
219
  const nullResult = { delta: null, gamma: null, theta: null, vega: null, iv: null };
220
220
  if (dte < BACHELIER_DTE_THRESHOLD) {
221
- const iv2 = solveNormalIV(bsType, optionPrice, underlyingPrice, strike, T, riskFreeRate, dividendYield);
221
+ const iv2 = solveNormalIV(
222
+ bsType,
223
+ optionPrice,
224
+ underlyingPrice,
225
+ strike,
226
+ T,
227
+ riskFreeRate,
228
+ dividendYield
229
+ );
222
230
  if (iv2 === null) return nullResult;
223
231
  return {
224
232
  delta: bachelierDelta(bsType, underlyingPrice, strike, T, riskFreeRate, dividendYield, iv2),
@@ -0,0 +1,16 @@
1
+ import {
2
+ _resetProvider,
3
+ getProvider
4
+ } from "./chunk-NWNSKALN.js";
5
+ import "./chunk-2I3S2ZLT.js";
6
+ import "./chunk-WNTYA7ER.js";
7
+ import "./chunk-ZTJXC3LS.js";
8
+ import "./chunk-YZA4RS76.js";
9
+ import "./chunk-27S67XW3.js";
10
+ import "./chunk-2E63THNI.js";
11
+ import "./chunk-5WRI5ZAA.js";
12
+ export {
13
+ _resetProvider,
14
+ getProvider
15
+ };
16
+ //# sourceMappingURL=market-provider-6U33TQUT.js.map
@@ -2,11 +2,11 @@ import {
2
2
  loadMddsGrpcPackage,
3
3
  loadMddsProtoRoot,
4
4
  resolveMddsProtoPath
5
- } from "./chunk-FGZH632F.js";
5
+ } from "./chunk-2E63THNI.js";
6
6
  import "./chunk-5WRI5ZAA.js";
7
7
  export {
8
8
  loadMddsGrpcPackage,
9
9
  loadMddsProtoRoot,
10
10
  resolveMddsProtoPath
11
11
  };
12
- //# sourceMappingURL=proto-3HZTCWK4.js.map
12
+ //# sourceMappingURL=proto-V23GIZG2.js.map
@@ -8,12 +8,12 @@ import {
8
8
  syncAllBlocks,
9
9
  syncBlock,
10
10
  upsertSyncMetadata
11
- } from "./chunk-4BLCXNQ6.js";
12
- import "./chunk-QTTR7AAW.js";
13
- import "./chunk-PRAYH3RT.js";
14
- import "./chunk-W2PP3LEH.js";
15
- import "./chunk-BKQ4PM4Y.js";
16
- import "./chunk-LDKTV7GW.js";
11
+ } from "./chunk-4HZLVUOZ.js";
12
+ import "./chunk-2I3S2ZLT.js";
13
+ import "./chunk-WNTYA7ER.js";
14
+ import "./chunk-ZTJXC3LS.js";
15
+ import "./chunk-YZA4RS76.js";
16
+ import "./chunk-27S67XW3.js";
17
17
  import "./chunk-5WRI5ZAA.js";
18
18
  export {
19
19
  deleteSyncMetadata,
@@ -26,4 +26,4 @@ export {
26
26
  syncBlock,
27
27
  upsertSyncMetadata
28
28
  };
29
- //# sourceMappingURL=sync-PO4IPCYV.js.map
29
+ //# sourceMappingURL=sync-3UBC37VW.js.map