Files
managing-apps/.cursor/commands/build-indicator.md
cryptooda 6376e13b07 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.
2025-11-25 02:12:57 +07:00

20 KiB

build-indicator

When to Use

Use this command when you need to:

  • Create new technical indicators based on pattern descriptions
  • Add signal, trend, or context indicators to the trading system
  • Update all related files and configurations automatically
  • Follow the established indicator architecture and conventions

Prerequisites

  • Clear indicator specification with Type, Label, Core Logic, Triggers, and Parameters
  • Understanding of indicator categories (Signal/Trend/Context)
  • Access to existing indicator implementations for reference
  • Knowledge of the indicator's mathematical calculations

Execution Steps

Step 1: Parse Indicator Specification

Analyze the indicator description and extract:

Required Information:

  • Type: Signal/Trend/Context (determines folder location)
  • Label: Indicator name (e.g., "Stochastic Filtered")
  • Core Logic: Technical description of what the indicator does
  • Trigger Conditions: When to generate signals (for Signal indicators)
  • Parameters: Configuration values with defaults
  • Signal Type: Long/Short for Signal indicators, Confidence levels for Context

Example Format:

Type: Signal
Label: Stochastic Filtered
Core Logic: Generates signals by filtering %K / %D crossovers to occur only within extreme overbought (above 80) or oversold (below 20) zones.
Trigger a Long when → The %K line crosses above the %D line (bullish momentum shift). The crossover occurs in the oversold zone (both %K and %D lines are below 20).
Trigger a Short when → The %K line crosses below the %D line (bearish momentum shift). The crossover occurs in the overbought zone (both %K and %D lines are above 80).
Parameters:
%K Period (default: 14)
%K Slowing (default: 3)
%D Period (default: 3)
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:

  • Search codebase: grep -r "Get{IndicatorType}" src/Managing.Domain/Indicators/
  • Look for similar Skender.Stock.Indicators usage patterns
  • Check if candle mapping logic can be shared with existing indicators

Class Name Convention:

  • Signal indicators: {IndicatorName}.cs (e.g., StochasticFiltered.cs)
  • Trend indicators: {IndicatorName}.cs (e.g., EmaTrend.cs)
  • Context indicators: {IndicatorName}.cs (e.g., StDev.cs)

Inheritance Strategy:

  • Default: Extend IndicatorBase directly
  • Shared Mapping: Extend from existing shared base class if mappings overlap
  • New Shared Base: Create base class only if multiple indicators will share the same mapping

Class Name Pattern:

  • For signal/trend indicators: Class name = {IndicatorName} (inherits from IndicatorBase or shared base)
  • For context indicators: Class name = {IndicatorName} (inherits from IndicatorBase or shared base)

Location:

  • Signal → src/Managing.Domain/Indicators/Signals/
  • Trend → src/Managing.Domain/Indicators/Trends/
  • Context → src/Managing.Domain/Indicators/Context/

Enum Name:

  • Convert label to PascalCase: StochasticFiltered
  • Add to IndicatorType enum in src/Managing.Common/Enums.cs

Step 3: Implement Indicator Class

Create the indicator class following the established pattern. Check if other indicators use similar candle mappings - if so, consider creating or extending a base class.

Check for Existing Candle Mappings:

  • Search for similar indicator types that might share candle mappings
  • If another indicator uses the same Skender.Stock.Indicators result type, consider extending an existing base class or creating a shared base class
  • Only create a new base class if no other indicator shares the same candle mapping pattern

Base Structure:

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.{TypeFolder};

public class {IndicatorName} : IndicatorBase
{
    public List<LightSignal> Signals { get; set; }

    public {IndicatorName}(string name, {parameters}) :
        base(name, IndicatorType.{EnumName})
    {
        Signals = new List<LightSignal>();
        // Initialize parameters (e.g., Period, Multiplier, StDev)
    }

    // Implementation methods...
}

For Bollinger Bands (use shared base):

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:

public class {SharedBaseName}Base : IndicatorBase
{
    // Shared candle mapping logic here
    protected List<{CandleResultType}> Map{Indicator}ToCandle(List<{SkenderResult}> results, IEnumerable<Candle> candles)
    {
        // Shared mapping implementation
    }
}

public class {IndicatorName} : {SharedBaseName}Base
{
    // Indicator-specific logic only
}

Key Methods to Implement:

  1. Run(HashSet<Candle> candles) - Main calculation logic
  2. Run(HashSet<Candle> candles, IndicatorsResultBase preCalculatedValues) - Optimized version
  3. GetIndicatorValues(HashSet<Candle> candles) - Return calculated values
  4. Private processing methods for signal generation

Signal Generation Pattern:

