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.
This commit is contained in:
@@ -56,6 +56,11 @@ public class IndicatorRequest
|
||||
/// </summary>
|
||||
public double? Multiplier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Standard deviation parameter for indicators like Bollinger Bands
|
||||
/// </summary>
|
||||
public double? StDev { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Smooth periods parameter
|
||||
/// </summary>
|
||||
@@ -70,4 +75,45 @@ public class IndicatorRequest
|
||||
/// Cycle periods parameter
|
||||
/// </summary>
|
||||
public int? CyclePeriods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// K factor parameter for Stochastic Cross
|
||||
/// </summary>
|
||||
public double? KFactor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// D factor parameter for Stochastic Cross
|
||||
/// </summary>
|
||||
public double? DFactor { get; set; }
|
||||
|
||||
// Ichimoku-specific parameters
|
||||
/// <summary>
|
||||
/// Tenkan periods for Ichimoku
|
||||
/// </summary>
|
||||
public int? TenkanPeriods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Kijun periods for Ichimoku
|
||||
/// </summary>
|
||||
public int? KijunPeriods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Senkou B periods for Ichimoku
|
||||
/// </summary>
|
||||
public int? SenkouBPeriods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Offset periods for Ichimoku
|
||||
/// </summary>
|
||||
public int? OffsetPeriods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Senkou offset for Ichimoku
|
||||
/// </summary>
|
||||
public int? SenkouOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Chikou offset for Ichimoku
|
||||
/// </summary>
|
||||
public int? ChikouOffset { get; set; }
|
||||
}
|
||||
162
src/Managing.Domain/Indicators/Base/BollingerBandsBase.cs
Normal file
162
src/Managing.Domain/Indicators/Base/BollingerBandsBase.cs
Normal file
@@ -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<LightSignal> Signals { get; set; }
|
||||
|
||||
protected BollingerBandsBase(string name, IndicatorType type, int period, double stdev) : base(name, type)
|
||||
{
|
||||
Signals = new List<LightSignal>();
|
||||
Period = period;
|
||||
StDev = stdev;
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run(HashSet<Candle> 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<LightSignal> Run(HashSet<Candle> candles, IndicatorsResultBase preCalculatedValues)
|
||||
{
|
||||
if (candles.Count <= Period)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Use pre-calculated Bollinger Bands values if available
|
||||
List<BollingerBandsResult> 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<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
BollingerBands = candles.GetBollingerBands(Period.Value, StDev.Value)
|
||||
.ToList()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abstract method for processing Bollinger Bands signals - implemented by child classes
|
||||
/// </summary>
|
||||
protected abstract void ProcessBollingerBandsSignals(List<BollingerBandsResult> bbResults, HashSet<Candle> candles);
|
||||
|
||||
/// <summary>
|
||||
/// Maps Bollinger Bands results to candle objects with all BollingerBandsResult properties
|
||||
/// </summary>
|
||||
protected virtual IEnumerable<CandleBollingerBands> MapBollingerBandsToCandle(IEnumerable<BollingerBandsResult> bbResults, IEnumerable<Candle> candles)
|
||||
{
|
||||
var bbCandles = new List<CandleBollingerBands>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shared method for adding signals with duplicate prevention
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base candle class for Bollinger Bands indicators with all BollingerBandsResult properties
|
||||
/// </summary>
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="bbResults">List of Bollinger Bands calculation results</param>
|
||||
/// <param name="candles">Candles to process</param>
|
||||
protected override void ProcessBollingerBandsSignals(List<BollingerBandsResult> bbResults, HashSet<Candle> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
@@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// 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,
|
||||
|
||||
@@ -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<LightSignal> 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<LightSignal>();
|
||||
Period = period;
|
||||
Multiplier = stdDev; // Using Multiplier property for stdDev since it's a double
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run(HashSet<Candle> 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<LightSignal> Run(HashSet<Candle> candles, IndicatorsResultBase preCalculatedValues)
|
||||
{
|
||||
if (candles.Count <= 10 * Period.Value + 50)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Use pre-calculated Bollinger Bands values if available
|
||||
List<BollingerBandsResult> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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)
|
||||
/// </summary>
|
||||
private void ProcessBollingerBandsSignals(List<BollingerBandsResult> bbResults, HashSet<Candle> candles)
|
||||
protected override void ProcessBollingerBandsSignals(List<BollingerBandsResult> bbResults, HashSet<Candle> 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<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
BollingerBands = candles.GetBollingerBands(Period.Value, (double)Multiplier.Value)
|
||||
.ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private List<CandleBollingerBands> MapBollingerBandsToCandle(IEnumerable<BollingerBandsResult> bbResults, IEnumerable<Candle> candles)
|
||||
{
|
||||
var bbCandles = new List<CandleBollingerBands>();
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ public class IndicatorComboConfig
|
||||
/// <summary>
|
||||
/// Minimum confidence level to return a signal (default: Low)
|
||||
/// </summary>
|
||||
public Confidence MinimumConfidence { get; set; } = Confidence.Low;
|
||||
public Confidence MinimumConfidence { get; set; } = Confidence.Medium;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum confidence level required from context strategies (default: Medium)
|
||||
|
||||
Reference in New Issue
Block a user