backtest-kit 1.1.8 → 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 +806 -970
  2. package/build/index.cjs +3588 -275
  3. package/build/index.mjs +3569 -275
  4. package/package.json +1 -1
  5. package/types.d.ts +2955 -520
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[]>;
@@ -757,6 +1513,102 @@ declare function listStrategies(): Promise<IStrategySchema[]>;
757
1513
  * ```
758
1514
  */
759
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
+ * // ]
1609
+ * ```
1610
+ */
1611
+ declare function listRisks(): Promise<IRiskSchema[]>;
760
1612
 
761
1613
  /**
762
1614
  * Contract for background execution completion events.
@@ -850,6 +1702,8 @@ type PerformanceMetricType = "backtest_total" | "backtest_timeframe" | "backtest
850
1702
  interface PerformanceContract {
851
1703
  /** Timestamp when the metric was recorded (milliseconds since epoch) */
852
1704
  timestamp: number;
1705
+ /** Timestamp of the previous event (milliseconds since epoch, null for first event) */
1706
+ previousTimestamp: number | null;
853
1707
  /** Type of operation being measured */
854
1708
  metricType: PerformanceMetricType;
855
1709
  /** Duration of the operation in milliseconds */
@@ -864,6 +1718,37 @@ interface PerformanceContract {
864
1718
  backtest: boolean;
865
1719
  }
866
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
+
867
1752
  /**
868
1753
  * Subscribes to all signal events with queued async processing.
869
1754
  *
@@ -1034,9 +1919,9 @@ declare function listenSignalBacktestOnce(filterFn: (event: IStrategyTickResult)
1034
1919
  */
1035
1920
  declare function listenError(fn: (error: Error) => void): () => void;
1036
1921
  /**
1037
- * Subscribes to background execution completion events with queued async processing.
1922
+ * Subscribes to live background execution completion events with queued async processing.
1038
1923
  *
1039
- * Emits when Live.background() or Backtest.background() completes execution.
1924
+ * Emits when Live.background() completes execution.
1040
1925
  * Events are processed sequentially in order received, even if callback is async.
1041
1926
  * Uses queued wrapper to prevent concurrent execution of the callback.
1042
1927
  *
@@ -1045,29 +1930,82 @@ declare function listenError(fn: (error: Error) => void): () => void;
1045
1930
  *
1046
1931
  * @example
1047
1932
  * ```typescript
1048
- * import { listenDone, Live } from "backtest-kit";
1933
+ * import { listenDoneLive, Live } from "backtest-kit";
1049
1934
  *
1050
- * const unsubscribe = listenDone((event) => {
1051
- * console.log("Completed:", event.strategyName, event.exchangeName, event.symbol);
1052
- * if (event.backtest) {
1053
- * console.log("Backtest mode completed");
1054
- * }
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"
1055
1942
  * });
1056
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
+ *
1057
1969
  * Live.background("BTCUSDT", {
1058
1970
  * strategyName: "my-strategy",
1059
1971
  * exchangeName: "binance"
1060
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
+ * });
1061
1999
  *
1062
2000
  * // Later: stop listening
1063
2001
  * unsubscribe();
1064
2002
  * ```
1065
2003
  */
1066
- declare function listenDone(fn: (event: DoneContract) => void): () => void;
2004
+ declare function listenDoneBacktest(fn: (event: DoneContract) => void): () => void;
1067
2005
  /**
1068
- * Subscribes to filtered background execution completion events with one-time execution.
2006
+ * Subscribes to filtered backtest background execution completion events with one-time execution.
1069
2007
  *
1070
- * Emits when Live.background() or Backtest.background() completes execution.
2008
+ * Emits when Backtest.background() completes execution.
1071
2009
  * Executes callback once and automatically unsubscribes.
1072
2010
  *
1073
2011
  * @param filterFn - Predicate to filter which events trigger the callback
@@ -1076,11 +2014,11 @@ declare function listenDone(fn: (event: DoneContract) => void): () => void;
1076
2014
  *
1077
2015
  * @example
1078
2016
  * ```typescript
1079
- * import { listenDoneOnce, Backtest } from "backtest-kit";
2017
+ * import { listenDoneBacktestOnce, Backtest } from "backtest-kit";
1080
2018
  *
1081
2019
  * // Wait for first backtest completion
1082
- * listenDoneOnce(
1083
- * (event) => event.backtest && event.symbol === "BTCUSDT",
2020
+ * listenDoneBacktestOnce(
2021
+ * (event) => event.symbol === "BTCUSDT",
1084
2022
  * (event) => console.log("BTCUSDT backtest completed:", event.strategyName)
1085
2023
  * );
1086
2024
  *
@@ -1091,7 +2029,60 @@ declare function listenDone(fn: (event: DoneContract) => void): () => void;
1091
2029
  * });
1092
2030
  * ```
1093
2031
  */
1094
- 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;
1095
2086
  /**
1096
2087
  * Subscribes to backtest progress events with queued async processing.
1097
2088
  *
@@ -1155,7 +2146,135 @@ declare function listenProgress(fn: (event: ProgressContract) => void): () => vo
1155
2146
  * unsubscribe();
1156
2147
  * ```
1157
2148
  */
1158
- declare function listenPerformance(fn: (event: PerformanceContract) => void): () => void;
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;
1159
2278
 
1160
2279
  /**
1161
2280
  * Fetches historical candle data from the registered exchange.
@@ -1259,187 +2378,58 @@ declare function getDate(): Promise<Date>;
1259
2378
  declare function getMode(): Promise<"backtest" | "live">;
1260
2379
 
1261
2380
  /**
1262
- * Statistical data calculated from backtest results.
1263
- *
1264
- * All numeric values are null if calculation is unsafe (NaN, Infinity, etc).
1265
- * Provides comprehensive metrics for strategy performance analysis.
1266
- *
1267
- * @example
1268
- * ```typescript
1269
- * const stats = await Backtest.getData("my-strategy");
1270
- *
1271
- * console.log(`Total signals: ${stats.totalSignals}`);
1272
- * console.log(`Win rate: ${stats.winRate}%`);
1273
- * console.log(`Sharpe Ratio: ${stats.sharpeRatio}`);
1274
- *
1275
- * // Access raw signal data
1276
- * stats.signalList.forEach(signal => {
1277
- * console.log(`Signal ${signal.signal.id}: ${signal.pnl.pnlPercentage}%`);
1278
- * });
1279
- * ```
2381
+ * Portfolio heatmap statistics for a single symbol.
2382
+ * Aggregated metrics across all strategies for one trading pair.
1280
2383
  */
