backtest-kit 1.1.7 → 1.1.9

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 (5) hide show
  1. package/README.md +809 -841
  2. package/build/index.cjs +4404 -577
  3. package/build/index.mjs +4383 -577
  4. package/package.json +2 -2
  5. package/types.d.ts +3259 -486
package/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as di_scoped from 'di-scoped';
2
2
  import * as functools_kit from 'functools-kit';
3
+ import { Subject } from 'functools-kit';
3
4
 
4
5
  /**
5
6
  * Interface representing a logging mechanism for the swarm system.
@@ -353,6 +354,137 @@ declare const MethodContextService: (new () => {
353
354
  };
354
355
  }, "prototype"> & di_scoped.IScopedClassRun<[context: IMethodContext]>;
355
356
 
357
+ /**
358
+ * Risk check arguments for evaluating whether to allow opening a new position.
359
+ * Called BEFORE signal creation to validate if conditions allow new signals.
360
+ * Contains only passthrough arguments from ClientStrategy context.
361
+ */
362
+ interface IRiskCheckArgs {
363
+ /** Trading pair symbol (e.g., "BTCUSDT") */
364
+ symbol: string;
365
+ /** Strategy name requesting to open a position */
366
+ strategyName: StrategyName;
367
+ /** Exchange name */
368
+ exchangeName: ExchangeName;
369
+ /** Current VWAP price */
370
+ currentPrice: number;
371
+ /** Current timestamp */
372
+ timestamp: number;
373
+ }
374
+ /**
375
+ * Active position tracked by ClientRisk for cross-strategy analysis.
376
+ */
377
+ interface IRiskActivePosition {
378
+ /** Signal details for the active position */
379
+ signal: ISignalRow;
380
+ /** Strategy name owning the position */
381
+ strategyName: string;
382
+ /** Exchange name */
383
+ exchangeName: string;
384
+ /** Timestamp when the position was opened */
385
+ openTimestamp: number;
386
+ }
387
+ /**
388
+ * Optional callbacks for risk events.
389
+ */
390
+ interface IRiskCallbacks {
391
+ /** Called when a signal is rejected due to risk limits */
392
+ onRejected: (symbol: string, params: IRiskCheckArgs) => void;
393
+ /** Called when a signal passes risk checks */
394
+ onAllowed: (symbol: string, params: IRiskCheckArgs) => void;
395
+ }
396
+ /**
397
+ * Payload passed to risk validation functions.
398
+ * Extends IRiskCheckArgs with portfolio state data.
399
+ */
400
+ interface IRiskValidationPayload extends IRiskCheckArgs {
401
+ /** Number of currently active positions across all strategies */
402
+ activePositionCount: number;
403
+ /** List of currently active positions across all strategies */
404
+ activePositions: IRiskActivePosition[];
405
+ }
406
+ /**
407
+ * Risk validation function type.
408
+ * Validates risk parameters and throws error if validation fails.
409
+ */
410
+ interface IRiskValidationFn {
411
+ (payload: IRiskValidationPayload): void | Promise<void>;
412
+ }
413
+ /**
414
+ * Risk validation configuration.
415
+ * Defines validation logic with optional documentation.
416
+ */
417
+ interface IRiskValidation {
418
+ /**
419
+ * The validation function to apply to the risk check parameters.
420
+ */
421
+ validate: IRiskValidationFn;
422
+ /**
423
+ * Optional description for documentation purposes.
424
+ * Aids in understanding the purpose or behavior of the validation.
425
+ */
426
+ note?: string;
427
+ }
428
+ /**
429
+ * Risk schema registered via addRisk().
430
+ * Defines portfolio-level risk controls via custom validations.
431
+ */
432
+ interface IRiskSchema {
433
+ /** Unique risk profile identifier */
434
+ riskName: RiskName;
435
+ /** Optional developer note for documentation */
436
+ note?: string;
437
+ /** Optional lifecycle event callbacks (onRejected, onAllowed) */
438
+ callbacks?: Partial<IRiskCallbacks>;
439
+ /** Custom validations array for risk logic */
440
+ validations: (IRiskValidation | IRiskValidationFn)[];
441
+ }
442
+ /**
443
+ * Risk parameters passed to ClientRisk constructor.
444
+ * Combines schema with runtime dependencies.
445
+ */
446
+ interface IRiskParams extends IRiskSchema {
447
+ /** Logger service for debug output */
448
+ logger: ILogger;
449
+ }
450
+ /**
451
+ * Risk interface implemented by ClientRisk.
452
+ * Provides risk checking for signals and position tracking.
453
+ */
454
+ interface IRisk {
455
+ /**
456
+ * Check if a signal should be allowed based on risk limits.
457
+ *
458
+ * @param params - Risk check arguments (position size, portfolio state, etc.)
459
+ * @returns Promise resolving to risk check result
460
+ */
461
+ checkSignal: (params: IRiskCheckArgs) => Promise<boolean>;
462
+ /**
463
+ * Register a new opened signal/position.
464
+ *
465
+ * @param symbol - Trading pair symbol
466
+ * @param context - Context information (strategyName, riskName)
467
+ */
468
+ addSignal: (symbol: string, context: {
469
+ strategyName: string;
470
+ riskName: string;
471
+ }) => Promise<void>;
472
+ /**
473
+ * Remove a closed signal/position.
474
+ *
475
+ * @param symbol - Trading pair symbol
476
+ * @param context - Context information (strategyName, riskName)
477
+ */
478
+ removeSignal: (symbol: string, context: {
479
+ strategyName: string;
480
+ riskName: string;
481
+ }) => Promise<void>;
482
+ }
483
+ /**
484
+ * Unique risk profile identifier.
485
+ */
486
+ type RiskName = string;
487
+
356
488
  /**
357
489
  * Signal generation interval for throttling.
358
490
  * Enforces minimum time between getSignal calls.
@@ -427,6 +559,8 @@ interface IStrategySchema {
427
559
  getSignal: (symbol: string) => Promise<ISignalDto | null>;
428
560
  /** Optional lifecycle event callbacks (onOpen, onClose) */
429
561
  callbacks?: Partial<IStrategyCallbacks>;
562
+ /** Optional risk profile identifier for risk management */
563
+ riskName?: RiskName;
430
564
  }
431
565
  /**
432
566
  * Reason why signal was closed.
@@ -457,6 +591,8 @@ interface IStrategyTickResultIdle {
457
591
  strategyName: StrategyName;
458
592
  /** Exchange name for tracking idle events */
459
593
  exchangeName: ExchangeName;
594
+ /** Trading pair symbol (e.g., "BTCUSDT") */
595
+ symbol: string;
460
596
  /** Current VWAP price during idle state */
461
597
  currentPrice: number;
462
598
  }
@@ -473,6 +609,8 @@ interface IStrategyTickResultOpened {
473
609
  strategyName: StrategyName;
474
610
  /** Exchange name for tracking */
475
611
  exchangeName: ExchangeName;
612
+ /** Trading pair symbol (e.g., "BTCUSDT") */
613
+ symbol: string;
476
614
  /** Current VWAP price at signal open */
477
615
  currentPrice: number;
478
616
  }
@@ -491,6 +629,8 @@ interface IStrategyTickResultActive {
491
629
  strategyName: StrategyName;
492
630
  /** Exchange name for tracking */
493
631
  exchangeName: ExchangeName;
632
+ /** Trading pair symbol (e.g., "BTCUSDT") */
633
+ symbol: string;
494
634
  }
495
635
  /**
496
636
  * Tick result: signal closed with PNL.
@@ -513,6 +653,8 @@ interface IStrategyTickResultClosed {
513
653
  strategyName: StrategyName;
514
654
  /** Exchange name for tracking */
515
655
  exchangeName: ExchangeName;
656
+ /** Trading pair symbol (e.g., "BTCUSDT") */
657
+ symbol: string;
516
658
  }
517
659
  /**
518
660
  * Discriminated union of all tick results.
@@ -572,162 +714,776 @@ interface IStrategy {
572
714
  type StrategyName = string;
573
715
 
574
716
  /**
575
- * Registers a trading strategy in the framework.
576
- *
577
- * The strategy will be validated for:
578
- * - Signal validation (prices, TP/SL logic, timestamps)
579
- * - Interval throttling (prevents signal spam)
580
- * - Crash-safe persistence in live mode
581
- *
582
- * @param strategySchema - Strategy configuration object
583
- * @param strategySchema.strategyName - Unique strategy identifier
584
- * @param strategySchema.interval - Signal generation interval ("1m" | "3m" | "5m" | "15m" | "30m" | "1h")
585
- * @param strategySchema.getSignal - Async function that generates trading signals
586
- * @param strategySchema.callbacks - Optional lifecycle callbacks (onOpen, onClose)
587
- *
588
- * @example
589
- * ```typescript
590
- * addStrategy({
591
- * strategyName: "my-strategy",
592
- * interval: "5m",
593
- * getSignal: async (symbol) => ({
594
- * position: "long",
595
- * priceOpen: 50000,
596
- * priceTakeProfit: 51000,
597
- * priceStopLoss: 49000,
598
- * minuteEstimatedTime: 60,
599
- * timestamp: Date.now(),
600
- * }),
601
- * callbacks: {
602
- * onOpen: (symbol, signal, currentPrice, backtest) => console.log("Signal opened"),
603
- * onClose: (symbol, signal, priceClose, backtest) => console.log("Signal closed"),
604
- * },
605
- * });
606
- * ```
607
- */
608
- declare function addStrategy(strategySchema: IStrategySchema): void;
609
- /**
610
- * Registers an exchange data source in the framework.
611
- *
612
- * The exchange provides:
613
- * - Historical candle data via getCandles
614
- * - Price/quantity formatting for the exchange
615
- * - VWAP calculation from last 5 1m candles
616
- *
617
- * @param exchangeSchema - Exchange configuration object
618
- * @param exchangeSchema.exchangeName - Unique exchange identifier
619
- * @param exchangeSchema.getCandles - Async function to fetch candle data
620
- * @param exchangeSchema.formatPrice - Async function to format prices
621
- * @param exchangeSchema.formatQuantity - Async function to format quantities
622
- * @param exchangeSchema.callbacks - Optional callback for candle data events
623
- *
624
- * @example
625
- * ```typescript
626
- * addExchange({
627
- * exchangeName: "binance",
628
- * getCandles: async (symbol, interval, since, limit) => {
629
- * // Fetch from Binance API or database
630
- * return [{
631
- * timestamp: Date.now(),
632
- * open: 50000,
633
- * high: 51000,
634
- * low: 49000,
635
- * close: 50500,
636
- * volume: 1000,
637
- * }];
638
- * },
639
- * formatPrice: async (symbol, price) => price.toFixed(2),
640
- * formatQuantity: async (symbol, quantity) => quantity.toFixed(8),
641
- * });
642
- * ```
643
- */
644
- declare function addExchange(exchangeSchema: IExchangeSchema): void;
645
- /**
646
- * Registers a timeframe generator for backtesting.
647
- *
648
- * The frame defines:
649
- * - Start and end dates for backtest period
650
- * - Interval for timeframe generation
651
- * - Callback for timeframe generation events
717
+ * Statistical data calculated from backtest results.
652
718
  *
653
- * @param frameSchema - Frame configuration object
654
- * @param frameSchema.frameName - Unique frame identifier
655
- * @param frameSchema.interval - Timeframe interval ("1m" | "3m" | "5m" | "15m" | "30m" | "1h" | "2h" | "4h" | "6h" | "8h" | "12h" | "1d" | "3d")
656
- * @param frameSchema.startDate - Start date for timeframe generation
657
- * @param frameSchema.endDate - End date for timeframe generation
658
- * @param frameSchema.callbacks - Optional callback for timeframe events
719
+ * All numeric values are null if calculation is unsafe (NaN, Infinity, etc).
720
+ * Provides comprehensive metrics for strategy performance analysis.
659
721
  *
660
722
  * @example
661
723
  * ```typescript
662
- * addFrame({
663
- * frameName: "1d-backtest",
664
- * interval: "1m",
665
- * startDate: new Date("2024-01-01T00:00:00Z"),
666
- * endDate: new Date("2024-01-02T00:00:00Z"),
667
- * callbacks: {
668
- * onTimeframe: (timeframe, startDate, endDate, interval) => {
669
- * console.log(`Generated ${timeframe.length} timeframes`);
670
- * },
671
- * },
672
- * });
673
- * ```
674
- */
675
- declare function addFrame(frameSchema: IFrameSchema): void;
676
-
677
- /**
678
- * Returns a list of all registered exchange schemas.
679
- *
680
- * Retrieves all exchanges that have been registered via addExchange().
681
- * Useful for debugging, documentation, or building dynamic UIs.
682
- *
683
- * @returns Array of exchange schemas with their configurations
724
+ * const stats = await Backtest.getData("my-strategy");
684
725
  *
685
- * @example
686
- * ```typescript
687
- * import { listExchanges, addExchange } from "backtest-kit";
726
+ * console.log(`Total signals: ${stats.totalSignals}`);
727
+ * console.log(`Win rate: ${stats.winRate}%`);
728
+ * console.log(`Sharpe Ratio: ${stats.sharpeRatio}`);
688
729
  *
689
- * addExchange({
690
- * exchangeName: "binance",
691
- * note: "Binance cryptocurrency exchange",
692
- * getCandles: async (symbol, interval, since, limit) => [...],
693
- * formatPrice: async (symbol, price) => price.toFixed(2),
694
- * formatQuantity: async (symbol, quantity) => quantity.toFixed(8),
730
+ * // Access raw signal data
731
+ * stats.signalList.forEach(signal => {
732
+ * console.log(`Signal ${signal.signal.id}: ${signal.pnl.pnlPercentage}%`);
695
733
  * });
696
- *
697
- * const exchanges = listExchanges();
698
- * console.log(exchanges);
699
- * // [{ exchangeName: "binance", note: "Binance cryptocurrency exchange", ... }]
700
734
  * ```
701
735
  */
