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
@@ -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++) {
@@ -87,8 +85,9 @@ var CSVParser = class _CSVParser {
87
85
  return {
88
86
  data,
89
87
  headers: cleanHeaders,
90
- totalRows: totalRows - 1,
91
- // Excluding header
88
+ // Count data records actually encountered. Physical line count includes
89
+ // trailing blank lines, which are not CSV records when skipEmptyLines is on.
90
+ totalRows: processedRows,
92
91
  validRows,
93
92
  errors,
94
93
  warnings
@@ -262,8 +261,8 @@ var rawTradeDataSchema = z.object({
262
261
  "Date Opened": z.string().min(1, "Date Opened is required"),
263
262
  "Time Opened": z.string().min(1, "Time Opened is required"),
264
263
  "Opening Price": z.string().min(1, "Opening Price is required"),
265
- "Legs": z.string().min(1, "Legs description is required"),
266
- "Premium": z.string().min(1, "Premium is required"),
264
+ Legs: z.string().min(1, "Legs description is required"),
265
+ Premium: z.string().min(1, "Premium is required"),
267
266
  "Closing Price": z.string().optional(),
268
267
  "Date Closed": z.string().optional(),
269
268
  "Time Closed": z.string().optional(),
@@ -273,15 +272,15 @@ var rawTradeDataSchema = z.object({
273
272
  "No. of Contracts": z.string().min(1, "Number of Contracts is required"),
274
273
  "Funds at Close": z.string().min(1, "Funds at Close is required"),
275
274
  "Margin Req.": z.string().min(1, "Margin Requirement is required"),
276
- "Strategy": z.string().min(1, "Strategy is required"),
275
+ Strategy: z.string().min(1, "Strategy is required"),
277
276
  "Opening Commissions + Fees": z.string().default("0"),
278
277
  "Closing Commissions + Fees": z.string().optional(),
279
278
  "Opening Short/Long Ratio": z.string().default("0"),
280
279
  "Closing Short/Long Ratio": z.string().optional(),
281
280
  "Opening VIX": z.string().optional(),
282
281
  "Closing VIX": z.string().optional(),
283
- "Gap": z.string().optional(),
284
- "Movement": z.string().optional(),
282
+ Gap: z.string().optional(),
283
+ Movement: z.string().optional(),
285
284
  "Max Profit": z.string().optional(),
286
285
  "Max Loss": z.string().optional()
287
286
  }).passthrough();
@@ -316,11 +315,12 @@ var tradeSchema = z.object({
316
315
  customFields: z.record(z.string(), z.union([z.number(), z.string()])).optional()
317
316
  });
318
317
  var rawReportingTradeDataSchema = z.object({
319
- "Strategy": z.string().min(1, "Strategy is required"),
318
+ Strategy: z.string().min(1, "Strategy is required"),
319
+ Account: z.string().optional(),
320
320
  "Date Opened": z.string().min(1, "Date Opened is required"),
321
321
  "Time Opened": z.string().optional(),
322
322
  "Opening Price": z.string().min(1, "Opening Price is required"),
323
- "Legs": z.string().min(1, "Legs description is required"),
323
+ Legs: z.string().min(1, "Legs description is required"),
324
324
  "Initial Premium": z.string().min(1, "Initial Premium is required"),
325
325
  "No. of Contracts": z.string().min(1, "Number of Contracts is required"),
326
326
  "P/L": z.string().min(1, "P/L is required"),
@@ -328,12 +328,15 @@ var rawReportingTradeDataSchema = z.object({
328
328
  "Date Closed": z.string().optional(),
329
329
  "Time Closed": z.string().optional(),
330
330
  "Avg. Closing Cost": z.string().optional(),
331
- "Reason For Close": z.string().optional()
332
- });
331
+ "Reason For Close": z.string().optional(),
332
+ "Days in Trade": z.string().optional()
333
+ }).catchall(z.string());
333
334
  var reportingTradeSchema = z.object({
334
335
  strategy: z.string().min(1),
336
+ account: z.string().optional(),
335
337
  dateOpened: z.date(),
336
338
  timeOpened: z.string().optional(),
339
+ rawTimeOpened: z.string().optional(),
337
340
  openingPrice: z.number().finite(),
338
341
  legs: z.string().min(1),
339
342
  initialPremium: z.number().finite(),
@@ -342,14 +345,17 @@ var reportingTradeSchema = z.object({
342
345
  closingPrice: z.number().finite().optional(),
343
346
  dateClosed: z.date().optional(),
344
347
  timeClosed: z.string().optional(),
348
+ rawTimeClosed: z.string().optional(),
349
+ daysInTrade: z.number().finite().nonnegative().optional(),
345
350
  avgClosingCost: z.number().finite().optional(),
346
- reasonForClose: z.string().optional()
351
+ reasonForClose: z.string().optional(),
352
+ sourceFields: z.record(z.string(), z.string()).optional()
347
353
  });
348
354
  var rawDailyLogDataSchema = z.object({
349
- "Date": z.string().min(1, "Date is required"),
355
+ Date: z.string().min(1, "Date is required"),
350
356
  "Net Liquidity": z.string().min(1, "Net Liquidity is required"),
351
357
  "Current Funds": z.string().min(1, "Current Funds is required"),
352
- "Withdrawn": z.string().default("0"),
358
+ Withdrawn: z.string().default("0"),
353
359
  "Trading Funds": z.string().min(1, "Trading Funds is required"),
354
360
  "P/L": z.string().min(1, "P/L is required"),
355
361
  "P/L %": z.string().min(1, "P/L % is required"),
@@ -441,4 +447,4 @@ export {
441
447
  normalizeHeaders,
442
448
  findMissingHeaders
443
449
  };
444
- //# sourceMappingURL=chunk-5EBXHT6C.js.map
450
+ //# sourceMappingURL=chunk-SY6GDNVI.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":[]}
@@ -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(/* @__PURE__ */ new Set([
13
- resolve(moduleDir, "mdds.proto"),
14
- resolve(moduleDir, "../../../mdds.proto"),
15
- resolve(moduleDir, "../../../../server/mdds.proto"),
16
- resolve(moduleDir, "../../../../dist/mdds.proto"),
17
- resolve(process.cwd(), "packages/mcp-server/src/utils/providers/thetadata/mdds.proto"),
18
- resolve(process.cwd(), "src/utils/providers/thetadata/mdds.proto"),
19
- resolve(process.cwd(), "server/mdds.proto"),
20
- resolve(process.cwd(), "dist/mdds.proto")
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-FBNDMCT5.js.map
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
- "composite_block_date",
524
- ["blockId", "dateOpened"],
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
- INDEXES.REPORTING_LOGS_BY_STRATEGY,
553
- "strategy",
554
- { unique: false }
555
- );
556
- reportingStore.createIndex(
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
- INDEXES.STATIC_DATASET_ROWS_BY_DATASET,
600
- "datasetId",
601
- { unique: false }
602
- );
603
- staticDatasetRowsStore.createIndex(
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((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
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-NRFXAJF7.js.map
841
+ //# sourceMappingURL=chunk-W5E7FHC4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../lib/utils/combine-leg-groups.ts","../../lib/db/combined-trades-cache.ts","../../lib/db/performance-snapshot-cache.ts","../../lib/db/trades-store.ts","../../lib/db/index.ts","../../lib/db/daily-logs-store.ts"],"sourcesContent":["/**\n * Leg Group Combining Utility\n *\n * For MEIC (Multiple Entry Iron Condor) and similar strategies where the backtester\n * creates separate trade records for each leg group (e.g., calls and puts) that were\n * opened simultaneously but may have different exit conditions/times.\n *\n * This utility groups trades by entry timestamp and combines them into single trade records.\n */\n\nimport type { Trade } from \"../models/trade.ts\";\nimport type { ReportingTrade } from \"../models/reporting-trade.ts\";\nimport { yieldToMain, checkCancelled } from \"./async-helpers.ts\";\n\n/**\n * Key used to group trades that were opened at the same time\n */\nexport interface TradeGroupKey {\n dateOpened: string; // ISO date string\n timeOpened: string;\n strategy: string;\n}\n\n/**\n * Result of combining multiple leg groups into a single trade\n */\nexport interface CombinedTrade extends Trade {\n originalTradeCount: number; // Number of trades that were combined\n combinedLegs: string[]; // Array of leg strings from each trade\n}\n\n/**\n * Result of combining multiple ReportingTrade leg groups into a single trade\n */\nexport interface CombinedReportingTrade extends ReportingTrade {\n originalTradeCount: number; // Number of trades that were combined\n combinedLegs: string[]; // Array of leg strings from each trade\n}\n\n/**\n * Type guard to check if a Trade is a CombinedTrade\n */\nexport function isCombinedTrade(trade: Trade): trade is CombinedTrade {\n return \"originalTradeCount\" in trade && \"combinedLegs\" in trade;\n}\n\n/**\n * Type guard to check if a ReportingTrade is a CombinedReportingTrade\n */\nexport function isCombinedReportingTrade(trade: ReportingTrade): trade is CombinedReportingTrade {\n return \"originalTradeCount\" in trade && \"combinedLegs\" in trade;\n}\n\n/**\n * Generate a unique key for grouping trades by entry timestamp\n */\nfunction generateGroupKey(trade: Trade): string {\n const dateStr = trade.dateOpened.toISOString().split(\"T\")[0];\n return `${dateStr}|${trade.timeOpened}|${trade.strategy}`;\n}\n\n/**\n * Parse a group key back into its components\n * (Not currently used but kept for future API compatibility)\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nfunction parseGroupKey(key: string): TradeGroupKey {\n const [dateOpened, timeOpened, strategy] = key.split(\"|\");\n return { dateOpened, timeOpened, strategy };\n}\n\n/**\n * Group trades by their entry timestamp (date + time + strategy)\n * Returns a map where the key is the group identifier and value is array of trades\n */\nexport function groupTradesByEntry(trades: Trade[]): Map<string, Trade[]> {\n const groups = new Map<string, Trade[]>();\n\n for (const trade of trades) {\n const key = generateGroupKey(trade);\n const group = groups.get(key) || [];\n group.push(trade);\n groups.set(key, group);\n }\n\n return groups;\n}\n\n/**\n * Combine a group of trades that were opened at the same time into a single trade record\n *\n * Rules for combining:\n * - Opening fields: Use first trade's values (they should be identical)\n * - Closing fields: Use the last closing time among all trades\n * - Premium: Sum of all premiums\n * - P/L: Sum of all P/Ls\n * - Commissions: Sum of all commissions\n * - Margin: Use the maximum margin requirement\n * - Contracts: Sum of all contracts\n * - Legs: Concatenate all leg descriptions\n * - Closing price: Use weighted average based on premiums\n * - Funds at close: Use final funds from last closed trade\n */\nexport function combineLegGroup(trades: Trade[]): CombinedTrade {\n if (trades.length === 0) {\n throw new Error(\"Cannot combine empty trade group\");\n }\n\n // Sort trades by closing time (or use original order if not closed)\n const sortedTrades = [...trades].sort((a, b) => {\n if (!a.dateClosed && !b.dateClosed) return 0;\n if (!a.dateClosed) return 1;\n if (!b.dateClosed) return -1;\n\n const dateCompare = a.dateClosed.getTime() - b.dateClosed.getTime();\n if (dateCompare !== 0) return dateCompare;\n\n // Secondary sort by time if dates are equal\n const timeA = a.timeClosed || \"00:00:00\";\n const timeB = b.timeClosed || \"00:00:00\";\n return timeA.localeCompare(timeB);\n });\n\n // Use first trade as template (opening info should be identical)\n const firstTrade = sortedTrades[0];\n const lastTrade = sortedTrades[sortedTrades.length - 1];\n\n // Aggregate numeric values\n const totalPremium = trades.reduce((sum, t) => sum + t.premium, 0);\n const totalPL = trades.reduce((sum, t) => sum + t.pl, 0);\n // Use the contract size of the first leg to represent the \"Strategy Unit Size\"\n // e.g. A 10-lot Iron Condor has 4 legs of 10 contracts.\n // We want the combined trade to say \"10 contracts\" (10 ICs), not 40.\n const totalContracts = firstTrade.numContracts;\n const totalOpeningCommissions = trades.reduce((sum, t) => sum + t.openingCommissionsFees, 0);\n const totalClosingCommissions = trades.reduce((sum, t) => sum + t.closingCommissionsFees, 0);\n\n // For margin:\n // - Debit trades (totalPremium < 0): Sum margin (e.g. Straddle = Call + Put cost)\n // - Credit trades (totalPremium >= 0): Max margin (e.g. Iron Condor = Max(Call side, Put side))\n const maxMargin =\n totalPremium < 0\n ? trades.reduce((sum, t) => sum + t.marginReq, 0)\n : Math.max(...trades.map((t) => t.marginReq));\n\n // Calculate weighted average closing price\n let weightedClosingPrice: number | undefined;\n if (trades.every((t) => t.closingPrice !== undefined)) {\n const totalPremiumForClosedTrades = trades\n .filter((t) => t.closingPrice !== undefined)\n .reduce((sum, t) => sum + Math.abs(t.premium), 0);\n\n if (totalPremiumForClosedTrades > 0) {\n weightedClosingPrice =\n trades\n .filter((t) => t.closingPrice !== undefined)\n .reduce((sum, t) => sum + t.closingPrice! * Math.abs(t.premium), 0) /\n totalPremiumForClosedTrades;\n }\n }\n\n // Calculate total closing cost if all trades have it recorded\n let avgClosingCost: number | undefined;\n const tradesWithClosingCost = trades.filter((t) => t.avgClosingCost !== undefined);\n if (tradesWithClosingCost.length === trades.length) {\n avgClosingCost = tradesWithClosingCost.reduce((sum, t) => sum + t.avgClosingCost!, 0);\n }\n\n // Combine leg descriptions\n const combinedLegs = trades.map((t) => t.legs);\n const combinedLegsString = combinedLegs.join(\" | \");\n\n // Use last trade's closing information (latest exit)\n const dateClosed = lastTrade.dateClosed;\n const timeClosed = lastTrade.timeClosed;\n const reasonForClose = lastTrade.reasonForClose;\n const fundsAtClose = lastTrade.fundsAtClose;\n const closingShortLongRatio = lastTrade.closingShortLongRatio;\n const closingVix = lastTrade.closingVix;\n\n // Calculate combined opening short/long ratio (weighted by premium)\n const totalAbsPremium = trades.reduce((sum, t) => sum + Math.abs(t.premium), 0);\n const weightedOpeningShortLongRatio =\n totalAbsPremium > 0\n ? trades.reduce((sum, t) => sum + t.openingShortLongRatio * Math.abs(t.premium), 0) /\n totalAbsPremium\n : trades[0].openingShortLongRatio;\n\n // For optional fields, use first trade's value or undefined\n const gap = firstTrade.gap;\n const movement = firstTrade.movement;\n\n // Max profit/loss handling:\n // - For single trades (originalTradeCount === 1): preserve original percentage values from CSV\n // - For combined trades (originalTradeCount > 1): derive dollar amounts from margin\n let maxProfit: number | undefined;\n let maxLoss: number | undefined;\n\n if (trades.length === 1) {\n // Single trade: preserve original values (percentages from CSV)\n maxProfit = firstTrade.maxProfit;\n maxLoss = firstTrade.maxLoss;\n // For debit trades without explicit maxLoss, use premium paid as max loss\n if (maxLoss === undefined && firstTrade.premium < 0) {\n maxLoss = firstTrade.premium;\n }\n } else {\n // Combined trades: sum maxProfit, use margin for maxLoss\n if (trades.every((t) => t.maxProfit !== undefined)) {\n maxProfit = trades.reduce((sum, t) => sum + t.maxProfit!, 0);\n }\n\n // Use margin requirement as ground truth for worst-case loss\n if (maxMargin && Number.isFinite(maxMargin) && maxMargin > 0) {\n maxLoss = -maxMargin;\n } else if (trades.every((t) => t.maxLoss !== undefined)) {\n maxLoss = trades.reduce((sum, t) => sum + t.maxLoss!, 0);\n } else if (totalPremium < 0) {\n // Fallback: For debit trades, the max loss is at least the premium paid\n maxLoss = totalPremium;\n }\n }\n\n const combined: CombinedTrade = {\n // Opening information (from first trade)\n dateOpened: firstTrade.dateOpened,\n timeOpened: firstTrade.timeOpened,\n openingPrice: firstTrade.openingPrice,\n legs: combinedLegsString,\n premium: totalPremium,\n premiumPrecision: firstTrade.premiumPrecision,\n openingVix: firstTrade.openingVix,\n\n // Closing information (from last closed trade)\n closingPrice: weightedClosingPrice,\n dateClosed,\n timeClosed,\n avgClosingCost,\n reasonForClose,\n closingVix,\n\n // Aggregated values\n pl: totalPL,\n numContracts: totalContracts,\n fundsAtClose,\n marginReq: maxMargin,\n openingCommissionsFees: totalOpeningCommissions,\n closingCommissionsFees: totalClosingCommissions,\n\n // Strategy and ratios\n strategy: firstTrade.strategy,\n openingShortLongRatio: weightedOpeningShortLongRatio,\n closingShortLongRatio,\n\n // Optional market data\n gap,\n movement,\n maxProfit,\n maxLoss,\n\n // Combined trade metadata\n originalTradeCount: trades.length,\n combinedLegs,\n };\n\n return combined;\n}\n\nexport interface CombineLegGroupsProgress {\n step: string;\n percent: number;\n}\n\nexport interface CombineLegGroupsOptions {\n onProgress?: (progress: CombineLegGroupsProgress) => void;\n signal?: AbortSignal;\n}\n\n/**\n * Process all trades and combine leg groups that share the same entry timestamp\n *\n * @param trades - Array of trades to process\n * @returns Array of trades with leg groups combined (single trades are preserved as-is)\n */\nexport function combineAllLegGroups(trades: Trade[]): CombinedTrade[] {\n const groups = groupTradesByEntry(trades);\n const combinedTrades: CombinedTrade[] = [];\n\n for (const [, groupTrades] of groups) {\n combinedTrades.push(combineLegGroup(groupTrades));\n }\n\n // Sort by date/time to maintain chronological order\n combinedTrades.sort((a, b) => {\n const dateCompare = a.dateOpened.getTime() - b.dateOpened.getTime();\n if (dateCompare !== 0) return dateCompare;\n return a.timeOpened.localeCompare(b.timeOpened);\n });\n\n return combinedTrades;\n}\n\n/**\n * Async version of combineAllLegGroups with progress reporting and cancellation support\n * Use this for large datasets to keep UI responsive\n *\n * @param trades - Array of trades to process\n * @param options - Progress callback and abort signal\n * @returns Array of trades with leg groups combined\n */\nexport async function combineAllLegGroupsAsync(\n trades: Trade[],\n options?: CombineLegGroupsOptions,\n): Promise<CombinedTrade[]> {\n const { onProgress, signal } = options ?? {};\n\n checkCancelled(signal);\n onProgress?.({ step: \"Grouping trades by entry\", percent: 0 });\n await yieldToMain();\n\n const groups = groupTradesByEntry(trades);\n const combinedTrades: CombinedTrade[] = [];\n const totalGroups = groups.size;\n let processedGroups = 0;\n\n checkCancelled(signal);\n onProgress?.({ step: \"Combining leg groups\", percent: 10 });\n\n for (const [, groupTrades] of groups) {\n combinedTrades.push(combineLegGroup(groupTrades));\n processedGroups++;\n\n // Yield every 100 groups to keep UI responsive\n if (processedGroups % 100 === 0) {\n checkCancelled(signal);\n const percent = 10 + Math.floor((processedGroups / totalGroups) * 70);\n onProgress?.({ step: `Combining leg groups (${processedGroups}/${totalGroups})`, percent });\n await yieldToMain();\n }\n }\n\n checkCancelled(signal);\n onProgress?.({ step: \"Sorting combined trades\", percent: 85 });\n await yieldToMain();\n\n // Sort by date/time to maintain chronological order\n combinedTrades.sort((a, b) => {\n const dateCompare = a.dateOpened.getTime() - b.dateOpened.getTime();\n if (dateCompare !== 0) return dateCompare;\n return a.timeOpened.localeCompare(b.timeOpened);\n });\n\n onProgress?.({ step: \"Complete\", percent: 100 });\n\n return combinedTrades;\n}\n\n/**\n * Identify which trades would be affected by combining leg groups\n * Useful for showing users what will change before they enable the feature\n *\n * @returns Object with statistics about grouping\n */\nexport function analyzeLegGroups(trades: Trade[]): {\n totalTrades: number;\n uniqueEntries: number;\n groupedEntries: number;\n maxGroupSize: number;\n avgGroupSize: number;\n groupSizeDistribution: Record<number, number>; // size -> count\n} {\n const groups = groupTradesByEntry(trades);\n\n const groupSizes = Array.from(groups.values()).map((g) => g.length);\n const groupedEntries = groupSizes.filter((size) => size > 1).length;\n\n const distribution: Record<number, number> = {};\n for (const size of groupSizes) {\n distribution[size] = (distribution[size] || 0) + 1;\n }\n\n return {\n totalTrades: trades.length,\n uniqueEntries: groups.size,\n groupedEntries,\n maxGroupSize: groupSizes.length > 0 ? Math.max(...groupSizes) : 0,\n avgGroupSize:\n groupSizes.length > 0 ? groupSizes.reduce((a, b) => a + b, 0) / groupSizes.length : 0,\n groupSizeDistribution: distribution,\n };\n}\n\n// =============================================================================\n// ReportingTrade Combining Functions\n// =============================================================================\n\n/**\n * Generate a unique key for grouping ReportingTrades by entry timestamp\n */\nfunction generateReportingGroupKey(trade: ReportingTrade): string {\n const dateStr = trade.dateOpened.toISOString().split(\"T\")[0];\n const timeStr = trade.timeOpened ?? \"00:00:00\";\n return `${dateStr}|${timeStr}|${trade.strategy}`;\n}\n\n/**\n * Group ReportingTrades by their entry timestamp (date + time + strategy)\n * Returns a map where the key is the group identifier and value is array of trades\n */\nexport function groupReportingTradesByEntry(\n trades: ReportingTrade[],\n): Map<string, ReportingTrade[]> {\n const groups = new Map<string, ReportingTrade[]>();\n\n for (const trade of trades) {\n const key = generateReportingGroupKey(trade);\n const group = groups.get(key) || [];\n group.push(trade);\n groups.set(key, group);\n }\n\n return groups;\n}\n\n/**\n * Combine a group of ReportingTrades that were opened at the same time into a single trade record\n *\n * Rules for combining (simpler than Trade - fewer fields):\n * - Opening fields: Use first trade's values (should be identical)\n * - Closing fields: Use the last closing time among all trades\n * - Premium: Sum of all initial premiums\n * - P/L: Sum of all P/Ls\n * - Contracts: Use first trade's contract count (strategy unit size)\n * - Legs: Concatenate all leg descriptions\n */\nexport function combineReportingLegGroup(trades: ReportingTrade[]): CombinedReportingTrade {\n if (trades.length === 0) {\n throw new Error(\"Cannot combine empty reporting trade group\");\n }\n\n // Sort trades by closing time\n const sortedTrades = [...trades].sort((a, b) => {\n if (!a.dateClosed && !b.dateClosed) return 0;\n if (!a.dateClosed) return 1;\n if (!b.dateClosed) return -1;\n\n const dateCompare = a.dateClosed.getTime() - b.dateClosed.getTime();\n if (dateCompare !== 0) return dateCompare;\n\n const timeA = a.timeClosed || \"00:00:00\";\n const timeB = b.timeClosed || \"00:00:00\";\n return timeA.localeCompare(timeB);\n });\n\n const firstTrade = sortedTrades[0];\n const lastTrade = sortedTrades[sortedTrades.length - 1];\n\n // Aggregate numeric values\n const totalPremium = trades.reduce((sum, t) => sum + t.initialPremium, 0);\n const totalPL = trades.reduce((sum, t) => sum + t.pl, 0);\n const totalContracts = firstTrade.numContracts; // Strategy unit size\n\n // Combine leg descriptions\n const combinedLegs = trades.map((t) => t.legs);\n const combinedLegsString = combinedLegs.join(\" | \");\n\n // Calculate total closing cost if all trades have it\n let avgClosingCost: number | undefined;\n const tradesWithClosingCost = trades.filter((t) => t.avgClosingCost !== undefined);\n if (tradesWithClosingCost.length === trades.length) {\n avgClosingCost = tradesWithClosingCost.reduce((sum, t) => sum + t.avgClosingCost!, 0);\n }\n\n // Weighted average closing price\n let closingPrice: number | undefined;\n if (trades.every((t) => t.closingPrice !== undefined)) {\n const totalPremiumAbs = trades.reduce((sum, t) => sum + Math.abs(t.initialPremium), 0);\n if (totalPremiumAbs > 0) {\n closingPrice =\n trades.reduce((sum, t) => sum + t.closingPrice! * Math.abs(t.initialPremium), 0) /\n totalPremiumAbs;\n }\n }\n\n return {\n strategy: firstTrade.strategy,\n dateOpened: firstTrade.dateOpened,\n timeOpened: firstTrade.timeOpened,\n openingPrice: firstTrade.openingPrice,\n legs: combinedLegsString,\n initialPremium: totalPremium,\n numContracts: totalContracts,\n pl: totalPL,\n closingPrice,\n dateClosed: lastTrade.dateClosed,\n timeClosed: lastTrade.timeClosed,\n avgClosingCost,\n reasonForClose: lastTrade.reasonForClose,\n originalTradeCount: trades.length,\n combinedLegs,\n };\n}\n\n/**\n * Process all ReportingTrades and combine leg groups that share the same entry timestamp\n *\n * @param trades - Array of ReportingTrades to process\n * @returns Array of trades with leg groups combined\n */\nexport function combineAllReportingLegGroups(trades: ReportingTrade[]): CombinedReportingTrade[] {\n const groups = groupReportingTradesByEntry(trades);\n const combinedTrades: CombinedReportingTrade[] = [];\n\n for (const [, groupTrades] of groups) {\n combinedTrades.push(combineReportingLegGroup(groupTrades));\n }\n\n // Sort chronologically\n combinedTrades.sort((a, b) => {\n const dateCompare = a.dateOpened.getTime() - b.dateOpened.getTime();\n if (dateCompare !== 0) return dateCompare;\n const timeA = a.timeOpened || \"00:00:00\";\n const timeB = b.timeOpened || \"00:00:00\";\n return timeA.localeCompare(timeB);\n });\n\n return combinedTrades;\n}\n","/**\n * Combined Trades Cache\n *\n * Caches pre-calculated combined leg group trades in IndexedDB\n * to avoid expensive recalculation on every page load.\n */\n\nimport type { CombinedTrade } from \"../utils/combine-leg-groups.ts\";\nimport {\n INDEXES,\n promisifyRequest,\n STORES,\n withReadTransaction,\n withWriteTransaction,\n} from \"./index.ts\";\n\n/**\n * Cache entry for combined trades\n */\ninterface CombinedTradesCache {\n id: string; // Format: `combined_trades_${blockId}`\n blockId: string;\n calculationType: \"combined_trades\";\n trades: CombinedTrade[];\n tradeCount: number;\n calculatedAt: Date;\n}\n\n/**\n * Generate the cache ID for a block\n */\nfunction getCacheId(blockId: string): string {\n return `combined_trades_${blockId}`;\n}\n\n/**\n * Store pre-calculated combined trades for a block\n */\nexport async function storeCombinedTradesCache(\n blockId: string,\n combinedTrades: CombinedTrade[],\n): Promise<void> {\n const cacheEntry: CombinedTradesCache = {\n id: getCacheId(blockId),\n blockId,\n calculationType: \"combined_trades\",\n trades: combinedTrades,\n tradeCount: combinedTrades.length,\n calculatedAt: new Date(),\n };\n\n await withWriteTransaction(STORES.CALCULATIONS, async (transaction) => {\n const store = transaction.objectStore(STORES.CALCULATIONS);\n await promisifyRequest(store.put(cacheEntry));\n });\n}\n\n/**\n * Get cached combined trades for a block\n * Returns null if cache doesn't exist\n */\nexport async function getCombinedTradesCache(blockId: string): Promise<CombinedTrade[] | null> {\n return withReadTransaction(STORES.CALCULATIONS, async (transaction) => {\n const store = transaction.objectStore(STORES.CALCULATIONS);\n const cacheId = getCacheId(blockId);\n const result = await promisifyRequest(store.get(cacheId));\n\n if (!result || result.calculationType !== \"combined_trades\") {\n return null;\n }\n\n const cache = result as CombinedTradesCache;\n\n // Restore Date objects that were serialized\n return cache.trades.map((trade) => ({\n ...trade,\n dateOpened: new Date(trade.dateOpened),\n dateClosed: trade.dateClosed ? new Date(trade.dateClosed) : undefined,\n }));\n });\n}\n\n/**\n * Delete cached combined trades for a block\n */\nexport async function deleteCombinedTradesCache(blockId: string): Promise<void> {\n await withWriteTransaction(STORES.CALCULATIONS, async (transaction) => {\n const store = transaction.objectStore(STORES.CALCULATIONS);\n const cacheId = getCacheId(blockId);\n\n // Check if entry exists before trying to delete\n const existing = await promisifyRequest(store.get(cacheId));\n if (existing) {\n await promisifyRequest(store.delete(cacheId));\n }\n });\n}\n\n/**\n * Check if combined trades cache exists for a block\n */\nexport async function hasCombinedTradesCache(blockId: string): Promise<boolean> {\n return withReadTransaction(STORES.CALCULATIONS, async (transaction) => {\n const store = transaction.objectStore(STORES.CALCULATIONS);\n const cacheId = getCacheId(blockId);\n const result = await promisifyRequest(store.get(cacheId));\n return result?.calculationType === \"combined_trades\";\n });\n}\n\n/**\n * Invalidate all calculation caches for a block\n * (including combined trades cache)\n */\nexport async function invalidateBlockCaches(blockId: string): Promise<void> {\n await withWriteTransaction(STORES.CALCULATIONS, async (transaction) => {\n const store = transaction.objectStore(STORES.CALCULATIONS);\n const index = store.index(INDEXES.CALCULATIONS_BY_BLOCK);\n const request = index.openCursor(IDBKeyRange.only(blockId));\n\n await new Promise<void>((resolve, reject) => {\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n cursor.delete();\n cursor.continue();\n } else {\n resolve();\n }\n };\n request.onerror = () => reject(request.error);\n });\n });\n}\n","/**\n * Performance Snapshot Cache\n *\n * Caches pre-calculated performance snapshots in IndexedDB\n * to avoid expensive recalculation on every page load.\n */\n\nimport type { PortfolioStats } from \"../models/portfolio-stats.ts\";\nimport type { Trade } from \"../models/trade.ts\";\nimport type { DailyLogEntry } from \"../models/daily-log.ts\";\nimport type { SnapshotChartData } from \"../services/performance-snapshot.ts\";\nimport { promisifyRequest, STORES, withReadTransaction, withWriteTransaction } from \"./index.ts\";\n\n/**\n * Cache entry for performance snapshot\n */\ninterface PerformanceSnapshotCache {\n id: string; // Format: `performance_snapshot_${blockId}`\n blockId: string;\n calculationType: \"performance_snapshot\";\n portfolioStats: PortfolioStats;\n chartData: SnapshotChartData;\n filteredTrades: Trade[];\n filteredDailyLogs: DailyLogEntry[];\n calculatedAt: Date;\n}\n\n/**\n * Public interface for cached snapshot data\n */\nexport interface CachedPerformanceSnapshot {\n portfolioStats: PortfolioStats;\n chartData: SnapshotChartData;\n filteredTrades: Trade[];\n filteredDailyLogs: DailyLogEntry[];\n calculatedAt: Date;\n}\n\n/**\n * Generate the cache ID for a block\n */\nfunction getCacheId(blockId: string): string {\n return `performance_snapshot_${blockId}`;\n}\n\n/**\n * Store pre-calculated performance snapshot for a block\n */\nexport async function storePerformanceSnapshotCache(\n blockId: string,\n snapshot: {\n portfolioStats: PortfolioStats;\n chartData: SnapshotChartData;\n filteredTrades: Trade[];\n filteredDailyLogs: DailyLogEntry[];\n },\n): Promise<void> {\n const cacheEntry: PerformanceSnapshotCache = {\n id: getCacheId(blockId),\n blockId,\n calculationType: \"performance_snapshot\",\n portfolioStats: snapshot.portfolioStats,\n chartData: snapshot.chartData,\n filteredTrades: snapshot.filteredTrades,\n filteredDailyLogs: snapshot.filteredDailyLogs,\n calculatedAt: new Date(),\n };\n\n await withWriteTransaction(STORES.CALCULATIONS, async (transaction) => {\n const store = transaction.objectStore(STORES.CALCULATIONS);\n await promisifyRequest(store.put(cacheEntry));\n });\n}\n\n/**\n * Restore Date objects from serialized cache data\n */\nfunction restoreDates<T extends { dateOpened?: Date | string; dateClosed?: Date | string | null }>(\n items: T[],\n): T[] {\n return items.map((item) => ({\n ...item,\n dateOpened: item.dateOpened ? new Date(item.dateOpened) : undefined,\n dateClosed: item.dateClosed ? new Date(item.dateClosed) : undefined,\n }));\n}\n\n/**\n * Restore Date objects in daily logs\n */\nfunction restoreDailyLogDates(logs: DailyLogEntry[]): DailyLogEntry[] {\n return logs.map((log) => ({\n ...log,\n date: new Date(log.date),\n }));\n}\n\n/**\n * Restore Date objects in chart data\n */\nfunction restoreChartDataDates(chartData: SnapshotChartData): SnapshotChartData {\n return {\n ...chartData,\n // Most chart data uses ISO string dates, which is fine\n // Only restore where Date objects are expected\n };\n}\n\n/**\n * Get cached performance snapshot for a block\n * Returns null if cache doesn't exist\n */\nexport async function getPerformanceSnapshotCache(\n blockId: string,\n): Promise<CachedPerformanceSnapshot | null> {\n return withReadTransaction(STORES.CALCULATIONS, async (transaction) => {\n const store = transaction.objectStore(STORES.CALCULATIONS);\n const cacheId = getCacheId(blockId);\n const result = await promisifyRequest(store.get(cacheId));\n\n if (!result || result.calculationType !== \"performance_snapshot\") {\n return null;\n }\n\n const cache = result as PerformanceSnapshotCache;\n\n // Restore Date objects that were serialized\n return {\n portfolioStats: cache.portfolioStats,\n chartData: restoreChartDataDates(cache.chartData),\n filteredTrades: restoreDates(cache.filteredTrades) as Trade[],\n filteredDailyLogs: restoreDailyLogDates(cache.filteredDailyLogs),\n calculatedAt: new Date(cache.calculatedAt),\n };\n });\n}\n\n/**\n * Delete cached performance snapshot for a block\n */\nexport async function deletePerformanceSnapshotCache(blockId: string): Promise<void> {\n await withWriteTransaction(STORES.CALCULATIONS, async (transaction) => {\n const store = transaction.objectStore(STORES.CALCULATIONS);\n const cacheId = getCacheId(blockId);\n\n // Check if entry exists before trying to delete\n const existing = await promisifyRequest(store.get(cacheId));\n if (existing) {\n await promisifyRequest(store.delete(cacheId));\n }\n });\n}\n\n/**\n * Check if performance snapshot cache exists for a block\n */\nexport async function hasPerformanceSnapshotCache(blockId: string): Promise<boolean> {\n return withReadTransaction(STORES.CALCULATIONS, async (transaction) => {\n const store = transaction.objectStore(STORES.CALCULATIONS);\n const cacheId = getCacheId(blockId);\n const result = await promisifyRequest(store.get(cacheId));\n return result?.calculationType === \"performance_snapshot\";\n });\n}\n","/**\n * Trades Store - CRUD operations for trade data\n */\n\nimport type { Trade } from \"../models/trade.ts\";\nimport { combineAllLegGroups, type CombinedTrade } from \"../utils/combine-leg-groups.ts\";\nimport {\n INDEXES,\n promisifyRequest,\n STORES,\n withReadTransaction,\n withWriteTransaction,\n} from \"./index.ts\";\nimport {\n deleteCombinedTradesCache,\n getCombinedTradesCache,\n storeCombinedTradesCache,\n} from \"./combined-trades-cache.ts\";\nimport { deletePerformanceSnapshotCache } from \"./performance-snapshot-cache.ts\";\n\n// Track in-flight combined cache writes to avoid redundant work when multiple callers miss the cache simultaneously.\nconst combinedCacheInflight = new Map<string, Promise<void>>();\n\n/**\n * Extended trade with block association\n */\nexport interface StoredTrade extends Trade {\n blockId: string;\n id?: number; // Auto-generated by IndexedDB\n}\n\n/**\n * Add trades for a block (batch operation)\n */\nexport async function addTrades(blockId: string, trades: Trade[]): Promise<void> {\n if (trades.length === 0) return;\n\n await withWriteTransaction(STORES.TRADES, async (transaction) => {\n const store = transaction.objectStore(STORES.TRADES);\n\n // Use Promise.all for better performance with large datasets\n const promises = trades.map((trade) => {\n const storedTrade: StoredTrade = { ...trade, blockId };\n return promisifyRequest(store.add(storedTrade));\n });\n\n await Promise.all(promises);\n });\n\n // Invalidate caches since trades changed\n await deleteCombinedTradesCache(blockId);\n await deletePerformanceSnapshotCache(blockId);\n}\n\n/**\n * Get all trades for a block\n */\nexport async function getTradesByBlock(blockId: string): Promise<StoredTrade[]> {\n return withReadTransaction(STORES.TRADES, async (transaction) => {\n const store = transaction.objectStore(STORES.TRADES);\n const index = store.index(INDEXES.TRADES_BY_BLOCK);\n const result = await promisifyRequest(index.getAll(blockId));\n\n // Sort by date opened (chronological order)\n return result.sort((a, b) => {\n const dateA = new Date(a.dateOpened).getTime();\n const dateB = new Date(b.dateOpened).getTime();\n if (dateA !== dateB) return dateA - dateB;\n\n // If same date, sort by time\n return a.timeOpened.localeCompare(b.timeOpened);\n });\n });\n}\n\n/**\n * Get all trades for a block with optional leg group combining\n *\n * Uses cached combined trades when available for better performance.\n * Falls back to on-demand calculation if cache is missing.\n *\n * @param blockId - Block ID to fetch trades for\n * @param options.combineLegGroups - Whether to combine trades with same entry timestamp\n * @param options.skipCache - Force recalculation (bypass cache)\n * @returns Array of trades (combined or raw)\n */\nexport async function getTradesByBlockWithOptions(\n blockId: string,\n options: { combineLegGroups?: boolean; skipCache?: boolean } = {},\n): Promise<(StoredTrade | (CombinedTrade & { blockId: string }))[]> {\n // If combining is enabled, check cache FIRST before fetching raw trades\n // This avoids the expensive raw trade fetch when we have cached data\n if (options.combineLegGroups && !options.skipCache) {\n const cachedTrades = await getCombinedTradesCache(blockId);\n if (cachedTrades) {\n // Add blockId back to cached trades\n return cachedTrades.map((trade) => ({ ...trade, blockId }));\n }\n }\n\n // Fetch raw trades (only if not combining, or cache miss)\n const trades = await getTradesByBlock(blockId);\n\n if (!options.combineLegGroups) {\n return trades;\n }\n\n // Cache miss: calculate combined trades on-demand\n const tradesWithoutBlockId = trades.map((storedTrade) => {\n const { blockId: _ignored, ...trade } = storedTrade;\n void _ignored;\n return trade as Trade;\n });\n const combined = combineAllLegGroups(tradesWithoutBlockId);\n\n queueCombinedTradesCache(blockId, combined);\n\n // Add blockId back to combined trades\n return combined.map((trade) => ({ ...trade, blockId }));\n}\n\nfunction queueCombinedTradesCache(blockId: string, combinedTrades: CombinedTrade[]) {\n if (combinedCacheInflight.has(blockId)) {\n return;\n }\n\n const task = storeCombinedTradesCache(blockId, combinedTrades)\n .catch((err) => {\n console.warn(\"Failed to cache combined trades:\", err);\n })\n .finally(() => {\n combinedCacheInflight.delete(blockId);\n });\n\n combinedCacheInflight.set(blockId, task);\n}\n\n/**\n * Get trades by date range for a block\n */\nexport async function getTradesByDateRange(\n blockId: string,\n startDate: Date,\n endDate: Date,\n): Promise<StoredTrade[]> {\n return withReadTransaction(STORES.TRADES, async (transaction) => {\n const store = transaction.objectStore(STORES.TRADES);\n const index = store.index(\"composite_block_date\");\n\n // Create compound key range [blockId, startDate] to [blockId, endDate]\n const range = IDBKeyRange.bound([blockId, startDate], [blockId, endDate], false, false);\n\n const result = await promisifyRequest(index.getAll(range));\n return result;\n });\n}\n\n/**\n * Get trades by strategy for a block\n */\nexport async function getTradesByStrategy(\n blockId: string,\n strategy: string,\n): Promise<StoredTrade[]> {\n return withReadTransaction(STORES.TRADES, async (transaction) => {\n const store = transaction.objectStore(STORES.TRADES);\n const blockIndex = store.index(INDEXES.TRADES_BY_BLOCK);\n const blockTrades = await promisifyRequest(blockIndex.getAll(blockId));\n\n // Filter by strategy (IndexedDB doesn't support compound queries easily)\n return blockTrades.filter((trade) => trade.strategy === strategy);\n });\n}\n\n/**\n * Get unique strategies for a block\n */\nexport async function getStrategiesByBlock(blockId: string): Promise<string[]> {\n const trades = await getTradesByBlock(blockId);\n const strategies = new Set(trades.map((trade) => trade.strategy));\n return Array.from(strategies).sort();\n}\n\n/**\n * Get trade count by block\n */\nexport async function getTradeCountByBlock(blockId: string): Promise<number> {\n return withReadTransaction(STORES.TRADES, async (transaction) => {\n const store = transaction.objectStore(STORES.TRADES);\n const index = store.index(INDEXES.TRADES_BY_BLOCK);\n const result = await promisifyRequest(index.count(blockId));\n return result;\n });\n}\n\n/**\n * Delete all trades for a block\n */\nexport async function deleteTradesByBlock(blockId: string): Promise<void> {\n await withWriteTransaction(STORES.TRADES, async (transaction) => {\n const store = transaction.objectStore(STORES.TRADES);\n const index = store.index(INDEXES.TRADES_BY_BLOCK);\n const request = index.openCursor(IDBKeyRange.only(blockId));\n\n await new Promise<void>((resolve, reject) => {\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n cursor.delete();\n cursor.continue();\n } else {\n resolve();\n }\n };\n request.onerror = () => reject(request.error);\n });\n });\n\n // Invalidate caches since trades changed\n await deleteCombinedTradesCache(blockId);\n await deletePerformanceSnapshotCache(blockId);\n}\n\n/**\n * Update trades for a block (replace all)\n */\nexport async function updateTradesForBlock(blockId: string, trades: Trade[]): Promise<void> {\n await withWriteTransaction(STORES.TRADES, async (transaction) => {\n // First delete existing trades\n const store = transaction.objectStore(STORES.TRADES);\n const index = store.index(INDEXES.TRADES_BY_BLOCK);\n const deleteRequest = index.openCursor(IDBKeyRange.only(blockId));\n\n await new Promise<void>((resolve, reject) => {\n deleteRequest.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n cursor.delete();\n cursor.continue();\n } else {\n resolve();\n }\n };\n deleteRequest.onerror = () => reject(deleteRequest.error);\n });\n\n // Then add new trades\n const promises = trades.map((trade) => {\n const storedTrade: StoredTrade = { ...trade, blockId };\n return promisifyRequest(store.add(storedTrade));\n });\n\n await Promise.all(promises);\n });\n\n // Invalidate caches since trades changed\n await deleteCombinedTradesCache(blockId);\n await deletePerformanceSnapshotCache(blockId);\n}\n\n/**\n * Get trade statistics for a block (aggregated)\n */\nexport async function getTradeStatistics(blockId: string): Promise<{\n totalTrades: number;\n totalPl: number;\n winningTrades: number;\n losingTrades: number;\n totalCommissions: number;\n dateRange: { start: Date | null; end: Date | null };\n strategies: string[];\n}> {\n const trades = await getTradesByBlock(blockId);\n\n if (trades.length === 0) {\n return {\n totalTrades: 0,\n totalPl: 0,\n winningTrades: 0,\n losingTrades: 0,\n totalCommissions: 0,\n dateRange: { start: null, end: null },\n strategies: [],\n };\n }\n\n const totalTrades = trades.length;\n const totalPl = trades.reduce((sum, trade) => sum + trade.pl, 0);\n const winningTrades = trades.filter((trade) => trade.pl > 0).length;\n const losingTrades = trades.filter((trade) => trade.pl < 0).length;\n const totalCommissions = trades.reduce(\n (sum, trade) => sum + trade.openingCommissionsFees + trade.closingCommissionsFees,\n 0,\n );\n\n // Get date range\n const dates = trades.map((trade) => new Date(trade.dateOpened));\n const start = new Date(Math.min(...dates.map((d) => d.getTime())));\n const end = new Date(Math.max(...dates.map((d) => d.getTime())));\n\n // Get unique strategies\n const strategies = Array.from(new Set(trades.map((trade) => trade.strategy))).sort();\n\n return {\n totalTrades,\n totalPl,\n winningTrades,\n losingTrades,\n totalCommissions,\n dateRange: { start, end },\n strategies,\n };\n}\n\n/**\n * Search trades by text (strategy, legs, reason for close)\n */\nexport async function searchTrades(blockId: string, query: string): Promise<StoredTrade[]> {\n const trades = await getTradesByBlock(blockId);\n const lowerQuery = query.toLowerCase();\n\n return trades.filter(\n (trade) =>\n trade.strategy.toLowerCase().includes(lowerQuery) ||\n trade.legs.toLowerCase().includes(lowerQuery) ||\n (trade.reasonForClose && trade.reasonForClose.toLowerCase().includes(lowerQuery)),\n );\n}\n\n/**\n * Get trades with pagination\n */\nexport async function getTradesPage(\n blockId: string,\n offset: number,\n limit: number,\n): Promise<{ trades: StoredTrade[]; total: number }> {\n const allTrades = await getTradesByBlock(blockId);\n const total = allTrades.length;\n const trades = allTrades.slice(offset, offset + limit);\n\n return { trades, total };\n}\n\n/**\n * Export trades to CSV format (for backup/analysis)\n */\nexport async function exportTradesToCSV(blockId: string): Promise<string> {\n const trades = await getTradesByBlock(blockId);\n\n if (trades.length === 0) {\n return \"\";\n }\n\n // CSV headers\n const headers = [\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 \"Opening Commissions + Fees\",\n \"Closing Commissions + Fees\",\n \"Opening Short/Long Ratio\",\n \"Closing Short/Long Ratio\",\n \"Opening VIX\",\n \"Closing VIX\",\n \"Gap\",\n \"Movement\",\n \"Max Profit\",\n \"Max Loss\",\n ];\n\n // Convert trades to CSV rows\n const rows = trades.map((trade) => {\n // Format dateOpened - handle both Date objects and strings\n const dateOpened =\n trade.dateOpened instanceof Date\n ? trade.dateOpened.toISOString().split(\"T\")[0]\n : typeof trade.dateOpened === \"string\"\n ? new Date(trade.dateOpened).toISOString().split(\"T\")[0]\n : trade.dateOpened;\n\n return [\n dateOpened,\n trade.timeOpened,\n trade.openingPrice.toString(),\n trade.legs,\n trade.premium.toString(),\n trade.closingPrice?.toString() || \"\",\n trade.dateClosed instanceof Date\n ? trade.dateClosed.toISOString().split(\"T\")[0]\n : typeof trade.dateClosed === \"string\" && trade.dateClosed !== \"\"\n ? new Date(trade.dateClosed).toISOString().split(\"T\")[0]\n : \"\",\n trade.timeClosed || \"\",\n trade.avgClosingCost?.toString() || \"\",\n trade.reasonForClose || \"\",\n trade.pl.toString(),\n trade.numContracts.toString(),\n trade.fundsAtClose.toString(),\n trade.marginReq.toString(),\n trade.strategy,\n trade.openingCommissionsFees.toString(),\n trade.closingCommissionsFees.toString(),\n trade.openingShortLongRatio.toString(),\n trade.closingShortLongRatio?.toString() || \"\",\n trade.openingVix?.toString() || \"\",\n trade.closingVix?.toString() || \"\",\n trade.gap?.toString() || \"\",\n trade.movement?.toString() || \"\",\n trade.maxProfit?.toString() || \"\",\n trade.maxLoss?.toString() || \"\",\n ];\n });\n\n // Combine headers and rows\n const csvContent = [headers, ...rows]\n .map((row) => row.map((field) => `\"${field}\"`).join(\",\"))\n .join(\"\\n\");\n\n return csvContent;\n}\n","/**\n * IndexedDB Database Service for TradeBlocks\n *\n * Manages the client-side database for storing blocks, trades, and daily logs.\n * Uses a versioned schema with migration support.\n */\n\n// Types imported for reference (commented out to avoid unused warnings)\n// import { ProcessedBlock } from '../models/block.ts'\n// import { Trade } from '../models/trade.ts'\n// import { DailyLogEntry } from '../models/daily-log.ts'\n// import { PortfolioStats, StrategyStats, PerformanceMetrics } from '../models/portfolio-stats.ts'\n\n// Database configuration\nexport const DB_NAME = \"TradeBlocksDB\";\nexport const DB_VERSION = 4;\n\n// Object store names\nexport const STORES = {\n BLOCKS: \"blocks\",\n TRADES: \"trades\",\n DAILY_LOGS: \"dailyLogs\",\n CALCULATIONS: \"calculations\",\n REPORTING_LOGS: \"reportingLogs\",\n WALK_FORWARD: \"walkForwardAnalyses\",\n STATIC_DATASETS: \"staticDatasets\",\n STATIC_DATASET_ROWS: \"staticDatasetRows\",\n} as const;\n\n// Index names\nexport const INDEXES = {\n TRADES_BY_BLOCK: \"blockId\",\n TRADES_BY_DATE: \"dateOpened\",\n TRADES_BY_STRATEGY: \"strategy\",\n DAILY_LOGS_BY_BLOCK: \"blockId\",\n DAILY_LOGS_BY_DATE: \"date\",\n CALCULATIONS_BY_BLOCK: \"blockId\",\n REPORTING_LOGS_BY_BLOCK: \"blockId\",\n REPORTING_LOGS_BY_STRATEGY: \"strategy\",\n WALK_FORWARD_BY_BLOCK: \"blockId\",\n STATIC_DATASET_ROWS_BY_DATASET: \"datasetId\",\n STATIC_DATASET_ROWS_BY_TIMESTAMP: \"timestamp\",\n} as const;\n\n/**\n * Database instance singleton\n */\nlet dbInstance: IDBDatabase | null = null;\n\n/**\n * Initialize the IndexedDB database\n */\nexport async function initializeDatabase(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n if (dbInstance) {\n resolve(dbInstance);\n return;\n }\n\n const request = indexedDB.open(DB_NAME, DB_VERSION);\n\n request.onerror = () => {\n reject(new Error(`Failed to open database: ${request.error?.message}`));\n };\n\n request.onsuccess = () => {\n dbInstance = request.result;\n resolve(dbInstance);\n };\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n const transaction = (event.target as IDBOpenDBRequest).transaction!;\n\n // Create blocks store\n if (!db.objectStoreNames.contains(STORES.BLOCKS)) {\n const blocksStore = db.createObjectStore(STORES.BLOCKS, {\n keyPath: \"id\",\n });\n blocksStore.createIndex(\"name\", \"name\", { unique: false });\n blocksStore.createIndex(\"isActive\", \"isActive\", { unique: false });\n blocksStore.createIndex(\"created\", \"created\", { unique: false });\n blocksStore.createIndex(\"lastModified\", \"lastModified\", {\n unique: false,\n });\n }\n\n // Create trades store\n if (!db.objectStoreNames.contains(STORES.TRADES)) {\n const tradesStore = db.createObjectStore(STORES.TRADES, {\n autoIncrement: true,\n });\n tradesStore.createIndex(INDEXES.TRADES_BY_BLOCK, \"blockId\", {\n unique: false,\n });\n tradesStore.createIndex(INDEXES.TRADES_BY_DATE, \"dateOpened\", {\n unique: false,\n });\n tradesStore.createIndex(INDEXES.TRADES_BY_STRATEGY, \"strategy\", {\n unique: false,\n });\n tradesStore.createIndex(\"pl\", \"pl\", { unique: false });\n tradesStore.createIndex(\"composite_block_date\", [\"blockId\", \"dateOpened\"], {\n unique: false,\n });\n }\n\n // Create daily logs store\n if (!db.objectStoreNames.contains(STORES.DAILY_LOGS)) {\n const dailyLogsStore = db.createObjectStore(STORES.DAILY_LOGS, {\n autoIncrement: true,\n });\n dailyLogsStore.createIndex(INDEXES.DAILY_LOGS_BY_BLOCK, \"blockId\", {\n unique: false,\n });\n dailyLogsStore.createIndex(INDEXES.DAILY_LOGS_BY_DATE, \"date\", {\n unique: false,\n });\n dailyLogsStore.createIndex(\"composite_block_date\", [\"blockId\", \"date\"], { unique: false });\n }\n\n // Create reporting logs store\n if (!db.objectStoreNames.contains(STORES.REPORTING_LOGS)) {\n const reportingStore = db.createObjectStore(STORES.REPORTING_LOGS, {\n autoIncrement: true,\n });\n reportingStore.createIndex(INDEXES.REPORTING_LOGS_BY_BLOCK, \"blockId\", {\n unique: false,\n });\n reportingStore.createIndex(INDEXES.REPORTING_LOGS_BY_STRATEGY, \"strategy\", {\n unique: false,\n });\n reportingStore.createIndex(\"composite_block_date\", [\"blockId\", \"dateOpened\"], {\n unique: false,\n });\n }\n\n // Create calculations store (for cached computations)\n if (!db.objectStoreNames.contains(STORES.CALCULATIONS)) {\n const calculationsStore = db.createObjectStore(STORES.CALCULATIONS, {\n keyPath: \"id\",\n });\n calculationsStore.createIndex(INDEXES.CALCULATIONS_BY_BLOCK, \"blockId\", { unique: false });\n calculationsStore.createIndex(\"calculationType\", \"calculationType\", {\n unique: false,\n });\n calculationsStore.createIndex(\"calculatedAt\", \"calculatedAt\", {\n unique: false,\n });\n }\n\n // Create walk-forward analysis store\n if (!db.objectStoreNames.contains(STORES.WALK_FORWARD)) {\n const walkForwardStore = db.createObjectStore(STORES.WALK_FORWARD, {\n keyPath: \"id\",\n });\n walkForwardStore.createIndex(INDEXES.WALK_FORWARD_BY_BLOCK, \"blockId\", {\n unique: false,\n });\n walkForwardStore.createIndex(\"createdAt\", \"createdAt\", { unique: false });\n }\n\n // Create static datasets store (metadata)\n if (!db.objectStoreNames.contains(STORES.STATIC_DATASETS)) {\n const staticDatasetsStore = db.createObjectStore(STORES.STATIC_DATASETS, {\n keyPath: \"id\",\n });\n staticDatasetsStore.createIndex(\"name\", \"name\", { unique: true });\n staticDatasetsStore.createIndex(\"uploadedAt\", \"uploadedAt\", { unique: false });\n }\n\n // Create static dataset rows store (data rows)\n if (!db.objectStoreNames.contains(STORES.STATIC_DATASET_ROWS)) {\n const staticDatasetRowsStore = db.createObjectStore(STORES.STATIC_DATASET_ROWS, {\n autoIncrement: true,\n });\n staticDatasetRowsStore.createIndex(INDEXES.STATIC_DATASET_ROWS_BY_DATASET, \"datasetId\", {\n unique: false,\n });\n staticDatasetRowsStore.createIndex(INDEXES.STATIC_DATASET_ROWS_BY_TIMESTAMP, \"timestamp\", {\n unique: false,\n });\n staticDatasetRowsStore.createIndex(\n \"composite_dataset_timestamp\",\n [\"datasetId\", \"timestamp\"],\n { unique: false },\n );\n }\n\n transaction.oncomplete = () => {\n dbInstance = db;\n resolve(db);\n };\n };\n });\n}\n\n/**\n * Get database instance (initialize if needed)\n */\nexport async function getDatabase(): Promise<IDBDatabase> {\n if (dbInstance) {\n return dbInstance;\n }\n return initializeDatabase();\n}\n\n/**\n * Close database connection\n */\nexport function closeDatabase(): void {\n if (dbInstance) {\n dbInstance.close();\n dbInstance = null;\n }\n}\n\n/**\n * Delete the entire database (for testing/reset)\n * This version is more robust for corrupted databases:\n * - Doesn't require opening the database first\n * - Has timeout to prevent hanging forever\n * - Resolves on blocked (since deletion completes after reload)\n */\nexport async function deleteDatabase(): Promise<void> {\n return new Promise((resolve) => {\n // Close any existing connection (don't wait for it)\n if (dbInstance) {\n try {\n dbInstance.close();\n } catch {\n // Ignore close errors - database might be in bad state\n }\n dbInstance = null;\n }\n\n const deleteRequest = indexedDB.deleteDatabase(DB_NAME);\n\n // Timeout to prevent hanging forever on corrupted database\n const timeout = setTimeout(() => {\n console.warn(\"Database deletion timed out - will retry after reload\");\n resolve(); // Resolve anyway so we can reload\n }, 5000);\n\n deleteRequest.onsuccess = () => {\n clearTimeout(timeout);\n resolve();\n };\n\n deleteRequest.onerror = () => {\n clearTimeout(timeout);\n console.error(\"Failed to delete database:\", deleteRequest.error);\n // Still resolve - user can retry after page reload\n resolve();\n };\n\n deleteRequest.onblocked = () => {\n clearTimeout(timeout);\n console.warn(\"Database deletion blocked - will complete after reload\");\n // Resolve instead of reject - the deletion will complete once all connections close\n // After page reload, there will be no connections blocking it\n resolve();\n };\n });\n}\n\n/**\n * Transaction helper for read operations\n */\nexport async function withReadTransaction<T>(\n stores: string | string[],\n callback: (transaction: IDBTransaction) => Promise<T>,\n): Promise<T> {\n const db = await getDatabase();\n const storeNames = Array.isArray(stores) ? stores : [stores];\n const transaction = db.transaction(storeNames, \"readonly\");\n\n return callback(transaction);\n}\n\n/**\n * Transaction helper for write operations\n */\nexport async function withWriteTransaction<T>(\n stores: string | string[],\n callback: (transaction: IDBTransaction) => Promise<T>,\n): Promise<T> {\n const db = await getDatabase();\n const storeNames = Array.isArray(stores) ? stores : [stores];\n const transaction = db.transaction(storeNames, \"readwrite\");\n\n return callback(transaction);\n}\n\n/**\n * Generic helper for promisifying IDBRequest\n */\nexport function promisifyRequest<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\n/**\n * Storage quota management\n */\nexport interface StorageInfo {\n quota: number;\n usage: number;\n available: number;\n persistent: boolean;\n}\n\n/**\n * Get storage quota information\n */\nexport async function getStorageInfo(): Promise<StorageInfo> {\n if (\"storage\" in navigator && \"estimate\" in navigator.storage) {\n const estimate = await navigator.storage.estimate();\n const persistent = await navigator.storage.persisted();\n\n return {\n quota: estimate.quota || 0,\n usage: estimate.usage || 0,\n available: (estimate.quota || 0) - (estimate.usage || 0),\n persistent,\n };\n }\n\n // Fallback for browsers without storage API\n return {\n quota: 0,\n usage: 0,\n available: 0,\n persistent: false,\n };\n}\n\n/**\n * Request persistent storage\n */\nexport async function requestPersistentStorage(): Promise<boolean> {\n if (\"storage\" in navigator && \"persist\" in navigator.storage) {\n return navigator.storage.persist();\n }\n return false;\n}\n\n/**\n * Database error types\n */\nexport class DatabaseError extends Error {\n readonly operation: string;\n readonly store?: string;\n override readonly cause?: Error;\n constructor(message: string, operation: string, store?: string, cause?: Error) {\n super(message);\n this.name = \"DatabaseError\";\n this.operation = operation;\n this.store = store;\n this.cause = cause;\n }\n}\n\nexport class QuotaExceededError extends DatabaseError {\n constructor(operation: string, store?: string) {\n super(\"Storage quota exceeded\", operation, store);\n this.name = \"QuotaExceededError\";\n }\n}\n\nexport class TransactionError extends DatabaseError {\n constructor(message: string, operation: string, store?: string, cause?: Error) {\n super(message, operation, store, cause);\n this.name = \"TransactionError\";\n }\n}\n\n// Re-export functions from individual stores\nexport {\n createBlock,\n deleteBlock,\n getActiveBlock,\n getAllBlocks,\n getBlock,\n updateBlock,\n updateBlockStats,\n} from \"./blocks-store.ts\";\nexport {\n addDailyLogEntries,\n deleteDailyLogsByBlock,\n getDailyLogCountByBlock,\n getDailyLogsByBlock,\n updateDailyLogsForBlock,\n} from \"./daily-logs-store.ts\";\nexport type { StoredDailyLogEntry } from \"./daily-logs-store.ts\";\nexport {\n addReportingTrades,\n deleteReportingTradesByBlock,\n getReportingStrategiesByBlock,\n getReportingTradeCountByBlock,\n getReportingTradesByBlock,\n updateReportingTradesForBlock,\n} from \"./reporting-logs-store.ts\";\nexport {\n addTrades,\n deleteTradesByBlock,\n getTradeCountByBlock,\n getTradesByBlock,\n getTradesByBlockWithOptions,\n updateTradesForBlock,\n} from \"./trades-store.ts\";\nexport type { StoredTrade } from \"./trades-store.ts\";\nexport {\n saveWalkForwardAnalysis,\n getWalkForwardAnalysis,\n getWalkForwardAnalysesByBlock,\n deleteWalkForwardAnalysis,\n deleteWalkForwardAnalysesByBlock,\n} from \"./walk-forward-store.ts\";\nexport {\n storeCombinedTradesCache,\n getCombinedTradesCache,\n deleteCombinedTradesCache,\n hasCombinedTradesCache,\n invalidateBlockCaches,\n} from \"./combined-trades-cache.ts\";\nexport {\n storePerformanceSnapshotCache,\n getPerformanceSnapshotCache,\n deletePerformanceSnapshotCache,\n hasPerformanceSnapshotCache,\n} from \"./performance-snapshot-cache.ts\";\nexport type { CachedPerformanceSnapshot } from \"./performance-snapshot-cache.ts\";\nexport {\n storeEnrichedTradesCache,\n getEnrichedTradesCache,\n deleteEnrichedTradesCache,\n hasEnrichedTradesCache,\n} from \"./enriched-trades-cache.ts\";\nexport {\n createStaticDataset,\n getStaticDataset,\n getStaticDatasetByName,\n getAllStaticDatasets,\n updateStaticDatasetMatchStrategy,\n updateStaticDatasetName,\n deleteStaticDataset,\n isDatasetNameTaken,\n getStaticDatasetCount,\n} from \"./static-datasets-store.ts\";\nexport {\n addStaticDatasetRows,\n getStaticDatasetRows,\n getStaticDatasetRowsByRange,\n getStaticDatasetRowCount,\n deleteStaticDatasetRows,\n deleteStaticDatasetWithRows,\n getStaticDatasetDateRange,\n} from \"./static-dataset-rows-store.ts\";\n","/**\n * Daily Logs Store - CRUD operations for daily log data\n */\n\nimport type { DailyLogEntry } from \"../models/daily-log.ts\";\nimport {\n STORES,\n INDEXES,\n withReadTransaction,\n withWriteTransaction,\n promisifyRequest,\n} from \"./index.ts\";\n\n/**\n * Extended daily log entry with block association\n */\nexport interface StoredDailyLogEntry extends DailyLogEntry {\n blockId: string;\n id?: number; // Auto-generated by IndexedDB\n}\n\n/**\n * Add daily log entries for a block (batch operation)\n */\nexport async function addDailyLogEntries(blockId: string, entries: DailyLogEntry[]): Promise<void> {\n if (entries.length === 0) return;\n\n await withWriteTransaction(STORES.DAILY_LOGS, async (transaction) => {\n const store = transaction.objectStore(STORES.DAILY_LOGS);\n\n // Use Promise.all for better performance with large datasets\n const promises = entries.map((entry) => {\n const storedEntry: StoredDailyLogEntry = { ...entry, blockId };\n return promisifyRequest(store.add(storedEntry));\n });\n\n await Promise.all(promises);\n });\n}\n\n/**\n * Get all daily log entries for a block\n */\nexport async function getDailyLogsByBlock(blockId: string): Promise<StoredDailyLogEntry[]> {\n return withReadTransaction(STORES.DAILY_LOGS, async (transaction) => {\n const store = transaction.objectStore(STORES.DAILY_LOGS);\n const index = store.index(INDEXES.DAILY_LOGS_BY_BLOCK);\n const result = await promisifyRequest(index.getAll(blockId));\n\n // Sort by date (chronological order)\n return result.sort((a, b) => {\n const dateA = new Date(a.date).getTime();\n const dateB = new Date(b.date).getTime();\n return dateA - dateB;\n });\n });\n}\n\n/**\n * Get daily log entries by date range for a block\n */\nexport async function getDailyLogsByDateRange(\n blockId: string,\n startDate: Date,\n endDate: Date,\n): Promise<StoredDailyLogEntry[]> {\n return withReadTransaction(STORES.DAILY_LOGS, async (transaction) => {\n const store = transaction.objectStore(STORES.DAILY_LOGS);\n const index = store.index(\"composite_block_date\");\n\n // Create compound key range [blockId, startDate] to [blockId, endDate]\n const range = IDBKeyRange.bound([blockId, startDate], [blockId, endDate], false, false);\n\n const result = await promisifyRequest(index.getAll(range));\n return result;\n });\n}\n\n/**\n * Get daily log entry for a specific date\n */\nexport async function getDailyLogByDate(\n blockId: string,\n date: Date,\n): Promise<StoredDailyLogEntry | null> {\n const entries = await getDailyLogsByDateRange(blockId, date, date);\n return entries.length > 0 ? entries[0] : null;\n}\n\n/**\n * Get daily log count by block\n */\nexport async function getDailyLogCountByBlock(blockId: string): Promise<number> {\n return withReadTransaction(STORES.DAILY_LOGS, async (transaction) => {\n const store = transaction.objectStore(STORES.DAILY_LOGS);\n const index = store.index(INDEXES.DAILY_LOGS_BY_BLOCK);\n const result = await promisifyRequest(index.count(blockId));\n return result;\n });\n}\n\n/**\n * Delete all daily log entries for a block\n */\nexport async function deleteDailyLogsByBlock(blockId: string): Promise<void> {\n await withWriteTransaction(STORES.DAILY_LOGS, async (transaction) => {\n const store = transaction.objectStore(STORES.DAILY_LOGS);\n const index = store.index(INDEXES.DAILY_LOGS_BY_BLOCK);\n const request = index.openCursor(IDBKeyRange.only(blockId));\n\n await new Promise<void>((resolve, reject) => {\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n cursor.delete();\n cursor.continue();\n } else {\n resolve();\n }\n };\n request.onerror = () => reject(request.error);\n });\n });\n}\n\n/**\n * Update daily log entries for a block (replace all)\n */\nexport async function updateDailyLogsForBlock(\n blockId: string,\n entries: DailyLogEntry[],\n): Promise<void> {\n await withWriteTransaction(STORES.DAILY_LOGS, async (transaction) => {\n // First delete existing entries\n const store = transaction.objectStore(STORES.DAILY_LOGS);\n const index = store.index(INDEXES.DAILY_LOGS_BY_BLOCK);\n const deleteRequest = index.openCursor(IDBKeyRange.only(blockId));\n\n await new Promise<void>((resolve, reject) => {\n deleteRequest.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n cursor.delete();\n cursor.continue();\n } else {\n resolve();\n }\n };\n deleteRequest.onerror = () => reject(deleteRequest.error);\n });\n\n // Then add new entries\n const promises = entries.map((entry) => {\n const storedEntry: StoredDailyLogEntry = { ...entry, blockId };\n return promisifyRequest(store.add(storedEntry));\n });\n\n await Promise.all(promises);\n });\n}\n\n/**\n * Get daily log statistics for a block\n */\nexport async function getDailyLogStatistics(blockId: string): Promise<{\n totalEntries: number;\n dateRange: { start: Date | null; end: Date | null };\n finalPortfolioValue: number;\n maxDrawdown: number;\n totalPl: number;\n avgDailyPl: number;\n}> {\n const entries = await getDailyLogsByBlock(blockId);\n\n if (entries.length === 0) {\n return {\n totalEntries: 0,\n dateRange: { start: null, end: null },\n finalPortfolioValue: 0,\n maxDrawdown: 0,\n totalPl: 0,\n avgDailyPl: 0,\n };\n }\n\n const totalEntries = entries.length;\n\n // Get date range\n const dates = entries.map((entry) => new Date(entry.date));\n const start = new Date(Math.min(...dates.map((d) => d.getTime())));\n const end = new Date(Math.max(...dates.map((d) => d.getTime())));\n\n // Final portfolio value (last entry)\n const sortedEntries = entries.sort(\n (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),\n );\n const finalPortfolioValue = sortedEntries[0]?.netLiquidity || 0;\n\n // Calculate max drawdown (most negative value)\n const maxDrawdown = Math.min(...entries.map((entry) => entry.drawdownPct));\n\n // Total P/L (sum of all daily P/L)\n const totalPl = entries.reduce((sum, entry) => sum + entry.dailyPl, 0);\n\n // Average daily P/L\n const avgDailyPl = totalPl / totalEntries;\n\n return {\n totalEntries,\n dateRange: { start, end },\n finalPortfolioValue,\n maxDrawdown,\n totalPl,\n avgDailyPl,\n };\n}\n\n/**\n * Get portfolio value over time (for charts)\n */\nexport async function getPortfolioValueTimeSeries(blockId: string): Promise<\n Array<{\n date: Date;\n netLiquidity: number;\n dailyPl: number;\n drawdownPct: number;\n }>\n> {\n const entries = await getDailyLogsByBlock(blockId);\n\n return entries.map((entry) => ({\n date: new Date(entry.date),\n netLiquidity: entry.netLiquidity,\n dailyPl: entry.dailyPl,\n drawdownPct: entry.drawdownPct,\n }));\n}\n\n/**\n * Get daily P/L aggregated by month\n */\nexport async function getMonthlyPl(blockId: string): Promise<Record<string, number>> {\n const entries = await getDailyLogsByBlock(blockId);\n const monthlyPl: Record<string, number> = {};\n\n entries.forEach((entry) => {\n const date = new Date(entry.date);\n const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, \"0\")}`;\n\n if (!monthlyPl[monthKey]) {\n monthlyPl[monthKey] = 0;\n }\n monthlyPl[monthKey] += entry.dailyPl;\n });\n\n return monthlyPl;\n}\n\n/**\n * Get daily P/L aggregated by week\n */\nexport async function getWeeklyPl(blockId: string): Promise<Record<string, number>> {\n const entries = await getDailyLogsByBlock(blockId);\n const weeklyPl: Record<string, number> = {};\n\n entries.forEach((entry) => {\n const date = new Date(entry.date);\n const year = date.getFullYear();\n const week = getWeekNumber(date);\n const weekKey = `${year}-W${String(week).padStart(2, \"0\")}`;\n\n if (!weeklyPl[weekKey]) {\n weeklyPl[weekKey] = 0;\n }\n weeklyPl[weekKey] += entry.dailyPl;\n });\n\n return weeklyPl;\n}\n\n/**\n * Export daily logs to CSV format\n */\nexport async function exportDailyLogsToCSV(blockId: string): Promise<string> {\n const entries = await getDailyLogsByBlock(blockId);\n\n if (entries.length === 0) {\n return \"\";\n }\n\n // CSV headers\n const headers = [\n \"Date\",\n \"Net Liquidity\",\n \"Current Funds\",\n \"Withdrawn\",\n \"Trading Funds\",\n \"P/L\",\n \"P/L %\",\n \"Drawdown %\",\n ];\n\n // Convert entries to CSV rows\n const rows = entries.map((entry) => [\n entry.date instanceof Date ? entry.date.toISOString().split(\"T\")[0] : entry.date,\n entry.netLiquidity.toString(),\n entry.currentFunds.toString(),\n entry.withdrawn.toString(),\n entry.tradingFunds.toString(),\n entry.dailyPl.toString(),\n entry.dailyPlPct.toString(),\n entry.drawdownPct.toString(),\n ]);\n\n // Combine headers and rows\n const csvContent = [headers, ...rows]\n .map((row) => row.map((field) => `\"${field}\"`).join(\",\"))\n .join(\"\\n\");\n\n return csvContent;\n}\n\n/**\n * Helper function to get week number\n */\nfunction getWeekNumber(date: Date): number {\n const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));\n const dayNum = d.getUTCDay() || 7;\n d.setUTCDate(d.getUTCDate() + 4 - dayNum);\n const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));\n return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);\n}\n"],"mappings":"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","names":["getCacheId","getCacheId"]}
@@ -38,4 +38,4 @@ function loadAuthConfig(options = {}) {
38
38
  export {
39
39
  loadAuthConfig
40
40
  };
41
- //# sourceMappingURL=config-6IZXEFEX.js.map
41
+ //# sourceMappingURL=config-DK7KOMNL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth/config.ts"],"sourcesContent":["export interface AuthConfig {\n username: string;\n password: string;\n jwtSecret: string;\n jwtExpiry: string;\n issuerUrl?: string;\n noAuth: boolean;\n}\n\nexport function loadAuthConfig(options: { noAuth?: boolean } = {}): AuthConfig {\n const noAuth = options.noAuth || process.env.TRADEBLOCKS_NO_AUTH === \"true\";\n\n if (noAuth) {\n return {\n username: \"\",\n password: \"\",\n jwtSecret: \"\",\n jwtExpiry: \"24h\",\n noAuth: true,\n };\n }\n\n const username = process.env.TRADEBLOCKS_USERNAME;\n const password = process.env.TRADEBLOCKS_PASSWORD;\n const jwtSecret = process.env.TRADEBLOCKS_JWT_SECRET;\n const jwtExpiry = process.env.TRADEBLOCKS_JWT_EXPIRY || \"24h\";\n const issuerUrl = process.env.TRADEBLOCKS_ISSUER_URL;\n\n if (!username) {\n throw new Error(\n \"TRADEBLOCKS_USERNAME is required for HTTP mode.\\n\" +\n \"Set it in your .env file or pass --no-auth to disable authentication.\",\n );\n }\n if (!password) {\n throw new Error(\n \"TRADEBLOCKS_PASSWORD is required for HTTP mode.\\n\" + \"Set it in your .env file.\",\n );\n }\n if (!jwtSecret) {\n throw new Error(\n \"TRADEBLOCKS_JWT_SECRET is required for HTTP mode.\\n\" +\n \"Generate one with: openssl rand -hex 32\",\n );\n }\n\n return { username, password, jwtSecret, jwtExpiry, issuerUrl, noAuth: false };\n}\n"],"mappings":";;;;AASO,SAAS,eAAe,UAAgC,CAAC,GAAe;AAC7E,QAAM,SAAS,QAAQ,UAAU,QAAQ,IAAI,wBAAwB;AAErE,MAAI,QAAQ;AACV,WAAO;AAAA,MACL,UAAU;AAAA,MACV,UAAU;AAAA,MACV,WAAW;AAAA,MACX,WAAW;AAAA,MACX,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,YAAY,QAAQ,IAAI;AAC9B,QAAM,YAAY,QAAQ,IAAI,0BAA0B;AACxD,QAAM,YAAY,QAAQ,IAAI;AAE9B,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,UAAU,WAAW,WAAW,WAAW,QAAQ,MAAM;AAC9E;","names":[]}