1281
- interface BacktestStatistics {
1282
- /** Array of all closed signals with full details (price, PNL, timestamps, etc.) */
1283
- signalList: IStrategyTickResultClosed[];
1284
- /** Total number of closed signals */
1285
- totalSignals: number;
1286
- /** Number of winning signals (PNL > 0) */
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 */
1287
2396
  winCount: number;
1288
- /** Number of losing signals (PNL < 0) */
2397
+ /** Number of losing trades */
1289
2398
  lossCount: number;
1290
- /** Win rate as percentage (0-100), null if unsafe. Higher is better. */
2399
+ /** Win rate percentage */
1291
2400
  winRate: number | null;
1292
- /** Average PNL per signal as percentage, null if unsafe. Higher is better. */
2401
+ /** Average PNL per trade */
1293
2402
  avgPnl: number | null;
1294
- /** Cumulative PNL across all signals as percentage, null if unsafe. Higher is better. */
1295
- totalPnl: number | null;
1296
- /** Standard deviation of returns (volatility metric), null if unsafe. Lower is better. */
2403
+ /** Standard deviation of PNL */
1297
2404
  stdDev: number | null;
1298
- /** Sharpe Ratio (risk-adjusted return = avgPnl / stdDev), null if unsafe. Higher is better. */
1299
- sharpeRatio: number | null;
1300
- /** Annualized Sharpe Ratio (sharpeRatio × √365), null if unsafe. Higher is better. */
1301
- annualizedSharpeRatio: number | null;
1302
- /** Certainty Ratio (avgWin / |avgLoss|), null if unsafe. Higher is better. */
1303
- certaintyRatio: number | null;
1304
- /** Expected yearly returns based on average trade duration and PNL, null if unsafe. Higher is better. */
1305
- expectedYearlyReturns: 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;
1306
2417
  }
1307
2418
  /**
1308
- * Service for generating and saving backtest markdown reports.
1309
- *
1310
- * Features:
1311
- * - Listens to signal events via onTick callback
1312
- * - Accumulates closed signals per strategy using memoized storage
1313
- * - Generates markdown tables with detailed signal information
1314
- * - Saves reports to disk in logs/backtest/{strategyName}.md
1315
- *
1316
- * @example
1317
- * ```typescript
1318
- * const service = new BacktestMarkdownService();
1319
- *
1320
- * // Add to strategy callbacks
1321
- * addStrategy({
1322
- * strategyName: "my-strategy",
1323
- * callbacks: {
1324
- * onTick: (symbol, result, backtest) => {
1325
- * service.tick(result);
1326
- * }
1327
- * }
1328
- * });
1329
- *
1330
- * // After backtest, generate and save report
1331
- * await service.saveReport("my-strategy");
1332
- * ```
2419
+ * Portfolio heatmap statistics structure.
2420
+ * Contains aggregated data for all symbols in the portfolio.
1333
2421
  */
1334
- declare class BacktestMarkdownService {
1335
- /** Logger service for debug output */
1336
- private readonly loggerService;
1337
- /**
1338
- * Memoized function to get or create ReportStorage for a strategy.
1339
- * Each strategy gets its own isolated storage instance.
1340
- */
1341
- private getStorage;
1342
- /**
1343
- * Processes tick events and accumulates closed signals.
1344
- * Should be called from IStrategyCallbacks.onTick.
1345
- *
1346
- * Only processes closed signals - opened signals are ignored.
1347
- *
1348
- * @param data - Tick result from strategy execution (opened or closed)
1349
- *
1350
- * @example
1351
- * ```typescript
1352
- * const service = new BacktestMarkdownService();
1353
- *
1354
- * callbacks: {
1355
- * onTick: (symbol, result, backtest) => {
1356
- * service.tick(result);
1357
- * }
1358
- * }
1359
- * ```
1360
- */
1361
- private tick;
1362
- /**
1363
- * Gets statistical data from all closed signals for a strategy.
1364
- * Delegates to ReportStorage.getData().
1365
- *
1366
- * @param strategyName - Strategy name to get data for
1367
- * @returns Statistical data object with all metrics
1368
- *
1369
- * @example
1370
- * ```typescript
1371
- * const service = new BacktestMarkdownService();
1372
- * const stats = await service.getData("my-strategy");
1373
- * console.log(stats.sharpeRatio, stats.winRate);
1374
- * ```
1375
- */
1376
- getData: (strategyName: StrategyName) => Promise<BacktestStatistics>;
1377
- /**
1378
- * Generates markdown report with all closed signals for a strategy.
1379
- * Delegates to ReportStorage.generateReport().
1380
- *
1381
- * @param strategyName - Strategy name to generate report for
1382
- * @returns Markdown formatted report string with table of all closed signals
1383
- *
1384
- * @example
1385
- * ```typescript
1386
- * const service = new BacktestMarkdownService();
1387
- * const markdown = await service.getReport("my-strategy");
1388
- * console.log(markdown);
1389
- * ```
1390
- */
1391
- getReport: (strategyName: StrategyName) => Promise<string>;
1392
- /**
1393
- * Saves strategy report to disk.
1394
- * Creates directory if it doesn't exist.
1395
- * Delegates to ReportStorage.dump().
1396
- *
1397
- * @param strategyName - Strategy name to save report for
1398
- * @param path - Directory path to save report (default: "./logs/backtest")
1399
- *
1400
- * @example
1401
- * ```typescript
1402
- * const service = new BacktestMarkdownService();
1403
- *
1404
- * // Save to default path: ./logs/backtest/my-strategy.md
1405
- * await service.dump("my-strategy");
1406
- *
1407
- * // Save to custom path: ./custom/path/my-strategy.md
1408
- * await service.dump("my-strategy", "./custom/path");
1409
- * ```
1410
- */
1411
- dump: (strategyName: StrategyName, path?: string) => Promise<void>;
1412
- /**
1413
- * Clears accumulated signal data from storage.
1414
- * If strategyName is provided, clears only that strategy's data.
1415
- * If strategyName is omitted, clears all strategies' data.
1416
- *
1417
- * @param strategyName - Optional strategy name to clear specific strategy data
1418
- *
1419
- * @example
1420
- * ```typescript
1421
- * const service = new BacktestMarkdownService();
1422
- *
1423
- * // Clear specific strategy data
1424
- * await service.clear("my-strategy");
1425
- *
1426
- * // Clear all strategies' data
1427
- * await service.clear();
1428
- * ```
1429
- */
1430
- clear: (strategyName?: StrategyName) => Promise<void>;
1431
- /**
1432
- * Initializes the service by subscribing to backtest signal events.
1433
- * Uses singleshot to ensure initialization happens only once.
1434
- * Automatically called on first use.
1435
- *
1436
- * @example
1437
- * ```typescript
1438
- * const service = new BacktestMarkdownService();
1439
- * await service.init(); // Subscribe to backtest events
1440
- * ```
1441
- */
1442
- protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
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;
1443
2433
  }