702
- declare function listExchanges(): Promise<IExchangeSchema[]>;
736
+ interface BacktestStatistics {
737
+ /** Array of all closed signals with full details (price, PNL, timestamps, etc.) */
738
+ signalList: IStrategyTickResultClosed[];
739
+ /** Total number of closed signals */
740
+ totalSignals: number;
741
+ /** Number of winning signals (PNL > 0) */
742
+ winCount: number;
743
+ /** Number of losing signals (PNL < 0) */
744
+ lossCount: number;
745
+ /** Win rate as percentage (0-100), null if unsafe. Higher is better. */
746
+ winRate: number | null;
747
+ /** Average PNL per signal as percentage, null if unsafe. Higher is better. */
748
+ avgPnl: number | null;
749
+ /** Cumulative PNL across all signals as percentage, null if unsafe. Higher is better. */
750
+ totalPnl: number | null;
751
+ /** Standard deviation of returns (volatility metric), null if unsafe. Lower is better. */
752
+ stdDev: number | null;
753
+ /** Sharpe Ratio (risk-adjusted return = avgPnl / stdDev), null if unsafe. Higher is better. */
754
+ sharpeRatio: number | null;
755
+ /** Annualized Sharpe Ratio (sharpeRatio × √365), null if unsafe. Higher is better. */
756
+ annualizedSharpeRatio: number | null;
757
+ /** Certainty Ratio (avgWin / |avgLoss|), null if unsafe. Higher is better. */
758
+ certaintyRatio: number | null;
759
+ /** Expected yearly returns based on average trade duration and PNL, null if unsafe. Higher is better. */
760
+ expectedYearlyReturns: number | null;
761
+ }
703
762
  /**
704
- * Returns a list of all registered strategy schemas.
705
- *
706
- * Retrieves all strategies that have been registered via addStrategy().
707
- * Useful for debugging, documentation, or building dynamic UIs.
763
+ * Service for generating and saving backtest markdown reports.
708
764
  *
709
- * @returns Array of strategy schemas with their configurations
765
+ * Features:
766
+ * - Listens to signal events via onTick callback
767
+ * - Accumulates closed signals per strategy using memoized storage
768
+ * - Generates markdown tables with detailed signal information
769
+ * - Saves reports to disk in logs/backtest/{strategyName}.md
710
770
  *
711
771
  * @example
712
772
  * ```typescript
713
- * import { listStrategies, addStrategy } from "backtest-kit";
773
+ * const service = new BacktestMarkdownService();
714
774
  *
775
+ * // Add to strategy callbacks
715
776
  * addStrategy({
716
777
  * strategyName: "my-strategy",
717
- * note: "Simple moving average crossover strategy",
718
- * interval: "5m",
719
- * getSignal: async (symbol) => ({
720
- * position: "long",
721
- * priceOpen: 50000,
722
- * priceTakeProfit: 51000,
723
- * priceStopLoss: 49000,
724
- * minuteEstimatedTime: 60,
725
- * }),
778
+ * callbacks: {
779
+ * onTick: (symbol, result, backtest) => {
780
+ * service.tick(result);
781
+ * }
782
+ * }
726
783
  * });
727
784
  *
728
- * const strategies = listStrategies();
729
- * console.log(strategies);
730
- * // [{ strategyName: "my-strategy", note: "Simple moving average...", ... }]
785
+ * // After backtest, generate and save report
786
+ * await service.saveReport("my-strategy");
787
+ * ```
788
+ */
789
+ declare class BacktestMarkdownService {
790
+ /** Logger service for debug output */
791
+ private readonly loggerService;
792
+ /**
793
+ * Memoized function to get or create ReportStorage for a strategy.
794
+ * Each strategy gets its own isolated storage instance.
795
+ */
796
+ private getStorage;
797
+ /**
798
+ * Processes tick events and accumulates closed signals.
799
+ * Should be called from IStrategyCallbacks.onTick.
800
+ *
801
+ * Only processes closed signals - opened signals are ignored.
802
+ *
803
+ * @param data - Tick result from strategy execution (opened or closed)
804
+ *
805
+ * @example
806
+ * ```typescript
807
+ * const service = new BacktestMarkdownService();
808
+ *
809
+ * callbacks: {
810
+ * onTick: (symbol, result, backtest) => {
811
+ * service.tick(result);
812
+ * }
813
+ * }
814
+ * ```
815
+ */
816
+ private tick;
817
+ /**
818
+ * Gets statistical data from all closed signals for a strategy.
819
+ * Delegates to ReportStorage.getData().
820
+ *
821
+ * @param strategyName - Strategy name to get data for
822
+ * @returns Statistical data object with all metrics
823
+ *
824
+ * @example
825
+ * ```typescript
826
+ * const service = new BacktestMarkdownService();
827
+ * const stats = await service.getData("my-strategy");
828
+ * console.log(stats.sharpeRatio, stats.winRate);
829
+ * ```
830
+ */
831
+ getData: (strategyName: StrategyName) => Promise<BacktestStatistics>;
832
+ /**
833
+ * Generates markdown report with all closed signals for a strategy.
834
+ * Delegates to ReportStorage.generateReport().
835
+ *
836
+ * @param strategyName - Strategy name to generate report for
837
+ * @returns Markdown formatted report string with table of all closed signals
838
+ *
839
+ * @example
840
+ * ```typescript
841
+ * const service = new BacktestMarkdownService();
842
+ * const markdown = await service.getReport("my-strategy");
843
+ * console.log(markdown);
844
+ * ```
845
+ */
846
+ getReport: (strategyName: StrategyName) => Promise<string>;
847
+ /**
848
+ * Saves strategy report to disk.
849
+ * Creates directory if it doesn't exist.
850
+ * Delegates to ReportStorage.dump().
851
+ *
852
+ * @param strategyName - Strategy name to save report for
853
+ * @param path - Directory path to save report (default: "./logs/backtest")
854
+ *
855
+ * @example
856
+ * ```typescript
857
+ * const service = new BacktestMarkdownService();
858
+ *
859
+ * // Save to default path: ./logs/backtest/my-strategy.md
860
+ * await service.dump("my-strategy");
861
+ *
862
+ * // Save to custom path: ./custom/path/my-strategy.md
863
+ * await service.dump("my-strategy", "./custom/path");
864
+ * ```
865
+ */
866
+ dump: (strategyName: StrategyName, path?: string) => Promise<void>;
867
+ /**
868
+ * Clears accumulated signal data from storage.
869
+ * If strategyName is provided, clears only that strategy's data.
870
+ * If strategyName is omitted, clears all strategies' data.
871
+ *
872
+ * @param strategyName - Optional strategy name to clear specific strategy data
873
+ *
874
+ * @example
875
+ * ```typescript
876
+ * const service = new BacktestMarkdownService();
877
+ *
878
+ * // Clear specific strategy data
879
+ * await service.clear("my-strategy");
880
+ *
881
+ * // Clear all strategies' data
882
+ * await service.clear();
883
+ * ```
884
+ */
885
+ clear: (strategyName?: StrategyName) => Promise<void>;
886
+ /**
887
+ * Initializes the service by subscribing to backtest signal events.
888
+ * Uses singleshot to ensure initialization happens only once.
889
+ * Automatically called on first use.
890
+ *
891
+ * @example
892
+ * ```typescript
893
+ * const service = new BacktestMarkdownService();
894
+ * await service.init(); // Subscribe to backtest events
895
+ * ```
896
+ */
897
+ protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
898
+ }
899
+
900
+ /**
901
+ * Optimization metric for comparing strategies.
902
+ * Higher values are always better (metric is maximized).
903
+ */
904
+ type WalkerMetric = "sharpeRatio" | "annualizedSharpeRatio" | "winRate" | "totalPnl" | "certaintyRatio" | "avgPnl" | "expectedYearlyReturns";
905
+ /**
906
+ * Walker schema registered via addWalker().
907
+ * Defines A/B testing configuration for multiple strategies.
908
+ */
909
+ interface IWalkerSchema {
910
+ /** Unique walker identifier for registration */
911
+ walkerName: WalkerName;
912
+ /** Optional developer note for documentation */
913
+ note?: string;
914
+ /** Exchange to use for backtesting all strategies */
915
+ exchangeName: ExchangeName;
916
+ /** Timeframe generator to use for backtesting all strategies */
917
+ frameName: FrameName;
918
+ /** List of strategy names to compare (must be registered via addStrategy) */
919
+ strategies: StrategyName[];
920
+ /** Metric to optimize (default: "sharpeRatio") */
921
+ metric?: WalkerMetric;
922
+ /** Optional lifecycle event callbacks */
923
+ callbacks?: Partial<IWalkerCallbacks>;
924
+ }
925
+ /**
926
+ * Optional lifecycle callbacks for walker events.
927
+ * Called during strategy comparison process.
928
+ */
929
+ interface IWalkerCallbacks {
930
+ /** Called when starting to test a specific strategy */
931
+ onStrategyStart: (strategyName: StrategyName, symbol: string) => void;
932
+ /** Called when a strategy backtest completes */
933
+ onStrategyComplete: (strategyName: StrategyName, symbol: string, stats: BacktestStatistics, metric: number | null) => void;
934
+ /** Called when all strategies have been tested */
935
+ onComplete: (results: IWalkerResults) => void;
936
+ }
937
+ /**
938
+ * Result for a single strategy in the comparison.
939
+ */
940
+ interface IWalkerStrategyResult {
941
+ /** Strategy name */
942
+ strategyName: StrategyName;
943
+ /** Backtest statistics for this strategy */
944
+ stats: BacktestStatistics;
945
+ /** Metric value used for comparison (null if invalid) */
946
+ metric: number | null;
947
+ /** Rank position (1 = best, 2 = second best, etc.) */
948
+ rank: number;
949
+ }
950
+ /**
951
+ * Complete walker results after comparing all strategies.
952
+ */
953
+ interface IWalkerResults {
954
+ /** Walker name */
955
+ walkerName: WalkerName;
956
+ /** Symbol tested */
957
+ symbol: string;
958
+ /** Exchange used */
959
+ exchangeName: ExchangeName;
960
+ /** Frame used */
961
+ frameName: FrameName;
962
+ /** Metric used for optimization */
963
+ metric: WalkerMetric;
964
+ /** Total number of strategies tested */
965
+ totalStrategies: number;
966
+ /** Best performing strategy name */
967
+ bestStrategy: StrategyName | null;
968
+ /** Best metric value achieved */
969
+ bestMetric: number | null;
970
+ /** Best strategy statistics */
971
+ bestStats: BacktestStatistics | null;
972
+ }
973
+ /**
974
+ * Unique walker identifier.
975
+ */
976
+ type WalkerName = string;
977
+
978
+ /**
979
+ * Base parameters common to all sizing calculations.
980
+ */
981
+ interface ISizingCalculateParamsBase {
982
+ /** Trading pair symbol (e.g., "BTCUSDT") */
983
+ symbol: string;
984
+ /** Current account balance */
985
+ accountBalance: number;
986
+ /** Planned entry price */
987
+ priceOpen: number;
988
+ }
989
+ /**
990
+ * Public API parameters for fixed percentage sizing (without method field).
991
+ */
992
+ interface IPositionSizeFixedPercentageParams extends ISizingCalculateParamsBase {
993
+ /** Stop-loss price */
994
+ priceStopLoss: number;
995
+ }
996
+ /**
997
+ * Public API parameters for Kelly Criterion sizing (without method field).
998
+ */
999
+ interface IPositionSizeKellyParams extends ISizingCalculateParamsBase {
1000
+ /** Win rate (0-1) */
1001
+ winRate: number;
1002
+ /** Average win/loss ratio */
1003
+ winLossRatio: number;
1004
+ }
1005
+ /**
1006
+ * Public API parameters for ATR-based sizing (without method field).
1007
+ */
1008
+ interface IPositionSizeATRParams extends ISizingCalculateParamsBase {
1009
+ /** Current ATR value */
1010
+ atr: number;
1011
+ }
1012
+ /**
1013
+ * Parameters for fixed percentage sizing calculation.
1014
+ */
1015
+ interface ISizingCalculateParamsFixedPercentage extends ISizingCalculateParamsBase {
1016
+ method: "fixed-percentage";
1017
+ /** Stop-loss price */
1018
+ priceStopLoss: number;
1019
+ }
1020
+ /**
1021
+ * Parameters for Kelly Criterion sizing calculation.
1022
+ */
1023
+ interface ISizingCalculateParamsKelly extends ISizingCalculateParamsBase {
1024
+ method: "kelly-criterion";
1025
+ /** Win rate (0-1) */
1026
+ winRate: number;
1027
+ /** Average win/loss ratio */
1028
+ winLossRatio: number;
1029
+ }
1030
+ /**
1031
+ * Parameters for ATR-based sizing calculation.
1032
+ */
1033
+ interface ISizingCalculateParamsATR extends ISizingCalculateParamsBase {
1034
+ method: "atr-based";
1035
+ /** Current ATR value */
1036
+ atr: number;
1037
+ }
1038
+ /**
1039
+ * Discriminated union for position size calculation parameters.
1040
+ * Type-safe parameters based on sizing method.
1041
+ */
1042
+ type ISizingCalculateParams = ISizingCalculateParamsFixedPercentage | ISizingCalculateParamsKelly | ISizingCalculateParamsATR;
1043
+ /**
1044
+ * Fixed percentage sizing parameters for ClientSizing constructor.
1045
+ */
1046
+ interface ISizingParamsFixedPercentage extends ISizingSchemaFixedPercentage {
1047
+ /** Logger service for debug output */
1048
+ logger: ILogger;
1049
+ }
1050
+ /**
1051
+ * Kelly Criterion sizing parameters for ClientSizing constructor.
1052
+ */
1053
+ interface ISizingParamsKelly extends ISizingSchemaKelly {
1054
+ /** Logger service for debug output */
1055
+ logger: ILogger;
1056
+ }
1057
+ /**
1058
+ * ATR-based sizing parameters for ClientSizing constructor.
1059
+ */
1060
+ interface ISizingParamsATR extends ISizingSchemaATR {
1061
+ /** Logger service for debug output */
1062
+ logger: ILogger;
1063
+ }
1064
+ /**
1065
+ * Discriminated union for sizing parameters passed to ClientSizing constructor.
1066
+ * Extends ISizingSchema with logger instance for internal logging.
1067
+ */
1068
+ type ISizingParams = ISizingParamsFixedPercentage | ISizingParamsKelly | ISizingParamsATR;
1069
+ /**
1070
+ * Callbacks for sizing lifecycle events.
1071
+ */
1072
+ interface ISizingCallbacks {
1073
+ /**
1074
+ * Called after position size calculation.
1075
+ * Useful for logging or validating the calculated size.
1076
+ *
1077
+ * @param quantity - Calculated position size
1078
+ * @param params - Parameters used for calculation
1079
+ */
1080
+ onCalculate: (quantity: number, params: ISizingCalculateParams) => void;
1081
+ }
1082
+ /**
1083
+ * Base sizing schema with common fields.
1084
+ */
1085
+ interface ISizingSchemaBase {
1086
+ /** Unique identifier for this sizing configuration */
1087
+ sizingName: SizingName;
1088
+ /** Optional developer note for documentation */
1089
+ note?: string;
1090
+ /** Maximum position size as % of account (0-100) */
1091
+ maxPositionPercentage?: number;
1092
+ /** Minimum position size (absolute value) */
1093
+ minPositionSize?: number;
1094
+ /** Maximum position size (absolute value) */
1095
+ maxPositionSize?: number;
1096
+ /** Optional lifecycle callbacks */
1097
+ callbacks?: Partial<ISizingCallbacks>;
1098
+ }
1099
+ /**
1100
+ * Fixed percentage sizing schema.
1101
+ *
1102
+ * @example
1103
+ * ```typescript
1104
+ * addSizing({
1105
+ * sizingName: "conservative",
1106
+ * method: "fixed-percentage",
1107
+ * riskPercentage: 1,
1108
+ * });
1109
+ * ```
1110
+ */
1111
+ interface ISizingSchemaFixedPercentage extends ISizingSchemaBase {
1112
+ method: "fixed-percentage";
1113
+ /** Risk percentage per trade (0-100) */
1114
+ riskPercentage: number;
1115
+ }
1116
+ /**
1117
+ * Kelly Criterion sizing schema.
1118
+ *
1119
+ * @example
1120
+ * ```typescript
1121
+ * addSizing({
1122
+ * sizingName: "kelly",
1123
+ * method: "kelly-criterion",
1124
+ * kellyMultiplier: 0.25,
1125
+ * });
1126
+ * ```
1127
+ */
1128
+ interface ISizingSchemaKelly extends ISizingSchemaBase {
1129
+ method: "kelly-criterion";
1130
+ /** Kelly Criterion multiplier (0-1, default 0.25 for quarter Kelly) */
1131
+ kellyMultiplier?: number;
1132
+ }
1133
+ /**
1134
+ * ATR-based sizing schema.
1135
+ *
1136
+ * @example
1137
+ * ```typescript
1138
+ * addSizing({
1139
+ * sizingName: "atr",
1140
+ * method: "atr-based",
1141
+ * riskPercentage: 2,
1142
+ * atrMultiplier: 2,
1143
+ * });
1144
+ * ```
1145
+ */
1146
+ interface ISizingSchemaATR extends ISizingSchemaBase {
1147
+ method: "atr-based";
1148
+ /** Risk percentage per trade (0-100) */
1149
+ riskPercentage: number;
1150
+ /** ATR multiplier for stop distance calculation */
1151
+ atrMultiplier?: number;
1152
+ }
1153
+ /**
1154
+ * Discriminated union for sizing schemas.
1155
+ * Type-safe configuration based on sizing method.
1156
+ */
1157
+ type ISizingSchema = ISizingSchemaFixedPercentage | ISizingSchemaKelly | ISizingSchemaATR;
1158
+ /**
1159
+ * Sizing interface for position size calculation.
1160
+ * Used internally by strategy execution.
1161
+ */
1162
+ interface ISizing {
1163
+ /**
1164
+ * Calculates position size based on risk parameters.
1165
+ *
1166
+ * @param params - Calculation parameters (symbol, balance, prices, etc.)
1167
+ * @returns Promise resolving to calculated position size
1168
+ */
1169
+ calculate: (params: ISizingCalculateParams) => Promise<number>;
1170
+ }
1171
+ /**
1172
+ * Unique identifier for a sizing schema.
1173
+ * Used to retrieve sizing instances via dependency injection.
1174
+ */
1175
+ type SizingName = string;
1176
+
1177
+ /**
1178
+ * Registers a trading strategy in the framework.
1179
+ *
1180
+ * The strategy will be validated for:
1181
+ * - Signal validation (prices, TP/SL logic, timestamps)
1182
+ * - Interval throttling (prevents signal spam)
1183
+ * - Crash-safe persistence in live mode
1184
+ *
1185
+ * @param strategySchema - Strategy configuration object
1186
+ * @param strategySchema.strategyName - Unique strategy identifier
1187
+ * @param strategySchema.interval - Signal generation interval ("1m" | "3m" | "5m" | "15m" | "30m" | "1h")
1188
+ * @param strategySchema.getSignal - Async function that generates trading signals
1189
+ * @param strategySchema.callbacks - Optional lifecycle callbacks (onOpen, onClose)
1190
+ *
1191
+ * @example
1192
+ * ```typescript
1193
+ * addStrategy({
1194
+ * strategyName: "my-strategy",
1195
+ * interval: "5m",
1196
+ * getSignal: async (symbol) => ({
1197
+ * position: "long",
1198
+ * priceOpen: 50000,
1199
+ * priceTakeProfit: 51000,
1200
+ * priceStopLoss: 49000,
1201
+ * minuteEstimatedTime: 60,
1202
+ * timestamp: Date.now(),
1203
+ * }),
1204
+ * callbacks: {
1205
+ * onOpen: (symbol, signal, currentPrice, backtest) => console.log("Signal opened"),
1206
+ * onClose: (symbol, signal, priceClose, backtest) => console.log("Signal closed"),
1207
+ * },
1208
+ * });
1209
+ * ```
1210
+ */
1211
+ declare function addStrategy(strategySchema: IStrategySchema): void;
1212
+ /**
1213
+ * Registers an exchange data source in the framework.
1214
+ *
1215
+ * The exchange provides:
1216
+ * - Historical candle data via getCandles
1217
+ * - Price/quantity formatting for the exchange
1218
+ * - VWAP calculation from last 5 1m candles
1219
+ *
1220
+ * @param exchangeSchema - Exchange configuration object
1221
+ * @param exchangeSchema.exchangeName - Unique exchange identifier
1222
+ * @param exchangeSchema.getCandles - Async function to fetch candle data
1223
+ * @param exchangeSchema.formatPrice - Async function to format prices
1224
+ * @param exchangeSchema.formatQuantity - Async function to format quantities
1225
+ * @param exchangeSchema.callbacks - Optional callback for candle data events
1226
+ *
1227
+ * @example
1228
+ * ```typescript
1229
+ * addExchange({
1230
+ * exchangeName: "binance",
1231
+ * getCandles: async (symbol, interval, since, limit) => {
1232
+ * // Fetch from Binance API or database
1233
+ * return [{
1234
+ * timestamp: Date.now(),
1235
+ * open: 50000,
1236
+ * high: 51000,
1237
+ * low: 49000,
1238
+ * close: 50500,
1239
+ * volume: 1000,
1240
+ * }];
1241
+ * },
1242
+ * formatPrice: async (symbol, price) => price.toFixed(2),
1243
+ * formatQuantity: async (symbol, quantity) => quantity.toFixed(8),
1244
+ * });
1245
+ * ```
1246
+ */
1247
+ declare function addExchange(exchangeSchema: IExchangeSchema): void;
1248
+ /**
1249
+ * Registers a timeframe generator for backtesting.
1250
+ *
1251
+ * The frame defines:
1252
+ * - Start and end dates for backtest period
1253
+ * - Interval for timeframe generation
1254
+ * - Callback for timeframe generation events
1255
+ *
1256
+ * @param frameSchema - Frame configuration object
1257
+ * @param frameSchema.frameName - Unique frame identifier
1258
+ * @param frameSchema.interval - Timeframe interval ("1m" | "3m" | "5m" | "15m" | "30m" | "1h" | "2h" | "4h" | "6h" | "8h" | "12h" | "1d" | "3d")
1259
+ * @param frameSchema.startDate - Start date for timeframe generation
1260
+ * @param frameSchema.endDate - End date for timeframe generation
1261
+ * @param frameSchema.callbacks - Optional callback for timeframe events
1262
+ *
1263
+ * @example
1264
+ * ```typescript
1265
+ * addFrame({
1266
+ * frameName: "1d-backtest",
1267
+ * interval: "1m",
1268
+ * startDate: new Date("2024-01-01T00:00:00Z"),
1269
+ * endDate: new Date("2024-01-02T00:00:00Z"),
1270
+ * callbacks: {
1271
+ * onTimeframe: (timeframe, startDate, endDate, interval) => {
1272
+ * console.log(`Generated ${timeframe.length} timeframes`);
1273
+ * },
1274
+ * },
1275
+ * });
1276
+ * ```
1277
+ */
1278
+ declare function addFrame(frameSchema: IFrameSchema): void;
1279
+ /**
1280
+ * Registers a walker for strategy comparison.
1281
+ *
1282
+ * The walker executes backtests for multiple strategies on the same
1283
+ * historical data and compares their performance using a specified metric.
1284
+ *
1285
+ * @param walkerSchema - Walker configuration object
1286
+ * @param walkerSchema.walkerName - Unique walker identifier
1287
+ * @param walkerSchema.exchangeName - Exchange to use for all strategies
1288
+ * @param walkerSchema.frameName - Timeframe to use for all strategies
1289
+ * @param walkerSchema.strategies - Array of strategy names to compare
1290
+ * @param walkerSchema.metric - Metric to optimize (default: "sharpeRatio")
1291
+ * @param walkerSchema.callbacks - Optional lifecycle callbacks
1292
+ *
1293
+ * @example
1294
+ * ```typescript
1295
+ * addWalker({
1296
+ * walkerName: "llm-prompt-optimizer",
1297
+ * exchangeName: "binance",
1298
+ * frameName: "1d-backtest",
1299
+ * strategies: [
1300
+ * "my-strategy-v1",
1301
+ * "my-strategy-v2",
1302
+ * "my-strategy-v3"
1303
+ * ],
1304
+ * metric: "sharpeRatio",
1305
+ * callbacks: {
1306
+ * onStrategyComplete: (strategyName, symbol, stats, metric) => {
1307
+ * console.log(`${strategyName}: ${metric}`);
1308
+ * },
1309
+ * onComplete: (results) => {
1310
+ * console.log(`Best strategy: ${results.bestStrategy}`);
1311
+ * }
1312
+ * }
1313
+ * });
1314
+ * ```
1315
+ */
1316
+ declare function addWalker(walkerSchema: IWalkerSchema): void;
1317
+ /**
1318
+ * Registers a position sizing configuration in the framework.
1319
+ *
1320
+ * The sizing configuration defines:
1321
+ * - Position sizing method (fixed-percentage, kelly-criterion, atr-based)
1322
+ * - Risk parameters (risk percentage, Kelly multiplier, ATR multiplier)
1323
+ * - Position constraints (min/max size, max position percentage)
1324
+ * - Callback for calculation events
1325
+ *
1326
+ * @param sizingSchema - Sizing configuration object (discriminated union)
1327
+ * @param sizingSchema.sizingName - Unique sizing identifier
1328
+ * @param sizingSchema.method - Sizing method ("fixed-percentage" | "kelly-criterion" | "atr-based")
1329
+ * @param sizingSchema.riskPercentage - Risk percentage per trade (for fixed-percentage and atr-based)
1330
+ * @param sizingSchema.kellyMultiplier - Kelly multiplier (for kelly-criterion, default: 0.25)
1331
+ * @param sizingSchema.atrMultiplier - ATR multiplier (for atr-based, default: 2)
1332
+ * @param sizingSchema.maxPositionPercentage - Optional max position size as % of account
1333
+ * @param sizingSchema.minPositionSize - Optional minimum position size
1334
+ * @param sizingSchema.maxPositionSize - Optional maximum position size
1335
+ * @param sizingSchema.callbacks - Optional lifecycle callbacks
1336
+ *
1337
+ * @example
1338
+ * ```typescript
1339
+ * // Fixed percentage sizing
1340
+ * addSizing({
1341
+ * sizingName: "conservative",
1342
+ * method: "fixed-percentage",
1343
+ * riskPercentage: 1,
1344
+ * maxPositionPercentage: 10,
1345
+ * });
1346
+ *
1347
+ * // Kelly Criterion sizing
1348
+ * addSizing({
1349
+ * sizingName: "kelly",
1350
+ * method: "kelly-criterion",
1351
+ * kellyMultiplier: 0.25,
1352
+ * maxPositionPercentage: 20,
1353
+ * });
1354
+ *
1355
+ * // ATR-based sizing
1356
+ * addSizing({
1357
+ * sizingName: "atr-dynamic",
1358
+ * method: "atr-based",
1359
+ * riskPercentage: 2,
1360
+ * atrMultiplier: 2,
1361
+ * callbacks: {
1362
+ * onCalculate: (quantity, params) => {
1363
+ * console.log(`Calculated size: ${quantity} for ${params.symbol}`);
1364
+ * },
1365
+ * },
1366
+ * });
1367
+ * ```
1368
+ */
1369
+ declare function addSizing(sizingSchema: ISizingSchema): void;
1370
+ /**
1371
+ * Registers a risk management configuration in the framework.
1372
+ *
1373
+ * The risk configuration defines:
1374
+ * - Maximum concurrent positions across all strategies
1375
+ * - Custom validations for advanced risk logic (portfolio metrics, correlations, etc.)
1376
+ * - Callbacks for rejected/allowed signals
1377
+ *
1378
+ * Multiple ClientStrategy instances share the same ClientRisk instance,
1379
+ * enabling cross-strategy risk analysis. ClientRisk tracks all active positions
1380
+ * and provides access to them via validation functions.
1381
+ *
1382
+ * @param riskSchema - Risk configuration object
1383
+ * @param riskSchema.riskName - Unique risk profile identifier
1384
+ * @param riskSchema.maxConcurrentPositions - Optional max number of open positions across all strategies
1385
+ * @param riskSchema.validations - Optional custom validation functions with access to params and active positions
1386
+ * @param riskSchema.callbacks - Optional lifecycle callbacks (onRejected, onAllowed)
1387
+ *
1388
+ * @example
1389
+ * ```typescript
1390
+ * // Basic risk limit
1391
+ * addRisk({
1392
+ * riskName: "conservative",
1393
+ * maxConcurrentPositions: 5,
1394
+ * });
1395
+ *
1396
+ * // With custom validations (access to signal data and portfolio state)
1397
+ * addRisk({
1398
+ * riskName: "advanced",
1399
+ * maxConcurrentPositions: 10,
1400
+ * validations: [
1401
+ * {
1402
+ * validate: async ({ params }) => {
1403
+ * // params contains: symbol, strategyName, exchangeName, signal, currentPrice, timestamp
1404
+ * // Calculate portfolio metrics from external data source
1405
+ * const portfolio = await getPortfolioState();
1406
+ * if (portfolio.drawdown > 20) {
1407
+ * throw new Error("Portfolio drawdown exceeds 20%");
1408
+ * }
1409
+ * },
1410
+ * docDescription: "Prevents trading during high drawdown",
1411
+ * },
1412
+ * ({ params }) => {
1413
+ * // Access signal details
1414
+ * const positionValue = calculatePositionValue(params.signal, params.currentPrice);
1415
+ * if (positionValue > 10000) {
1416
+ * throw new Error("Position value exceeds $10,000 limit");
1417
+ * }
1418
+ * },
1419
+ * ],
1420
+ * callbacks: {
1421
+ * onRejected: (symbol, reason, limit, params) => {
1422
+ * console.log(`[RISK] Signal rejected for ${symbol}: ${reason}`);
1423
+ * },
1424
+ * onAllowed: (symbol, params) => {
1425
+ * console.log(`[RISK] Signal allowed for ${symbol}`);
1426
+ * },
1427
+ * },
1428
+ * });
1429
+ * ```
1430
+ */
1431
+ declare function addRisk(riskSchema: IRiskSchema): void;
1432
+
1433
+ /**
1434
+ * Returns a list of all registered exchange schemas.
1435
+ *
1436
+ * Retrieves all exchanges that have been registered via addExchange().
1437
+ * Useful for debugging, documentation, or building dynamic UIs.
1438
+ *
1439
+ * @returns Array of exchange schemas with their configurations
1440
+ *
1441
+ * @example
1442
+ * ```typescript
1443
+ * import { listExchanges, addExchange } from "backtest-kit";
1444
+ *
1445
+ * addExchange({
1446
+ * exchangeName: "binance",
1447
+ * note: "Binance cryptocurrency exchange",
1448
+ * getCandles: async (symbol, interval, since, limit) => [...],
1449
+ * formatPrice: async (symbol, price) => price.toFixed(2),
1450
+ * formatQuantity: async (symbol, quantity) => quantity.toFixed(8),
1451
+ * });
1452
+ *
1453
+ * const exchanges = listExchanges();
1454
+ * console.log(exchanges);
1455
+ * // [{ exchangeName: "binance", note: "Binance cryptocurrency exchange", ... }]
1456
+ * ```
1457
+ */
1458
+ declare function listExchanges(): Promise<IExchangeSchema[]>;
1459
+ /**
1460
+ * Returns a list of all registered strategy schemas.
1461
+ *
1462
+ * Retrieves all strategies that have been registered via addStrategy().
1463
+ * Useful for debugging, documentation, or building dynamic UIs.
1464
+ *
1465
+ * @returns Array of strategy schemas with their configurations
1466
+ *
1467
+ * @example
1468
+ * ```typescript
1469
+ * import { listStrategies, addStrategy } from "backtest-kit";
1470
+ *
1471
+ * addStrategy({
1472
+ * strategyName: "my-strategy",
1473
+ * note: "Simple moving average crossover strategy",
1474
+ * interval: "5m",
1475
+ * getSignal: async (symbol) => ({
1476
+ * position: "long",
1477
+ * priceOpen: 50000,
1478
+ * priceTakeProfit: 51000,
1479
+ * priceStopLoss: 49000,
1480
+ * minuteEstimatedTime: 60,
1481
+ * }),
1482
+ * });
1483
+ *
1484
+ * const strategies = listStrategies();
1485
+ * console.log(strategies);
1486
+ * // [{ strategyName: "my-strategy", note: "Simple moving average...", ... }]
731
1487
  * ```
732
1488
  */
733
1489
  declare function listStrategies(): Promise<IStrategySchema[]>;
@@ -751,12 +1507,108 @@ declare function listStrategies(): Promise<IStrategySchema[]>;
751
1507
  * endDate: new Date("2024-01-02T00:00:00Z"),
752
1508
  * });
753
1509
  *