private void ProcessSignals(List<{Indicator}Result> results, HashSet<Candle> candles)
{
    var mappedData = Map{Indicator}ToCandle(results, candles);

    if (mappedData.Count == 0) return;

    var previousCandle = mappedData[0];
    foreach (var currentCandle in mappedData.Skip(1))
    {
        // Check trigger conditions
        if (/* Long condition */)
        {
            AddSignal(currentCandle, TradeDirection.Long, Confidence.Medium);
        }

        if (/* Short condition */)
        {
            AddSignal(currentCandle, TradeDirection.Short, Confidence.Medium);
        }

        previousCandle = currentCandle;
    }
}

Step 4: Update Configuration Files

Update Enums.cs:

public enum IndicatorType
{
    // ... existing indicators
    StochasticFiltered,
    // ... continue
}

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 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:

# 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

The following indicators are available from the Skender.Stock.Indicators library and can be used as the basis for custom trading indicators:

Trend Indicators

  • EMA (Exponential Moving Average): GetEma(period) - Smooths price data with exponential weighting
  • SMA (Simple Moving Average): GetSma(period) - Arithmetic mean of prices over period
  • WMA (Weighted Moving Average): GetWma(period) - Weighted average favoring recent prices
  • HMA (Hull Moving Average): GetHma(period) - Responsive moving average using WMA
  • DEMA (Double Exponential Moving Average): GetDema(period) - Two EMAs for reduced lag
  • TEMA (Triple Exponential Moving Average): GetTema(period) - Three EMAs for further lag reduction
  • VWMA (Volume Weighted Moving Average): GetVwma(period) - Volume-weighted price average

Momentum Oscillators

  • RSI (Relative Strength Index): GetRsi(period) - Momentum oscillator (0-100)
  • Stochastic Oscillator: GetStoch(kPeriod, kSlowing, dPeriod) - %K and %D lines
  • Stochastic RSI: GetStochRsi(rsiPeriod, stochPeriod, signalPeriod, smoothPeriod) - Stochastic of RSI
  • Williams %R: GetWilliamsR(period) - Momentum oscillator (-100 to 0)
  • CCI (Commodity Channel Index): GetCci(period) - Mean deviation from average price
  • MFI (Money Flow Index): GetMfi(period) - Volume-weighted RSI
  • AO (Awesome Oscillator): GetAo() - MACD of median price
  • KVO (Klinger Volume Oscillator): GetKvo(fastPeriod, slowPeriod, signalPeriod) - Volume oscillator

Trend Following

  • MACD (Moving Average Convergence Divergence): GetMacd(fastPeriod, slowPeriod, signalPeriod) - Trend momentum indicator
  • SuperTrend: GetSuperTrend(period, multiplier) - ATR-based trailing stop
  • Chandelier Exit: GetChandelier(period, multiplier, type) - ATR-based exit levels
  • Parabolic SAR: GetParabolicSar(accelerationStep, maxAcceleration) - Trailing stop and reversal
  • ADX (Average Directional Index): GetAdx(period) - Trend strength indicator
  • DMI (Directional Movement Index): GetDmi(period) - Trend direction and strength
  • PSAR (Parabolic SAR): GetPsar(accelerationStep, maxAcceleration) - Dynamic support/resistance

Volatility Indicators

  • ATR (Average True Range): GetAtr(period) - Volatility measurement
  • Bollinger Bands: GetBollingerBands(period, standardDeviations) - Price volatility bands
  • Standard Deviation: GetStdDev(period) - Statistical volatility measure
  • TR (True Range): GetTr() - Maximum price movement range

Volume Indicators

  • OBV (On Balance Volume): GetObv() - Cumulative volume based on price direction
  • CMF (Chaikin Money Flow): GetCmf(period) - Volume-weighted price trend
  • ADL (Accumulation/Distribution Line): GetAdl() - Volume-based price accumulation
  • EMV (Ease of Movement): GetEmv(period) - Price movement relative to volume
  • NVI (Negative Volume Index): GetNvi() - Volume-based trend indicator

Cycle Indicators

  • STC (Schaff Trend Cycle): GetStc(cyclePeriod, fastPeriod, slowPeriod) - Cycle oscillator (0-100)
  • DPO (Detrended Price Oscillator): GetDpo(period) - Removes trend from price
  • EPMA (Endpoint Moving Average): GetEpma(period) - End-point moving average

Support/Resistance

  • Pivot Points: GetPivotPoints(period) - Traditional pivot levels
  • Fibonacci Retracements: GetFibonacciRetracements() - Fibonacci ratio levels

Candlestick Patterns

  • Doji: GetDoji() - Doji candlestick patterns
  • Hammer: GetHammer() - Hammer patterns
  • Engulfing: GetEngulfing() - Bullish/bearish engulfing
  • Marubozu: GetMarubozu() - Marubozu patterns
  • And many more...

Usage Examples

// Basic usage
var ema = candles.GetEma(20).ToList();
var macd = candles.GetMacd(12, 26, 9).ToList();
var rsi = candles.GetRsi(14).ToList();
var stoch = candles.GetStoch(14, 3, 3).ToList();
var superTrend = candles.GetSuperTrend(10, 3.0).ToList();