1444
2434
 
1445
2435
  /**
@@ -1692,6 +2682,12 @@ interface MetricStats {
1692
2682
  p95: number;
1693
2683
  /** 99th percentile duration (ms) */
1694
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;
1695
2691
  }
1696
2692
  /**
1697
2693
  * Performance statistics aggregated by strategy.
@@ -1781,28 +2777,172 @@ declare class PerformanceMarkdownService {
1781
2777
  /**
1782
2778
  * Saves performance report to disk.
1783
2779
  *
1784
- * @param strategyName - Strategy name to save report for
1785
- * @param path - Directory path to save report
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;
2804
+ }
2805
+
2806
+ /**
2807
+ * Alias for walker statistics result interface.
2808
+ * Used for clarity in markdown service context.
2809
+ *
2810
+ */
2811
+ type WalkerStatistics = IWalkerResults;
2812
+ /**
2813
+ * Service for generating and saving walker markdown reports.
2814
+ *
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
2820
+ *
2821
+ * @example
2822
+ * ```typescript
2823
+ * const service = new WalkerMarkdownService();
2824
+ * const results = await service.getData("my-walker");
2825
+ * await service.dump("my-walker");
2826
+ * ```
2827
+ */
2828
+ declare class WalkerMarkdownService {
2829
+ /** Logger service for debug output */
2830
+ private readonly loggerService;
2831
+ /**
2832
+ * Memoized function to get or create ReportStorage for a walker.
2833
+ * Each walker gets its own isolated storage instance.
2834
+ */
2835
+ private getStorage;
2836
+ /**
2837
+ * Processes walker progress events and accumulates strategy results.
2838
+ * Should be called from walkerEmitter.
2839
+ *
2840
+ * @param data - Walker contract from walker execution
2841
+ *
2842
+ * @example
2843
+ * ```typescript
2844
+ * const service = new WalkerMarkdownService();
2845
+ * walkerEmitter.subscribe((data) => service.tick(data));
2846
+ * ```
2847
+ */
2848
+ private tick;
2849
+ /**
2850
+ * Gets walker results data from all strategy results.
2851
+ * Delegates to ReportStorage.getData().
2852
+ *
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
2858
+ *
2859
+ * @example
2860
+ * ```typescript
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);
2864
+ * ```
2865
+ */
2866
+ getData: (walkerName: WalkerName, symbol: string, metric: WalkerMetric, context: {
2867
+ exchangeName: string;
2868
+ frameName: string;
2869
+ }) => Promise<IWalkerResults>;
2870
+ /**
2871
+ * Generates markdown report with all strategy results for a walker.
2872
+ * Delegates to ReportStorage.getReport().
2873
+ *
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
2879
+ *
2880
+ * @example
2881
+ * ```typescript
2882
+ * const service = new WalkerMarkdownService();
2883
+ * const markdown = await service.getReport("my-walker", "BTCUSDT", "sharpeRatio", { exchangeName: "binance", frameName: "1d" });
2884
+ * console.log(markdown);
2885
+ * ```
2886
+ */
2887
+ getReport: (walkerName: WalkerName, symbol: string, metric: WalkerMetric, context: {
2888
+ exchangeName: string;
2889
+ frameName: string;
2890
+ }) => Promise<string>;
2891
+ /**
2892
+ * Saves walker report to disk.
2893
+ * Creates directory if it doesn't exist.
2894
+ * Delegates to ReportStorage.dump().
2895
+ *
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")
1786
2901
  *
1787
2902
  * @example
1788
2903
  * ```typescript
1789
- * // Save to default path: ./logs/performance/my-strategy.md
1790
- * await performanceService.dump("my-strategy");
2904
+ * const service = new WalkerMarkdownService();
1791
2905
  *
1792
- * // Save to custom path
1793
- * await performanceService.dump("my-strategy", "./custom/path");
2906
+ * // Save to default path: ./logs/walker/my-walker.md
2907
+ * await service.dump("my-walker", "BTCUSDT", "sharpeRatio", { exchangeName: "binance", frameName: "1d" });
2908
+ *
2909
+ * // Save to custom path: ./custom/path/my-walker.md
2910
+ * await service.dump("my-walker", "BTCUSDT", "sharpeRatio", { exchangeName: "binance", frameName: "1d" }, "./custom/path");
1794
2911
  * ```
1795
2912
  */
1796
- dump: (strategyName: string, path?: string) => Promise<void>;
2913
+ dump: (walkerName: WalkerName, symbol: string, metric: WalkerMetric, context: {
2914
+ exchangeName: string;
2915
+ frameName: string;
2916
+ }, path?: string) => Promise<void>;
1797
2917
  /**
1798
- * Clears accumulated performance data from storage.
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.
1799
2921
  *
1800
- * @param strategyName - Optional strategy name to clear specific strategy data
2922
+ * @param walkerName - Optional walker name to clear specific walker data
2923
+ *
2924
+ * @example
2925
+ * ```typescript
2926
+ * const service = new WalkerMarkdownService();
2927
+ *
2928
+ * // Clear specific walker data
2929
+ * await service.clear("my-walker");
2930
+ *
2931
+ * // Clear all walkers' data
2932
+ * await service.clear();
2933
+ * ```
1801
2934
  */
1802
- clear: (strategyName?: string) => Promise<void>;
2935
+ clear: (walkerName?: WalkerName) => Promise<void>;
1803
2936
  /**
1804
- * Initializes the service by subscribing to performance events.
2937
+ * Initializes the service by subscribing to walker events.
1805
2938
  * Uses singleshot to ensure initialization happens only once.
2939
+ * Automatically called on first use.
2940
+ *
2941
+ * @example
2942
+ * ```typescript
2943
+ * const service = new WalkerMarkdownService();
2944
+ * await service.init(); // Subscribe to walker events
2945
+ * ```
1806
2946
  */
1807
2947
  protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
1808
2948
  }