754
- * const frames = listFrames();
755
- * console.log(frames);
756
- * // [{ frameName: "1d-backtest", note: "One day backtest...", ... }]
1510
+ * const frames = listFrames();
1511
+ * console.log(frames);
1512
+ * // [{ frameName: "1d-backtest", note: "One day backtest...", ... }]
1513
+ * ```
1514
+ */
1515
+ declare function listFrames(): Promise<IFrameSchema[]>;
1516
+ /**
1517
+ * Returns a list of all registered walker schemas.
1518
+ *
1519
+ * Retrieves all walkers that have been registered via addWalker().
1520
+ * Useful for debugging, documentation, or building dynamic UIs.
1521
+ *
1522
+ * @returns Array of walker schemas with their configurations
1523
+ *
1524
+ * @example
1525
+ * ```typescript
1526
+ * import { listWalkers, addWalker } from "backtest-kit";
1527
+ *
1528
+ * addWalker({
1529
+ * walkerName: "llm-prompt-optimizer",
1530
+ * note: "Compare LLM-based trading strategies",
1531
+ * exchangeName: "binance",
1532
+ * frameName: "1d-backtest",
1533
+ * strategies: ["my-strategy-v1", "my-strategy-v2"],
1534
+ * metric: "sharpeRatio",
1535
+ * });
1536
+ *
1537
+ * const walkers = listWalkers();
1538
+ * console.log(walkers);
1539
+ * // [{ walkerName: "llm-prompt-optimizer", note: "Compare LLM...", ... }]
1540
+ * ```
1541
+ */
1542
+ declare function listWalkers(): Promise<IWalkerSchema[]>;
1543
+ /**
1544
+ * Returns a list of all registered sizing schemas.
1545
+ *
1546
+ * Retrieves all sizing configurations that have been registered via addSizing().
1547
+ * Useful for debugging, documentation, or building dynamic UIs.
1548
+ *
1549
+ * @returns Array of sizing schemas with their configurations
1550
+ *
1551
+ * @example
1552
+ * ```typescript
1553
+ * import { listSizings, addSizing } from "backtest-kit";
1554
+ *
1555
+ * addSizing({
1556
+ * sizingName: "conservative",
1557
+ * note: "Low risk fixed percentage sizing",
1558
+ * method: "fixed-percentage",
1559
+ * riskPercentage: 1,
1560
+ * maxPositionPercentage: 10,
1561
+ * });
1562
+ *
1563
+ * addSizing({
1564
+ * sizingName: "kelly",
1565
+ * note: "Kelly Criterion with quarter multiplier",
1566
+ * method: "kelly-criterion",
1567
+ * kellyMultiplier: 0.25,
1568
+ * });
1569
+ *
1570
+ * const sizings = listSizings();
1571
+ * console.log(sizings);
1572
+ * // [
1573
+ * // { sizingName: "conservative", method: "fixed-percentage", ... },
1574
+ * // { sizingName: "kelly", method: "kelly-criterion", ... }
1575
+ * // ]
1576
+ * ```
1577
+ */
1578
+ declare function listSizings(): Promise<ISizingSchema[]>;
1579
+ /**
1580
+ * Returns a list of all registered risk schemas.
1581
+ *
1582
+ * Retrieves all risk configurations that have been registered via addRisk().
1583
+ * Useful for debugging, documentation, or building dynamic UIs.
1584
+ *
1585
+ * @returns Array of risk schemas with their configurations
1586
+ *
1587
+ * @example
1588
+ * ```typescript
1589
+ * import { listRisks, addRisk } from "backtest-kit";
1590
+ *
1591
+ * addRisk({
1592
+ * riskName: "conservative",
1593
+ * note: "Conservative risk management with tight position limits",
1594
+ * maxConcurrentPositions: 5,
1595
+ * });
1596
+ *
1597
+ * addRisk({
1598
+ * riskName: "aggressive",
1599
+ * note: "Aggressive risk management with loose limits",
1600
+ * maxConcurrentPositions: 10,
1601
+ * });
1602
+ *
1603
+ * const risks = listRisks();
1604
+ * console.log(risks);
1605
+ * // [
1606
+ * // { riskName: "conservative", maxConcurrentPositions: 5, ... },
1607
+ * // { riskName: "aggressive", maxConcurrentPositions: 10, ... }
1608
+ * // ]
757
1609
  * ```
758
1610
  */
759
- declare function listFrames(): Promise<IFrameSchema[]>;
1611
+ declare function listRisks(): Promise<IRiskSchema[]>;
760
1612
 
761
1613
  /**
762
1614
  * Contract for background execution completion events.
@@ -819,6 +1671,84 @@ interface ProgressContract {
819
1671
  progress: number;
820
1672
  }
821
1673
 
1674
+ /**
1675
+ * Performance metric types tracked by the system.
1676
+ *
1677
+ * Backtest metrics:
1678
+ * - backtest_total: Total backtest duration from start to finish
1679
+ * - backtest_timeframe: Duration to process a single timeframe iteration
1680
+ * - backtest_signal: Duration to process a signal (tick + getNextCandles + backtest)
1681
+ *
1682
+ * Live metrics:
1683
+ * - live_tick: Duration of a single live tick iteration
1684
+ */
1685
+ type PerformanceMetricType = "backtest_total" | "backtest_timeframe" | "backtest_signal" | "live_tick";
1686
+ /**
1687
+ * Contract for performance tracking events.
1688
+ *
1689
+ * Emitted during execution to track performance metrics for various operations.
1690
+ * Useful for profiling and identifying bottlenecks.
1691
+ *
1692
+ * @example
1693
+ * ```typescript
1694
+ * import { listenPerformance } from "backtest-kit";
1695
+ *
1696
+ * listenPerformance((event) => {
1697
+ * console.log(`${event.metricType}: ${event.duration.toFixed(2)}ms`);
1698
+ * console.log(`${event.strategyName} @ ${event.exchangeName}`);
1699
+ * });
1700
+ * ```
1701
+ */
1702
+ interface PerformanceContract {
1703
+ /** Timestamp when the metric was recorded (milliseconds since epoch) */
1704
+ timestamp: number;
1705
+ /** Timestamp of the previous event (milliseconds since epoch, null for first event) */
1706
+ previousTimestamp: number | null;
1707
+ /** Type of operation being measured */
1708
+ metricType: PerformanceMetricType;
1709
+ /** Duration of the operation in milliseconds */
1710
+ duration: number;
1711
+ /** Strategy name associated with this metric */
1712
+ strategyName: string;
1713
+ /** Exchange name associated with this metric */
1714
+ exchangeName: string;
1715
+ /** Trading symbol associated with this metric */
1716
+ symbol: string;
1717
+ /** Whether this metric is from backtest mode (true) or live mode (false) */
1718
+ backtest: boolean;
1719
+ }
1720
+
1721
+ /**
1722
+ * Contract for walker progress events during strategy comparison.
1723
+ * Emitted each time a strategy completes testing with its current ranking.
1724
+ */
1725
+ interface WalkerContract {
1726
+ /** Walker name */
1727
+ walkerName: WalkerName;
1728
+ /** Exchange name */
1729
+ exchangeName: ExchangeName;
1730
+ /** Frame name */
1731
+ frameName: string;
1732
+ /** Symbol being tested */
1733
+ symbol: string;
1734
+ /** Strategy that just completed */
1735
+ strategyName: StrategyName;
1736
+ /** Backtest statistics for this strategy */
1737
+ stats: BacktestStatistics;
1738
+ /** Metric value for this strategy (null if invalid) */
1739
+ metricValue: number | null;
1740
+ /** Metric being optimized */
1741
+ metric: WalkerMetric;
1742
+ /** Current best metric value across all tested strategies so far */
1743
+ bestMetric: number | null;
1744
+ /** Current best strategy name */
1745
+ bestStrategy: StrategyName | null;
1746
+ /** Number of strategies tested so far */
1747
+ strategiesTested: number;
1748
+ /** Total number of strategies to test */
1749
+ totalStrategies: number;
1750
+ }
1751
+
822
1752
  /**
823
1753
  * Subscribes to all signal events with queued async processing.
824
1754
  *
@@ -989,9 +1919,9 @@ declare function listenSignalBacktestOnce(filterFn: (event: IStrategyTickResult)
989
1919
  */
990
1920
  declare function listenError(fn: (error: Error) => void): () => void;
991
1921
  /**
992
- * Subscribes to background execution completion events with queued async processing.
1922
+ * Subscribes to live background execution completion events with queued async processing.
993
1923
  *
994
- * Emits when Live.background() or Backtest.background() completes execution.
1924
+ * Emits when Live.background() completes execution.
995
1925
  * Events are processed sequentially in order received, even if callback is async.
996
1926
  * Uses queued wrapper to prevent concurrent execution of the callback.
997
1927
  *
@@ -1000,29 +1930,82 @@ declare function listenError(fn: (error: Error) => void): () => void;
1000
1930
  *
1001
1931
  * @example
1002
1932
  * ```typescript
1003
- * import { listenDone, Live } from "backtest-kit";
1933
+ * import { listenDoneLive, Live } from "backtest-kit";
1004
1934
  *