// Chain indicators (indicator of indicators)
var rsiOfObv = candles.GetObv().GetRsi(14).ToList();
var smaOfRsi = candles.GetRsi(14).GetSma(9).ToList();

For complete documentation and examples, visit: Skender.Stock.Indicators Guide

Finding the Right Method

When implementing a new indicator, search the Skender documentation for your indicator concept:

  1. Identify the core calculation: What mathematical formula does your indicator use?
  2. Find the Skender equivalent: Search for methods like Get{IndicatorName}()
  3. Check parameters: Most indicators follow common patterns:
    • period: Lookback period (typically 5-300)
    • fastPeriod/slowPeriod: For dual moving averages
    • signalPeriod: For signal line calculations
    • multiplier: ATR multipliers (typically 1.0-5.0)
  4. Verify result structure: Check what properties the result object contains

Parameter Guidelines

Common Ranges by Indicator Type:

  • 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 (StDev) 1.0-3.0 (Bollinger Bands)
  • Volume: Period 5-50 (OBV uses no period)

Testing Parameters:

  • Start with industry standard defaults
  • Test multiple parameter combinations
  • Consider timeframe: Shorter timeframes may need smaller periods

Result Object Patterns

Different indicators return different result objects. Common patterns:

Single Value Results:

  • EmaResult: { Date, Ema }
  • RsiResult: { Date, Rsi }
  • AtrResult: { Date, Atr }
  • ObvResult: { Date, Obv }

Dual Value Results:

  • StochResult: { Date, PercentK, PercentD, Oscillator }
  • MacdResult: { Date, Macd, Signal, Histogram }
  • StochRsiResult: { Date, Rsi, StochRsi, Signal }

Triple+ Value Results:

  • BollingerBandsResult: { Date, Sma, UpperBand, LowerBand }
  • SuperTrendResult: { Date, SuperTrend, UpperBand, LowerBand }
  • ChandelierResult: { Date, ChandelierExit }

Candlestick Results:

  • CandleResult: { Date, Price, Match, Candle } (for pattern recognition)

When creating your Candle{Indicator} mapping class, include all relevant result properties plus the base Candle properties (Close, Open, Date, Ticker, Exchange).

Quick Reference - Currently Used Indicators

In This Codebase:

  • GetEma(period)EmaResult - Used in EMA Trend, EMA Cross, Dual EMA Cross
  • GetMacd(fast, slow, signal)MacdResult - Used in MACD Cross
  • GetRsi(period)RsiResult - Used in RSI Divergence variants
  • GetStoch(kPeriod, kSlowing, dPeriod)StochResult - Used in Stochastic Filtered
  • GetStochRsi(rsiPeriod, stochPeriod, signalPeriod, smoothPeriod)StochRsiResult - Used in Stoch RSI Trend
  • GetSuperTrend(period, multiplier)SuperTrendResult - Used in SuperTrend, SuperTrend Cross EMA
  • 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:

  • GetBollingerBands(period, stdDev)BollingerBandsResult
  • GetAtr(period)AtrResult
  • GetObv()ObvResult
  • GetCci(period)CciResult
  • GetMfi(period)MfiResult
  • And many more... (see full list above)

Common Patterns

Signal Indicator Pattern

  • Uses TradeDirection.Long/Short with Confidence levels
  • Implements crossover or threshold-based logic
  • Returns filtered signals only when conditions are met

Trend Indicator Pattern

  • Uses TradeDirection.Long/Short for trend direction
  • Continuous assessment rather than discrete signals
  • Lower confidence levels for trend indicators

Context Indicator Pattern

  • Uses Confidence.None/Low/Medium/High for veto power
  • Acts as filter for other indicators
  • No directional signals, only context assessment

Shared Base Class Pattern

When to Use:

  • Multiple indicators use the same Skender.Stock.Indicators result type
  • Indicators share identical candle mapping logic
  • Common signal processing patterns exist

Example:

public abstract class StochasticBase : IndicatorBase
{
    protected List<CandleStoch> MapStochToCandle(List<StochResult> stochResults, IEnumerable<Candle> candles)
    {
        // Shared mapping logic for all Stochastic-based indicators
    }
}

public class StochasticFiltered : StochasticBase { /* Specific logic */ }
public class AnotherStochasticIndicator : StochasticBase { /* Specific logic */ }

Bollinger Bands Example (Implemented):

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<CandleBollingerBands> MapBollingerBandsToCandle(
        IEnumerable<BollingerBandsResult> bbResults, IEnumerable<Candle> 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
  • Only one indicator uses a particular pattern

Error Handling

Common Issues:

  • Missing parameters in constructor
  • Incorrect SignalType assignment
  • Wrong folder location (Signals/Trends/Context)
  • Missing enum updates
  • Parameter range mismatches

Validation Checklist:

  • Checked for existing indicators with similar candle mappings
  • Used appropriate base class (IndicatorBase or shared base if mappings overlap)
  • Constructor parameters match IIndicator interface
  • SignalType correctly assigned
  • Enum added to IndicatorType
  • 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