@@ -2035,6 +3175,79 @@ declare class PersistSignalUtils {
2035
3175
  * ```
2036
3176
  */
2037
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;
2038
3251
 
2039
3252
  /**
2040
3253
  * Utility class for backtest operations.
@@ -2274,135 +3487,534 @@ declare class LiveUtils {
2274
3487
  *
2275
3488
  * @example
2276
3489
  * ```typescript
2277
- * import { Live } from "./classes/Live";
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
3659
+ * @returns Cancellation closure
3660
+ *
3661
+ * @example
3662
+ * ```typescript
3663
+ * // Run walker silently, only callbacks will fire
3664
+ * await Walker.background("BTCUSDT", {
3665
+ * walkerName: "my-walker"
3666
+ * });
3667
+ * console.log("Walker comparison completed");
3668
+ * ```
3669
+ */
3670
+ background: (symbol: string, context: {
3671
+ walkerName: string;
3672
+ }) => () => void;
3673
+ /**
3674
+ * Gets walker results data from all strategy comparisons.
3675
+ *
3676
+ * @param symbol - Trading symbol
3677
+ * @param walkerName - Walker name to get data for
3678
+ * @returns Promise resolving to walker results data object
3679
+ *
3680
+ * @example
3681
+ * ```typescript
3682
+ * const results = await Walker.getData("BTCUSDT", "my-walker");
3683
+ * console.log(results.bestStrategy, results.bestMetric);
3684
+ * ```
3685
+ */
3686
+ getData: (symbol: string, walkerName: WalkerName) => Promise<IWalkerResults>;
3687
+ /**
3688
+ * Generates markdown report with all strategy comparisons for a walker.
3689
+ *
3690
+ * @param symbol - Trading symbol
3691
+ * @param walkerName - Walker name to generate report for
3692
+ * @returns Promise resolving to markdown formatted report string
3693
+ *
3694
+ * @example
3695
+ * ```typescript
3696
+ * const markdown = await Walker.getReport("BTCUSDT", "my-walker");
3697
+ * console.log(markdown);
3698
+ * ```
3699
+ */
3700
+ getReport: (symbol: string, walkerName: WalkerName) => Promise<string>;
3701
+ /**
3702
+ * Saves walker report to disk.
3703
+ *
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")
3707
+ *
3708
+ * @example
3709
+ * ```typescript
3710
+ * // Save to default path: ./logs/walker/my-walker.md
3711
+ * await Walker.dump("BTCUSDT", "my-walker");
3712
+ *
3713
+ * // Save to custom path: ./custom/path/my-walker.md
3714
+ * await Walker.dump("BTCUSDT", "my-walker", "./custom/path");
3715
+ * ```
3716
+ */
3717
+ dump: (symbol: string, walkerName: WalkerName, path?: string) => Promise<void>;
3718
+ }
3719
+ /**
3720
+ * Singleton instance of WalkerUtils for convenient walker operations.
3721
+ *
3722
+ * @example
3723
+ * ```typescript
3724
+ * import { Walker } from "./classes/Walker";
2278
3725
  *
2279
- * for await (const result of Live.run("BTCUSDT", {
2280
- * strategyName: "my-strategy",
2281
- * exchangeName: "my-exchange",
3726
+ * for await (const result of Walker.run("BTCUSDT", {
3727
+ * walkerName: "my-walker"
2282
3728
  * })) {
2283
- * console.log("Result:", result.action);
3729
+ * console.log("Progress:", result.strategiesTested, "/", result.totalStrategies);
3730
+ * console.log("Best so far:", result.bestStrategy, result.bestMetric);
2284
3731
  * }
2285
3732
  * ```
2286
3733
  */
2287
- declare const Live: LiveUtils;
3734
+ declare const Walker: WalkerUtils;
2288
3735
 