1005
- * const unsubscribe = listenDone((event) => {
1006
- * console.log("Completed:", event.strategyName, event.exchangeName, event.symbol);
1007
- * if (event.backtest) {
1008
- * console.log("Backtest mode completed");
1009
- * }
1935
+ * const unsubscribe = listenDoneLive((event) => {
1936
+ * console.log("Live completed:", event.strategyName, event.exchangeName, event.symbol);
1937
+ * });
1938
+ *
1939
+ * Live.background("BTCUSDT", {
1940
+ * strategyName: "my-strategy",
1941
+ * exchangeName: "binance"
1010
1942
  * });
1011
1943
  *
1944
+ * // Later: stop listening
1945
+ * unsubscribe();
1946
+ * ```
1947
+ */
1948
+ declare function listenDoneLive(fn: (event: DoneContract) => void): () => void;
1949
+ /**
1950
+ * Subscribes to filtered live background execution completion events with one-time execution.
1951
+ *
1952
+ * Emits when Live.background() completes execution.
1953
+ * Executes callback once and automatically unsubscribes.
1954
+ *
1955
+ * @param filterFn - Predicate to filter which events trigger the callback
1956
+ * @param fn - Callback function to handle the filtered event (called only once)
1957
+ * @returns Unsubscribe function to cancel the listener before it fires
1958
+ *
1959
+ * @example
1960
+ * ```typescript
1961
+ * import { listenDoneLiveOnce, Live } from "backtest-kit";
1962
+ *
1963
+ * // Wait for first live completion
1964
+ * listenDoneLiveOnce(
1965
+ * (event) => event.symbol === "BTCUSDT",
1966
+ * (event) => console.log("BTCUSDT live completed:", event.strategyName)
1967
+ * );
1968
+ *
1012
1969
  * Live.background("BTCUSDT", {
1013
1970
  * strategyName: "my-strategy",
1014
1971
  * exchangeName: "binance"
1015
1972
  * });
1973
+ * ```
1974
+ */
1975
+ declare function listenDoneLiveOnce(filterFn: (event: DoneContract) => boolean, fn: (event: DoneContract) => void): () => void;
1976
+ /**
1977
+ * Subscribes to backtest background execution completion events with queued async processing.
1978
+ *
1979
+ * Emits when Backtest.background() completes execution.
1980
+ * Events are processed sequentially in order received, even if callback is async.
1981
+ * Uses queued wrapper to prevent concurrent execution of the callback.
1982
+ *
1983
+ * @param fn - Callback function to handle completion events
1984
+ * @returns Unsubscribe function to stop listening to events
1985
+ *
1986
+ * @example
1987
+ * ```typescript
1988
+ * import { listenDoneBacktest, Backtest } from "backtest-kit";
1989
+ *
1990
+ * const unsubscribe = listenDoneBacktest((event) => {
1991
+ * console.log("Backtest completed:", event.strategyName, event.exchangeName, event.symbol);
1992
+ * });
1993
+ *
1994
+ * Backtest.background("BTCUSDT", {
1995
+ * strategyName: "my-strategy",
1996
+ * exchangeName: "binance",
1997
+ * frameName: "1d-backtest"
1998
+ * });
1016
1999
  *
1017
2000
  * // Later: stop listening
1018
2001
  * unsubscribe();
1019
2002
  * ```
1020
2003
  */
1021
- declare function listenDone(fn: (event: DoneContract) => void): () => void;
2004
+ declare function listenDoneBacktest(fn: (event: DoneContract) => void): () => void;
1022
2005
  /**
1023
- * Subscribes to filtered background execution completion events with one-time execution.
2006
+ * Subscribes to filtered backtest background execution completion events with one-time execution.
1024
2007
  *
1025
- * Emits when Live.background() or Backtest.background() completes execution.
2008
+ * Emits when Backtest.background() completes execution.
1026
2009
  * Executes callback once and automatically unsubscribes.
1027
2010
  *
1028
2011
  * @param filterFn - Predicate to filter which events trigger the callback
@@ -1031,11 +2014,11 @@ declare function listenDone(fn: (event: DoneContract) => void): () => void;
1031
2014
  *
1032
2015
  * @example
1033
2016
  * ```typescript
1034
- * import { listenDoneOnce, Backtest } from "backtest-kit";
2017
+ * import { listenDoneBacktestOnce, Backtest } from "backtest-kit";
1035
2018
  *
1036
2019
  * // Wait for first backtest completion
1037
- * listenDoneOnce(
1038
- * (event) => event.backtest && event.symbol === "BTCUSDT",
2020
+ * listenDoneBacktestOnce(
2021
+ * (event) => event.symbol === "BTCUSDT",
1039
2022
  * (event) => console.log("BTCUSDT backtest completed:", event.strategyName)
1040
2023
  * );
1041
2024
  *
@@ -1046,7 +2029,60 @@ declare function listenDone(fn: (event: DoneContract) => void): () => void;
1046
2029
  * });
1047
2030
  * ```
1048
2031
  */
1049
- declare function listenDoneOnce(filterFn: (event: DoneContract) => boolean, fn: (event: DoneContract) => void): () => void;
2032
+ declare function listenDoneBacktestOnce(filterFn: (event: DoneContract) => boolean, fn: (event: DoneContract) => void): () => void;
2033
+ /**
2034
+ * Subscribes to walker background execution completion events with queued async processing.
2035
+ *
2036
+ * Emits when Walker.background() completes execution.
2037
+ * Events are processed sequentially in order received, even if callback is async.
2038
+ * Uses queued wrapper to prevent concurrent execution of the callback.
2039
+ *
2040
+ * @param fn - Callback function to handle completion events
2041
+ * @returns Unsubscribe function to stop listening to events
2042
+ *
2043
+ * @example
2044
+ * ```typescript
2045
+ * import { listenDoneWalker, Walker } from "backtest-kit";
2046
+ *
2047
+ * const unsubscribe = listenDoneWalker((event) => {
2048
+ * console.log("Walker completed:", event.strategyName, event.exchangeName, event.symbol);
2049
+ * });
2050
+ *
2051
+ * Walker.background("BTCUSDT", {
2052
+ * walkerName: "my-walker"
2053
+ * });
2054
+ *
2055
+ * // Later: stop listening
2056
+ * unsubscribe();
2057
+ * ```
2058
+ */
2059
+ declare function listenDoneWalker(fn: (event: DoneContract) => void): () => void;
2060
+ /**
2061
+ * Subscribes to filtered walker background execution completion events with one-time execution.
2062
+ *
2063
+ * Emits when Walker.background() completes execution.
2064
+ * Executes callback once and automatically unsubscribes.
2065
+ *
2066
+ * @param filterFn - Predicate to filter which events trigger the callback
2067
+ * @param fn - Callback function to handle the filtered event (called only once)
2068
+ * @returns Unsubscribe function to cancel the listener before it fires
2069
+ *
2070
+ * @example
2071
+ * ```typescript
2072
+ * import { listenDoneWalkerOnce, Walker } from "backtest-kit";
2073
+ *
2074
+ * // Wait for first walker completion
2075
+ * listenDoneWalkerOnce(
2076
+ * (event) => event.symbol === "BTCUSDT",
2077
+ * (event) => console.log("BTCUSDT walker completed:", event.strategyName)
2078
+ * );
2079
+ *
2080
+ * Walker.background("BTCUSDT", {
2081
+ * walkerName: "my-walker"
2082
+ * });
2083
+ * ```
2084
+ */
2085
+ declare function listenDoneWalkerOnce(filterFn: (event: DoneContract) => boolean, fn: (event: DoneContract) => void): () => void;
1050
2086
  /**
1051
2087
  * Subscribes to backtest progress events with queued async processing.
1052
2088
  *
@@ -1078,6 +2114,167 @@ declare function listenDoneOnce(filterFn: (event: DoneContract) => boolean, fn:
1078
2114
  * ```
1079
2115
  */
1080
2116
  declare function listenProgress(fn: (event: ProgressContract) => void): () => void;
2117
+ /**
2118
+ * Subscribes to performance metric events with queued async processing.
2119
+ *
2120
+ * Emits during strategy execution to track timing metrics for operations.
2121
+ * Useful for profiling and identifying performance bottlenecks.
2122
+ * Events are processed sequentially in order received, even if callback is async.
2123
+ * Uses queued wrapper to prevent concurrent execution of the callback.
2124
+ *
2125
+ * @param fn - Callback function to handle performance events
2126
+ * @returns Unsubscribe function to stop listening to events
2127
+ *
2128
+ * @example
2129
+ * ```typescript
2130
+ * import { listenPerformance, Backtest } from "backtest-kit";
2131
+ *
2132
+ * const unsubscribe = listenPerformance((event) => {
2133
+ * console.log(`${event.metricType}: ${event.duration.toFixed(2)}ms`);
2134
+ * if (event.duration > 100) {
2135
+ * console.warn("Slow operation detected:", event.metricType);
2136
+ * }
2137
+ * });
2138
+ *
2139
+ * Backtest.background("BTCUSDT", {
2140
+ * strategyName: "my-strategy",
2141
+ * exchangeName: "binance",
2142
+ * frameName: "1d-backtest"
2143
+ * });
2144
+ *
2145
+ * // Later: stop listening
2146
+ * unsubscribe();
2147
+ * ```
2148
+ */
2149
+ declare function listenPerformance(fn: (event: PerformanceContract) => void): () => void;
2150
+ /**
2151
+ * Subscribes to walker progress events with queued async processing.
2152
+ *
2153
+ * Emits during Walker.run() execution after each strategy completes.
2154
+ * Events are processed sequentially in order received, even if callback is async.
2155
+ * Uses queued wrapper to prevent concurrent execution of the callback.
2156
+ *
2157
+ * @param fn - Callback function to handle walker progress events
2158
+ * @returns Unsubscribe function to stop listening to events
2159
+ *
2160
+ * @example
2161
+ * ```typescript
2162
+ * import { listenWalker, Walker } from "backtest-kit";
2163
+ *
2164
+ * const unsubscribe = listenWalker((event) => {
2165
+ * console.log(`Progress: ${event.strategiesTested} / ${event.totalStrategies}`);
2166
+ * console.log(`Best strategy: ${event.bestStrategy} (${event.bestMetric})`);
2167
+ * console.log(`Current strategy: ${event.strategyName} (${event.metricValue})`);
2168
+ * });
2169
+ *
2170
+ * Walker.run("BTCUSDT", {
2171
+ * walkerName: "my-walker",
2172
+ * exchangeName: "binance",
2173
+ * frameName: "1d-backtest"
2174
+ * });
2175
+ *
2176
+ * // Later: stop listening
2177
+ * unsubscribe();
2178
+ * ```
2179
+ */
2180
+ declare function listenWalker(fn: (event: WalkerContract) => void): () => void;
2181
+ /**
2182
+ * Subscribes to filtered walker progress events with one-time execution.
2183
+ *
2184
+ * Listens for events matching the filter predicate, then executes callback once
2185
+ * and automatically unsubscribes. Useful for waiting for specific walker conditions.
2186
+ *
2187
+ * @param filterFn - Predicate to filter which events trigger the callback
2188
+ * @param fn - Callback function to handle the filtered event (called only once)
2189
+ * @returns Unsubscribe function to cancel the listener before it fires
2190
+ *
2191
+ * @example
2192
+ * ```typescript
2193
+ * import { listenWalkerOnce, Walker } from "backtest-kit";
2194
+ *
2195
+ * // Wait for walker to complete all strategies
2196
+ * listenWalkerOnce(
2197
+ * (event) => event.strategiesTested === event.totalStrategies,
2198
+ * (event) => {
2199
+ * console.log("Walker completed!");
2200
+ * console.log("Best strategy:", event.bestStrategy, event.bestMetric);
2201
+ * }
2202
+ * );
2203
+ *
2204
+ * // Wait for specific strategy to be tested
2205
+ * const cancel = listenWalkerOnce(
2206
+ * (event) => event.strategyName === "my-strategy-v2",
2207
+ * (event) => console.log("Strategy v2 tested:", event.metricValue)
2208
+ * );
2209
+ *
2210
+ * Walker.run("BTCUSDT", {
2211
+ * walkerName: "my-walker",
2212
+ * exchangeName: "binance",
2213
+ * frameName: "1d-backtest"
2214
+ * });
2215
+ *
2216
+ * // Cancel if needed before event fires
2217
+ * cancel();
2218
+ * ```
2219
+ */
2220
+ declare function listenWalkerOnce(filterFn: (event: WalkerContract) => boolean, fn: (event: WalkerContract) => void): () => void;
2221
+ /**
2222
+ * Subscribes to walker completion events with queued async processing.
2223
+ *
2224
+ * Emits when Walker.run() completes testing all strategies.
2225
+ * Events are processed sequentially in order received, even if callback is async.
2226
+ * Uses queued wrapper to prevent concurrent execution of the callback.
2227
+ *
2228
+ * @param fn - Callback function to handle walker completion event
2229
+ * @returns Unsubscribe function to stop listening to events
2230
+ *
2231
+ * @example
2232
+ * ```typescript
2233
+ * import { listenWalkerComplete, Walker } from "backtest-kit";
2234
+ *
2235
+ * const unsubscribe = listenWalkerComplete((results) => {
2236
+ * console.log(`Walker ${results.walkerName} completed!`);
2237
+ * console.log(`Best strategy: ${results.bestStrategy}`);
2238
+ * console.log(`Best ${results.metric}: ${results.bestMetric}`);
2239
+ * console.log(`Tested ${results.totalStrategies} strategies`);
2240
+ * });
2241
+ *
2242
+ * Walker.run("BTCUSDT", {
2243
+ * walkerName: "my-walker",
2244
+ * exchangeName: "binance",
2245
+ * frameName: "1d-backtest"
2246
+ * });
2247
+ *
2248
+ * // Later: stop listening
2249
+ * unsubscribe();
2250
+ * ```
2251
+ */
2252
+ declare function listenWalkerComplete(fn: (event: IWalkerResults) => void): () => void;
2253
+ /**
2254
+ * Subscribes to risk validation errors with queued async processing.
2255
+ *
2256
+ * Emits when risk validation functions throw errors during signal checking.
2257
+ * Useful for debugging and monitoring risk validation failures.
2258
+ * Events are processed sequentially in order received, even if callback is async.
2259
+ * Uses queued wrapper to prevent concurrent execution of the callback.
2260
+ *
2261
+ * @param fn - Callback function to handle validation errors
2262
+ * @returns Unsubscribe function to stop listening to events
2263
+ *
2264
+ * @example
2265
+ * ```typescript
2266
+ * import { listenValidation } from "./function/event";
2267
+ *
2268
+ * const unsubscribe = listenValidation((error) => {
2269
+ * console.error("Risk validation error:", error.message);
2270
+ * // Log to monitoring service for debugging
2271
+ * });
2272
+ *
2273
+ * // Later: stop listening
2274
+ * unsubscribe();
2275
+ * ```
2276
+ */
2277
+ declare function listenValidation(fn: (error: Error) => void): () => void;
1081
2278
 
1082
2279
  /**
1083
2280
  * Fetches historical candle data from the registered exchange.
@@ -1181,39 +2378,131 @@ declare function getDate(): Promise<Date>;
1181
2378
  declare function getMode(): Promise<"backtest" | "live">;
1182
2379
 
1183
2380
  /**
1184
- * Statistical data calculated from backtest results.
2381
+ * Portfolio heatmap statistics for a single symbol.
2382
+ * Aggregated metrics across all strategies for one trading pair.
2383
+ */
2384
+ interface IHeatmapRow {
2385
+ /** Trading pair symbol (e.g., "BTCUSDT") */
2386
+ symbol: string;
2387
+ /** Total profit/loss percentage across all closed trades */
2388
+ totalPnl: number | null;
2389
+ /** Risk-adjusted return (Sharpe Ratio) */
2390
+ sharpeRatio: number | null;
2391
+ /** Maximum drawdown percentage (largest peak-to-trough decline) */
2392
+ maxDrawdown: number | null;
2393
+ /** Total number of closed trades */
2394
+ totalTrades: number;
2395
+ /** Number of winning trades */
2396
+ winCount: number;
2397
+ /** Number of losing trades */
2398
+ lossCount: number;
2399
+ /** Win rate percentage */
2400
+ winRate: number | null;
2401
+ /** Average PNL per trade */
2402
+ avgPnl: number | null;
2403
+ /** Standard deviation of PNL */
2404
+ stdDev: number | null;
2405
+ /** Profit factor: sum of wins / sum of losses */
2406
+ profitFactor: number | null;
2407
+ /** Average profit percentage on winning trades */
2408
+ avgWin: number | null;
2409
+ /** Average loss percentage on losing trades */
2410
+ avgLoss: number | null;
2411
+ /** Maximum consecutive winning trades */
2412
+ maxWinStreak: number;
2413
+ /** Maximum consecutive losing trades */
2414
+ maxLossStreak: number;
2415
+ /** Expectancy: (winRate * avgWin) - (lossRate * avgLoss) */
2416
+ expectancy: number | null;
2417
+ }
2418
+ /**
2419
+ * Portfolio heatmap statistics structure.
2420
+ * Contains aggregated data for all symbols in the portfolio.
2421
+ */
2422
+ interface IHeatmapStatistics {
2423
+ /** Array of symbol statistics */
2424
+ symbols: IHeatmapRow[];
2425
+ /** Total number of symbols tracked */
2426
+ totalSymbols: number;
2427
+ /** Portfolio-wide total PNL */
2428
+ portfolioTotalPnl: number | null;
2429
+ /** Portfolio-wide Sharpe Ratio */
2430
+ portfolioSharpeRatio: number | null;
2431
+ /** Portfolio-wide total trades */
2432
+ portfolioTotalTrades: number;
2433
+ }
2434
+
2435
+ /**
2436
+ * Unified tick event data for report generation.
2437
+ * Contains all information about a tick event regardless of action type.
2438
+ */
2439
+ interface TickEvent {
2440
+ /** Event timestamp in milliseconds */
2441
+ timestamp: number;
2442
+ /** Event action type */
2443
+ action: "idle" | "opened" | "active" | "closed";
2444
+ /** Trading pair symbol (only for non-idle events) */
2445
+ symbol?: string;
2446
+ /** Signal ID (only for opened/active/closed) */
2447
+ signalId?: string;
2448
+ /** Position type (only for opened/active/closed) */
2449
+ position?: string;
2450
+ /** Signal note (only for opened/active/closed) */
2451
+ note?: string;
2452
+ /** Current price */
2453
+ currentPrice: number;
2454
+ /** Open price (only for opened/active/closed) */
2455
+ openPrice?: number;
2456
+ /** Take profit price (only for opened/active/closed) */
2457
+ takeProfit?: number;
2458
+ /** Stop loss price (only for opened/active/closed) */
2459
+ stopLoss?: number;
2460
+ /** PNL percentage (only for closed) */
2461
+ pnl?: number;
2462
+ /** Close reason (only for closed) */
2463
+ closeReason?: string;
2464
+ /** Duration in minutes (only for closed) */
2465
+ duration?: number;
2466
+ }
2467
+ /**
2468
+ * Statistical data calculated from live trading results.
1185
2469
  *
1186
2470
  * All numeric values are null if calculation is unsafe (NaN, Infinity, etc).
1187
- * Provides comprehensive metrics for strategy performance analysis.
2471
+ * Provides comprehensive metrics for live trading performance analysis.
1188
2472
  *
1189
2473
  * @example
1190
2474
  * ```typescript
1191
- * const stats = await Backtest.getData("my-strategy");
2475
+ * const stats = await Live.getData("my-strategy");
1192
2476
  *
1193
- * console.log(`Total signals: ${stats.totalSignals}`);
2477
+ * console.log(`Total events: ${stats.totalEvents}`);
2478
+ * console.log(`Closed signals: ${stats.totalClosed}`);
1194
2479
  * console.log(`Win rate: ${stats.winRate}%`);
1195
2480
  * console.log(`Sharpe Ratio: ${stats.sharpeRatio}`);
1196
2481
  *
1197
- * // Access raw signal data
1198
- * stats.signalList.forEach(signal => {
1199
- * console.log(`Signal ${signal.signal.id}: ${signal.pnl.pnlPercentage}%`);
2482
+ * // Access raw event data (includes idle, opened, active, closed)
2483
+ * stats.eventList.forEach(event => {
2484
+ * if (event.action === "closed") {
2485
+ * console.log(`Closed signal: ${event.pnl}%`);
2486
+ * }
1200
2487
  * });
1201
2488
  * ```
1202
2489
  */
1203
- interface BacktestStatistics {
1204
- /** Array of all closed signals with full details (price, PNL, timestamps, etc.) */
1205
- signalList: IStrategyTickResultClosed[];
1206
- /** Total number of closed signals */
1207
- totalSignals: number;
1208
- /** Number of winning signals (PNL > 0) */
2490
+ interface LiveStatistics {
2491
+ /** Array of all events (idle, opened, active, closed) with full details */
2492
+ eventList: TickEvent[];
2493
+ /** Total number of all events (includes idle, opened, active, closed) */
2494
+ totalEvents: number;
2495
+ /** Total number of closed signals only */
2496
+ totalClosed: number;
2497
+ /** Number of winning closed signals (PNL > 0) */
1209
2498
  winCount: number;
1210
- /** Number of losing signals (PNL < 0) */
2499
+ /** Number of losing closed signals (PNL < 0) */
1211
2500
  lossCount: number;
1212
- /** Win rate as percentage (0-100), null if unsafe. Higher is better. */
2501
+ /** Win rate as percentage (0-100) based on closed signals, null if unsafe. Higher is better. */
1213
2502
  winRate: number | null;
1214
- /** Average PNL per signal as percentage, null if unsafe. Higher is better. */
2503
+ /** Average PNL per closed signal as percentage, null if unsafe. Higher is better. */
1215
2504
  avgPnl: number | null;
1216
- /** Cumulative PNL across all signals as percentage, null if unsafe. Higher is better. */
2505
+ /** Cumulative PNL across all closed signals as percentage, null if unsafe. Higher is better. */
1217
2506
  totalPnl: number | null;
1218
2507
  /** Standard deviation of returns (volatility metric), null if unsafe. Lower is better. */
1219
2508
  stdDev: number | null;
@@ -1227,33 +2516,36 @@ interface BacktestStatistics {
1227
2516
  expectedYearlyReturns: number | null;
1228
2517
  }
1229
2518
  /**
1230
- * Service for generating and saving backtest markdown reports.
2519
+ * Service for generating and saving live trading markdown reports.
1231
2520
  *
1232
2521
  * Features:
1233
- * - Listens to signal events via onTick callback
1234
- * - Accumulates closed signals per strategy using memoized storage
1235
- * - Generates markdown tables with detailed signal information
1236
- * - Saves reports to disk in logs/backtest/{strategyName}.md
2522
+ * - Listens to all signal events via onTick callback
2523
+ * - Accumulates all events (idle, opened, active, closed) per strategy
2524
+ * - Generates markdown tables with detailed event information
2525
+ * - Provides trading statistics (win rate, average PNL)
2526
+ * - Saves reports to disk in logs/live/{strategyName}.md
1237
2527
  *
1238
2528
  * @example
1239
2529
  * ```typescript
1240
- * const service = new BacktestMarkdownService();
2530
+ * const service = new LiveMarkdownService();
1241
2531
  *
1242
2532
  * // Add to strategy callbacks
1243
2533
  * addStrategy({
1244
2534
  * strategyName: "my-strategy",
1245
2535
  * callbacks: {
1246
2536
  * onTick: (symbol, result, backtest) => {
1247
- * service.tick(result);
2537
+ * if (!backtest) {
2538
+ * service.tick(result);
2539
+ * }
1248
2540
  * }
1249
2541
  * }
1250
2542
  * });
1251
2543
  *
1252
- * // After backtest, generate and save report
1253
- * await service.saveReport("my-strategy");
2544
+ * // Later: generate and save report
2545
+ * await service.dump("my-strategy");
1254
2546
  * ```
1255
2547
  */
1256
- declare class BacktestMarkdownService {
2548
+ declare class LiveMarkdownService {
1257
2549
  /** Logger service for debug output */
1258
2550
  private readonly loggerService;
1259
2551
  /**
@@ -1262,27 +2554,29 @@ declare class BacktestMarkdownService {
1262
2554
  */
1263
2555
  private getStorage;
1264
2556
  /**
1265
- * Processes tick events and accumulates closed signals.
2557
+ * Processes tick events and accumulates all event types.
1266
2558
  * Should be called from IStrategyCallbacks.onTick.
1267
2559
  *
1268
- * Only processes closed signals - opened signals are ignored.
2560
+ * Processes all event types: idle, opened, active, closed.
1269
2561
  *
1270
- * @param data - Tick result from strategy execution (opened or closed)
2562
+ * @param data - Tick result from strategy execution
1271
2563
  *
1272
2564
  * @example
1273
2565
  * ```typescript
1274
- * const service = new BacktestMarkdownService();
2566
+ * const service = new LiveMarkdownService();
1275
2567
  *
1276
2568
  * callbacks: {
1277
2569
  * onTick: (symbol, result, backtest) => {
1278
- * service.tick(result);
2570
+ * if (!backtest) {
2571
+ * service.tick(result);
2572
+ * }
1279
2573
  * }
1280
2574
  * }
1281
2575
  * ```
1282
2576
  */
1283
2577
  private tick;
1284
2578
  /**
1285
- * Gets statistical data from all closed signals for a strategy.
2579
+ * Gets statistical data from all live trading events for a strategy.
1286
2580
  * Delegates to ReportStorage.getData().
1287
2581
  *
1288
2582
  * @param strategyName - Strategy name to get data for
@@ -1290,22 +2584,22 @@ declare class BacktestMarkdownService {
1290
2584
  *
1291
2585
  * @example
1292
2586
  * ```typescript
1293
- * const service = new BacktestMarkdownService();
2587
+ * const service = new LiveMarkdownService();
1294
2588
  * const stats = await service.getData("my-strategy");
1295
2589
  * console.log(stats.sharpeRatio, stats.winRate);
1296
2590
  * ```
1297
2591
  */
1298
- getData: (strategyName: StrategyName) => Promise<BacktestStatistics>;
2592
+ getData: (strategyName: StrategyName) => Promise<LiveStatistics>;
1299
2593
  /**
1300
- * Generates markdown report with all closed signals for a strategy.
1301
- * Delegates to ReportStorage.generateReport().
2594
+ * Generates markdown report with all events for a strategy.
2595
+ * Delegates to ReportStorage.getReport().
1302
2596
  *
1303
2597
  * @param strategyName - Strategy name to generate report for
1304
- * @returns Markdown formatted report string with table of all closed signals
2598
+ * @returns Markdown formatted report string with table of all events
1305
2599
  *
1306
2600
  * @example
1307
2601
  * ```typescript
1308
- * const service = new BacktestMarkdownService();
2602
+ * const service = new LiveMarkdownService();
1309
2603
  * const markdown = await service.getReport("my-strategy");
1310
2604
  * console.log(markdown);
1311
2605
  * ```
@@ -1317,13 +2611,13 @@ declare class BacktestMarkdownService {
1317
2611
  * Delegates to ReportStorage.dump().
1318
2612
  *
1319
2613
  * @param strategyName - Strategy name to save report for
1320
- * @param path - Directory path to save report (default: "./logs/backtest")
2614
+ * @param path - Directory path to save report (default: "./logs/live")
1321
2615
  *
1322
2616
  * @example
1323
2617
  * ```typescript
1324
- * const service = new BacktestMarkdownService();
2618
+ * const service = new LiveMarkdownService();
1325
2619
  *
1326
- * // Save to default path: ./logs/backtest/my-strategy.md
2620
+ * // Save to default path: ./logs/live/my-strategy.md
1327
2621
  * await service.dump("my-strategy");
1328
2622
  *
1329
2623
  * // Save to custom path: ./custom/path/my-strategy.md
@@ -1332,7 +2626,7 @@ declare class BacktestMarkdownService {
1332
2626
  */
1333
2627
  dump: (strategyName: StrategyName, path?: string) => Promise<void>;
1334
2628
  /**
1335
- * Clears accumulated signal data from storage.
2629
+ * Clears accumulated event data from storage.
1336
2630
  * If strategyName is provided, clears only that strategy's data.
1337
2631
  * If strategyName is omitted, clears all strategies' data.
1338
2632
  *
@@ -1340,7 +2634,7 @@ declare class BacktestMarkdownService {
1340
2634
  *
1341
2635
  * @example
1342
2636
  * ```typescript
1343
- * const service = new BacktestMarkdownService();
2637
+ * const service = new LiveMarkdownService();
1344
2638
  *
1345
2639
  * // Clear specific strategy data
1346
2640
  * await service.clear("my-strategy");
@@ -1351,240 +2645,303 @@ declare class BacktestMarkdownService {
1351
2645
  */
1352
2646
  clear: (strategyName?: StrategyName) => Promise<void>;
1353
2647
  /**
1354
- * Initializes the service by subscribing to backtest signal events.
2648
+ * Initializes the service by subscribing to live signal events.
1355
2649
  * Uses singleshot to ensure initialization happens only once.
1356
2650
  * Automatically called on first use.
1357
2651
  *
1358
2652
  * @example
1359
2653
  * ```typescript
1360
- * const service = new BacktestMarkdownService();
1361
- * await service.init(); // Subscribe to backtest events
2654
+ * const service = new LiveMarkdownService();
2655
+ * await service.init(); // Subscribe to live events
1362
2656
  * ```
1363
2657
  */
1364
2658
  protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
1365
2659
  }
1366
2660
 
1367
2661
  /**
1368
- * Unified tick event data for report generation.
1369
- * Contains all information about a tick event regardless of action type.
2662
+ * Aggregated statistics for a specific metric type.
1370
2663
  */
1371
- interface TickEvent {
1372
- /** Event timestamp in milliseconds */
1373
- timestamp: number;
1374
- /** Event action type */
1375
- action: "idle" | "opened" | "active" | "closed";
1376
- /** Trading pair symbol (only for non-idle events) */
1377
- symbol?: string;
1378
- /** Signal ID (only for opened/active/closed) */
1379
- signalId?: string;
1380
- /** Position type (only for opened/active/closed) */
1381
- position?: string;
1382
- /** Signal note (only for opened/active/closed) */
1383
- note?: string;
1384
- /** Current price */
1385
- currentPrice: number;
1386
- /** Open price (only for opened/active/closed) */
1387
- openPrice?: number;
1388
- /** Take profit price (only for opened/active/closed) */
1389
- takeProfit?: number;
1390
- /** Stop loss price (only for opened/active/closed) */
1391
- stopLoss?: number;
1392
- /** PNL percentage (only for closed) */
1393
- pnl?: number;
1394
- /** Close reason (only for closed) */
1395
- closeReason?: string;
1396
- /** Duration in minutes (only for closed) */
1397
- duration?: number;
2664
+ interface MetricStats {
2665
+ /** Type of metric */
2666
+ metricType: PerformanceMetricType;
2667
+ /** Number of recorded samples */
2668
+ count: number;
2669
+ /** Total duration across all samples (ms) */
2670
+ totalDuration: number;
2671
+ /** Average duration (ms) */
2672
+ avgDuration: number;
2673
+ /** Minimum duration (ms) */
2674
+ minDuration: number;
2675
+ /** Maximum duration (ms) */
2676
+ maxDuration: number;
2677
+ /** Standard deviation of duration (ms) */
2678
+ stdDev: number;
2679
+ /** Median duration (ms) */
2680
+ median: number;
2681
+ /** 95th percentile duration (ms) */
2682
+ p95: number;
2683
+ /** 99th percentile duration (ms) */
2684
+ p99: number;
2685
+ /** Average wait time between events (ms) */
2686
+ avgWaitTime: number;
2687
+ /** Minimum wait time between events (ms) */
2688
+ minWaitTime: number;
2689
+ /** Maximum wait time between events (ms) */
2690
+ maxWaitTime: number;
1398
2691
  }
1399
2692
  /**
1400
- * Statistical data calculated from live trading results.
2693
+ * Performance statistics aggregated by strategy.
2694
+ */
2695
+ interface PerformanceStatistics {
2696
+ /** Strategy name */
2697
+ strategyName: string;
2698
+ /** Total number of performance events recorded */
2699
+ totalEvents: number;
2700
+ /** Total execution time across all metrics (ms) */
2701
+ totalDuration: number;
2702
+ /** Statistics grouped by metric type */
2703
+ metricStats: Record<string, MetricStats>;
2704
+ /** All raw performance events */
2705
+ events: PerformanceContract[];
2706
+ }
2707
+ /**
2708
+ * Service for collecting and analyzing performance metrics.
1401
2709
  *
1402
- * All numeric values are null if calculation is unsafe (NaN, Infinity, etc).
1403
- * Provides comprehensive metrics for live trading performance analysis.
2710
+ * Features:
2711
+ * - Listens to performance events via performanceEmitter
2712
+ * - Accumulates metrics per strategy
2713
+ * - Calculates aggregated statistics (avg, min, max, percentiles)
2714
+ * - Generates markdown reports with bottleneck analysis
2715
+ * - Saves reports to disk in logs/performance/{strategyName}.md
1404
2716
  *
1405
2717
  * @example
1406
2718
  * ```typescript
1407
- * const stats = await Live.getData("my-strategy");
1408
- *
1409
- * console.log(`Total events: ${stats.totalEvents}`);
1410
- * console.log(`Closed signals: ${stats.totalClosed}`);
1411
- * console.log(`Win rate: ${stats.winRate}%`);
1412
- * console.log(`Sharpe Ratio: ${stats.sharpeRatio}`);
2719
+ * import { listenPerformance } from "backtest-kit";
1413
2720
  *
1414
- * // Access raw event data (includes idle, opened, active, closed)
1415
- * stats.eventList.forEach(event => {
1416
- * if (event.action === "closed") {
1417
- * console.log(`Closed signal: ${event.pnl}%`);
1418
- * }
2721
+ * // Subscribe to performance events
2722
+ * listenPerformance((event) => {
2723
+ * console.log(`${event.metricType}: ${event.duration.toFixed(2)}ms`);
1419
2724
  * });
2725
+ *
2726
+ * // After execution, generate report
2727
+ * const stats = await Performance.getData("my-strategy");
2728
+ * console.log("Bottlenecks:", stats.metricStats);
2729
+ *
2730
+ * // Save report to disk
2731
+ * await Performance.dump("my-strategy");
1420
2732
  * ```
1421
2733
  */
1422
- interface LiveStatistics {
1423
- /** Array of all events (idle, opened, active, closed) with full details */
1424
- eventList: TickEvent[];
1425
- /** Total number of all events (includes idle, opened, active, closed) */
1426
- totalEvents: number;
1427
- /** Total number of closed signals only */
1428
- totalClosed: number;
1429
- /** Number of winning closed signals (PNL > 0) */
1430
- winCount: number;
1431
- /** Number of losing closed signals (PNL < 0) */
1432
- lossCount: number;
1433
- /** Win rate as percentage (0-100) based on closed signals, null if unsafe. Higher is better. */
1434
- winRate: number | null;
1435
- /** Average PNL per closed signal as percentage, null if unsafe. Higher is better. */
1436
- avgPnl: number | null;
1437
- /** Cumulative PNL across all closed signals as percentage, null if unsafe. Higher is better. */
1438
- totalPnl: number | null;
1439
- /** Standard deviation of returns (volatility metric), null if unsafe. Lower is better. */
1440
- stdDev: number | null;
1441
- /** Sharpe Ratio (risk-adjusted return = avgPnl / stdDev), null if unsafe. Higher is better. */
1442
- sharpeRatio: number | null;
1443
- /** Annualized Sharpe Ratio (sharpeRatio × √365), null if unsafe. Higher is better. */
1444
- annualizedSharpeRatio: number | null;
1445
- /** Certainty Ratio (avgWin / |avgLoss|), null if unsafe. Higher is better. */
1446
- certaintyRatio: number | null;
1447
- /** Expected yearly returns based on average trade duration and PNL, null if unsafe. Higher is better. */
1448
- expectedYearlyReturns: number | null;
2734
+ declare class PerformanceMarkdownService {
2735
+ /** Logger service for debug output */
2736
+ private readonly loggerService;
2737
+ /**
2738
+ * Memoized function to get or create PerformanceStorage for a strategy.
2739
+ * Each strategy gets its own isolated storage instance.
2740
+ */
2741
+ private getStorage;
2742
+ /**
2743
+ * Processes performance events and accumulates metrics.
2744
+ * Should be called from performance tracking code.
2745
+ *
2746
+ * @param event - Performance event with timing data
2747
+ */
2748
+ private track;
2749
+ /**
2750
+ * Gets aggregated performance statistics for a strategy.
2751
+ *
2752
+ * @param strategyName - Strategy name to get data for
2753
+ * @returns Performance statistics with aggregated metrics
2754
+ *
2755
+ * @example
2756
+ * ```typescript
2757
+ * const stats = await performanceService.getData("my-strategy");
2758
+ * console.log("Total time:", stats.totalDuration);
2759
+ * console.log("Slowest operation:", Object.values(stats.metricStats)
2760
+ * .sort((a, b) => b.avgDuration - a.avgDuration)[0]);
2761
+ * ```
2762
+ */
2763
+ getData: (strategyName: string) => Promise<PerformanceStatistics>;
2764
+ /**
2765
+ * Generates markdown report with performance analysis.
2766
+ *
2767
+ * @param strategyName - Strategy name to generate report for
2768
+ * @returns Markdown formatted report string
2769
+ *
2770
+ * @example
2771
+ * ```typescript
2772
+ * const markdown = await performanceService.getReport("my-strategy");
2773
+ * console.log(markdown);
2774
+ * ```
2775
+ */
2776
+ getReport: (strategyName: string) => Promise<string>;
2777
+ /**
2778
+ * Saves performance report to disk.
2779
+ *
2780
+ * @param strategyName - Strategy name to save report for
2781
+ * @param path - Directory path to save report
2782
+ *
2783
+ * @example
2784
+ * ```typescript
2785
+ * // Save to default path: ./logs/performance/my-strategy.md
2786
+ * await performanceService.dump("my-strategy");
2787
+ *
2788
+ * // Save to custom path
2789
+ * await performanceService.dump("my-strategy", "./custom/path");
2790
+ * ```
2791
+ */
2792
+ dump: (strategyName: string, path?: string) => Promise<void>;
2793
+ /**
2794
+ * Clears accumulated performance data from storage.
2795
+ *
2796
+ * @param strategyName - Optional strategy name to clear specific strategy data
2797
+ */
2798
+ clear: (strategyName?: string) => Promise<void>;
2799
+ /**
2800
+ * Initializes the service by subscribing to performance events.
2801
+ * Uses singleshot to ensure initialization happens only once.
2802
+ */
2803
+ protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
1449
2804
  }
2805
+
1450
2806
  /**
1451
- * Service for generating and saving live trading markdown reports.
1452
- *
1453
- * Features:
1454
- * - Listens to all signal events via onTick callback
1455
- * - Accumulates all events (idle, opened, active, closed) per strategy
1456
- * - Generates markdown tables with detailed event information
1457
- * - Provides trading statistics (win rate, average PNL)
1458
- * - Saves reports to disk in logs/live/{strategyName}.md
2807
+ * Alias for walker statistics result interface.
2808
+ * Used for clarity in markdown service context.
1459
2809
  *
1460
- * @example
1461
- * ```typescript
1462
- * const service = new LiveMarkdownService();
2810
+ */
2811
+ type WalkerStatistics = IWalkerResults;
2812
+ /**
2813
+ * Service for generating and saving walker markdown reports.
1463
2814
  *
1464
- * // Add to strategy callbacks
1465
- * addStrategy({
1466
- * strategyName: "my-strategy",
1467
- * callbacks: {
1468
- * onTick: (symbol, result, backtest) => {
1469
- * if (!backtest) {
1470
- * service.tick(result);
1471
- * }
1472
- * }
1473
- * }
1474
- * });
2815
+ * Features:
2816
+ * - Listens to walker events via tick callback
2817
+ * - Accumulates strategy results per walker using memoized storage
2818
+ * - Generates markdown tables with detailed strategy comparison
2819
+ * - Saves reports to disk in logs/walker/{walkerName}.md
1475
2820
  *
1476
- * // Later: generate and save report
1477
- * await service.dump("my-strategy");
2821
+ * @example
2822
+ * ```typescript
2823
+ * const service = new WalkerMarkdownService();
2824
+ * const results = await service.getData("my-walker");
2825
+ * await service.dump("my-walker");
1478
2826
  * ```
1479
2827
  */
1480
- declare class LiveMarkdownService {
2828
+ declare class WalkerMarkdownService {
1481
2829
  /** Logger service for debug output */
1482
2830
  private readonly loggerService;
1483
2831
  /**
1484
- * Memoized function to get or create ReportStorage for a strategy.
1485
- * Each strategy gets its own isolated storage instance.
2832
+ * Memoized function to get or create ReportStorage for a walker.
2833
+ * Each walker gets its own isolated storage instance.
1486
2834
  */
1487
2835
  private getStorage;
1488
2836
  /**
1489
- * Processes tick events and accumulates all event types.
1490
- * Should be called from IStrategyCallbacks.onTick.
1491
- *
1492
- * Processes all event types: idle, opened, active, closed.
2837
+ * Processes walker progress events and accumulates strategy results.
2838
+ * Should be called from walkerEmitter.
1493
2839
  *
1494
- * @param data - Tick result from strategy execution
2840
+ * @param data - Walker contract from walker execution
1495
2841
  *
1496
2842
  * @example
1497
2843
  * ```typescript
1498
- * const service = new LiveMarkdownService();
1499
- *
1500
- * callbacks: {
1501
- * onTick: (symbol, result, backtest) => {
1502
- * if (!backtest) {
1503
- * service.tick(result);
1504
- * }
1505
- * }
1506
- * }
2844
+ * const service = new WalkerMarkdownService();
2845
+ * walkerEmitter.subscribe((data) => service.tick(data));
1507
2846
  * ```
1508
2847
  */
1509
2848
  private tick;
1510
2849
  /**
1511
- * Gets statistical data from all live trading events for a strategy.
2850
+ * Gets walker results data from all strategy results.
1512
2851
  * Delegates to ReportStorage.getData().
1513
2852
  *
1514
- * @param strategyName - Strategy name to get data for
1515
- * @returns Statistical data object with all metrics
2853
+ * @param walkerName - Walker name to get data for
2854
+ * @param symbol - Trading symbol
2855
+ * @param metric - Metric being optimized
2856
+ * @param context - Context with exchangeName and frameName
2857
+ * @returns Walker results data object with all metrics
1516
2858
  *
1517
2859
  * @example
1518
2860
  * ```typescript
1519
- * const service = new LiveMarkdownService();
1520
- * const stats = await service.getData("my-strategy");
1521
- * console.log(stats.sharpeRatio, stats.winRate);
2861
+ * const service = new WalkerMarkdownService();
2862
+ * const results = await service.getData("my-walker", "BTCUSDT", "sharpeRatio", { exchangeName: "binance", frameName: "1d" });
2863
+ * console.log(results.bestStrategy, results.bestMetric);
1522
2864
  * ```
1523
2865
  */
1524
- getData: (strategyName: StrategyName) => Promise<LiveStatistics>;
2866
+ getData: (walkerName: WalkerName, symbol: string, metric: WalkerMetric, context: {
2867
+ exchangeName: string;
2868
+ frameName: string;
2869
+ }) => Promise<IWalkerResults>;
1525
2870
  /**
1526
- * Generates markdown report with all events for a strategy.
2871
+ * Generates markdown report with all strategy results for a walker.
1527
2872
  * Delegates to ReportStorage.getReport().
1528
2873
  *
1529
- * @param strategyName - Strategy name to generate report for
1530
- * @returns Markdown formatted report string with table of all events
2874
+ * @param walkerName - Walker name to generate report for
2875
+ * @param symbol - Trading symbol
2876
+ * @param metric - Metric being optimized
2877
+ * @param context - Context with exchangeName and frameName
2878
+ * @returns Markdown formatted report string
1531
2879
  *
1532
2880
  * @example
1533
2881
  * ```typescript
1534
- * const service = new LiveMarkdownService();
1535
- * const markdown = await service.getReport("my-strategy");
2882
+ * const service = new WalkerMarkdownService();
2883
+ * const markdown = await service.getReport("my-walker", "BTCUSDT", "sharpeRatio", { exchangeName: "binance", frameName: "1d" });
1536
2884
  * console.log(markdown);
1537
2885
  * ```
1538
2886
  */
1539
- getReport: (strategyName: StrategyName) => Promise<string>;
2887
+ getReport: (walkerName: WalkerName, symbol: string, metric: WalkerMetric, context: {
2888
+ exchangeName: string;
2889
+ frameName: string;
2890
+ }) => Promise<string>;
1540
2891
  /**
1541
- * Saves strategy report to disk.
2892
+ * Saves walker report to disk.
1542
2893
  * Creates directory if it doesn't exist.
1543
2894
  * Delegates to ReportStorage.dump().
1544
2895
  *
1545
- * @param strategyName - Strategy name to save report for
1546
- * @param path - Directory path to save report (default: "./logs/live")
2896
+ * @param walkerName - Walker name to save report for
2897
+ * @param symbol - Trading symbol
2898
+ * @param metric - Metric being optimized
2899
+ * @param context - Context with exchangeName and frameName
2900
+ * @param path - Directory path to save report (default: "./logs/walker")
1547
2901
  *
1548
2902
  * @example
1549
2903
  * ```typescript
1550
- * const service = new LiveMarkdownService();
2904
+ * const service = new WalkerMarkdownService();
1551
2905
  *
1552
- * // Save to default path: ./logs/live/my-strategy.md
1553
- * await service.dump("my-strategy");
2906
+ * // Save to default path: ./logs/walker/my-walker.md
2907
+ * await service.dump("my-walker", "BTCUSDT", "sharpeRatio", { exchangeName: "binance", frameName: "1d" });
1554
2908
  *
1555
- * // Save to custom path: ./custom/path/my-strategy.md
1556
- * await service.dump("my-strategy", "./custom/path");
2909
+ * // Save to custom path: ./custom/path/my-walker.md
2910
+ * await service.dump("my-walker", "BTCUSDT", "sharpeRatio", { exchangeName: "binance", frameName: "1d" }, "./custom/path");
1557
2911
  * ```
1558
2912
  */
1559
- dump: (strategyName: StrategyName, path?: string) => Promise<void>;
2913
+ dump: (walkerName: WalkerName, symbol: string, metric: WalkerMetric, context: {
2914
+ exchangeName: string;
2915
+ frameName: string;
2916
+ }, path?: string) => Promise<void>;
1560
2917
  /**
1561
- * Clears accumulated event data from storage.
1562
- * If strategyName is provided, clears only that strategy's data.
1563
- * If strategyName is omitted, clears all strategies' data.
2918
+ * Clears accumulated result data from storage.
2919
+ * If walkerName is provided, clears only that walker's data.
2920
+ * If walkerName is omitted, clears all walkers' data.
1564
2921
  *
1565
- * @param strategyName - Optional strategy name to clear specific strategy data
2922
+ * @param walkerName - Optional walker name to clear specific walker data
1566
2923
  *
1567
2924
  * @example
1568
2925
  * ```typescript
1569
- * const service = new LiveMarkdownService();
2926
+ * const service = new WalkerMarkdownService();
1570
2927
  *
1571
- * // Clear specific strategy data
1572
- * await service.clear("my-strategy");
2928
+ * // Clear specific walker data
2929
+ * await service.clear("my-walker");
1573
2930
  *
1574
- * // Clear all strategies' data
2931
+ * // Clear all walkers' data
1575
2932
  * await service.clear();
1576
2933
  * ```
1577
2934
  */
1578
- clear: (strategyName?: StrategyName) => Promise<void>;
2935
+ clear: (walkerName?: WalkerName) => Promise<void>;
1579
2936
  /**
1580
- * Initializes the service by subscribing to live signal events.
2937
+ * Initializes the service by subscribing to walker events.
1581
2938
  * Uses singleshot to ensure initialization happens only once.
1582
2939
  * Automatically called on first use.
1583
2940
  *
1584
2941
  * @example
1585
2942
  * ```typescript
1586
- * const service = new LiveMarkdownService();
1587
- * await service.init(); // Subscribe to live events
2943
+ * const service = new WalkerMarkdownService();
2944
+ * await service.init(); // Subscribe to walker events
1588
2945
  * ```
1589
2946
  */
1590
2947
  protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
@@ -1818,6 +3175,79 @@ declare class PersistSignalUtils {
1818
3175
  * ```
1819
3176
  */
1820
3177
  declare const PersistSignalAdaper: PersistSignalUtils;
3178
+ /**
3179
+ * Type for persisted risk positions data.
3180
+ * Stores Map entries as array of [key, value] tuples for JSON serialization.
3181
+ */
3182
+ type RiskData = Array<[string, IRiskActivePosition]>;
3183
+ /**
3184
+ * Utility class for managing risk active positions persistence.
3185
+ *
3186
+ * Features:
3187
+ * - Memoized storage instances per risk profile
3188
+ * - Custom adapter support
3189
+ * - Atomic read/write operations for RiskData
3190
+ * - Crash-safe position state management
3191
+ *
3192
+ * Used by ClientRisk for live mode persistence of active positions.
3193
+ */
3194
+ declare class PersistRiskUtils {
3195
+ private PersistRiskFactory;
3196
+ private getRiskStorage;
3197
+ /**
3198
+ * Registers a custom persistence adapter.
3199
+ *
3200
+ * @param Ctor - Custom PersistBase constructor
3201
+ *
3202
+ * @example
3203
+ * ```typescript
3204
+ * class RedisPersist extends PersistBase {
3205
+ * async readValue(id) { return JSON.parse(await redis.get(id)); }
3206
+ * async writeValue(id, entity) { await redis.set(id, JSON.stringify(entity)); }
3207
+ * }
3208
+ * PersistRiskAdapter.usePersistRiskAdapter(RedisPersist);
3209
+ * ```
3210
+ */
3211
+ usePersistRiskAdapter(Ctor: TPersistBaseCtor<RiskName, RiskData>): void;
3212
+ /**
3213
+ * Reads persisted active positions for a risk profile.
3214
+ *
3215
+ * Called by ClientRisk.waitForInit() to restore state.
3216
+ * Returns empty Map if no positions exist.
3217
+ *
3218
+ * @param riskName - Risk profile identifier
3219
+ * @returns Promise resolving to Map of active positions
3220
+ */
3221
+ readPositionData: (riskName: RiskName) => Promise<RiskData>;
3222
+ /**
3223
+ * Writes active positions to disk with atomic file writes.
3224
+ *
3225
+ * Called by ClientRisk after addSignal/removeSignal to persist state.
3226
+ * Uses atomic writes to prevent corruption on crashes.
3227
+ *
3228
+ * @param positions - Map of active positions
3229
+ * @param riskName - Risk profile identifier
3230
+ * @returns Promise that resolves when write is complete
3231
+ */
3232
+ writePositionData: (riskRow: RiskData, riskName: RiskName) => Promise<void>;
3233
+ }
3234
+ /**
3235
+ * Global singleton instance of PersistRiskUtils.
3236
+ * Used by ClientRisk for active positions persistence.
3237
+ *
3238
+ * @example
3239
+ * ```typescript
3240
+ * // Custom adapter
3241
+ * PersistRiskAdapter.usePersistRiskAdapter(RedisPersist);
3242
+ *
3243
+ * // Read positions
3244
+ * const positions = await PersistRiskAdapter.readPositionData("my-risk");
3245
+ *
3246
+ * // Write positions
3247
+ * await PersistRiskAdapter.writePositionData(positionsMap, "my-risk");
3248
+ * ```
3249
+ */
3250
+ declare const PersistRiskAdapter: PersistRiskUtils;
1821
3251
 
1822
3252
  /**
1823
3253
  * Utility class for backtest operations.
@@ -1992,82 +3422,600 @@ declare class LiveUtils {
1992
3422
  * Useful for running live trading for side effects only (callbacks, persistence).
1993
3423
  *
1994
3424
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
1995
- * @param context - Execution context with strategy and exchange names
3425
+ * @param context - Execution context with strategy and exchange names
3426
+ * @returns Cancellation closure
3427
+ *
3428
+ * @example
3429
+ * ```typescript
3430
+ * // Run live trading silently in background, only callbacks will fire
3431
+ * // This will run forever until Ctrl+C
3432
+ * await Live.background("BTCUSDT", {
3433
+ * strategyName: "my-strategy",
3434
+ * exchangeName: "my-exchange"
3435
+ * });
3436
+ * ```
3437
+ */
3438
+ background: (symbol: string, context: {
3439
+ strategyName: string;
3440
+ exchangeName: string;
3441
+ }) => () => void;
3442
+ /**
3443
+ * Gets statistical data from all live trading events for a strategy.
3444
+ *
3445
+ * @param strategyName - Strategy name to get data for
3446
+ * @returns Promise resolving to statistical data object
3447
+ *
3448
+ * @example
3449
+ * ```typescript
3450
+ * const stats = await Live.getData("my-strategy");
3451
+ * console.log(stats.sharpeRatio, stats.winRate);
3452
+ * ```
3453
+ */
3454
+ getData: (strategyName: StrategyName) => Promise<LiveStatistics>;
3455
+ /**
3456
+ * Generates markdown report with all events for a strategy.
3457
+ *
3458
+ * @param strategyName - Strategy name to generate report for
3459
+ * @returns Promise resolving to markdown formatted report string
3460
+ *
3461
+ * @example
3462
+ * ```typescript
3463
+ * const markdown = await Live.getReport("my-strategy");
3464
+ * console.log(markdown);
3465
+ * ```
3466
+ */
3467
+ getReport: (strategyName: StrategyName) => Promise<string>;
3468
+ /**
3469
+ * Saves strategy report to disk.
3470
+ *
3471
+ * @param strategyName - Strategy name to save report for
3472
+ * @param path - Optional directory path to save report (default: "./logs/live")
3473
+ *
3474
+ * @example
3475
+ * ```typescript
3476
+ * // Save to default path: ./logs/live/my-strategy.md
3477
+ * await Live.dump("my-strategy");
3478
+ *
3479
+ * // Save to custom path: ./custom/path/my-strategy.md
3480
+ * await Live.dump("my-strategy", "./custom/path");
3481
+ * ```
3482
+ */
3483
+ dump: (strategyName: StrategyName, path?: string) => Promise<void>;
3484
+ }
3485
+ /**
3486
+ * Singleton instance of LiveUtils for convenient live trading operations.
3487
+ *
3488
+ * @example
3489
+ * ```typescript
3490
+ * import { Live } from "./classes/Live";
3491
+ *
3492
+ * for await (const result of Live.run("BTCUSDT", {
3493
+ * strategyName: "my-strategy",
3494
+ * exchangeName: "my-exchange",
3495
+ * })) {
3496
+ * console.log("Result:", result.action);
3497
+ * }
3498
+ * ```
3499
+ */
3500
+ declare const Live: LiveUtils;
3501
+
3502
+ /**
3503
+ * Performance class provides static methods for performance metrics analysis.
3504
+ *
3505
+ * Features:
3506
+ * - Get aggregated performance statistics by strategy
3507
+ * - Generate markdown reports with bottleneck analysis
3508
+ * - Save reports to disk
3509
+ * - Clear accumulated metrics
3510
+ *
3511
+ * @example
3512
+ * ```typescript
3513
+ * import { Performance, listenPerformance } from "backtest-kit";
3514
+ *
3515
+ * // Subscribe to performance events
3516
+ * listenPerformance((event) => {
3517
+ * console.log(`${event.metricType}: ${event.duration.toFixed(2)}ms`);
3518
+ * });
3519
+ *
3520
+ * // Run backtest...
3521
+ *
3522
+ * // Get aggregated statistics
3523
+ * const stats = await Performance.getData("my-strategy");
3524
+ * console.log("Total time:", stats.totalDuration);
3525
+ * console.log("Slowest operations:", Object.values(stats.metricStats)
3526
+ * .sort((a, b) => b.avgDuration - a.avgDuration)
3527
+ * .slice(0, 5));
3528
+ *
3529
+ * // Generate and save report
3530
+ * await Performance.dump("my-strategy");
3531
+ * ```
3532
+ */
3533
+ declare class Performance {
3534
+ /**
3535
+ * Gets aggregated performance statistics for a strategy.
3536
+ *
3537
+ * Returns detailed metrics grouped by operation type:
3538
+ * - Count, total duration, average, min, max
3539
+ * - Standard deviation for volatility
3540
+ * - Percentiles (median, P95, P99) for outlier detection
3541
+ *
3542
+ * @param strategyName - Strategy name to analyze
3543
+ * @returns Performance statistics with aggregated metrics
3544
+ *
3545
+ * @example
3546
+ * ```typescript
3547
+ * const stats = await Performance.getData("my-strategy");
3548
+ *
3549
+ * // Find slowest operation type
3550
+ * const slowest = Object.values(stats.metricStats)
3551
+ * .sort((a, b) => b.avgDuration - a.avgDuration)[0];
3552
+ * console.log(`Slowest: ${slowest.metricType} (${slowest.avgDuration.toFixed(2)}ms avg)`);
3553
+ *
3554
+ * // Check for outliers
3555
+ * for (const metric of Object.values(stats.metricStats)) {
3556
+ * if (metric.p99 > metric.avgDuration * 5) {
3557
+ * console.warn(`High variance in ${metric.metricType}: P99=${metric.p99}ms, Avg=${metric.avgDuration}ms`);
3558
+ * }
3559
+ * }
3560
+ * ```
3561
+ */
3562
+ static getData(strategyName: string): Promise<PerformanceStatistics>;
3563
+ /**
3564
+ * Generates markdown report with performance analysis.
3565
+ *
3566
+ * Report includes:
3567
+ * - Time distribution across operation types
3568
+ * - Detailed metrics table with statistics
3569
+ * - Percentile analysis for bottleneck detection
3570
+ *
3571
+ * @param strategyName - Strategy name to generate report for
3572
+ * @returns Markdown formatted report string
3573
+ *
3574
+ * @example
3575
+ * ```typescript
3576
+ * const markdown = await Performance.getReport("my-strategy");
3577
+ * console.log(markdown);
3578
+ *
3579
+ * // Or save to file
3580
+ * import fs from "fs/promises";
3581
+ * await fs.writeFile("performance-report.md", markdown);
3582
+ * ```
3583
+ */
3584
+ static getReport(strategyName: string): Promise<string>;
3585
+ /**
3586
+ * Saves performance report to disk.
3587
+ *
3588
+ * Creates directory if it doesn't exist.
3589
+ * Default path: ./logs/performance/{strategyName}.md
3590
+ *
3591
+ * @param strategyName - Strategy name to save report for
3592
+ * @param path - Optional custom directory path
3593
+ *
3594
+ * @example
3595
+ * ```typescript
3596
+ * // Save to default path: ./logs/performance/my-strategy.md
3597
+ * await Performance.dump("my-strategy");
3598
+ *
3599
+ * // Save to custom path: ./reports/perf/my-strategy.md
3600
+ * await Performance.dump("my-strategy", "./reports/perf");
3601
+ * ```
3602
+ */
3603
+ static dump(strategyName: string, path?: string): Promise<void>;
3604
+ /**
3605
+ * Clears accumulated performance metrics from memory.
3606
+ *
3607
+ * @param strategyName - Optional strategy name to clear specific strategy's metrics
3608
+ *
3609
+ * @example
3610
+ * ```typescript
3611
+ * // Clear specific strategy metrics
3612
+ * await Performance.clear("my-strategy");
3613
+ *
3614
+ * // Clear all metrics for all strategies
3615
+ * await Performance.clear();
3616
+ * ```
3617
+ */
3618
+ static clear(strategyName?: string): Promise<void>;
3619
+ }
3620
+
3621
+ /**
3622
+ * Utility class for walker operations.
3623
+ *
3624
+ * Provides simplified access to walkerGlobalService.run() with logging.
3625
+ * Automatically pulls exchangeName and frameName from walker schema.
3626
+ * Exported as singleton instance for convenient usage.
3627
+ *
3628
+ * @example
3629
+ * ```typescript
3630
+ * import { Walker } from "./classes/Walker";
3631
+ *
3632
+ * for await (const result of Walker.run("BTCUSDT", {
3633
+ * walkerName: "my-walker"
3634
+ * })) {
3635
+ * console.log("Progress:", result.strategiesTested, "/", result.totalStrategies);
3636
+ * console.log("Best strategy:", result.bestStrategy, result.bestMetric);
3637
+ * }
3638
+ * ```
3639
+ */
3640
+ declare class WalkerUtils {
3641
+ /**
3642
+ * Runs walker comparison for a symbol with context propagation.
3643
+ *
3644
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3645
+ * @param context - Execution context with walker name
3646
+ * @returns Async generator yielding progress updates after each strategy
3647
+ */
3648
+ run: (symbol: string, context: {
3649
+ walkerName: string;
3650
+ }) => AsyncGenerator<WalkerContract, any, any>;
3651
+ /**
3652
+ * Runs walker comparison in background without yielding results.
3653
+ *
3654
+ * Consumes all walker progress updates internally without exposing them.
3655
+ * Useful for running walker comparison for side effects only (callbacks, logging).
3656
+ *
3657
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3658
+ * @param context - Execution context with walker name
1996
3659
  * @returns Cancellation closure
1997
3660
  *
1998
3661
  * @example
1999
3662
  * ```typescript
2000
- * // Run live trading silently in background, only callbacks will fire
2001
- * // This will run forever until Ctrl+C
2002
- * await Live.background("BTCUSDT", {
2003
- * strategyName: "my-strategy",
2004
- * exchangeName: "my-exchange"
3663
+ * // Run walker silently, only callbacks will fire
3664
+ * await Walker.background("BTCUSDT", {
3665
+ * walkerName: "my-walker"
2005
3666
  * });
3667
+ * console.log("Walker comparison completed");
2006
3668
  * ```
2007
3669
  */
2008
3670
  background: (symbol: string, context: {
2009
- strategyName: string;
2010
- exchangeName: string;
3671
+ walkerName: string;
2011
3672
  }) => () => void;
2012
3673
  /**
2013
- * Gets statistical data from all live trading events for a strategy.
3674
+ * Gets walker results data from all strategy comparisons.
2014
3675
  *
2015
- * @param strategyName - Strategy name to get data for
2016
- * @returns Promise resolving to statistical data object
3676
+ * @param symbol - Trading symbol
3677
+ * @param walkerName - Walker name to get data for
3678
+ * @returns Promise resolving to walker results data object
2017
3679
  *
2018
3680
  * @example
2019
3681
  * ```typescript
2020
- * const stats = await Live.getData("my-strategy");
2021
- * console.log(stats.sharpeRatio, stats.winRate);
3682
+ * const results = await Walker.getData("BTCUSDT", "my-walker");
3683
+ * console.log(results.bestStrategy, results.bestMetric);
2022
3684
  * ```
2023
3685
  */
2024
- getData: (strategyName: StrategyName) => Promise<LiveStatistics>;
3686
+ getData: (symbol: string, walkerName: WalkerName) => Promise<IWalkerResults>;
2025
3687
  /**
2026
- * Generates markdown report with all events for a strategy.
3688
+ * Generates markdown report with all strategy comparisons for a walker.
2027
3689
  *
2028
- * @param strategyName - Strategy name to generate report for
3690
+ * @param symbol - Trading symbol
3691
+ * @param walkerName - Walker name to generate report for
2029
3692
  * @returns Promise resolving to markdown formatted report string
2030
3693
  *
2031
3694
  * @example
2032
3695
  * ```typescript
2033
- * const markdown = await Live.getReport("my-strategy");
3696
+ * const markdown = await Walker.getReport("BTCUSDT", "my-walker");
2034
3697
  * console.log(markdown);
2035
3698
  * ```
2036
3699
  */
2037
- getReport: (strategyName: StrategyName) => Promise<string>;
3700
+ getReport: (symbol: string, walkerName: WalkerName) => Promise<string>;
2038
3701
  /**
2039
- * Saves strategy report to disk.
3702
+ * Saves walker report to disk.
2040
3703
  *
2041
- * @param strategyName - Strategy name to save report for
2042
- * @param path - Optional directory path to save report (default: "./logs/live")
3704
+ * @param symbol - Trading symbol
3705
+ * @param walkerName - Walker name to save report for
3706
+ * @param path - Optional directory path to save report (default: "./logs/walker")
2043
3707
  *
2044
3708
  * @example
2045
3709
  * ```typescript
2046
- * // Save to default path: ./logs/live/my-strategy.md
2047
- * await Live.dump("my-strategy");
3710
+ * // Save to default path: ./logs/walker/my-walker.md
3711
+ * await Walker.dump("BTCUSDT", "my-walker");
2048
3712
  *
2049
- * // Save to custom path: ./custom/path/my-strategy.md
2050
- * await Live.dump("my-strategy", "./custom/path");
3713
+ * // Save to custom path: ./custom/path/my-walker.md
3714
+ * await Walker.dump("BTCUSDT", "my-walker", "./custom/path");
2051
3715
  * ```
2052
3716
  */
2053
- dump: (strategyName: StrategyName, path?: string) => Promise<void>;
3717
+ dump: (symbol: string, walkerName: WalkerName, path?: string) => Promise<void>;
2054
3718
  }
2055
3719
  /**
2056
- * Singleton instance of LiveUtils for convenient live trading operations.
3720
+ * Singleton instance of WalkerUtils for convenient walker operations.
2057
3721
  *
2058
3722
  * @example
2059
3723
  * ```typescript
2060
- * import { Live } from "./classes/Live";
3724
+ * import { Walker } from "./classes/Walker";
2061
3725
  *
2062
- * for await (const result of Live.run("BTCUSDT", {
2063
- * strategyName: "my-strategy",
2064
- * exchangeName: "my-exchange",
3726
+ * for await (const result of Walker.run("BTCUSDT", {
3727
+ * walkerName: "my-walker"
2065
3728
  * })) {
2066
- * console.log("Result:", result.action);
3729
+ * console.log("Progress:", result.strategiesTested, "/", result.totalStrategies);
3730
+ * console.log("Best so far:", result.bestStrategy, result.bestMetric);
2067
3731
  * }
2068
3732
  * ```
2069
3733
  */
2070
- declare const Live: LiveUtils;
3734
+ declare const Walker: WalkerUtils;
3735
+
3736
+ /**
3737
+ * Utility class for portfolio heatmap operations.
3738
+ *
3739
+ * Provides simplified access to heatMarkdownService with logging.
3740
+ * Automatically aggregates statistics across all symbols per strategy.
3741
+ * Exported as singleton instance for convenient usage.
3742
+ *
3743
+ * @example
3744
+ * ```typescript
3745
+ * import { Heat } from "backtest-kit";
3746
+ *
3747
+ * // Get raw heatmap data for a strategy
3748
+ * const stats = await Heat.getData("my-strategy");
3749
+ * console.log(`Portfolio PNL: ${stats.portfolioTotalPnl}%`);
3750
+ *
3751
+ * // Generate markdown report
3752
+ * const markdown = await Heat.getReport("my-strategy");
3753
+ * console.log(markdown);
3754
+ *
3755
+ * // Save to disk
3756
+ * await Heat.dump("my-strategy", "./reports");
3757
+ * ```
3758
+ */
3759
+ declare class HeatUtils {
3760
+ /**
3761
+ * Gets aggregated portfolio heatmap statistics for a strategy.
3762
+ *
3763
+ * Returns per-symbol breakdown and portfolio-wide metrics.
3764
+ * Data is automatically collected from all closed signals for the strategy.
3765
+ *
3766
+ * @param strategyName - Strategy name to get heatmap data for
3767
+ * @returns Promise resolving to heatmap statistics object
3768
+ *
3769
+ * @example
3770
+ * ```typescript
3771
+ * const stats = await Heat.getData("my-strategy");
3772
+ *
3773
+ * console.log(`Total symbols: ${stats.totalSymbols}`);
3774
+ * console.log(`Portfolio Total PNL: ${stats.portfolioTotalPnl}%`);
3775
+ * console.log(`Portfolio Sharpe Ratio: ${stats.portfolioSharpeRatio}`);
3776
+ *
3777
+ * // Iterate through per-symbol statistics
3778
+ * stats.symbols.forEach(row => {
3779
+ * console.log(`${row.symbol}: ${row.totalPnl}% (${row.totalTrades} trades)`);
3780
+ * });
3781
+ * ```
3782
+ */
3783
+ getData: (strategyName: StrategyName) => Promise<IHeatmapStatistics>;
3784
+ /**
3785
+ * Generates markdown report with portfolio heatmap table for a strategy.
3786
+ *
3787
+ * Table includes: Symbol, Total PNL, Sharpe Ratio, Max Drawdown, Trades.
3788
+ * Symbols are sorted by Total PNL descending.
3789
+ *
3790
+ * @param strategyName - Strategy name to generate heatmap report for
3791
+ * @returns Promise resolving to markdown formatted report string
3792
+ *
3793
+ * @example
3794
+ * ```typescript
3795
+ * const markdown = await Heat.getReport("my-strategy");
3796
+ * console.log(markdown);
3797
+ * // Output:
3798
+ * // # Portfolio Heatmap: my-strategy
3799
+ * //
3800
+ * // **Total Symbols:** 5 | **Portfolio PNL:** +45.3% | **Portfolio Sharpe:** 1.85 | **Total Trades:** 120
3801
+ * //
3802
+ * // | Symbol | Total PNL | Sharpe | Max DD | Trades |
3803
+ * // |--------|-----------|--------|--------|--------|
3804
+ * // | BTCUSDT | +15.5% | 2.10 | -2.5% | 45 |
3805
+ * // | ETHUSDT | +12.3% | 1.85 | -3.1% | 38 |
3806
+ * // ...
3807
+ * ```
3808
+ */
3809
+ getReport: (strategyName: StrategyName) => Promise<string>;
3810
+ /**
3811
+ * Saves heatmap report to disk for a strategy.
3812
+ *
3813
+ * Creates directory if it doesn't exist.
3814
+ * Default filename: {strategyName}.md
3815
+ *
3816
+ * @param strategyName - Strategy name to save heatmap report for
3817
+ * @param path - Optional directory path to save report (default: "./logs/heatmap")
3818
+ *
3819
+ * @example
3820
+ * ```typescript
3821
+ * // Save to default path: ./logs/heatmap/my-strategy.md
3822
+ * await Heat.dump("my-strategy");
3823
+ *
3824
+ * // Save to custom path: ./reports/my-strategy.md
3825
+ * await Heat.dump("my-strategy", "./reports");
3826
+ * ```
3827
+ */
3828
+ dump: (strategyName: StrategyName, path?: string) => Promise<void>;
3829
+ }
3830
+ /**
3831
+ * Singleton instance of HeatUtils for convenient heatmap operations.
3832
+ *
3833
+ * @example
3834
+ * ```typescript
3835
+ * import { Heat } from "backtest-kit";
3836
+ *
3837
+ * // Strategy-specific heatmap
3838
+ * const stats = await Heat.getData("my-strategy");
3839
+ * console.log(`Portfolio PNL: ${stats.portfolioTotalPnl}%`);
3840
+ * console.log(`Total Symbols: ${stats.totalSymbols}`);
3841
+ *
3842
+ * // Per-symbol breakdown
3843
+ * stats.symbols.forEach(row => {
3844
+ * console.log(`${row.symbol}:`);
3845
+ * console.log(` Total PNL: ${row.totalPnl}%`);
3846
+ * console.log(` Sharpe Ratio: ${row.sharpeRatio}`);
3847
+ * console.log(` Max Drawdown: ${row.maxDrawdown}%`);
3848
+ * console.log(` Trades: ${row.totalTrades}`);
3849
+ * });
3850
+ *
3851
+ * // Generate and save report
3852
+ * await Heat.dump("my-strategy", "./reports");
3853
+ * ```
3854
+ */
3855
+ declare const Heat: HeatUtils;
3856
+
3857
+ /**
3858
+ * Utility class for position sizing calculations.
3859
+ *
3860
+ * Provides static methods for each sizing method with validation.
3861
+ * Each method validates that the sizing schema matches the requested method.
3862
+ *
3863
+ * @example
3864
+ * ```typescript
3865
+ * import { PositionSize } from "./classes/PositionSize";
3866
+ *
3867
+ * // Fixed percentage sizing
3868
+ * const quantity = await PositionSize.fixedPercentage(
3869
+ * "BTCUSDT",
3870
+ * 10000,
3871
+ * 50000,
3872
+ * 49000,
3873
+ * { sizingName: "conservative" }
3874
+ * );
3875
+ *
3876
+ * // Kelly Criterion sizing
3877
+ * const quantity = await PositionSize.kellyCriterion(
3878
+ * "BTCUSDT",
3879
+ * 10000,
3880
+ * 50000,
3881
+ * 0.55,
3882
+ * 1.5,
3883
+ * { sizingName: "kelly" }
3884
+ * );
3885
+ *
3886
+ * // ATR-based sizing
3887
+ * const quantity = await PositionSize.atrBased(
3888
+ * "BTCUSDT",
3889
+ * 10000,
3890
+ * 50000,
3891
+ * 500,
3892
+ * { sizingName: "atr-dynamic" }
3893
+ * );
3894
+ * ```
3895
+ */
3896
+ declare class PositionSizeUtils {
3897
+ /**
3898
+ * Calculates position size using fixed percentage risk method.
3899
+ *
3900
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3901
+ * @param accountBalance - Current account balance
3902
+ * @param priceOpen - Planned entry price
3903
+ * @param priceStopLoss - Stop-loss price
3904
+ * @param context - Execution context with sizing name
3905
+ * @returns Promise resolving to calculated position size
3906
+ * @throws Error if sizing schema method is not "fixed-percentage"
3907
+ */
3908
+ static fixedPercentage: (symbol: string, accountBalance: number, priceOpen: number, priceStopLoss: number, context: {
3909
+ sizingName: SizingName;
3910
+ }) => Promise<number>;
3911
+ /**
3912
+ * Calculates position size using Kelly Criterion method.
3913
+ *
3914
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3915
+ * @param accountBalance - Current account balance
3916
+ * @param priceOpen - Planned entry price
3917
+ * @param winRate - Win rate (0-1)
3918
+ * @param winLossRatio - Average win/loss ratio
3919
+ * @param context - Execution context with sizing name
3920
+ * @returns Promise resolving to calculated position size
3921
+ * @throws Error if sizing schema method is not "kelly-criterion"
3922
+ */
3923
+ static kellyCriterion: (symbol: string, accountBalance: number, priceOpen: number, winRate: number, winLossRatio: number, context: {
3924
+ sizingName: SizingName;
3925
+ }) => Promise<number>;
3926
+ /**
3927
+ * Calculates position size using ATR-based method.
3928
+ *
3929
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3930
+ * @param accountBalance - Current account balance
3931
+ * @param priceOpen - Planned entry price
3932
+ * @param atr - Current ATR value
3933
+ * @param context - Execution context with sizing name
3934
+ * @returns Promise resolving to calculated position size
3935
+ * @throws Error if sizing schema method is not "atr-based"
3936
+ */
3937
+ static atrBased: (symbol: string, accountBalance: number, priceOpen: number, atr: number, context: {
3938
+ sizingName: SizingName;
3939
+ }) => Promise<number>;
3940
+ }
3941
+ declare const PositionSize: typeof PositionSizeUtils;
3942
+
3943
+ /**
3944
+ * Global signal emitter for all trading events (live + backtest).
3945
+ * Emits all signal events regardless of execution mode.
3946
+ */
3947
+ declare const signalEmitter: Subject<IStrategyTickResult>;
3948
+ /**
3949
+ * Live trading signal emitter.
3950
+ * Emits only signals from live trading execution.
3951
+ */
3952
+ declare const signalLiveEmitter: Subject<IStrategyTickResult>;
3953
+ /**
3954
+ * Backtest signal emitter.
3955
+ * Emits only signals from backtest execution.
3956
+ */
3957
+ declare const signalBacktestEmitter: Subject<IStrategyTickResult>;
3958
+ /**
3959
+ * Error emitter for background execution errors.
3960
+ * Emits errors caught in background tasks (Live.background, Backtest.background).
3961
+ */
3962
+ declare const errorEmitter: Subject<Error>;
3963
+ /**
3964
+ * Done emitter for live background execution completion.
3965
+ * Emits when live background tasks complete (Live.background).
3966
+ */
3967
+ declare const doneLiveSubject: Subject<DoneContract>;
3968
+ /**
3969
+ * Done emitter for backtest background execution completion.
3970
+ * Emits when backtest background tasks complete (Backtest.background).
3971
+ */
3972
+ declare const doneBacktestSubject: Subject<DoneContract>;
3973
+ /**
3974
+ * Done emitter for walker background execution completion.
3975
+ * Emits when walker background tasks complete (Walker.background).
3976
+ */
3977
+ declare const doneWalkerSubject: Subject<DoneContract>;
3978
+ /**
3979
+ * Progress emitter for backtest execution progress.
3980
+ * Emits progress updates during backtest execution.
3981
+ */
3982
+ declare const progressEmitter: Subject<ProgressContract>;
3983
+ /**
3984
+ * Performance emitter for execution metrics.
3985
+ * Emits performance metrics for profiling and bottleneck detection.
3986
+ */
3987
+ declare const performanceEmitter: Subject<PerformanceContract>;
3988
+ /**
3989
+ * Walker emitter for strategy comparison progress.
3990
+ * Emits progress updates during walker execution (each strategy completion).
3991
+ */
3992
+ declare const walkerEmitter: Subject<WalkerContract>;
3993
+ /**
3994
+ * Walker complete emitter for strategy comparison completion.
3995
+ * Emits when all strategies have been tested and final results are available.
3996
+ */
3997
+ declare const walkerCompleteSubject: Subject<IWalkerResults>;
3998
+ /**
3999
+ * Validation emitter for risk validation errors.
4000
+ * Emits when risk validation functions throw errors during signal checking.
4001
+ */
4002
+ declare const validationSubject: Subject<Error>;
4003
+
4004
+ declare const emitters_doneBacktestSubject: typeof doneBacktestSubject;
4005
+ declare const emitters_doneLiveSubject: typeof doneLiveSubject;
4006
+ declare const emitters_doneWalkerSubject: typeof doneWalkerSubject;
4007
+ declare const emitters_errorEmitter: typeof errorEmitter;
4008
+ declare const emitters_performanceEmitter: typeof performanceEmitter;
4009
+ declare const emitters_progressEmitter: typeof progressEmitter;
4010
+ declare const emitters_signalBacktestEmitter: typeof signalBacktestEmitter;
4011
+ declare const emitters_signalEmitter: typeof signalEmitter;
4012
+ declare const emitters_signalLiveEmitter: typeof signalLiveEmitter;
4013
+ declare const emitters_validationSubject: typeof validationSubject;
4014
+ declare const emitters_walkerCompleteSubject: typeof walkerCompleteSubject;
4015
+ declare const emitters_walkerEmitter: typeof walkerEmitter;
4016
+ declare namespace emitters {
4017
+ export { emitters_doneBacktestSubject as doneBacktestSubject, emitters_doneLiveSubject as doneLiveSubject, emitters_doneWalkerSubject as doneWalkerSubject, emitters_errorEmitter as errorEmitter, emitters_performanceEmitter as performanceEmitter, emitters_progressEmitter as progressEmitter, emitters_signalBacktestEmitter as signalBacktestEmitter, emitters_signalEmitter as signalEmitter, emitters_signalLiveEmitter as signalLiveEmitter, emitters_validationSubject as validationSubject, emitters_walkerCompleteSubject as walkerCompleteSubject, emitters_walkerEmitter as walkerEmitter };
4018
+ }
2071
4019
 
2072
4020
  /**
2073
4021
  * Logger service with automatic context injection.
@@ -2314,6 +4262,7 @@ declare class StrategyConnectionService implements IStrategy {
2314
4262
  private readonly loggerService;
2315
4263
  private readonly executionContextService;
2316
4264
  private readonly strategySchemaService;
4265
+ private readonly riskConnectionService;
2317
4266
  private readonly exchangeConnectionService;
2318
4267
  private readonly methodContextService;
2319
4268
  /**
@@ -2346,97 +4295,327 @@ declare class StrategyConnectionService implements IStrategy {
2346
4295
  */
2347
4296
  backtest: (candles: ICandleData[]) => Promise<IStrategyBacktestResult>;
2348
4297
  /**
2349
- * Stops the specified strategy from generating new signals.
4298
+ * Stops the specified strategy from generating new signals.
4299
+ *
4300
+ * Delegates to ClientStrategy.stop() which sets internal flag to prevent
4301
+ * getSignal from being called on subsequent ticks.
4302
+ *
4303
+ * @param strategyName - Name of strategy to stop
4304
+ * @returns Promise that resolves when stop flag is set
4305
+ */
4306
+ stop: (strategyName: StrategyName) => Promise<void>;
4307
+ /**
4308
+ * Clears the memoized ClientStrategy instance from cache.
4309
+ *
4310
+ * Forces re-initialization of strategy on next getStrategy call.
4311
+ * Useful for resetting strategy state or releasing resources.
4312
+ *
4313
+ * @param strategyName - Name of strategy to clear from cache
4314
+ */
4315
+ clear: (strategyName: StrategyName) => Promise<void>;
4316
+ }
4317
+
4318
+ /**
4319
+ * Client implementation for backtest timeframe generation.
4320
+ *
4321
+ * Features:
4322
+ * - Generates timestamp arrays for backtest iteration
4323
+ * - Singleshot caching prevents redundant generation
4324
+ * - Configurable interval spacing (1m to 3d)
4325
+ * - Callback support for validation and logging
4326
+ *
4327
+ * Used by BacktestLogicPrivateService to iterate through historical periods.
4328
+ */
4329
+ declare class ClientFrame implements IFrame {
4330
+ readonly params: IFrameParams;
4331
+ constructor(params: IFrameParams);
4332
+ /**
4333
+ * Generates timeframe array for backtest period.
4334
+ * Results are cached via singleshot pattern.
4335
+ *
4336
+ * @param symbol - Trading pair symbol (unused, for API consistency)
4337
+ * @returns Promise resolving to array of Date objects
4338
+ * @throws Error if interval is invalid
4339
+ */
4340
+ getTimeframe: ((symbol: string) => Promise<Date[]>) & functools_kit.ISingleshotClearable;
4341
+ }
4342
+
4343
+ /**
4344
+ * Connection service routing frame operations to correct ClientFrame instance.
4345
+ *
4346
+ * Routes all IFrame method calls to the appropriate frame implementation
4347
+ * based on methodContextService.context.frameName. Uses memoization to cache
4348
+ * ClientFrame instances for performance.
4349
+ *
4350
+ * Key features:
4351
+ * - Automatic frame routing via method context
4352
+ * - Memoized ClientFrame instances by frameName
4353
+ * - Implements IFrame interface
4354
+ * - Backtest timeframe management (startDate, endDate, interval)
4355
+ *
4356
+ * Note: frameName is empty string for live mode (no frame constraints).
4357
+ *
4358
+ * @example
4359
+ * ```typescript
4360
+ * // Used internally by framework
4361
+ * const timeframe = await frameConnectionService.getTimeframe("BTCUSDT");
4362
+ * // Automatically routes to correct frame based on methodContext
4363
+ * ```
4364
+ */
4365
+ declare class FrameConnectionService implements IFrame {
4366
+ private readonly loggerService;
4367
+ private readonly frameSchemaService;
4368
+ private readonly methodContextService;
4369
+ /**
4370
+ * Retrieves memoized ClientFrame instance for given frame name.
4371
+ *
4372
+ * Creates ClientFrame on first call, returns cached instance on subsequent calls.
4373
+ * Cache key is frameName string.
4374
+ *
4375
+ * @param frameName - Name of registered frame schema
4376
+ * @returns Configured ClientFrame instance
4377
+ */
4378
+ getFrame: ((frameName: FrameName) => ClientFrame) & functools_kit.IClearableMemoize<string> & functools_kit.IControlMemoize<string, ClientFrame>;
4379
+ /**
4380
+ * Retrieves backtest timeframe boundaries for symbol.
4381
+ *
4382
+ * Returns startDate and endDate from frame configuration.
4383
+ * Used to limit backtest execution to specific date range.
4384
+ *
4385
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
4386
+ * @returns Promise resolving to { startDate: Date, endDate: Date }
4387
+ */
4388
+ getTimeframe: (symbol: string) => Promise<Date[]>;
4389
+ }
4390
+
4391
+ /**
4392
+ * Client implementation for position sizing calculation.
4393
+ *
4394
+ * Features:
4395
+ * - Multiple sizing methods (fixed %, Kelly, ATR)
4396
+ * - Min/max position constraints
4397
+ * - Max position percentage limit
4398
+ * - Callback support for validation and logging
4399
+ *
4400
+ * Used by strategy execution to determine optimal position sizes.
4401
+ */
4402
+ declare class ClientSizing implements ISizing {
4403
+ readonly params: ISizingParams;
4404
+ constructor(params: ISizingParams);
4405
+ /**
4406
+ * Calculates position size based on configured method and constraints.
4407
+ *
4408
+ * @param params - Calculation parameters (symbol, balance, prices, etc.)
4409
+ * @returns Promise resolving to calculated position size
4410
+ * @throws Error if required parameters are missing or invalid
4411
+ */
4412
+ calculate(params: ISizingCalculateParams): Promise<number>;
4413
+ }
4414
+
4415
+ /**
4416
+ * Connection service routing sizing operations to correct ClientSizing instance.
4417
+ *
4418
+ * Routes sizing method calls to the appropriate sizing implementation
4419
+ * based on the provided sizingName parameter. Uses memoization to cache
4420
+ * ClientSizing instances for performance.
4421
+ *
4422
+ * Key features:
4423
+ * - Explicit sizing routing via sizingName parameter
4424
+ * - Memoized ClientSizing instances by sizingName
4425
+ * - Position size calculation with risk management
4426
+ *
4427
+ * Note: sizingName is empty string for strategies without sizing configuration.
4428
+ *
4429
+ * @example
4430
+ * ```typescript
4431
+ * // Used internally by framework
4432
+ * const quantity = await sizingConnectionService.calculate(
4433
+ * {
4434
+ * symbol: "BTCUSDT",
4435
+ * accountBalance: 10000,
4436
+ * priceOpen: 50000,
4437
+ * priceStopLoss: 49000,
4438
+ * method: "fixed-percentage"
4439
+ * },
4440
+ * { sizingName: "conservative" }
4441
+ * );
4442
+ * ```
4443
+ */
4444
+ declare class SizingConnectionService {
4445
+ private readonly loggerService;
4446
+ private readonly sizingSchemaService;
4447
+ /**
4448
+ * Retrieves memoized ClientSizing instance for given sizing name.
2350
4449
  *
2351
- * Delegates to ClientStrategy.stop() which sets internal flag to prevent
2352
- * getSignal from being called on subsequent ticks.
4450
+ * Creates ClientSizing on first call, returns cached instance on subsequent calls.
4451
+ * Cache key is sizingName string.
2353
4452
  *
2354
- * @param strategyName - Name of strategy to stop
2355
- * @returns Promise that resolves when stop flag is set
4453
+ * @param sizingName - Name of registered sizing schema
4454
+ * @returns Configured ClientSizing instance
2356
4455
  */
2357
- stop: (strategyName: StrategyName) => Promise<void>;
4456
+ getSizing: ((sizingName: SizingName) => ClientSizing) & functools_kit.IClearableMemoize<string> & functools_kit.IControlMemoize<string, ClientSizing>;
2358
4457
  /**
2359
- * Clears the memoized ClientStrategy instance from cache.
4458
+ * Calculates position size based on risk parameters and configured method.
2360
4459
  *
2361
- * Forces re-initialization of strategy on next getStrategy call.
2362
- * Useful for resetting strategy state or releasing resources.
4460
+ * Routes to appropriate ClientSizing instance based on provided context.
4461
+ * Supports multiple sizing methods: fixed-percentage, kelly-criterion, atr-based.
2363
4462
  *
2364
- * @param strategyName - Name of strategy to clear from cache
4463
+ * @param params - Calculation parameters (symbol, balance, prices, method-specific data)
4464
+ * @param context - Execution context with sizing name
4465
+ * @returns Promise resolving to calculated position size
2365
4466
  */
2366
- clear: (strategyName: StrategyName) => Promise<void>;
4467
+ calculate: (params: ISizingCalculateParams, context: {
4468
+ sizingName: SizingName;
4469
+ }) => Promise<number>;
2367
4470
  }
2368
4471
 
4472
+ /** Type for active position map */
4473
+ type RiskMap = Map<string, IRiskActivePosition>;
4474
+ /** Symbol indicating that positions need to be fetched from persistence */
4475
+ declare const POSITION_NEED_FETCH: unique symbol;
2369
4476
  /**
2370
- * Client implementation for backtest timeframe generation.
4477
+ * ClientRisk implementation for portfolio-level risk management.
2371
4478
  *
2372
- * Features:
2373
- * - Generates timestamp arrays for backtest iteration
2374
- * - Singleshot caching prevents redundant generation
2375
- * - Configurable interval spacing (1m to 3d)
2376
- * - Callback support for validation and logging
4479
+ * Provides risk checking logic to prevent signals that violate configured limits:
4480
+ * - Maximum concurrent positions (tracks across all strategies)
4481
+ * - Custom validations with access to all active positions
2377
4482
  *
2378
- * Used by BacktestLogicPrivateService to iterate through historical periods.
4483
+ * Multiple ClientStrategy instances share the same ClientRisk instance,
4484
+ * allowing cross-strategy risk analysis.
4485
+ *
4486
+ * Used internally by strategy execution to validate signals before opening positions.
2379
4487
  */
2380
- declare class ClientFrame implements IFrame {
2381
- readonly params: IFrameParams;
2382
- constructor(params: IFrameParams);
4488
+ declare class ClientRisk implements IRisk {
4489
+ readonly params: IRiskParams;
2383
4490
  /**
2384
- * Generates timeframe array for backtest period.
2385
- * Results are cached via singleshot pattern.
4491
+ * Map of active positions tracked across all strategies.
4492
+ * Key: `${strategyName}:${exchangeName}:${symbol}`
4493
+ * Starts as POSITION_NEED_FETCH symbol, gets initialized on first use.
4494
+ */
4495
+ _activePositions: RiskMap | typeof POSITION_NEED_FETCH;
4496
+ constructor(params: IRiskParams);
4497
+ /**
4498
+ * Initializes active positions by loading from persistence.
4499
+ * Uses singleshot pattern to ensure initialization happens exactly once.
4500
+ * Skips persistence in backtest mode.
4501
+ */
4502
+ private waitForInit;
4503
+ /**
4504
+ * Persists current active positions to disk.
4505
+ */
4506
+ private _updatePositions;
4507
+ /**
4508
+ * Registers a new opened signal.
4509
+ * Called by StrategyConnectionService after signal is opened.
4510
+ */
4511
+ addSignal(symbol: string, context: {
4512
+ strategyName: string;
4513
+ riskName: string;
4514
+ }): Promise<void>;
4515
+ /**
4516
+ * Removes a closed signal.
4517
+ * Called by StrategyConnectionService when signal is closed.
4518
+ */
4519
+ removeSignal(symbol: string, context: {
4520
+ strategyName: string;
4521
+ riskName: string;
4522
+ }): Promise<void>;
4523
+ /**
4524
+ * Checks if a signal should be allowed based on risk limits.
2386
4525
  *
2387
- * @param symbol - Trading pair symbol (unused, for API consistency)
2388
- * @returns Promise resolving to array of Date objects
2389
- * @throws Error if interval is invalid
4526
+ * Executes custom validations with access to:
4527
+ * - Passthrough params from ClientStrategy (symbol, strategyName, exchangeName, currentPrice, timestamp)
4528
+ * - Active positions via this.activePositions getter
4529
+ *
4530
+ * Returns false immediately if any validation throws error.
4531
+ * Triggers callbacks (onRejected, onAllowed) based on result.
4532
+ *
4533
+ * @param params - Risk check arguments (passthrough from ClientStrategy)
4534
+ * @returns Promise resolving to true if allowed, false if rejected
2390
4535
  */
2391
- getTimeframe: ((symbol: string) => Promise<Date[]>) & functools_kit.ISingleshotClearable;
4536
+ checkSignal: (params: IRiskCheckArgs) => Promise<boolean>;
2392
4537
  }
2393
4538
 
2394
4539
  /**
2395
- * Connection service routing frame operations to correct ClientFrame instance.
4540
+ * Connection service routing risk operations to correct ClientRisk instance.
2396
4541
  *
2397
- * Routes all IFrame method calls to the appropriate frame implementation
2398
- * based on methodContextService.context.frameName. Uses memoization to cache
2399
- * ClientFrame instances for performance.
4542
+ * Routes risk checking calls to the appropriate risk implementation
4543
+ * based on the provided riskName parameter. Uses memoization to cache
4544
+ * ClientRisk instances for performance.
2400
4545
  *
2401
4546
  * Key features:
2402
- * - Automatic frame routing via method context
2403
- * - Memoized ClientFrame instances by frameName
2404
- * - Implements IFrame interface
2405
- * - Backtest timeframe management (startDate, endDate, interval)
4547
+ * - Explicit risk routing via riskName parameter
4548
+ * - Memoized ClientRisk instances by riskName
4549
+ * - Risk limit validation for signals
2406
4550
  *
2407
- * Note: frameName is empty string for live mode (no frame constraints).
4551
+ * Note: riskName is empty string for strategies without risk configuration.
2408
4552
  *
2409
4553
  * @example
2410
4554
  * ```typescript
2411
4555
  * // Used internally by framework
2412
- * const timeframe = await frameConnectionService.getTimeframe("BTCUSDT");
2413
- * // Automatically routes to correct frame based on methodContext
4556
+ * const result = await riskConnectionService.checkSignal(
4557
+ * {
4558
+ * symbol: "BTCUSDT",
4559
+ * positionSize: 0.5,
4560
+ * currentPrice: 50000,
4561
+ * portfolioBalance: 100000,
4562
+ * currentDrawdown: 5,
4563
+ * currentPositions: 3,
4564
+ * dailyPnl: -2,
4565
+ * currentSymbolExposure: 8
4566
+ * },
4567
+ * { riskName: "conservative" }
4568
+ * );
2414
4569
  * ```
2415
4570
  */
2416
- declare class FrameConnectionService implements IFrame {
4571
+ declare class RiskConnectionService {
2417
4572
  private readonly loggerService;
2418
- private readonly frameSchemaService;
2419
- private readonly methodContextService;
4573
+ private readonly riskSchemaService;
2420
4574
  /**
2421
- * Retrieves memoized ClientFrame instance for given frame name.
4575
+ * Retrieves memoized ClientRisk instance for given risk name.
2422
4576
  *
2423
- * Creates ClientFrame on first call, returns cached instance on subsequent calls.
2424
- * Cache key is frameName string.
4577
+ * Creates ClientRisk on first call, returns cached instance on subsequent calls.
4578
+ * Cache key is riskName string.
2425
4579
  *
2426
- * @param frameName - Name of registered frame schema
2427
- * @returns Configured ClientFrame instance
4580
+ * @param riskName - Name of registered risk schema
4581
+ * @returns Configured ClientRisk instance
2428
4582
  */
2429
- getFrame: ((frameName: FrameName) => ClientFrame) & functools_kit.IClearableMemoize<string> & functools_kit.IControlMemoize<string, ClientFrame>;
4583
+ getRisk: ((riskName: RiskName) => ClientRisk) & functools_kit.IClearableMemoize<string> & functools_kit.IControlMemoize<string, ClientRisk>;
2430
4584
  /**
2431
- * Retrieves backtest timeframe boundaries for symbol.
4585
+ * Checks if a signal should be allowed based on risk limits.
2432
4586
  *
2433
- * Returns startDate and endDate from frame configuration.
2434
- * Used to limit backtest execution to specific date range.
4587
+ * Routes to appropriate ClientRisk instance based on provided context.
4588
+ * Validates portfolio drawdown, symbol exposure, position count, and daily loss limits.
2435
4589
  *
2436
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
2437
- * @returns Promise resolving to { startDate: Date, endDate: Date }
4590
+ * @param params - Risk check arguments (portfolio state, position details)
4591
+ * @param context - Execution context with risk name
4592
+ * @returns Promise resolving to risk check result
2438
4593
  */
2439
- getTimeframe: (symbol: string) => Promise<Date[]>;
4594
+ checkSignal: (params: IRiskCheckArgs, context: {
4595
+ riskName: RiskName;
4596
+ }) => Promise<boolean>;
4597
+ /**
4598
+ * Registers an opened signal with the risk management system.
4599
+ * Routes to appropriate ClientRisk instance.
4600
+ *
4601
+ * @param symbol - Trading pair symbol
4602
+ * @param context - Context information (strategyName, riskName)
4603
+ */
4604
+ addSignal: (symbol: string, context: {
4605
+ strategyName: string;
4606
+ riskName: RiskName;
4607
+ }) => Promise<void>;
4608
+ /**
4609
+ * Removes a closed signal from the risk management system.
4610
+ * Routes to appropriate ClientRisk instance.
4611
+ *
4612
+ * @param symbol - Trading pair symbol
4613
+ * @param context - Context information (strategyName, riskName)
4614
+ */
4615
+ removeSignal: (symbol: string, context: {
4616
+ strategyName: string;
4617
+ riskName: RiskName;
4618
+ }) => Promise<void>;
2440
4619
  }
2441
4620
 
2442
4621
  /**
@@ -2578,6 +4757,90 @@ declare class FrameGlobalService {
2578
4757
  getTimeframe: (symbol: string) => Promise<Date[]>;
2579
4758
  }
2580
4759
 
4760
+ /**
4761
+ * Global service for sizing operations.
4762
+ *
4763
+ * Wraps SizingConnectionService for position size calculation.
4764
+ * Used internally by strategy execution and public API.
4765
+ */
4766
+ declare class SizingGlobalService {
4767
+ private readonly loggerService;
4768
+ private readonly sizingConnectionService;
4769
+ /**
4770
+ * Calculates position size based on risk parameters.
4771
+ *
4772
+ * @param params - Calculation parameters (symbol, balance, prices, method-specific data)
4773
+ * @param context - Execution context with sizing name
4774
+ * @returns Promise resolving to calculated position size
4775
+ */
4776
+ calculate: (params: ISizingCalculateParams, context: {
4777
+ sizingName: SizingName;
4778
+ }) => Promise<number>;
4779
+ }
4780
+
4781
+ /**
4782
+ * Global service for risk operations.
4783
+ *
4784
+ * Wraps RiskConnectionService for risk limit validation.
4785
+ * Used internally by strategy execution and public API.
4786
+ */
4787
+ declare class RiskGlobalService {
4788
+ private readonly loggerService;
4789
+ private readonly riskConnectionService;
4790
+ /**
4791
+ * Checks if a signal should be allowed based on risk limits.
4792
+ *
4793
+ * @param params - Risk check arguments (portfolio state, position details)
4794
+ * @param context - Execution context with risk name
4795
+ * @returns Promise resolving to risk check result
4796
+ */
4797
+ checkSignal: (params: IRiskCheckArgs, context: {
4798
+ riskName: RiskName;
4799
+ }) => Promise<boolean>;
4800
+ /**
4801
+ * Registers an opened signal with the risk management system.
4802
+ *
4803
+ * @param symbol - Trading pair symbol
4804
+ * @param context - Context information (strategyName, riskName)
4805
+ */
4806
+ addSignal: (symbol: string, context: {
4807
+ strategyName: string;
4808
+ riskName: RiskName;
4809
+ }) => Promise<void>;
4810
+ /**
4811
+ * Removes a closed signal from the risk management system.
4812
+ *
4813
+ * @param symbol - Trading pair symbol
4814
+ * @param context - Context information (strategyName, riskName)
4815
+ */
4816
+ removeSignal: (symbol: string, context: {
4817
+ strategyName: string;
4818
+ riskName: RiskName;
4819
+ }) => Promise<void>;
4820
+ }
4821
+
4822
+ /**
4823
+ * Global service providing access to walker functionality.
4824
+ *
4825
+ * Simple wrapper around WalkerLogicPublicService for dependency injection.
4826
+ * Used by public API exports.
4827
+ */
4828
+ declare class WalkerGlobalService {
4829
+ private readonly loggerService;
4830
+ private readonly walkerLogicPublicService;
4831
+ /**
4832
+ * Runs walker comparison for a symbol with context propagation.
4833
+ *
4834
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
4835
+ * @param context - Walker context with strategies and metric
4836
+ */
4837
+ run: (symbol: string, context: {
4838
+ walkerName: string;
4839
+ exchangeName: string;
4840
+ frameName: string;
4841
+ }) => AsyncGenerator<WalkerContract, any, any>;
4842
+ }
4843
+
2581
4844
  /**
2582
4845
  * Service for managing exchange schema registry.
2583
4846
  *
@@ -2618,109 +4881,252 @@ declare class ExchangeSchemaService {
2618
4881
  */
2619
4882
  override: (key: ExchangeName, value: Partial<IExchangeSchema>) => IExchangeSchema;
2620
4883
  /**
2621
- * Retrieves an exchange schema by name.
4884
+ * Retrieves an exchange schema by name.
4885
+ *
4886
+ * @param key - Exchange name
4887
+ * @returns Exchange schema configuration
4888
+ * @throws Error if exchange name doesn't exist
4889
+ */
4890
+ get: (key: ExchangeName) => IExchangeSchema;
4891
+ }
4892
+
4893
+ /**
4894
+ * Service for managing strategy schema registry.
4895
+ *
4896
+ * Uses ToolRegistry from functools-kit for type-safe schema storage.
4897
+ * Strategies are registered via addStrategy() and retrieved by name.
4898
+ */
4899
+ declare class StrategySchemaService {
4900
+ readonly loggerService: LoggerService;
4901
+ private _registry;
4902
+ /**
4903
+ * Registers a new strategy schema.
4904
+ *
4905
+ * @param key - Unique strategy name
4906
+ * @param value - Strategy schema configuration
4907
+ * @throws Error if strategy name already exists
4908
+ */
4909
+ register: (key: StrategyName, value: IStrategySchema) => void;
4910
+ /**
4911
+ * Validates strategy schema structure for required properties.
4912
+ *
4913
+ * Performs shallow validation to ensure all required properties exist
4914
+ * and have correct types before registration in the registry.
4915
+ *
4916
+ * @param strategySchema - Strategy schema to validate
4917
+ * @throws Error if strategyName is missing or not a string
4918
+ * @throws Error if interval is missing or not a valid SignalInterval
4919
+ * @throws Error if getSignal is missing or not a function
4920
+ */
4921
+ private validateShallow;
4922
+ /**
4923
+ * Overrides an existing strategy schema with partial updates.
4924
+ *
4925
+ * @param key - Strategy name to override
4926
+ * @param value - Partial schema updates
4927
+ * @returns Updated strategy schema
4928
+ * @throws Error if strategy name doesn't exist
4929
+ */
4930
+ override: (key: StrategyName, value: Partial<IStrategySchema>) => IStrategySchema;
4931
+ /**
4932
+ * Retrieves a strategy schema by name.
4933
+ *
4934
+ * @param key - Strategy name
4935
+ * @returns Strategy schema configuration
4936
+ * @throws Error if strategy name doesn't exist
4937
+ */
4938
+ get: (key: StrategyName) => IStrategySchema;
4939
+ }
4940
+
4941
+ /**
4942
+ * Service for managing frame schema registry.
4943
+ *
4944
+ * Uses ToolRegistry from functools-kit for type-safe schema storage.
4945
+ * Frames are registered via addFrame() and retrieved by name.
4946
+ */
4947
+ declare class FrameSchemaService {
4948
+ readonly loggerService: LoggerService;
4949
+ private _registry;
4950
+ /**
4951
+ * Registers a new frame schema.
4952
+ *
4953
+ * @param key - Unique frame name
4954
+ * @param value - Frame schema configuration
4955
+ * @throws Error if frame name already exists
4956
+ */
4957
+ register(key: FrameName, value: IFrameSchema): void;
4958
+ /**
4959
+ * Validates frame schema structure for required properties.
4960
+ *
4961
+ * Performs shallow validation to ensure all required properties exist
4962
+ * and have correct types before registration in the registry.
4963
+ *
4964
+ * @param frameSchema - Frame schema to validate
4965
+ * @throws Error if frameName is missing or not a string
4966
+ * @throws Error if interval is missing or not a valid FrameInterval
4967
+ * @throws Error if startDate is missing or not a Date
4968
+ * @throws Error if endDate is missing or not a Date
4969
+ */
4970
+ private validateShallow;
4971
+ /**
4972
+ * Overrides an existing frame schema with partial updates.
4973
+ *
4974
+ * @param key - Frame name to override
4975
+ * @param value - Partial schema updates
4976
+ * @throws Error if frame name doesn't exist
4977
+ */
4978
+ override(key: FrameName, value: Partial<IFrameSchema>): IFrameSchema;
4979
+ /**
4980
+ * Retrieves a frame schema by name.
4981
+ *
4982
+ * @param key - Frame name
4983
+ * @returns Frame schema configuration
4984
+ * @throws Error if frame name doesn't exist
4985
+ */
4986
+ get(key: FrameName): IFrameSchema;
4987
+ }
4988
+
4989
+ /**
4990
+ * Service for managing sizing schema registry.
4991
+ *
4992
+ * Uses ToolRegistry from functools-kit for type-safe schema storage.
4993
+ * Sizing schemas are registered via addSizing() and retrieved by name.
4994
+ */
4995
+ declare class SizingSchemaService {
4996
+ readonly loggerService: LoggerService;
4997
+ private _registry;
4998
+ /**
4999
+ * Registers a new sizing schema.
5000
+ *
5001
+ * @param key - Unique sizing name
5002
+ * @param value - Sizing schema configuration
5003
+ * @throws Error if sizing name already exists
5004
+ */
5005
+ register(key: SizingName, value: ISizingSchema): void;
5006
+ /**
5007
+ * Validates sizing schema structure for required properties.
5008
+ *
5009
+ * Performs shallow validation to ensure all required properties exist
5010
+ * and have correct types before registration in the registry.
5011
+ *
5012
+ * @param sizingSchema - Sizing schema to validate
5013
+ * @throws Error if sizingName is missing or not a string
5014
+ * @throws Error if method is missing or not a valid sizing method
5015
+ * @throws Error if required method-specific fields are missing
5016
+ */
5017
+ private validateShallow;
5018
+ /**
5019
+ * Overrides an existing sizing schema with partial updates.
5020
+ *
5021
+ * @param key - Sizing name to override
5022
+ * @param value - Partial schema updates
5023
+ * @throws Error if sizing name doesn't exist
5024
+ */
5025
+ override(key: SizingName, value: Partial<ISizingSchema>): ISizingSchema;
5026
+ /**
5027
+ * Retrieves a sizing schema by name.
2622
5028
  *
2623
- * @param key - Exchange name
2624
- * @returns Exchange schema configuration
2625
- * @throws Error if exchange name doesn't exist
5029
+ * @param key - Sizing name
5030
+ * @returns Sizing schema configuration
5031
+ * @throws Error if sizing name doesn't exist
2626
5032
  */
2627
- get: (key: ExchangeName) => IExchangeSchema;
5033
+ get(key: SizingName): ISizingSchema;
2628
5034
  }
2629
5035
 
2630
5036
  /**
2631
- * Service for managing strategy schema registry.
5037
+ * Service for managing risk schema registry.
2632
5038
  *
2633
5039
  * Uses ToolRegistry from functools-kit for type-safe schema storage.
2634
- * Strategies are registered via addStrategy() and retrieved by name.
5040
+ * Risk profiles are registered via addRisk() and retrieved by name.
2635
5041
  */
2636
- declare class StrategySchemaService {
5042
+ declare class RiskSchemaService {
2637
5043
  readonly loggerService: LoggerService;
2638
5044
  private _registry;
2639
5045
  /**
2640
- * Registers a new strategy schema.
5046
+ * Registers a new risk schema.
2641
5047
  *
2642
- * @param key - Unique strategy name
2643
- * @param value - Strategy schema configuration
2644
- * @throws Error if strategy name already exists
5048
+ * @param key - Unique risk profile name
5049
+ * @param value - Risk schema configuration
5050
+ * @throws Error if risk name already exists
2645
5051
  */
2646
- register: (key: StrategyName, value: IStrategySchema) => void;
5052
+ register: (key: RiskName, value: IRiskSchema) => void;
2647
5053
  /**
2648
- * Validates strategy schema structure for required properties.
5054
+ * Validates risk schema structure for required properties.
2649
5055
  *
2650
5056
  * Performs shallow validation to ensure all required properties exist
2651
5057
  * and have correct types before registration in the registry.
2652
5058
  *
2653
- * @param strategySchema - Strategy schema to validate
2654
- * @throws Error if strategyName is missing or not a string
2655
- * @throws Error if interval is missing or not a valid SignalInterval
2656
- * @throws Error if getSignal is missing or not a function
5059
+ * @param riskSchema - Risk schema to validate
5060
+ * @throws Error if riskName is missing or not a string
2657
5061
  */
2658
5062
  private validateShallow;
2659
5063
  /**
2660
- * Overrides an existing strategy schema with partial updates.
5064
+ * Overrides an existing risk schema with partial updates.
2661
5065
  *
2662
- * @param key - Strategy name to override
5066
+ * @param key - Risk name to override
2663
5067
  * @param value - Partial schema updates
2664
- * @returns Updated strategy schema
2665
- * @throws Error if strategy name doesn't exist
5068
+ * @returns Updated risk schema
5069
+ * @throws Error if risk name doesn't exist
2666
5070
  */
2667
- override: (key: StrategyName, value: Partial<IStrategySchema>) => IStrategySchema;
5071
+ override: (key: RiskName, value: Partial<IRiskSchema>) => IRiskSchema;
2668
5072
  /**
2669
- * Retrieves a strategy schema by name.
5073
+ * Retrieves a risk schema by name.
2670
5074
  *
2671
- * @param key - Strategy name
2672
- * @returns Strategy schema configuration
2673
- * @throws Error if strategy name doesn't exist
5075
+ * @param key - Risk name
5076
+ * @returns Risk schema configuration
5077
+ * @throws Error if risk name doesn't exist
2674
5078
  */
2675
- get: (key: StrategyName) => IStrategySchema;
5079
+ get: (key: RiskName) => IRiskSchema;
2676
5080
  }
2677
5081
 
2678
5082
  /**
2679
- * Service for managing frame schema registry.
5083
+ * Service for managing walker schema registry.
2680
5084
  *
2681
5085
  * Uses ToolRegistry from functools-kit for type-safe schema storage.
2682
- * Frames are registered via addFrame() and retrieved by name.
5086
+ * Walkers are registered via addWalker() and retrieved by name.
2683
5087
  */
2684
- declare class FrameSchemaService {
5088
+ declare class WalkerSchemaService {
2685
5089
  readonly loggerService: LoggerService;
2686
5090
  private _registry;
2687
5091
  /**
2688
- * Registers a new frame schema.
5092
+ * Registers a new walker schema.
2689
5093
  *
2690
- * @param key - Unique frame name
2691
- * @param value - Frame schema configuration
2692
- * @throws Error if frame name already exists
5094
+ * @param key - Unique walker name
5095
+ * @param value - Walker schema configuration
5096
+ * @throws Error if walker name already exists
2693
5097
  */
2694
- register(key: FrameName, value: IFrameSchema): void;
5098
+ register: (key: WalkerName, value: IWalkerSchema) => void;
2695
5099
  /**
2696
- * Validates frame schema structure for required properties.
5100
+ * Validates walker schema structure for required properties.
2697
5101
  *
2698
5102
  * Performs shallow validation to ensure all required properties exist
2699
5103
  * and have correct types before registration in the registry.
2700
5104
  *
2701
- * @param frameSchema - Frame schema to validate
5105
+ * @param walkerSchema - Walker schema to validate
5106
+ * @throws Error if walkerName is missing or not a string
5107
+ * @throws Error if exchangeName is missing or not a string
2702
5108
  * @throws Error if frameName is missing or not a string
2703
- * @throws Error if interval is missing or not a valid FrameInterval
2704
- * @throws Error if startDate is missing or not a Date
2705
- * @throws Error if endDate is missing or not a Date
5109
+ * @throws Error if strategies is missing or not an array
5110
+ * @throws Error if strategies array is empty
2706
5111
  */
2707
5112
  private validateShallow;
2708
5113
  /**
2709
- * Overrides an existing frame schema with partial updates.
5114
+ * Overrides an existing walker schema with partial updates.
2710
5115
  *
2711
- * @param key - Frame name to override
5116
+ * @param key - Walker name to override
2712
5117
  * @param value - Partial schema updates
2713
- * @throws Error if frame name doesn't exist
5118
+ * @returns Updated walker schema
5119
+ * @throws Error if walker name doesn't exist
2714
5120
  */
2715
- override(key: FrameName, value: Partial<IFrameSchema>): IFrameSchema;
5121
+ override: (key: WalkerName, value: Partial<IWalkerSchema>) => IWalkerSchema;
2716
5122
  /**
2717
- * Retrieves a frame schema by name.
5123
+ * Retrieves a walker schema by name.
2718
5124
  *
2719
- * @param key - Frame name
2720
- * @returns Frame schema configuration
2721
- * @throws Error if frame name doesn't exist
5125
+ * @param key - Walker name
5126
+ * @returns Walker schema configuration
5127
+ * @throws Error if walker name doesn't exist
2722
5128
  */
2723
- get(key: FrameName): IFrameSchema;
5129
+ get: (key: WalkerName) => IWalkerSchema;
2724
5130
  }
2725
5131
 
2726
5132
  /**
@@ -2778,6 +5184,7 @@ declare class BacktestLogicPrivateService {
2778
5184
  declare class LiveLogicPrivateService {
2779
5185
  private readonly loggerService;
2780
5186
  private readonly strategyGlobalService;
5187
+ private readonly methodContextService;
2781
5188
  /**
2782
5189
  * Runs live trading for a symbol, streaming results as async generator.
2783
5190
  *
@@ -2803,6 +5210,56 @@ declare class LiveLogicPrivateService {
2803
5210
  run(symbol: string): AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
2804
5211
  }
2805
5212
 
5213
+ /**
5214
+ * Private service for walker orchestration (strategy comparison).
5215
+ *
5216
+ * Flow:
5217
+ * 1. Yields progress updates as each strategy completes
5218
+ * 2. Tracks best metric in real-time
5219
+ * 3. Returns final results with all strategies ranked
5220
+ *
5221
+ * Uses BacktestLogicPublicService internally for each strategy.
5222
+ */
5223
+ declare class WalkerLogicPrivateService {
5224
+ private readonly loggerService;
5225
+ private readonly backtestLogicPublicService;
5226
+ private readonly backtestMarkdownService;
5227
+ private readonly walkerSchemaService;
5228
+ /**
5229
+ * Runs walker comparison for a symbol.
5230
+ *
5231
+ * Executes backtest for each strategy sequentially.
5232
+ * Yields WalkerContract after each strategy completes.
5233
+ *
5234
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
5235
+ * @param strategies - List of strategy names to compare
5236
+ * @param metric - Metric to use for comparison
5237
+ * @param context - Walker context with exchangeName, frameName, walkerName
5238
+ * @yields WalkerContract with progress after each strategy
5239
+ *
5240
+ * @example
5241
+ * ```typescript
5242
+ * for await (const progress of walkerLogic.run(
5243
+ * "BTCUSDT",
5244
+ * ["strategy-v1", "strategy-v2"],
5245
+ * "sharpeRatio",
5246
+ * {
5247
+ * exchangeName: "binance",
5248
+ * frameName: "1d-backtest",
5249
+ * walkerName: "my-optimizer"
5250
+ * }
5251
+ * )) {
5252
+ * console.log("Progress:", progress.strategiesTested, "/", progress.totalStrategies);
5253
+ * }
5254
+ * ```
5255
+ */
5256
+ run(symbol: string, strategies: StrategyName[], metric: WalkerMetric, context: {
5257
+ exchangeName: string;
5258
+ frameName: string;
5259
+ walkerName: string;
5260
+ }): AsyncGenerator<WalkerContract>;
5261
+ }
5262
+
2806
5263
  /**
2807
5264
  * Public service for backtest orchestration with context management.
2808
5265
  *
@@ -2898,6 +5355,46 @@ declare class LiveLogicPublicService {
2898
5355
  }) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
2899
5356
  }
2900
5357
 
5358
+ /**
5359
+ * Public service for walker orchestration with context management.
5360
+ *
5361
+ * Wraps WalkerLogicPrivateService with MethodContextService to provide
5362
+ * implicit context propagation for strategyName, exchangeName, frameName, and walkerName.
5363
+ *
5364
+ * @example
5365
+ * ```typescript
5366
+ * const walkerLogicPublicService = inject(TYPES.walkerLogicPublicService);
5367
+ *
5368
+ * const results = await walkerLogicPublicService.run("BTCUSDT", {
5369
+ * walkerName: "my-optimizer",
5370
+ * exchangeName: "binance",
5371
+ * frameName: "1d-backtest",
5372
+ * strategies: ["strategy-v1", "strategy-v2"],
5373
+ * metric: "sharpeRatio",
5374
+ * });
5375
+ *
5376
+ * console.log("Best strategy:", results.bestStrategy);
5377
+ * ```
5378
+ */
5379
+ declare class WalkerLogicPublicService {
5380
+ private readonly loggerService;
5381
+ private readonly walkerLogicPrivateService;
5382
+ private readonly walkerSchemaService;
5383
+ /**
5384
+ * Runs walker comparison for a symbol with context propagation.
5385
+ *
5386
+ * Executes backtests for all strategies.
5387
+ *
5388
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
5389
+ * @param context - Walker context with strategies and metric
5390
+ */
5391
+ run: (symbol: string, context: {
5392
+ walkerName: string;
5393
+ exchangeName: string;
5394
+ frameName: string;
5395
+ }) => AsyncGenerator<WalkerContract, any, any>;
5396
+ }
5397
+
2901
5398
  /**
2902
5399
  * Global service providing access to live trading functionality.
2903
5400
  *
@@ -2950,6 +5447,147 @@ declare class BacktestGlobalService {
2950
5447
  }) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
2951
5448
  }
2952
5449
 
5450
+ /**
5451
+ * Portfolio Heatmap Markdown Service.
5452
+ *
5453
+ * Subscribes to signalEmitter and aggregates statistics across all symbols per strategy.
5454
+ * Provides portfolio-wide metrics and per-symbol breakdowns.
5455
+ *
5456
+ * Features:
5457
+ * - Real-time aggregation of closed signals
5458
+ * - Per-symbol statistics (Total PNL, Sharpe Ratio, Max Drawdown, Trades)
5459
+ * - Portfolio-wide aggregated metrics per strategy
5460
+ * - Markdown table report generation
5461
+ * - Safe math (handles NaN/Infinity gracefully)
5462
+ * - Strategy-based navigation using memoized storage
5463
+ *
5464
+ * @example
5465
+ * ```typescript
5466
+ * const service = new HeatMarkdownService();
5467
+ *
5468
+ * // Service automatically tracks all closed signals per strategy
5469
+ * const stats = await service.getData("my-strategy");
5470
+ * console.log(`Portfolio Total PNL: ${stats.portfolioTotalPnl}%`);
5471
+ *
5472
+ * // Generate and save report
5473
+ * await service.dump("my-strategy", "./reports");
5474
+ * ```
5475
+ */
5476
+ declare class HeatMarkdownService {
5477
+ /** Logger service for debug output */
5478
+ private readonly loggerService;
5479
+ /**
5480
+ * Memoized function to get or create HeatmapStorage for a strategy.
5481
+ * Each strategy gets its own isolated heatmap storage instance.
5482
+ */
5483
+ private getStorage;
5484
+ /**
5485
+ * Processes tick events and accumulates closed signals.
5486
+ * Should be called from signal emitter subscription.
5487
+ *
5488
+ * Only processes closed signals - opened signals are ignored.
5489
+ *
5490
+ * @param data - Tick result from strategy execution (closed signals only)
5491
+ */
5492
+ private tick;
5493
+ /**
5494
+ * Gets aggregated portfolio heatmap statistics for a strategy.
5495
+ *
5496
+ * @param strategyName - Strategy name to get heatmap data for
5497
+ * @returns Promise resolving to heatmap statistics with per-symbol and portfolio-wide metrics
5498
+ *
5499
+ * @example
5500
+ * ```typescript
5501
+ * const service = new HeatMarkdownService();
5502
+ * const stats = await service.getData("my-strategy");
5503
+ *
5504
+ * console.log(`Total symbols: ${stats.totalSymbols}`);
5505
+ * console.log(`Portfolio PNL: ${stats.portfolioTotalPnl}%`);
5506
+ *
5507
+ * stats.symbols.forEach(row => {
5508
+ * console.log(`${row.symbol}: ${row.totalPnl}% (${row.totalTrades} trades)`);
5509
+ * });
5510
+ * ```
5511
+ */
5512
+ getData: (strategyName: StrategyName) => Promise<IHeatmapStatistics>;
5513
+ /**
5514
+ * Generates markdown report with portfolio heatmap table for a strategy.
5515
+ *
5516
+ * @param strategyName - Strategy name to generate heatmap report for
5517
+ * @returns Promise resolving to markdown formatted report string
5518
+ *
5519
+ * @example
5520
+ * ```typescript
5521
+ * const service = new HeatMarkdownService();
5522
+ * const markdown = await service.getReport("my-strategy");
5523
+ * console.log(markdown);
5524
+ * // Output:
5525
+ * // # Portfolio Heatmap: my-strategy
5526
+ * //
5527
+ * // **Total Symbols:** 5 | **Portfolio PNL:** +45.3% | **Portfolio Sharpe:** 1.85 | **Total Trades:** 120
5528
+ * //
5529
+ * // | Symbol | Total PNL | Sharpe | Max DD | Trades |
5530
+ * // |--------|-----------|--------|--------|--------|
5531
+ * // | BTCUSDT | +15.5% | 2.10 | -2.5% | 45 |
5532
+ * // | ETHUSDT | +12.3% | 1.85 | -3.1% | 38 |
5533
+ * // ...
5534
+ * ```
5535
+ */
5536
+ getReport: (strategyName: StrategyName) => Promise<string>;
5537
+ /**
5538
+ * Saves heatmap report to disk for a strategy.
5539
+ *
5540
+ * Creates directory if it doesn't exist.
5541
+ * Default filename: {strategyName}.md
5542
+ *
5543
+ * @param strategyName - Strategy name to save heatmap report for
5544
+ * @param path - Optional directory path to save report (default: "./logs/heatmap")
5545
+ *
5546
+ * @example
5547
+ * ```typescript
5548
+ * const service = new HeatMarkdownService();
5549
+ *
5550
+ * // Save to default path: ./logs/heatmap/my-strategy.md
5551
+ * await service.dump("my-strategy");
5552
+ *
5553
+ * // Save to custom path: ./reports/my-strategy.md
5554
+ * await service.dump("my-strategy", "./reports");
5555
+ * ```
5556
+ */
5557
+ dump: (strategyName: StrategyName, path?: string) => Promise<void>;
5558
+ /**
5559
+ * Clears accumulated heatmap data from storage.
5560
+ * If strategyName is provided, clears only that strategy's data.
5561
+ * If strategyName is omitted, clears all strategies' data.
5562
+ *
5563
+ * @param strategyName - Optional strategy name to clear specific strategy data
5564
+ *
5565
+ * @example
5566
+ * ```typescript
5567
+ * const service = new HeatMarkdownService();
5568
+ *
5569
+ * // Clear specific strategy data
5570
+ * await service.clear("my-strategy");
5571
+ *
5572
+ * // Clear all strategies' data
5573
+ * await service.clear();
5574
+ * ```
5575
+ */
5576
+ clear: (strategyName?: StrategyName) => Promise<void>;
5577
+ /**
5578
+ * Initializes the service by subscribing to signal events.
5579
+ * Uses singleshot to ensure initialization happens only once.
5580
+ * Automatically called on first use.
5581
+ *
5582
+ * @example
5583
+ * ```typescript
5584
+ * const service = new HeatMarkdownService();
5585
+ * await service.init(); // Subscribe to signal events
5586
+ * ```
5587
+ */
5588
+ protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
5589
+ }
5590
+
2953
5591
  /**
2954
5592
  * @class ExchangeValidationService
2955
5593
  * Service for managing and validating exchange configurations
@@ -2998,6 +5636,12 @@ declare class StrategyValidationService {
2998
5636
  * Injected logger service instance
2999
5637
  */
3000
5638
  private readonly loggerService;
5639
+ /**
5640
+ * @private
5641
+ * @readonly
5642
+ * Injected risk validation service instance
5643
+ */
5644
+ private readonly riskValidationService;
3001
5645
  /**
3002
5646
  * @private
3003
5647
  * Map storing strategy schemas by strategy name
@@ -3010,9 +5654,10 @@ declare class StrategyValidationService {
3010
5654
  */
3011
5655
  addStrategy: (strategyName: StrategyName, strategySchema: IStrategySchema) => void;
3012
5656
  /**
3013
- * Validates the existence of a strategy
5657
+ * Validates the existence of a strategy and its risk profile (if configured)
3014
5658
  * @public
3015
5659
  * @throws {Error} If strategyName is not found
5660
+ * @throws {Error} If riskName is configured but not found
3016
5661
  * Memoized function to cache validation results
3017
5662
  */
3018
5663
  validate: (strategyName: StrategyName, source: string) => void;
@@ -3061,27 +5706,155 @@ declare class FrameValidationService {
3061
5706
  list: () => Promise<IFrameSchema[]>;
3062
5707
  }
3063
5708
 
5709
+ /**
5710
+ * @class WalkerValidationService
5711
+ * Service for managing and validating walker configurations
5712
+ */
5713
+ declare class WalkerValidationService {
5714
+ /**
5715
+ * @private
5716
+ * @readonly
5717
+ * Injected logger service instance
5718
+ */
5719
+ private readonly loggerService;
5720
+ /**
5721
+ * @private
5722
+ * Map storing walker schemas by walker name
5723
+ */
5724
+ private _walkerMap;
5725
+ /**
5726
+ * Adds a walker schema to the validation service
5727
+ * @public
5728
+ * @throws {Error} If walkerName already exists
5729
+ */
5730
+ addWalker: (walkerName: WalkerName, walkerSchema: IWalkerSchema) => void;
5731
+ /**
5732
+ * Validates the existence of a walker
5733
+ * @public
5734
+ * @throws {Error} If walkerName is not found
5735
+ * Memoized function to cache validation results
5736
+ */
5737
+ validate: (walkerName: WalkerName, source: string) => void;
5738
+ /**
5739
+ * Returns a list of all registered walker schemas
5740
+ * @public
5741
+ * @returns Array of walker schemas with their configurations
5742
+ */
5743
+ list: () => Promise<IWalkerSchema[]>;
5744
+ }
5745
+
5746
+ /**
5747
+ * @class SizingValidationService
5748
+ * Service for managing and validating sizing configurations
5749
+ */
5750
+ declare class SizingValidationService {
5751
+ /**
5752
+ * @private
5753
+ * @readonly
5754
+ * Injected logger service instance
5755
+ */
5756
+ private readonly loggerService;
5757
+ /**
5758
+ * @private
5759
+ * Map storing sizing schemas by sizing name
5760
+ */
5761
+ private _sizingMap;
5762
+ /**
5763
+ * Adds a sizing schema to the validation service
5764
+ * @public
5765
+ * @throws {Error} If sizingName already exists
5766
+ */
5767
+ addSizing: (sizingName: SizingName, sizingSchema: ISizingSchema) => void;
5768
+ /**
5769
+ * Validates the existence of a sizing and optionally its method
5770
+ * @public
5771
+ * @throws {Error} If sizingName is not found
5772
+ * @throws {Error} If method is provided and doesn't match sizing schema method
5773
+ * Memoized function to cache validation results
5774
+ */
5775
+ validate: (sizingName: SizingName, source: string, method?: "fixed-percentage" | "kelly-criterion" | "atr-based") => void;
5776
+ /**
5777
+ * Returns a list of all registered sizing schemas
5778
+ * @public
5779
+ * @returns Array of sizing schemas with their configurations
5780
+ */
5781
+ list: () => Promise<ISizingSchema[]>;
5782
+ }
5783
+
5784
+ /**
5785
+ * @class RiskValidationService
5786
+ * Service for managing and validating risk configurations
5787
+ */
5788
+ declare class RiskValidationService {
5789
+ /**
5790
+ * @private
5791
+ * @readonly
5792
+ * Injected logger service instance
5793
+ */
5794
+ private readonly loggerService;
5795
+ /**
5796
+ * @private
5797
+ * Map storing risk schemas by risk name
5798
+ */
5799
+ private _riskMap;
5800
+ /**
5801
+ * Adds a risk schema to the validation service
5802
+ * @public
5803
+ * @throws {Error} If riskName already exists
5804
+ */
5805
+ addRisk: (riskName: RiskName, riskSchema: IRiskSchema) => void;
5806
+ /**
5807
+ * Validates the existence of a risk profile
5808
+ * @public
5809
+ * @throws {Error} If riskName is not found
5810
+ * Memoized function to cache validation results
5811
+ */
5812
+ validate: (riskName: RiskName, source: string) => void;
5813
+ /**
5814
+ * Returns a list of all registered risk schemas
5815
+ * @public
5816
+ * @returns Array of risk schemas with their configurations
5817
+ */
5818
+ list: () => Promise<IRiskSchema[]>;
5819
+ }
5820
+
3064
5821
  declare const backtest: {
3065
5822
  exchangeValidationService: ExchangeValidationService;
3066
5823
  strategyValidationService: StrategyValidationService;
3067
5824
  frameValidationService: FrameValidationService;
5825
+ walkerValidationService: WalkerValidationService;
5826
+ sizingValidationService: SizingValidationService;
5827
+ riskValidationService: RiskValidationService;
3068
5828
  backtestMarkdownService: BacktestMarkdownService;
3069
5829
  liveMarkdownService: LiveMarkdownService;
5830
+ performanceMarkdownService: PerformanceMarkdownService;
5831
+ walkerMarkdownService: WalkerMarkdownService;
5832
+ heatMarkdownService: HeatMarkdownService;
3070
5833
  backtestLogicPublicService: BacktestLogicPublicService;
3071
5834
  liveLogicPublicService: LiveLogicPublicService;
5835
+ walkerLogicPublicService: WalkerLogicPublicService;
3072
5836
  backtestLogicPrivateService: BacktestLogicPrivateService;
3073
5837
  liveLogicPrivateService: LiveLogicPrivateService;
5838
+ walkerLogicPrivateService: WalkerLogicPrivateService;
3074
5839
  exchangeGlobalService: ExchangeGlobalService;
3075
5840
  strategyGlobalService: StrategyGlobalService;
3076
5841
  frameGlobalService: FrameGlobalService;
3077
5842
  liveGlobalService: LiveGlobalService;
3078
5843
  backtestGlobalService: BacktestGlobalService;
5844
+ walkerGlobalService: WalkerGlobalService;
5845
+ sizingGlobalService: SizingGlobalService;
5846
+ riskGlobalService: RiskGlobalService;
3079
5847
  exchangeSchemaService: ExchangeSchemaService;
3080
5848
  strategySchemaService: StrategySchemaService;
3081
5849
  frameSchemaService: FrameSchemaService;
5850
+ walkerSchemaService: WalkerSchemaService;
5851
+ sizingSchemaService: SizingSchemaService;
5852
+ riskSchemaService: RiskSchemaService;
3082
5853
  exchangeConnectionService: ExchangeConnectionService;
3083
5854
  strategyConnectionService: StrategyConnectionService;
3084
5855
  frameConnectionService: FrameConnectionService;
5856
+ sizingConnectionService: SizingConnectionService;
5857
+ riskConnectionService: RiskConnectionService;
3085
5858
  executionContextService: {
3086
5859
  readonly context: IExecutionContext;
3087
5860
  };
@@ -3091,4 +5864,4 @@ declare const backtest: {
3091
5864
  loggerService: LoggerService;
3092
5865
  };
3093
5866
 
3094
- export { Backtest, type BacktestStatistics, type CandleInterval, type DoneContract, type EntityId, ExecutionContextService, type FrameInterval, type ICandleData, type IExchangeSchema, type IFrameSchema, type IPersistBase, type ISignalDto, type ISignalRow, type IStrategyPnL, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, Live, type LiveStatistics, MethodContextService, PersistBase, PersistSignalAdaper, type ProgressContract, type SignalData, type SignalInterval, type TPersistBase, type TPersistBaseCtor, addExchange, addFrame, addStrategy, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listExchanges, listFrames, listStrategies, listenDone, listenDoneOnce, listenError, listenProgress, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, setLogger };
5867
+ export { Backtest, type BacktestStatistics, type CandleInterval, type DoneContract, type EntityId, ExecutionContextService, type FrameInterval, Heat, type ICandleData, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type IHeatmapStatistics, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStrategyPnL, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, Live, type LiveStatistics, MethodContextService, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatistics, PersistBase, PersistRiskAdapter, PersistSignalAdaper, PositionSize, type ProgressContract, type RiskData, type SignalData, type SignalInterval, type TPersistBase, type TPersistBaseCtor, Walker, type WalkerMetric, type WalkerStatistics, addExchange, addFrame, addRisk, addSizing, addStrategy, addWalker, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listExchanges, listFrames, listRisks, listSizings, listStrategies, listWalkers, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenPerformance, listenProgress, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, setLogger };