From 6376e13b077ebb004fdae56eb8692cd1db5465f4 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Tue, 25 Nov 2025 02:12:57 +0700 Subject: [PATCH] Add Bollinger Bands Volatility Protection indicator support - Introduced BollingerBandsVolatilityProtection indicator in GeneticService with configuration settings for period and standard deviation (stdev). - Updated ScenarioHelpers to handle creation and validation of the new indicator type. - Enhanced CustomScenario, backtest, and scenario pages to include BollingerBandsVolatilityProtection in indicator lists and parameter mappings. - Modified API and types to reflect the addition of the new indicator in relevant enums and mappings. - Updated frontend components to support new parameters and visualization for Bollinger Bands. --- .cursor/commands/build-indicator.md | 106 +++++++++++- .../Controllers/BacktestController.cs | 10 ++ .../Backtests/BacktestJobService.cs | 12 +- src/Managing.Application/GeneticService.cs | 23 ++- .../Grains/BundleBacktestGrain.cs | 1 - .../Workers/BundleBacktestWorker.cs | 1 - src/Managing.Common/Enums.cs | 1 + .../Backtests/IndicatorRequest.cs | 46 +++++ .../Indicators/Base/BollingerBandsBase.cs | 162 ++++++++++++++++++ .../BollingerBandsVolatilityProtection.cs | 71 ++++++++ .../Indicators/IndicatorBase.cs | 2 + .../Indicators/LightIndicator.cs | 25 +-- .../BollingerBandsPercentBMomentumBreakout.cs | 149 +--------------- .../Scenarios/ScenarioHelpers.cs | 62 ++++++- .../Shared/Helpers/TradingBox.cs | 2 +- .../CustomScenario/CustomScenario.tsx | 10 +- .../Trading/TradeChart/TradeChart.tsx | 111 ++++++++---- .../UnifiedTradingModal.tsx | 9 + .../src/generated/ManagingApi.ts | 12 ++ .../src/generated/ManagingApiTypes.ts | 12 ++ .../pages/backtestPage/BundleRequestModal.tsx | 11 +- 21 files changed, 618 insertions(+), 220 deletions(-) create mode 100644 src/Managing.Domain/Indicators/Base/BollingerBandsBase.cs create mode 100644 src/Managing.Domain/Indicators/Context/BollingerBandsVolatilityProtection.cs diff --git a/.cursor/commands/build-indicator.md b/.cursor/commands/build-indicator.md index 676fb5f4..326197da 100644 --- a/.cursor/commands/build-indicator.md +++ b/.cursor/commands/build-indicator.md @@ -44,6 +44,17 @@ Oversold Threshold: 20 Overbought Threshold: 80 ``` +**Bollinger Bands Example:** +``` +Type: Context +Label: Bollinger Bands Volatility Protection +Core Logic: Uses the Bandwidth (distance between Upper and Lower bands) to measure market volatility and apply veto filters during extreme conditions. +Context Confidence Levels: Block signals when bandwidth is extremely high (>0.15) or low (<0.02), validate when normal (0.02-0.15). +Parameters: +Period (default: 20) +StDev (default: 2.0) +``` + ### Step 2: Determine Implementation Details **Check Existing Indicators:** @@ -103,13 +114,26 @@ public class {IndicatorName} : IndicatorBase base(name, IndicatorType.{EnumName}) { Signals = new List(); - // Initialize parameters + // Initialize parameters (e.g., Period, Multiplier, StDev) } // Implementation methods... } ``` +**For Bollinger Bands (use shared base):** +```csharp +public class {IndicatorName} : BollingerBandsBase +{ + public {IndicatorName}(string name, int period, double stdev) : + base(name, IndicatorType.{EnumName}, period, stdev) + { + } + + // Only implement ProcessBollingerBandsSignals method +} +``` + **Shared Base Class Pattern (use only if mapping is shared):** If another indicator uses the same candle result mapping, extend from a shared base class: @@ -174,32 +198,69 @@ public enum IndicatorType } ``` +**Update IndicatorBase.cs:** +- Add any new parameter properties needed (e.g., `StDev` for Bollinger Bands) + +**Update LightIndicator.cs:** +- Add any new parameter properties with proper Id attributes for Orleans serialization +- Update `LightToBase()` method to copy new properties + +**Update IndicatorRequest.cs:** +- Add any new parameter properties to match LightIndicator + **Update ScenarioHelpers.cs:** - Add case to `BuildIndicator()` method: `IndicatorType.{EnumName} => new {IndicatorName}(indicator.Name, {parameters})` - Add case to `GetSignalType()` method: `IndicatorType.{EnumName} => SignalType.{Type}` -- Add parameters to `BuildIndicator()` overload if needed +- Add parameter validation in `BuildIndicator()` method switch statement +- Add new parameters to `BuildIndicator()` method signature if needed +- Update `BaseToLight()` method to copy all LightIndicator properties + +**Update BacktestJobService.cs:** +- Update LightIndicator creation in bundle job creation to include all new properties +- Ensure all indicator parameters are properly mapped from requests **Update GeneticService.cs:** - Add default values to `DefaultIndicatorValues`: `[IndicatorType.{EnumName}] = new() { {param_mappings} }` - Add parameter ranges to `IndicatorParameterRanges`: `[IndicatorType.{EnumName}] = new() { {param_ranges} }` - Add parameter mapping to `IndicatorParamMapping`: `[IndicatorType.{EnumName}] = [{param_names}]` +- Update `TradingBotChromosome.GetSelectedIndicators()` to handle new parameters + +**Update Frontend Files:** + +*CustomScenario.tsx:* +- Add new parameters to indicator type definitions +- Update parameter input handling (float vs int parsing) +- Add default values for new parameters + +*TradeChart.tsx (if applicable):* +- Add visualization logic for new indicator bands/lines +- Use appropriate colors and styles for differentiation ### Step 5: Test and Validate **Compile Check:** ```bash +# Backend compilation dotnet build + +# Frontend compilation +cd src/Managing.WebApp && npm run build ``` **Basic Validation:** - Verify indicator appears in GeneticService configurations - Check that BuildIndicator methods work correctly - Ensure proper SignalType assignment +- Verify LightIndicator serialization works (Orleans Id attributes) +- Check parameter validation in ScenarioHelpers.BuildIndicator +- Confirm frontend parameter handling works correctly **Integration Test:** - Create a simple backtest using the new indicator - Verify signals are generated correctly - Check parameter handling and validation +- Test frontend scenario creation with new parameters +- Verify chart visualization displays correctly (if applicable) ## Available Skender.Stock.Indicators @@ -298,7 +359,7 @@ When implementing a new indicator, search the [Skender documentation](https://do - **Moving Averages**: Period 5-300 (shorter = responsive, longer = smooth) - **Oscillators**: Period 5-50 (RSI: 14, Stoch: 14, CCI: 20) - **Trend Following**: Period 10-50, Multiplier 1.0-5.0 -- **Volatility**: Period 5-50, Standard Deviations 1.0-3.0 +- **Volatility**: Period 5-50, Standard Deviations (StDev) 1.0-3.0 (Bollinger Bands) - **Volume**: Period 5-50 (OBV uses no period) **Testing Parameters:** @@ -343,6 +404,7 @@ When creating your `Candle{Indicator}` mapping class, include all relevant resul - `GetStc(cyclePeriod, fastPeriod, slowPeriod)` → `StcResult` - Used in STC, Lagging STC - `GetStdDev(period)` → `StdDevResult` - Used in StDev Context - `GetChandelier(period, multiplier, type)` → `ChandelierResult` - Used in Chandelier Exit +- `GetBollingerBands(period, stdev)` → `BollingerBandsResult` - Used in Bollinger Bands indicators - `GetAdx(period)` → `AdxResult` - Used in SuperTrend Cross EMA **Available But Unused:** @@ -390,6 +452,31 @@ public class StochasticFiltered : StochasticBase { /* Specific logic */ } public class AnotherStochasticIndicator : StochasticBase { /* Specific logic */ } ``` +**Bollinger Bands Example (Implemented):** +```csharp +public abstract class BollingerBandsBase : IndicatorBase +{ + protected double Stdev { get; set; } + + protected BollingerBandsBase(string name, IndicatorType type, int period, double stdev) + : base(name, type) + { + Stdev = stdev; + Period = period; + } + + protected virtual IEnumerable MapBollingerBandsToCandle( + IEnumerable bbResults, IEnumerable candles) + { + // Shared Bollinger Bands mapping logic with all properties + // (Sma, UpperBand, LowerBand, PercentB, ZScore, Width) + } +} + +public class BollingerBandsPercentBMomentumBreakout : BollingerBandsBase { /* %B momentum logic */ } +public class BollingerBandsVolatilityProtection : BollingerBandsBase { /* Volatility protection logic */ } +``` + **When NOT to Use:** - Indicators have different result types (Stoch vs StochRsi) - Mapping logic differs significantly @@ -410,6 +497,13 @@ public class AnotherStochasticIndicator : StochasticBase { /* Specific logic */ - [ ] Constructor parameters match IIndicator interface - [ ] SignalType correctly assigned - [ ] Enum added to IndicatorType -- [ ] BuildIndicator methods updated -- [ ] GeneticService configurations updated -- [ ] Compiles without errors +- [ ] IndicatorBase.cs properties added if needed +- [ ] LightIndicator.cs properties added with proper Id attributes +- [ ] IndicatorRequest.cs properties added +- [ ] ScenarioHelpers.cs BuildIndicator and BaseToLight methods updated +- [ ] BacktestJobService.cs LightIndicator mapping updated +- [ ] GeneticService.cs configurations updated (defaults, ranges, mappings) +- [ ] Frontend CustomScenario.tsx updated for new parameters +- [ ] Frontend TradeChart.tsx updated for visualization if needed +- [ ] Compiles without errors (backend and frontend) +- [ ] TypeScript types properly aligned diff --git a/src/Managing.Api/Controllers/BacktestController.cs b/src/Managing.Api/Controllers/BacktestController.cs index 63a5a24e..751b12f3 100644 --- a/src/Managing.Api/Controllers/BacktestController.cs +++ b/src/Managing.Api/Controllers/BacktestController.cs @@ -666,6 +666,16 @@ public class BacktestController : BaseController } } + // Normalize SignalType for all indicators based on their IndicatorType + // This ensures the correct SignalType is saved regardless of what the frontend sent + if (request.UniversalConfig.Scenario?.Indicators != null) + { + foreach (var indicator in request.UniversalConfig.Scenario.Indicators) + { + indicator.SignalType = ScenarioHelpers.GetSignalType(indicator.Type); + } + } + // Create the bundle backtest request var bundleRequest = new BundleBacktestRequest { diff --git a/src/Managing.Application/Backtests/BacktestJobService.cs b/src/Managing.Application/Backtests/BacktestJobService.cs index 10214f20..2e61076a 100644 --- a/src/Managing.Application/Backtests/BacktestJobService.cs +++ b/src/Managing.Application/Backtests/BacktestJobService.cs @@ -155,16 +155,24 @@ public class JobService { Indicators = sReq.Indicators?.Select(ind => new LightIndicator(ind.Name, ind.Type) { - SignalType = ind.SignalType, MinimumHistory = ind.MinimumHistory, Period = ind.Period, FastPeriods = ind.FastPeriods, SlowPeriods = ind.SlowPeriods, SignalPeriods = ind.SignalPeriods, Multiplier = ind.Multiplier, + StDev = ind.StDev, SmoothPeriods = ind.SmoothPeriods, StochPeriods = ind.StochPeriods, - CyclePeriods = ind.CyclePeriods + CyclePeriods = ind.CyclePeriods, + KFactor = ind.KFactor, + DFactor = ind.DFactor, + TenkanPeriods = ind.TenkanPeriods, + KijunPeriods = ind.KijunPeriods, + SenkouBPeriods = ind.SenkouBPeriods, + OffsetPeriods = ind.OffsetPeriods, + SenkouOffset = ind.SenkouOffset, + ChikouOffset = ind.ChikouOffset }).ToList() ?? new List() }; } diff --git a/src/Managing.Application/GeneticService.cs b/src/Managing.Application/GeneticService.cs index dc5e2be5..b6173064 100644 --- a/src/Managing.Application/GeneticService.cs +++ b/src/Managing.Application/GeneticService.cs @@ -107,7 +107,12 @@ public class GeneticService : IGeneticService [IndicatorType.BollingerBandsPercentBMomentumBreakout] = new() { ["period"] = 20.0, - ["multiplier"] = 2.0 + ["stdev"] = 2.0 + }, + [IndicatorType.BollingerBandsVolatilityProtection] = new() + { + ["period"] = 20.0, + ["stdev"] = 2.0 }, [IndicatorType.IchimokuKumoTrend] = new() { @@ -202,7 +207,12 @@ public class GeneticService : IGeneticService [IndicatorType.BollingerBandsPercentBMomentumBreakout] = new() { ["period"] = (5.0, 50.0), - ["multiplier"] = (1.0, 5.0) + ["stdev"] = (1.0, 5.0) + }, + [IndicatorType.BollingerBandsVolatilityProtection] = new() + { + ["period"] = (10.0, 50.0), + ["stdev"] = (1.5, 3.0) }, [IndicatorType.IchimokuKumoTrend] = new() { @@ -231,7 +241,8 @@ public class GeneticService : IGeneticService [IndicatorType.StochasticCross] = ["stochPeriods", "signalPeriods", "smoothPeriods", "kFactor", "dFactor"], [IndicatorType.Stc] = ["cyclePeriods", "fastPeriods", "slowPeriods"], [IndicatorType.LaggingStc] = ["cyclePeriods", "fastPeriods", "slowPeriods"], - [IndicatorType.BollingerBandsPercentBMomentumBreakout] = ["period", "multiplier"], + [IndicatorType.BollingerBandsPercentBMomentumBreakout] = ["period", "stdev"], + [IndicatorType.BollingerBandsVolatilityProtection] = ["period", "stdev"], [IndicatorType.IchimokuKumoTrend] = ["tenkanPeriods", "kijunPeriods", "senkouBPeriods", "offsetPeriods"] }; @@ -860,10 +871,10 @@ public class TradingBotChromosome : ChromosomeBase indicator.SignalPeriods = Convert.ToInt32(genes[baseIndex + 3].Value); } - paramName = GetParameterName(4); // multiplier + paramName = GetParameterName(4); // stdev if (paramName != null && HasParameter(_eligibleIndicators[i], paramName)) { - indicator.Multiplier = Convert.ToDouble(genes[baseIndex + 4].Value); + indicator.StDev = Convert.ToDouble(genes[baseIndex + 4].Value); } paramName = GetParameterName(5); // stochPeriods @@ -983,7 +994,7 @@ public class TradingBotChromosome : ChromosomeBase 1 => "fastPeriods", 2 => "slowPeriods", 3 => "signalPeriods", - 4 => "multiplier", + 4 => "stdev", 5 => "stochPeriods", 6 => "smoothPeriods", 7 => "cyclePeriods", diff --git a/src/Managing.Application/Grains/BundleBacktestGrain.cs b/src/Managing.Application/Grains/BundleBacktestGrain.cs index 882b2cfe..4749113f 100644 --- a/src/Managing.Application/Grains/BundleBacktestGrain.cs +++ b/src/Managing.Application/Grains/BundleBacktestGrain.cs @@ -324,7 +324,6 @@ public class BundleBacktestGrain : Grain, IBundleBacktestGrain, IRemindable { Indicators = sReq.Indicators?.Select(i => new LightIndicator(i.Name, i.Type) { - SignalType = i.SignalType, MinimumHistory = i.MinimumHistory, Period = i.Period, FastPeriods = i.FastPeriods, diff --git a/src/Managing.Application/Workers/BundleBacktestWorker.cs b/src/Managing.Application/Workers/BundleBacktestWorker.cs index 89fe2fdf..47c6f404 100644 --- a/src/Managing.Application/Workers/BundleBacktestWorker.cs +++ b/src/Managing.Application/Workers/BundleBacktestWorker.cs @@ -229,7 +229,6 @@ public class BundleBacktestWorker : BaseWorker { Indicators = sReq.Indicators?.Select(i => new LightIndicator(i.Name, i.Type) { - SignalType = i.SignalType, MinimumHistory = i.MinimumHistory, Period = i.Period, FastPeriods = i.FastPeriods, diff --git a/src/Managing.Common/Enums.cs b/src/Managing.Common/Enums.cs index 0058d88d..9666052d 100644 --- a/src/Managing.Common/Enums.cs +++ b/src/Managing.Common/Enums.cs @@ -67,6 +67,7 @@ public static class Enums SuperTrendCrossEma, DualEmaCross, BollingerBandsPercentBMomentumBreakout, + BollingerBandsVolatilityProtection, IchimokuKumoTrend } diff --git a/src/Managing.Domain/Backtests/IndicatorRequest.cs b/src/Managing.Domain/Backtests/IndicatorRequest.cs index e64779a1..caa74d71 100644 --- a/src/Managing.Domain/Backtests/IndicatorRequest.cs +++ b/src/Managing.Domain/Backtests/IndicatorRequest.cs @@ -56,6 +56,11 @@ public class IndicatorRequest /// public double? Multiplier { get; set; } + /// + /// Standard deviation parameter for indicators like Bollinger Bands + /// + public double? StDev { get; set; } + /// /// Smooth periods parameter /// @@ -70,4 +75,45 @@ public class IndicatorRequest /// Cycle periods parameter /// public int? CyclePeriods { get; set; } + + /// + /// K factor parameter for Stochastic Cross + /// + public double? KFactor { get; set; } + + /// + /// D factor parameter for Stochastic Cross + /// + public double? DFactor { get; set; } + + // Ichimoku-specific parameters + /// + /// Tenkan periods for Ichimoku + /// + public int? TenkanPeriods { get; set; } + + /// + /// Kijun periods for Ichimoku + /// + public int? KijunPeriods { get; set; } + + /// + /// Senkou B periods for Ichimoku + /// + public int? SenkouBPeriods { get; set; } + + /// + /// Offset periods for Ichimoku + /// + public int? OffsetPeriods { get; set; } + + /// + /// Senkou offset for Ichimoku + /// + public int? SenkouOffset { get; set; } + + /// + /// Chikou offset for Ichimoku + /// + public int? ChikouOffset { get; set; } } \ No newline at end of file diff --git a/src/Managing.Domain/Indicators/Base/BollingerBandsBase.cs b/src/Managing.Domain/Indicators/Base/BollingerBandsBase.cs new file mode 100644 index 00000000..19976ec5 --- /dev/null +++ b/src/Managing.Domain/Indicators/Base/BollingerBandsBase.cs @@ -0,0 +1,162 @@ +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Domain.Indicators; +using Managing.Domain.Shared.Rules; +using Skender.Stock.Indicators; +using static Managing.Common.Enums; + +namespace Managing.Domain.Strategies.Base; + +public abstract class BollingerBandsBase : IndicatorBase +{ + public List Signals { get; set; } + + protected BollingerBandsBase(string name, IndicatorType type, int period, double stdev) : base(name, type) + { + Signals = new List(); + Period = period; + StDev = stdev; + } + + public override List Run(HashSet candles) + { + if (candles.Count <= Period) + { + return null; + } + + try + { + var bbResults = candles + .GetBollingerBands(Period.Value, StDev.Value) + .RemoveWarmupPeriods() + .ToList(); + + if (bbResults.Count == 0) + return null; + + ProcessBollingerBandsSignals(bbResults, candles); + + return Signals.Where(s => s.Confidence != Confidence.None).ToList(); + } + catch (RuleException) + { + return null; + } + } + + public override List Run(HashSet candles, IndicatorsResultBase preCalculatedValues) + { + if (candles.Count <= Period) + { + return null; + } + + try + { + // Use pre-calculated Bollinger Bands values if available + List bbResults = null; + if (preCalculatedValues?.BollingerBands != null && preCalculatedValues.BollingerBands.Any()) + { + // Filter pre-calculated values to match the candles we're processing + bbResults = preCalculatedValues.BollingerBands + .Where(bb => bb.UpperBand.HasValue && bb.LowerBand.HasValue && bb.Sma.HasValue && + candles.Any(c => c.Date == bb.Date)) + .ToList(); + } + + // If no pre-calculated values or they don't match, fall back to regular calculation + if (bbResults == null || !bbResults.Any()) + { + return Run(candles); + } + + ProcessBollingerBandsSignals(bbResults, candles); + + return Signals.Where(s => s.Confidence != Confidence.None).ToList(); + } + catch (RuleException) + { + return null; + } + } + + public override IndicatorsResultBase GetIndicatorValues(HashSet candles) + { + return new IndicatorsResultBase() + { + BollingerBands = candles.GetBollingerBands(Period.Value, StDev.Value) + .ToList() + }; + } + + /// + /// Abstract method for processing Bollinger Bands signals - implemented by child classes + /// + protected abstract void ProcessBollingerBandsSignals(List bbResults, HashSet candles); + + /// + /// Maps Bollinger Bands results to candle objects with all BollingerBandsResult properties + /// + protected virtual IEnumerable MapBollingerBandsToCandle(IEnumerable bbResults, IEnumerable candles) + { + var bbCandles = new List(); + foreach (var candle in candles) + { + var currentBB = bbResults.Find(candle.Date); + if (currentBB != null && currentBB.Sma.HasValue) + { + bbCandles.Add(new CandleBollingerBands() + { + Close = candle.Close, + Open = candle.Open, + Date = candle.Date, + Ticker = candle.Ticker, + Exchange = candle.Exchange, + Sma = currentBB.Sma, + UpperBand = currentBB.UpperBand, + LowerBand = currentBB.LowerBand, + PercentB = currentBB.PercentB, + ZScore = currentBB.ZScore, + Width = currentBB.Width + }); + } + } + + return bbCandles; + } + + /// + /// Shared method for adding signals with duplicate prevention + /// + protected void AddSignal(Candle candleSignal, TradeDirection direction, Confidence confidence) + { + var signal = new LightSignal( + candleSignal.Ticker, + direction, + confidence, + candleSignal, + candleSignal.Date, + candleSignal.Exchange, + Type, + SignalType, + Name); + if (!Signals.Any(s => s.Identifier == signal.Identifier)) + { + Signals.AddItem(signal); + } + } + + /// + /// Base candle class for Bollinger Bands indicators with all BollingerBandsResult properties + /// + public class CandleBollingerBands : Candle + { + public double? Sma { get; internal set; } + public double? UpperBand { get; internal set; } + public double? LowerBand { get; internal set; } + public double? PercentB { get; internal set; } + public double? ZScore { get; internal set; } + public double? Width { get; internal set; } + } +} diff --git a/src/Managing.Domain/Indicators/Context/BollingerBandsVolatilityProtection.cs b/src/Managing.Domain/Indicators/Context/BollingerBandsVolatilityProtection.cs new file mode 100644 index 00000000..72ba4d62 --- /dev/null +++ b/src/Managing.Domain/Indicators/Context/BollingerBandsVolatilityProtection.cs @@ -0,0 +1,71 @@ +using Managing.Domain.Candles; +using Managing.Domain.Strategies.Base; +using Skender.Stock.Indicators; +using static Managing.Common.Enums; + +namespace Managing.Domain.Strategies.Context; + +public class BollingerBandsVolatilityProtection : BollingerBandsBase +{ + public BollingerBandsVolatilityProtection(string name, int period, double stdev) : base(name, IndicatorType.BollingerBandsVolatilityProtection, period, stdev) + { + } + + /// + /// Processes Bollinger Bands volatility protection signals based on bandwidth analysis. + /// This method applies a veto filter during periods of market extremes: + /// - Blocks signals when bandwidth is extremely high (dangerous expansion) or extremely low (dead market) + /// - Validates signals when bandwidth is normal to low (excluding squeeze extremes) + /// Bandwidth = (UpperBand - LowerBand) / Sma represents market volatility + /// + /// List of Bollinger Bands calculation results + /// Candles to process + protected override void ProcessBollingerBandsSignals(List bbResults, HashSet candles) + { + var bbCandles = MapBollingerBandsToCandle(bbResults, candles.TakeLast(Period.Value)).ToList(); + + if (bbCandles.Count == 0) + return; + + foreach (var currentCandle in bbCandles) + { + var width = currentCandle.Width ?? 0; + + // Determine confidence based on width levels (bandwidth as % of SMA) + // Lower width = less volatility = higher confidence for trading + // Higher width = more volatility = lower confidence for trading + Confidence confidence; + + if (width >= 0.15) // Extremely high volatility - dangerous expansion + { + // Block all signals during dangerous volatility expansion + confidence = Confidence.None; + } + else if (width <= 0.02) // Extremely low volatility - dead market/squeeze + { + // Block all signals in dead markets or extreme squeezes + confidence = Confidence.None; + } + else if (width <= 0.05) // Low to normal volatility - good for trading + { + // Validate signals during low volatility trending conditions + confidence = Confidence.High; + } + else if (width <= 0.10) // Normal volatility - acceptable for trading + { + // Validate signals during normal volatility conditions + confidence = Confidence.Medium; + } + else // Moderate high volatility (0.10 - 0.15) + { + // Lower confidence during elevated but not extreme volatility + confidence = Confidence.Low; + } + + // Context strategies always return TradeDirection.None + // The confidence level indicates the quality of market conditions + AddSignal(currentCandle, TradeDirection.None, confidence); + } + } + +} diff --git a/src/Managing.Domain/Indicators/IndicatorBase.cs b/src/Managing.Domain/Indicators/IndicatorBase.cs index 5d34db52..792e05b8 100644 --- a/src/Managing.Domain/Indicators/IndicatorBase.cs +++ b/src/Managing.Domain/Indicators/IndicatorBase.cs @@ -33,6 +33,8 @@ namespace Managing.Domain.Strategies public double? Multiplier { get; set; } + public double? StDev { get; set; } + public int? SmoothPeriods { get; set; } public int? StochPeriods { get; set; } diff --git a/src/Managing.Domain/Indicators/LightIndicator.cs b/src/Managing.Domain/Indicators/LightIndicator.cs index d1794bd1..1e8a35a9 100644 --- a/src/Managing.Domain/Indicators/LightIndicator.cs +++ b/src/Managing.Domain/Indicators/LightIndicator.cs @@ -36,23 +36,25 @@ public class LightIndicator [Id(8)] public double? Multiplier { get; set; } - [Id(9)] public int? SmoothPeriods { get; set; } + [Id(9)] public double? StDev { get; set; } - [Id(10)] public int? StochPeriods { get; set; } + [Id(10)] public int? SmoothPeriods { get; set; } - [Id(11)] public int? CyclePeriods { get; set; } + [Id(11)] public int? StochPeriods { get; set; } - [Id(12)] public double? KFactor { get; set; } + [Id(12)] public int? CyclePeriods { get; set; } - [Id(13)] public double? DFactor { get; set; } + [Id(13)] public double? KFactor { get; set; } + + [Id(14)] public double? DFactor { get; set; } // Ichimoku-specific parameters - [Id(14)] public int? TenkanPeriods { get; set; } - [Id(15)] public int? KijunPeriods { get; set; } - [Id(16)] public int? SenkouBPeriods { get; set; } - [Id(17)] public int? OffsetPeriods { get; set; } - [Id(18)] public int? SenkouOffset { get; set; } - [Id(19)] public int? ChikouOffset { get; set; } + [Id(15)] public int? TenkanPeriods { get; set; } + [Id(16)] public int? KijunPeriods { get; set; } + [Id(17)] public int? SenkouBPeriods { get; set; } + [Id(18)] public int? OffsetPeriods { get; set; } + [Id(19)] public int? SenkouOffset { get; set; } + [Id(20)] public int? ChikouOffset { get; set; } /// /// Converts a LightIndicator back to a full Indicator @@ -74,6 +76,7 @@ public class LightIndicator SlowPeriods = SlowPeriods, SignalPeriods = SignalPeriods, Multiplier = Multiplier, + StDev = StDev, SmoothPeriods = SmoothPeriods, StochPeriods = StochPeriods, CyclePeriods = CyclePeriods, diff --git a/src/Managing.Domain/Indicators/Signals/BollingerBandsPercentBMomentumBreakout.cs b/src/Managing.Domain/Indicators/Signals/BollingerBandsPercentBMomentumBreakout.cs index 8344e89b..285dab66 100644 --- a/src/Managing.Domain/Indicators/Signals/BollingerBandsPercentBMomentumBreakout.cs +++ b/src/Managing.Domain/Indicators/Signals/BollingerBandsPercentBMomentumBreakout.cs @@ -1,88 +1,17 @@ -using Managing.Core; using Managing.Domain.Candles; -using Managing.Domain.Indicators; -using Managing.Domain.Shared.Rules; using Managing.Domain.Strategies.Base; using Skender.Stock.Indicators; using static Managing.Common.Enums; namespace Managing.Domain.Strategies.Signals; -public class BollingerBandsPercentBMomentumBreakout : IndicatorBase +public class BollingerBandsPercentBMomentumBreakout : BollingerBandsBase { - public List Signals { get; set; } - public BollingerBandsPercentBMomentumBreakout( string name, int period, - double stdDev) : base(name, IndicatorType.BollingerBandsPercentBMomentumBreakout) + double stdev) : base(name, IndicatorType.BollingerBandsPercentBMomentumBreakout, period, stdev) { - Signals = new List(); - Period = period; - Multiplier = stdDev; // Using Multiplier property for stdDev since it's a double - } - - public override List Run(HashSet candles) - { - if (candles.Count <= 10 * Period.Value + 50) - { - return null; - } - - try - { - var bbResults = candles - .GetBollingerBands(Period.Value, (double)Multiplier.Value) - .RemoveWarmupPeriods() - .ToList(); - - if (bbResults.Count == 0) - return null; - - ProcessBollingerBandsSignals(bbResults, candles); - - return Signals.ToList(); - } - catch (RuleException) - { - return null; - } - } - - public override List Run(HashSet candles, IndicatorsResultBase preCalculatedValues) - { - if (candles.Count <= 10 * Period.Value + 50) - { - return null; - } - - try - { - // Use pre-calculated Bollinger Bands values if available - List bbResults = null; - if (preCalculatedValues?.BollingerBands != null && preCalculatedValues.BollingerBands.Any()) - { - // Filter pre-calculated values to match the candles we're processing - bbResults = preCalculatedValues.BollingerBands - .Where(bb => bb.UpperBand.HasValue && bb.LowerBand.HasValue && bb.Sma.HasValue && - candles.Any(c => c.Date == bb.Date)) - .ToList(); - } - - // If no pre-calculated values or they don't match, fall back to regular calculation - if (bbResults == null || !bbResults.Any()) - { - return Run(candles); - } - - ProcessBollingerBandsSignals(bbResults, candles); - - return Signals.ToList(); - } - catch (RuleException) - { - return null; - } } /// @@ -90,9 +19,9 @@ public class BollingerBandsPercentBMomentumBreakout : IndicatorBase /// Long signals: %B crosses above 0.8 after being below (strong upward momentum) /// Short signals: %B crosses below 0.2 after being above (strong downward momentum) /// - private void ProcessBollingerBandsSignals(List bbResults, HashSet candles) + protected override void ProcessBollingerBandsSignals(List bbResults, HashSet candles) { - var bbCandles = MapBollingerBandsToCandle(bbResults, candles.TakeLast(Period.Value)); + var bbCandles = MapBollingerBandsToCandle(bbResults, candles.TakeLast(Period.Value)).ToList(); if (bbCandles.Count < 2) return; @@ -116,74 +45,4 @@ public class BollingerBandsPercentBMomentumBreakout : IndicatorBase } } - public override IndicatorsResultBase GetIndicatorValues(HashSet candles) - { - return new IndicatorsResultBase() - { - BollingerBands = candles.GetBollingerBands(Period.Value, (double)Multiplier.Value) - .ToList() - }; - } - - private List MapBollingerBandsToCandle(IEnumerable bbResults, IEnumerable candles) - { - var bbCandles = new List(); - foreach (var candle in candles) - { - var currentBB = bbResults.Find(candle.Date); - if (currentBB != null && currentBB.UpperBand.HasValue && currentBB.LowerBand.HasValue && currentBB.Sma.HasValue) - { - // Calculate %B = (Price - LowerBand) / (UpperBand - LowerBand) - var price = (double)candle.Close; - var upperBand = (double)currentBB.UpperBand.Value; - var lowerBand = (double)currentBB.LowerBand.Value; - var percentB = (double)currentBB.PercentB.Value; - - // Avoid division by zero - if (upperBand != lowerBand) - { - bbCandles.Add(new CandleBollingerBands() - { - Close = candle.Close, - Open = candle.Open, - Date = candle.Date, - Ticker = candle.Ticker, - Exchange = candle.Exchange, - PercentB = percentB, - UpperBand = upperBand, - LowerBand = lowerBand, - Sma = currentBB.Sma.Value - }); - } - } - } - - return bbCandles; - } - - private void AddSignal(CandleBollingerBands candleSignal, TradeDirection direction, Confidence confidence) - { - var signal = new LightSignal( - candleSignal.Ticker, - direction, - confidence, - candleSignal, - candleSignal.Date, - candleSignal.Exchange, - Type, - SignalType, - Name); - if (!Signals.Any(s => s.Identifier == signal.Identifier)) - { - Signals.AddItem(signal); - } - } - - private class CandleBollingerBands : Candle - { - public double PercentB { get; internal set; } - public double UpperBand { get; internal set; } - public double LowerBand { get; internal set; } - public double Sma { get; internal set; } - } } diff --git a/src/Managing.Domain/Scenarios/ScenarioHelpers.cs b/src/Managing.Domain/Scenarios/ScenarioHelpers.cs index 216d9567..52ac8975 100644 --- a/src/Managing.Domain/Scenarios/ScenarioHelpers.cs +++ b/src/Managing.Domain/Scenarios/ScenarioHelpers.cs @@ -100,8 +100,11 @@ public static class ScenarioHelpers indicator.FastPeriods.Value, indicator.SlowPeriods.Value), IndicatorType.SuperTrendCrossEma => new SuperTrendCrossEma(indicator.Name, indicator.Period.Value, indicator.Multiplier.Value), - IndicatorType.BollingerBandsPercentBMomentumBreakout => new BollingerBandsPercentBMomentumBreakout(indicator.Name, - indicator.Period.Value, indicator.Multiplier.Value), + IndicatorType.BollingerBandsPercentBMomentumBreakout => new BollingerBandsPercentBMomentumBreakout( + indicator.Name, + indicator.Period.Value, indicator.StDev.Value), + IndicatorType.BollingerBandsVolatilityProtection => new BollingerBandsVolatilityProtection(indicator.Name, + indicator.Period.Value, indicator.StDev.Value), IndicatorType.IchimokuKumoTrend => new IchimokuKumoTrend(indicator.Name, indicator.TenkanPeriods ?? 9, indicator.KijunPeriods ?? 26, @@ -112,6 +115,8 @@ public static class ScenarioHelpers _ => throw new NotImplementedException(), }; + result.SignalType = GetSignalType(indicator.Type); + return result; } @@ -122,16 +127,24 @@ public static class ScenarioHelpers { return new LightIndicator(indicatorBase.Name, indicatorBase.Type) { - SignalType = indicatorBase.SignalType, MinimumHistory = indicatorBase.MinimumHistory, Period = indicatorBase.Period, FastPeriods = indicatorBase.FastPeriods, SlowPeriods = indicatorBase.SlowPeriods, SignalPeriods = indicatorBase.SignalPeriods, Multiplier = indicatorBase.Multiplier, + StDev = indicatorBase.StDev, SmoothPeriods = indicatorBase.SmoothPeriods, StochPeriods = indicatorBase.StochPeriods, - CyclePeriods = indicatorBase.CyclePeriods + CyclePeriods = indicatorBase.CyclePeriods, + KFactor = indicatorBase.KFactor, + DFactor = indicatorBase.DFactor, + TenkanPeriods = indicatorBase.TenkanPeriods, + KijunPeriods = indicatorBase.KijunPeriods, + SenkouBPeriods = indicatorBase.SenkouBPeriods, + OffsetPeriods = indicatorBase.OffsetPeriods, + SenkouOffset = indicatorBase.SenkouOffset, + ChikouOffset = indicatorBase.ChikouOffset }; } @@ -143,11 +156,18 @@ public static class ScenarioHelpers int? slowPeriods = null, int? signalPeriods = null, double? multiplier = null, + double? stdev = null, int? stochPeriods = null, int? smoothPeriods = null, int? cyclePeriods = null, double? kFactor = null, - double? dFactor = null) + double? dFactor = null, + int? tenkanPeriods = null, + int? kijunPeriods = null, + int? senkouBPeriods = null, + int? offsetPeriods = null, + int? senkouOffset = null, + int? chikouOffset = null) { IIndicator indicator = new IndicatorBase(name, type); @@ -249,12 +269,43 @@ public static class ScenarioHelpers { throw new Exception($"kFactor must be greater than 0 for {indicator.Type} strategy type"); } + if (indicator.DFactor <= 0) { throw new Exception($"dFactor must be greater than 0 for {indicator.Type} strategy type"); } } + break; + case IndicatorType.BollingerBandsPercentBMomentumBreakout: + case IndicatorType.BollingerBandsVolatilityProtection: + if (!period.HasValue || !stdev.HasValue) + { + throw new Exception($"Missing period or stdev for {indicator.Type} strategy type"); + } + else + { + ((IndicatorBase)indicator).Period = period; + ((IndicatorBase)indicator).StDev = stdev; + } + + break; + case IndicatorType.IchimokuKumoTrend: + if (!tenkanPeriods.HasValue || !kijunPeriods.HasValue || !senkouBPeriods.HasValue || + !offsetPeriods.HasValue) + { + throw new Exception($"Missing Ichimoku parameters for {indicator.Type} strategy type"); + } + else + { + ((IndicatorBase)indicator).TenkanPeriods = tenkanPeriods; + ((IndicatorBase)indicator).KijunPeriods = kijunPeriods; + ((IndicatorBase)indicator).SenkouBPeriods = senkouBPeriods; + ((IndicatorBase)indicator).OffsetPeriods = offsetPeriods; + ((IndicatorBase)indicator).SenkouOffset = senkouOffset; + ((IndicatorBase)indicator).ChikouOffset = chikouOffset; + } + break; case IndicatorType.Stc: case IndicatorType.LaggingStc: @@ -299,6 +350,7 @@ public static class ScenarioHelpers IndicatorType.LaggingStc => SignalType.Signal, IndicatorType.SuperTrendCrossEma => SignalType.Signal, IndicatorType.BollingerBandsPercentBMomentumBreakout => SignalType.Signal, + IndicatorType.BollingerBandsVolatilityProtection => SignalType.Context, IndicatorType.IchimokuKumoTrend => SignalType.Trend, _ => throw new NotImplementedException(), }; diff --git a/src/Managing.Domain/Shared/Helpers/TradingBox.cs b/src/Managing.Domain/Shared/Helpers/TradingBox.cs index e65de308..247a703a 100644 --- a/src/Managing.Domain/Shared/Helpers/TradingBox.cs +++ b/src/Managing.Domain/Shared/Helpers/TradingBox.cs @@ -37,7 +37,7 @@ public class IndicatorComboConfig /// /// Minimum confidence level to return a signal (default: Low) /// - public Confidence MinimumConfidence { get; set; } = Confidence.Low; + public Confidence MinimumConfidence { get; set; } = Confidence.Medium; /// /// Minimum confidence level required from context strategies (default: Medium) diff --git a/src/Managing.WebApp/src/components/organism/CustomScenario/CustomScenario.tsx b/src/Managing.WebApp/src/components/organism/CustomScenario/CustomScenario.tsx index 36e82086..b2d04610 100644 --- a/src/Managing.WebApp/src/components/organism/CustomScenario/CustomScenario.tsx +++ b/src/Managing.WebApp/src/components/organism/CustomScenario/CustomScenario.tsx @@ -48,7 +48,8 @@ const CustomScenario: React.FC = ({ case IndicatorType.SuperTrendCrossEma: case IndicatorType.ChandelierExit: case IndicatorType.BollingerBandsPercentBMomentumBreakout: - params = ['period', 'multiplier']; + case IndicatorType.BollingerBandsVolatilityProtection: + params = ['period', 'stdev']; break; case IndicatorType.StochRsiTrend: @@ -147,6 +148,7 @@ const CustomScenario: React.FC = ({ slowPeriods: 26, signalPeriods: 9, multiplier: 3.0, + stDev: 2.0, stochPeriods: 14, smoothPeriods: 3, cyclePeriods: 10, @@ -284,10 +286,10 @@ const CustomScenario: React.FC = ({ updateIndicator(index, param, param.includes('multiplier') ? parseFloat(e.target.value) : parseInt(e.target.value))} + onChange={(e) => updateIndicator(index, param, param.includes('multiplier') || param.includes('stdev') ? parseFloat(e.target.value) : parseInt(e.target.value))} type='number' - step={param.includes('multiplier') ? '0.1' : '1'} - min={param.includes('multiplier') ? '0.1' : '1'} + step={param.includes('multiplier') || param.includes('stdev') ? '0.1' : '1'} + min={param.includes('multiplier') || param.includes('stdev') ? '0.1' : '1'} className='input input-bordered w-full' /> diff --git a/src/Managing.WebApp/src/components/organism/Trading/TradeChart/TradeChart.tsx b/src/Managing.WebApp/src/components/organism/Trading/TradeChart/TradeChart.tsx index b71ef52b..8bec5c61 100644 --- a/src/Managing.WebApp/src/components/organism/Trading/TradeChart/TradeChart.tsx +++ b/src/Managing.WebApp/src/components/organism/Trading/TradeChart/TradeChart.tsx @@ -39,7 +39,6 @@ import useTheme from '../../../../hooks/useTheme' // } - type ITradeChartProps = { candles: Candle[] positions: Position[] @@ -69,23 +68,23 @@ const TradeChart = ({ const series1 = useRef>() const [timeDiff, setTimeDiff] = useState(0) const [candleCount, setCandleCount] = useState(candles.length) - const [chartDimensions, setChartDimensions] = useState({ width: 0, height: 0 }) + const [chartDimensions, setChartDimensions] = useState({width: 0, height: 0}) // Get responsive dimensions const getResponsiveDimensions = () => { - if (!containerRef.current) return { width: width || 510, height: height || 300 } - + if (!containerRef.current) return {width: width || 510, height: height || 300} + const containerWidth = containerRef.current.offsetWidth const containerHeight = containerRef.current.offsetHeight - + // Use provided dimensions if available, otherwise calculate responsive ones if (width && height) { - return { width, height } + return {width, height} } - + // For responsive mode, calculate based on container const calculatedWidth = containerWidth > 0 ? containerWidth : 510 - + // Use different aspect ratios for different screen sizes let aspectRatio = 0.6 // Default ratio (height/width) if (containerWidth < 768) { // Mobile @@ -93,9 +92,9 @@ const TradeChart = ({ } else if (containerWidth < 1024) { // Tablet aspectRatio = 0.65 } - + const calculatedHeight = height || Math.max(250, calculatedWidth * aspectRatio) - + return { width: calculatedWidth, height: calculatedHeight @@ -109,7 +108,7 @@ const TradeChart = ({ const resizeObserver = new ResizeObserver(() => { const newDimensions = getResponsiveDimensions() setChartDimensions(newDimensions) - + if (chart.current) { chart.current.applyOptions({ width: newDimensions.width, @@ -131,7 +130,7 @@ const TradeChart = ({ setTimeout(() => { const newDimensions = getResponsiveDimensions() setChartDimensions(newDimensions) - + if (chart.current) { chart.current.applyOptions({ width: newDimensions.width, @@ -231,7 +230,7 @@ const TradeChart = ({ } else { color = negativeColor } - }else if (status == PositionStatus.Filled) { + } else if (status == PositionStatus.Filled) { color = theme.warning } } @@ -243,7 +242,7 @@ const TradeChart = ({ if (chartRef.current && containerRef.current) { const initialDimensions = getResponsiveDimensions() setChartDimensions(initialDimensions) - + const lineColor = theme['base-100'] chart.current = createChart(chartRef.current, { crosshair: { @@ -349,13 +348,13 @@ const TradeChart = ({ // Get the time range of candles const firstCandleTime = moment(candles[0].date).unix() const lastCandleTime = moment(candles[candles.length - 1].date).unix() - + // Filter signals that are within the candle range const filteredSignals = signals.filter((s) => { const signalTime = moment(s.date).unix() return signalTime >= firstCandleTime && signalTime <= lastCandleTime }) - + const signalMarkers = filteredSignals.map((s) => buildMarker( 'circle', @@ -393,9 +392,8 @@ const TradeChart = ({ } // Price panel - if (indicatorsValues?.EmaTrend != null || indicatorsValues?.EmaCross != null) - { - const emaSeries = chart.current.addLineSeries({ + if (indicatorsValues?.EmaTrend != null || indicatorsValues?.EmaCross != null) { + const emaSeries = chart.current.addLineSeries({ color: theme.secondary, lineWidth: 1, priceLineVisible: true, @@ -417,8 +415,7 @@ const TradeChart = ({ } }) - if (emaData != null) - { + if (emaData != null) { // @ts-ignore emaSeries.setData(emaData) } @@ -446,8 +443,8 @@ const TradeChart = ({ superTrendSeries.setData(superTrend) } - // Display chandeliers exits - if (indicatorsValues?.ChandelierExit != null) { + // Display chandeliers exits + if (indicatorsValues?.ChandelierExit != null) { const chandelierExitsLongsSeries = chart.current.addLineSeries({ color: theme.info, lineWidth: 1, @@ -568,6 +565,47 @@ const TradeChart = ({ lowerBandSeries.setData(lowerBandData) } + // Display Bollinger Bands on price chart for Volatility Protection + if (indicatorsValues?.BollingerBandsVolatilityProtection != null) { + const upperBandSeries = chart.current.addLineSeries({ + color: '#FF6B6B', // Lighter red for volatility protection bands + lineWidth: 1, + priceLineVisible: false, + priceLineWidth: 1, + title: 'Volatility Protection Upper Band', + pane: 0, + lineStyle: LineStyle.Dotted, + }) + + const upperBandData = indicatorsValues.BollingerBandsVolatilityProtection.bollingerBands?.map((w) => { + return { + time: moment(w.date).unix(), + value: w.upperBand, + } + }) + // @ts-ignore + upperBandSeries.setData(upperBandData) + + const lowerBandSeries = chart.current.addLineSeries({ + color: '#4ECDC4', // Teal for volatility protection bands + lineWidth: 1, + priceLineVisible: false, + priceLineWidth: 1, + title: 'Volatility Protection Lower Band', + pane: 0, + lineStyle: LineStyle.Dotted, + }) + + const lowerBandData = indicatorsValues.BollingerBandsVolatilityProtection.bollingerBands?.map((w) => { + return { + time: moment(w.date).unix(), + value: w.lowerBand, + } + }) + // @ts-ignore + lowerBandSeries.setData(lowerBandData) + } + if (markers.length > 0) { series1.current.setMarkers(markers) } @@ -575,9 +613,8 @@ const TradeChart = ({ // Indicator panel var paneCount = 1 - - if (indicatorsValues?.RsiDivergence != null || indicatorsValues?.RsiDivergenceConfirm != null) - { + + if (indicatorsValues?.RsiDivergence != null || indicatorsValues?.RsiDivergenceConfirm != null) { const rsiSeries = chart.current.addLineSeries({ pane: paneCount, title: 'RSI', @@ -608,7 +645,7 @@ const TradeChart = ({ const stcSeries = chart.current.addBaselineSeries({ pane: paneCount, baseValue: {price: 50, type: 'price'}, - + title: 'STC', }) @@ -641,7 +678,7 @@ const TradeChart = ({ const laggingStcSeries = chart.current.addBaselineSeries({ pane: paneCount, baseValue: {price: 50, type: 'price'}, - + title: 'Lagging STC', }) @@ -690,7 +727,7 @@ const TradeChart = ({ }) var priceOptions = { - scaleMargins:{ + scaleMargins: { top: 0.7, bottom: 0.02, } @@ -722,7 +759,7 @@ const TradeChart = ({ macdSeries.priceScale().applyOptions(priceOptions) // @ts-ignore - macdSeries.setData(macdData) + macdSeries.setData(macdData) const signalSeries = chart.current.addLineSeries({ color: theme.info, @@ -747,11 +784,11 @@ const TradeChart = ({ signalSeries.priceScale().applyOptions(priceOptions) // @ts-ignore signalSeries.setData(signalData) - + paneCount++ } - if (indicatorsValues?.StochRsiTrend){ + if (indicatorsValues?.StochRsiTrend) { const stochRsiSeries = chart.current.addLineSeries({ ...baselineOptions, priceLineVisible: false, @@ -859,7 +896,7 @@ const TradeChart = ({ paneCount++ } - + // Display dual EMA crossover if (indicatorsValues?.DualEmaCross != null) { const fastEmaSeries = chart.current.addLineSeries({ @@ -960,12 +997,12 @@ const TradeChart = ({ } return ( -
-
+
) } diff --git a/src/Managing.WebApp/src/components/organism/UnifiedTradingModal/UnifiedTradingModal.tsx b/src/Managing.WebApp/src/components/organism/UnifiedTradingModal/UnifiedTradingModal.tsx index 0a76f353..ea1957bb 100644 --- a/src/Managing.WebApp/src/components/organism/UnifiedTradingModal/UnifiedTradingModal.tsx +++ b/src/Managing.WebApp/src/components/organism/UnifiedTradingModal/UnifiedTradingModal.tsx @@ -572,9 +572,18 @@ const UnifiedTradingModal: React.FC = ({ slowPeriods: indicator.slowPeriods, signalPeriods: indicator.signalPeriods, multiplier: indicator.multiplier, + stDev: indicator.stDev, smoothPeriods: indicator.smoothPeriods, stochPeriods: indicator.stochPeriods, cyclePeriods: indicator.cyclePeriods, + kFactor: indicator.kFactor, + dFactor: indicator.dFactor, + tenkanPeriods: indicator.tenkanPeriods, + kijunPeriods: indicator.kijunPeriods, + senkouBPeriods: indicator.senkouBPeriods, + offsetPeriods: indicator.offsetPeriods, + senkouOffset: indicator.senkouOffset, + chikouOffset: indicator.chikouOffset, })).filter(indicator => indicator.type) || [] // Only filter out indicators without type }; }; diff --git a/src/Managing.WebApp/src/generated/ManagingApi.ts b/src/Managing.WebApp/src/generated/ManagingApi.ts index 650f5c51..9b15f1e7 100644 --- a/src/Managing.WebApp/src/generated/ManagingApi.ts +++ b/src/Managing.WebApp/src/generated/ManagingApi.ts @@ -4960,6 +4960,7 @@ export interface LightIndicator { slowPeriods?: number | null; signalPeriods?: number | null; multiplier?: number | null; + stDev?: number | null; smoothPeriods?: number | null; stochPeriods?: number | null; cyclePeriods?: number | null; @@ -4991,6 +4992,7 @@ export enum IndicatorType { SuperTrendCrossEma = "SuperTrendCrossEma", DualEmaCross = "DualEmaCross", BollingerBandsPercentBMomentumBreakout = "BollingerBandsPercentBMomentumBreakout", + BollingerBandsVolatilityProtection = "BollingerBandsVolatilityProtection", IchimokuKumoTrend = "IchimokuKumoTrend", } @@ -5263,9 +5265,18 @@ export interface IndicatorRequest { slowPeriods?: number | null; signalPeriods?: number | null; multiplier?: number | null; + stDev?: number | null; smoothPeriods?: number | null; stochPeriods?: number | null; cyclePeriods?: number | null; + kFactor?: number | null; + dFactor?: number | null; + tenkanPeriods?: number | null; + kijunPeriods?: number | null; + senkouBPeriods?: number | null; + offsetPeriods?: number | null; + senkouOffset?: number | null; + chikouOffset?: number | null; } export interface MoneyManagementRequest { @@ -5577,6 +5588,7 @@ export interface IndicatorBase { slowPeriods?: number | null; signalPeriods?: number | null; multiplier?: number | null; + stDev?: number | null; smoothPeriods?: number | null; stochPeriods?: number | null; cyclePeriods?: number | null; diff --git a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts index 9fd75e92..345f97ed 100644 --- a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts +++ b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts @@ -426,6 +426,7 @@ export interface LightIndicator { slowPeriods?: number | null; signalPeriods?: number | null; multiplier?: number | null; + stDev?: number | null; smoothPeriods?: number | null; stochPeriods?: number | null; cyclePeriods?: number | null; @@ -457,6 +458,7 @@ export enum IndicatorType { SuperTrendCrossEma = "SuperTrendCrossEma", DualEmaCross = "DualEmaCross", BollingerBandsPercentBMomentumBreakout = "BollingerBandsPercentBMomentumBreakout", + BollingerBandsVolatilityProtection = "BollingerBandsVolatilityProtection", IchimokuKumoTrend = "IchimokuKumoTrend", } @@ -729,9 +731,18 @@ export interface IndicatorRequest { slowPeriods?: number | null; signalPeriods?: number | null; multiplier?: number | null; + stDev?: number | null; smoothPeriods?: number | null; stochPeriods?: number | null; cyclePeriods?: number | null; + kFactor?: number | null; + dFactor?: number | null; + tenkanPeriods?: number | null; + kijunPeriods?: number | null; + senkouBPeriods?: number | null; + offsetPeriods?: number | null; + senkouOffset?: number | null; + chikouOffset?: number | null; } export interface MoneyManagementRequest { @@ -1043,6 +1054,7 @@ export interface IndicatorBase { slowPeriods?: number | null; signalPeriods?: number | null; multiplier?: number | null; + stDev?: number | null; smoothPeriods?: number | null; stochPeriods?: number | null; cyclePeriods?: number | null; diff --git a/src/Managing.WebApp/src/pages/backtestPage/BundleRequestModal.tsx b/src/Managing.WebApp/src/pages/backtestPage/BundleRequestModal.tsx index 179537ea..994b167d 100644 --- a/src/Managing.WebApp/src/pages/backtestPage/BundleRequestModal.tsx +++ b/src/Managing.WebApp/src/pages/backtestPage/BundleRequestModal.tsx @@ -231,9 +231,18 @@ const BundleRequestModal: React.FC = ({ slowPeriods: indicator.slowPeriods || 26, signalPeriods: indicator.signalPeriods || 9, multiplier: indicator.multiplier || 3.0, + stDev: indicator.stDev || 2.0, stochPeriods: indicator.stochPeriods || 14, smoothPeriods: indicator.smoothPeriods || 3, - cyclePeriods: indicator.cyclePeriods || 10 + cyclePeriods: indicator.cyclePeriods || 10, + kFactor: indicator.kFactor || 3.0, + dFactor: indicator.dFactor || 3.0, + tenkanPeriods: indicator.tenkanPeriods || 9, + kijunPeriods: indicator.kijunPeriods || 26, + senkouBPeriods: indicator.senkouBPeriods || 52, + offsetPeriods: indicator.offsetPeriods || 26, + senkouOffset: indicator.senkouOffset || 26, + chikouOffset: indicator.chikouOffset || 26 })), loopbackPeriod: scenario.loopbackPeriod || 1 } : undefined,