2289
3736
  /**
2290
- * Performance class provides static methods for performance metrics analysis.
3737
+ * Utility class for portfolio heatmap operations.
2291
3738
  *
2292
- * Features:
2293
- * - Get aggregated performance statistics by strategy
2294
- * - Generate markdown reports with bottleneck analysis
2295
- * - Save reports to disk
2296
- * - Clear accumulated metrics
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.
2297
3742
  *
2298
3743
  * @example
2299
3744
  * ```typescript
2300
- * import { Performance, listenPerformance } from "backtest-kit";
2301
- *
2302
- * // Subscribe to performance events
2303
- * listenPerformance((event) => {
2304
- * console.log(`${event.metricType}: ${event.duration.toFixed(2)}ms`);
2305
- * });
3745
+ * import { Heat } from "backtest-kit";
2306
3746
  *
2307
- * // Run backtest...
3747
+ * // Get raw heatmap data for a strategy
3748
+ * const stats = await Heat.getData("my-strategy");
3749
+ * console.log(`Portfolio PNL: ${stats.portfolioTotalPnl}%`);
2308
3750
  *
2309
- * // Get aggregated statistics
2310
- * const stats = await Performance.getData("my-strategy");
2311
- * console.log("Total time:", stats.totalDuration);
2312
- * console.log("Slowest operations:", Object.values(stats.metricStats)
2313
- * .sort((a, b) => b.avgDuration - a.avgDuration)
2314
- * .slice(0, 5));
3751
+ * // Generate markdown report
3752
+ * const markdown = await Heat.getReport("my-strategy");
3753
+ * console.log(markdown);
2315
3754
  *
2316
- * // Generate and save report
2317
- * await Performance.dump("my-strategy");
3755
+ * // Save to disk
3756
+ * await Heat.dump("my-strategy", "./reports");
2318
3757
  * ```
2319
3758
  */
2320
- declare class Performance {
3759
+ declare class HeatUtils {
2321
3760
  /**
2322
- * Gets aggregated performance statistics for a strategy.
3761
+ * Gets aggregated portfolio heatmap statistics for a strategy.
2323
3762
  *
2324
- * Returns detailed metrics grouped by operation type:
2325
- * - Count, total duration, average, min, max
2326
- * - Standard deviation for volatility
2327
- * - Percentiles (median, P95, P99) for outlier detection
3763
+ * Returns per-symbol breakdown and portfolio-wide metrics.
3764
+ * Data is automatically collected from all closed signals for the strategy.
2328
3765
  *
2329
- * @param strategyName - Strategy name to analyze
2330
- * @returns Performance statistics with aggregated metrics
3766
+ * @param strategyName - Strategy name to get heatmap data for
3767
+ * @returns Promise resolving to heatmap statistics object
2331
3768
  *
2332
3769
  * @example
2333
3770
  * ```typescript
2334
- * const stats = await Performance.getData("my-strategy");
3771
+ * const stats = await Heat.getData("my-strategy");
2335
3772
  *
2336
- * // Find slowest operation type
2337
- * const slowest = Object.values(stats.metricStats)
2338
- * .sort((a, b) => b.avgDuration - a.avgDuration)[0];
2339
- * console.log(`Slowest: ${slowest.metricType} (${slowest.avgDuration.toFixed(2)}ms avg)`);
3773
+ * console.log(`Total symbols: ${stats.totalSymbols}`);
3774
+ * console.log(`Portfolio Total PNL: ${stats.portfolioTotalPnl}%`);
3775
+ * console.log(`Portfolio Sharpe Ratio: ${stats.portfolioSharpeRatio}`);
2340
3776
  *
2341
- * // Check for outliers
2342
- * for (const metric of Object.values(stats.metricStats)) {
2343
- * if (metric.p99 > metric.avgDuration * 5) {
2344
- * console.warn(`High variance in ${metric.metricType}: P99=${metric.p99}ms, Avg=${metric.avgDuration}ms`);
2345
- * }
2346
- * }
3777
+ * // Iterate through per-symbol statistics
3778
+ * stats.symbols.forEach(row => {
3779
+ * console.log(`${row.symbol}: ${row.totalPnl}% (${row.totalTrades} trades)`);
3780
+ * });
2347
3781
  * ```
2348
3782
  */
2349
- static getData(strategyName: string): Promise<PerformanceStatistics>;
3783
+ getData: (strategyName: StrategyName) => Promise<IHeatmapStatistics>;
2350
3784
  /**
2351
- * Generates markdown report with performance analysis.
3785
+ * Generates markdown report with portfolio heatmap table for a strategy.
2352
3786
  *
2353
- * Report includes:
2354
- * - Time distribution across operation types
2355
- * - Detailed metrics table with statistics
2356
- * - Percentile analysis for bottleneck detection
3787
+ * Table includes: Symbol, Total PNL, Sharpe Ratio, Max Drawdown, Trades.
3788
+ * Symbols are sorted by Total PNL descending.
2357
3789
  *
2358
- * @param strategyName - Strategy name to generate report for
2359
- * @returns Markdown formatted report string
3790
+ * @param strategyName - Strategy name to generate heatmap report for
3791
+ * @returns Promise resolving to markdown formatted report string
2360
3792
  *
2361
3793
  * @example
2362
3794
  * ```typescript
2363
- * const markdown = await Performance.getReport("my-strategy");
3795
+ * const markdown = await Heat.getReport("my-strategy");
2364
3796
  * console.log(markdown);
2365
- *
2366
- * // Or save to file
2367
- * import fs from "fs/promises";
2368
- * await fs.writeFile("performance-report.md", 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
+ * // ...
2369
3807
  * ```
2370
3808
  */
2371
- static getReport(strategyName: string): Promise<string>;
3809
+ getReport: (strategyName: StrategyName) => Promise<string>;
2372
3810
  /**
2373
- * Saves performance report to disk.
3811
+ * Saves heatmap report to disk for a strategy.
2374
3812
  *
2375
3813
  * Creates directory if it doesn't exist.
2376
- * Default path: ./logs/performance/{strategyName}.md
3814
+ * Default filename: {strategyName}.md
2377
3815
  *
2378
- * @param strategyName - Strategy name to save report for
2379
- * @param path - Optional custom directory path
3816
+ * @param strategyName - Strategy name to save heatmap report for
3817
+ * @param path - Optional directory path to save report (default: "./logs/heatmap")
2380
3818
  *
2381
3819
  * @example
2382
3820
  * ```typescript
2383
- * // Save to default path: ./logs/performance/my-strategy.md
2384
- * await Performance.dump("my-strategy");
3821
+ * // Save to default path: ./logs/heatmap/my-strategy.md
3822
+ * await Heat.dump("my-strategy");
2385
3823
  *
2386
- * // Save to custom path: ./reports/perf/my-strategy.md
2387
- * await Performance.dump("my-strategy", "./reports/perf");
3824
+ * // Save to custom path: ./reports/my-strategy.md
3825
+ * await Heat.dump("my-strategy", "./reports");
2388
3826
  * ```
2389
3827
  */
2390
- static dump(strategyName: string, path?: string): Promise<void>;
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 {
2391
3897
  /**
2392
- * Clears accumulated performance metrics from memory.
3898
+ * Calculates position size using fixed percentage risk method.
2393
3899
  *
2394
- * @param strategyName - Optional strategy name to clear specific strategy's metrics
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.
2395
3913
  *
2396
- * @example
2397
- * ```typescript
2398
- * // Clear specific strategy metrics
2399
- * await Performance.clear("my-strategy");
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.
2400
3928
  *
2401
- * // Clear all metrics for all strategies
2402
- * await Performance.clear();
2403
- * ```
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"
2404
3936
  */
2405
- static clear(strategyName?: string): Promise<void>;
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 };
2406
4018
  }
2407
4019
 
2408
4020
  /**
@@ -2650,6 +4262,7 @@ declare class StrategyConnectionService implements IStrategy {
2650
4262
  private readonly loggerService;
2651
4263
  private readonly executionContextService;
2652
4264
  private readonly strategySchemaService;
4265
+ private readonly riskConnectionService;
2653
4266
  private readonly exchangeConnectionService;
2654
4267
  private readonly methodContextService;
2655
4268
  /**
@@ -2682,97 +4295,327 @@ declare class StrategyConnectionService implements IStrategy {
2682
4295
  */
2683
4296
  backtest: (candles: ICandleData[]) => Promise<IStrategyBacktestResult>;
2684
4297
  /**
2685
- * 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.
2686
4449
  *
2687
- * Delegates to ClientStrategy.stop() which sets internal flag to prevent
2688
- * 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.
2689
4452
  *
2690
- * @param strategyName - Name of strategy to stop
2691
- * @returns Promise that resolves when stop flag is set
4453
+ * @param sizingName - Name of registered sizing schema
4454
+ * @returns Configured ClientSizing instance
2692
4455
  */
2693
- stop: (strategyName: StrategyName) => Promise<void>;
4456
+ getSizing: ((sizingName: SizingName) => ClientSizing) & functools_kit.IClearableMemoize<string> & functools_kit.IControlMemoize<string, ClientSizing>;
2694
4457
  /**
2695
- * Clears the memoized ClientStrategy instance from cache.
4458
+ * Calculates position size based on risk parameters and configured method.
2696
4459
  *
2697
- * Forces re-initialization of strategy on next getStrategy call.
2698
- * 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.
2699
4462
  *
2700
- * @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
2701
4466
  */
2702
- clear: (strategyName: StrategyName) => Promise<void>;
4467
+ calculate: (params: ISizingCalculateParams, context: {
4468
+ sizingName: SizingName;
4469
+ }) => Promise<number>;
2703
4470
  }
2704
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;
2705
4476
  /**
2706
- * Client implementation for backtest timeframe generation.
4477
+ * ClientRisk implementation for portfolio-level risk management.
2707
4478
  *
2708
- * Features:
2709
- * - Generates timestamp arrays for backtest iteration
2710
- * - Singleshot caching prevents redundant generation
2711
- * - Configurable interval spacing (1m to 3d)
2712
- * - 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
2713
4482
  *
2714
- * 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.
2715
4487
  */
2716
- declare class ClientFrame implements IFrame {
2717
- readonly params: IFrameParams;
2718
- constructor(params: IFrameParams);
4488
+ declare class ClientRisk implements IRisk {
4489
+ readonly params: IRiskParams;
2719
4490
  /**
2720
- * Generates timeframe array for backtest period.
2721
- * 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.
2722
4525
  *
2723
- * @param symbol - Trading pair symbol (unused, for API consistency)
2724
- * @returns Promise resolving to array of Date objects
2725
- * @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
2726
4535
  */
2727
- getTimeframe: ((symbol: string) => Promise<Date[]>) & functools_kit.ISingleshotClearable;
4536
+ checkSignal: (params: IRiskCheckArgs) => Promise<boolean>;
2728
4537
  }
2729
4538
 
2730
4539
  /**
2731
- * Connection service routing frame operations to correct ClientFrame instance.
4540
+ * Connection service routing risk operations to correct ClientRisk instance.
2732
4541
  *
2733
- * Routes all IFrame method calls to the appropriate frame implementation
2734
- * based on methodContextService.context.frameName. Uses memoization to cache
2735
- * 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.
2736
4545
  *
2737
4546
  * Key features:
2738
- * - Automatic frame routing via method context
2739
- * - Memoized ClientFrame instances by frameName
2740
- * - Implements IFrame interface
2741
- * - 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
2742
4550
  *
2743
- * Note: frameName is empty string for live mode (no frame constraints).
4551
+ * Note: riskName is empty string for strategies without risk configuration.
2744
4552
  *
2745
4553
  * @example
2746
4554
  * ```typescript
2747
4555
  * // Used internally by framework
2748
- * const timeframe = await frameConnectionService.getTimeframe("BTCUSDT");
2749
- * // 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
+ * );
2750
4569
  * ```
2751
4570
  */
2752
- declare class FrameConnectionService implements IFrame {
4571
+ declare class RiskConnectionService {
2753
4572
  private readonly loggerService;
2754
- private readonly frameSchemaService;
2755
- private readonly methodContextService;
4573
+ private readonly riskSchemaService;
2756
4574
  /**
2757
- * Retrieves memoized ClientFrame instance for given frame name.
4575
+ * Retrieves memoized ClientRisk instance for given risk name.
2758
4576
  *
2759
- * Creates ClientFrame on first call, returns cached instance on subsequent calls.
2760
- * Cache key is frameName string.
4577
+ * Creates ClientRisk on first call, returns cached instance on subsequent calls.
4578
+ * Cache key is riskName string.
2761
4579
  *
2762
- * @param frameName - Name of registered frame schema
2763
- * @returns Configured ClientFrame instance
4580
+ * @param riskName - Name of registered risk schema
4581
+ * @returns Configured ClientRisk instance
2764
4582
  */
2765
- 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>;
2766
4584
  /**
2767
- * Retrieves backtest timeframe boundaries for symbol.
4585
+ * Checks if a signal should be allowed based on risk limits.
2768
4586
  *
2769
- * Returns startDate and endDate from frame configuration.
2770
- * 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.
2771
4589
  *
2772
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
2773
- * @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
2774
4593
  */
2775
- 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>;
2776
4619
  }
2777
4620
 
2778
4621
  /**
@@ -2914,6 +4757,90 @@ declare class FrameGlobalService {
2914
4757
  getTimeframe: (symbol: string) => Promise<Date[]>;
2915
4758
  }
2916
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
+
2917
4844
  /**
2918
4845
  * Service for managing exchange schema registry.
2919
4846
  *
@@ -2954,109 +4881,252 @@ declare class ExchangeSchemaService {
2954
4881
  */
2955
4882
  override: (key: ExchangeName, value: Partial<IExchangeSchema>) => IExchangeSchema;
2956
4883
  /**
2957
- * 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.
2958
5028
  *
2959
- * @param key - Exchange name
2960
- * @returns Exchange schema configuration
2961
- * @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
2962
5032
  */
2963
- get: (key: ExchangeName) => IExchangeSchema;
5033
+ get(key: SizingName): ISizingSchema;
2964
5034
  }
2965
5035
 
2966
5036
  /**
2967
- * Service for managing strategy schema registry.
5037
+ * Service for managing risk schema registry.
2968
5038
  *
2969
5039
  * Uses ToolRegistry from functools-kit for type-safe schema storage.
2970
- * Strategies are registered via addStrategy() and retrieved by name.
5040
+ * Risk profiles are registered via addRisk() and retrieved by name.
2971
5041
  */
2972
- declare class StrategySchemaService {
5042
+ declare class RiskSchemaService {
2973
5043
  readonly loggerService: LoggerService;
2974
5044
  private _registry;
2975
5045
  /**
2976
- * Registers a new strategy schema.
5046
+ * Registers a new risk schema.
2977
5047
  *
2978
- * @param key - Unique strategy name
2979
- * @param value - Strategy schema configuration
2980
- * @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
2981
5051
  */
2982
- register: (key: StrategyName, value: IStrategySchema) => void;
5052
+ register: (key: RiskName, value: IRiskSchema) => void;
2983
5053
  /**
2984
- * Validates strategy schema structure for required properties.
5054
+ * Validates risk schema structure for required properties.
2985
5055
  *
2986
5056
  * Performs shallow validation to ensure all required properties exist
2987
5057
  * and have correct types before registration in the registry.
2988
5058
  *
2989
- * @param strategySchema - Strategy schema to validate
2990
- * @throws Error if strategyName is missing or not a string
2991
- * @throws Error if interval is missing or not a valid SignalInterval
2992
- * @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
2993
5061
  */
2994
5062
  private validateShallow;
2995
5063
  /**
2996
- * Overrides an existing strategy schema with partial updates.
5064
+ * Overrides an existing risk schema with partial updates.
2997
5065
  *
2998
- * @param key - Strategy name to override
5066
+ * @param key - Risk name to override
2999
5067
  * @param value - Partial schema updates
3000
- * @returns Updated strategy schema
3001
- * @throws Error if strategy name doesn't exist
5068
+ * @returns Updated risk schema
5069
+ * @throws Error if risk name doesn't exist
3002
5070
  */
3003
- override: (key: StrategyName, value: Partial<IStrategySchema>) => IStrategySchema;
5071
+ override: (key: RiskName, value: Partial<IRiskSchema>) => IRiskSchema;
3004
5072
  /**
3005
- * Retrieves a strategy schema by name.
5073
+ * Retrieves a risk schema by name.
3006
5074
  *
3007
- * @param key - Strategy name
3008
- * @returns Strategy schema configuration
3009
- * @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
3010
5078
  */
3011
- get: (key: StrategyName) => IStrategySchema;
5079
+ get: (key: RiskName) => IRiskSchema;
3012
5080
  }
3013
5081
 
3014
5082
  /**
3015
- * Service for managing frame schema registry.
5083
+ * Service for managing walker schema registry.
3016
5084
  *
3017
5085
  * Uses ToolRegistry from functools-kit for type-safe schema storage.
3018
- * Frames are registered via addFrame() and retrieved by name.
5086
+ * Walkers are registered via addWalker() and retrieved by name.
3019
5087
  */
3020
- declare class FrameSchemaService {
5088
+ declare class WalkerSchemaService {
3021
5089
  readonly loggerService: LoggerService;
3022
5090
  private _registry;
3023
5091
  /**
3024
- * Registers a new frame schema.
5092
+ * Registers a new walker schema.
3025
5093
  *
3026
- * @param key - Unique frame name
3027
- * @param value - Frame schema configuration
3028
- * @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
3029
5097
  */
3030
- register(key: FrameName, value: IFrameSchema): void;
5098
+ register: (key: WalkerName, value: IWalkerSchema) => void;
3031
5099
  /**
3032
- * Validates frame schema structure for required properties.
5100
+ * Validates walker schema structure for required properties.
3033
5101
  *
3034
5102
  * Performs shallow validation to ensure all required properties exist
3035
5103
  * and have correct types before registration in the registry.
3036
5104
  *
3037
- * @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
3038
5108
  * @throws Error if frameName is missing or not a string
3039
- * @throws Error if interval is missing or not a valid FrameInterval
3040
- * @throws Error if startDate is missing or not a Date
3041
- * @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
3042
5111
  */
3043
5112
  private validateShallow;
3044
5113
  /**
3045
- * Overrides an existing frame schema with partial updates.
5114
+ * Overrides an existing walker schema with partial updates.
3046
5115
  *
3047
- * @param key - Frame name to override
5116
+ * @param key - Walker name to override
3048
5117
  * @param value - Partial schema updates
3049
- * @throws Error if frame name doesn't exist
5118
+ * @returns Updated walker schema
5119
+ * @throws Error if walker name doesn't exist
3050
5120
  */
3051
- override(key: FrameName, value: Partial<IFrameSchema>): IFrameSchema;
5121
+ override: (key: WalkerName, value: Partial<IWalkerSchema>) => IWalkerSchema;
3052
5122
  /**
3053
- * Retrieves a frame schema by name.
5123
+ * Retrieves a walker schema by name.
3054
5124
  *
3055
- * @param key - Frame name
3056
- * @returns Frame schema configuration
3057
- * @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
3058
5128
  */
3059
- get(key: FrameName): IFrameSchema;
5129
+ get: (key: WalkerName) => IWalkerSchema;
3060
5130
  }
3061
5131
 
3062
5132
  /**
@@ -3140,6 +5210,56 @@ declare class LiveLogicPrivateService {
3140
5210
  run(symbol: string): AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
3141
5211
  }
3142
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
+
3143
5263
  /**
3144
5264
  * Public service for backtest orchestration with context management.
3145
5265
  *
@@ -3235,6 +5355,46 @@ declare class LiveLogicPublicService {
3235
5355
  }) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
3236
5356
  }
3237
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
+
3238
5398
  /**
3239
5399
  * Global service providing access to live trading functionality.
3240
5400
  *
@@ -3287,6 +5447,147 @@ declare class BacktestGlobalService {
3287
5447
  }) => AsyncGenerator<IStrategyTickResultClosed, void, unknown>;
3288
5448
  }
3289
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
+
3290
5591
  /**
3291
5592
  * @class ExchangeValidationService
3292
5593
  * Service for managing and validating exchange configurations
@@ -3335,6 +5636,12 @@ declare class StrategyValidationService {
3335
5636
  * Injected logger service instance
3336
5637
  */
3337
5638
  private readonly loggerService;
5639
+ /**
5640
+ * @private
5641
+ * @readonly
5642
+ * Injected risk validation service instance
5643
+ */
5644
+ private readonly riskValidationService;
3338
5645
  /**
3339
5646
  * @private
3340
5647
  * Map storing strategy schemas by strategy name
@@ -3347,9 +5654,10 @@ declare class StrategyValidationService {
3347
5654
  */
3348
5655
  addStrategy: (strategyName: StrategyName, strategySchema: IStrategySchema) => void;
3349
5656
  /**
3350
- * Validates the existence of a strategy
5657
+ * Validates the existence of a strategy and its risk profile (if configured)
3351
5658
  * @public
3352
5659
  * @throws {Error} If strategyName is not found
5660
+ * @throws {Error} If riskName is configured but not found
3353
5661
  * Memoized function to cache validation results
3354
5662
  */
3355
5663
  validate: (strategyName: StrategyName, source: string) => void;
@@ -3398,28 +5706,155 @@ declare class FrameValidationService {
3398
5706
  list: () => Promise<IFrameSchema[]>;
3399
5707
  }
3400
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
+
3401
5821
  declare const backtest: {
3402
5822
  exchangeValidationService: ExchangeValidationService;
3403
5823
  strategyValidationService: StrategyValidationService;
3404
5824
  frameValidationService: FrameValidationService;
5825
+ walkerValidationService: WalkerValidationService;
5826
+ sizingValidationService: SizingValidationService;
5827
+ riskValidationService: RiskValidationService;
3405
5828
  backtestMarkdownService: BacktestMarkdownService;
3406
5829
  liveMarkdownService: LiveMarkdownService;
3407
5830
  performanceMarkdownService: PerformanceMarkdownService;
5831
+ walkerMarkdownService: WalkerMarkdownService;
5832
+ heatMarkdownService: HeatMarkdownService;
3408
5833
  backtestLogicPublicService: BacktestLogicPublicService;
3409
5834
  liveLogicPublicService: LiveLogicPublicService;
5835
+ walkerLogicPublicService: WalkerLogicPublicService;
3410
5836
  backtestLogicPrivateService: BacktestLogicPrivateService;
3411
5837
  liveLogicPrivateService: LiveLogicPrivateService;
5838
+ walkerLogicPrivateService: WalkerLogicPrivateService;
3412
5839
  exchangeGlobalService: ExchangeGlobalService;
3413
5840
  strategyGlobalService: StrategyGlobalService;
3414
5841
  frameGlobalService: FrameGlobalService;
3415
5842
  liveGlobalService: LiveGlobalService;
3416
5843
  backtestGlobalService: BacktestGlobalService;
5844
+ walkerGlobalService: WalkerGlobalService;
5845
+ sizingGlobalService: SizingGlobalService;
5846
+ riskGlobalService: RiskGlobalService;
3417
5847
  exchangeSchemaService: ExchangeSchemaService;
3418
5848
  strategySchemaService: StrategySchemaService;
3419
5849
  frameSchemaService: FrameSchemaService;
5850
+ walkerSchemaService: WalkerSchemaService;
5851
+ sizingSchemaService: SizingSchemaService;
5852
+ riskSchemaService: RiskSchemaService;
3420
5853
  exchangeConnectionService: ExchangeConnectionService;
3421
5854
  strategyConnectionService: StrategyConnectionService;
3422
5855
  frameConnectionService: FrameConnectionService;
5856
+ sizingConnectionService: SizingConnectionService;
5857
+ riskConnectionService: RiskConnectionService;
3423
5858
  executionContextService: {
3424
5859
  readonly context: IExecutionContext;
3425
5860
  };
@@ -3429,4 +5864,4 @@ declare const backtest: {
3429
5864
  loggerService: LoggerService;
3430
5865
  };
3431
5866
 
3432
- 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, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatistics, 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, listenPerformance, 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 };