Files
managing-apps/.cursor/commands/build-indicator.md

16 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

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
    }

    // Implementation methods...
}

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

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}]

Step 5: Test and Validate

Compile Check:

dotnet build

Basic Validation:

  • Verify indicator appears in GeneticService configurations
  • Check that BuildIndicator methods work correctly
  • Ensure proper SignalType assignment

Integration Test:

  • Create a simple backtest using the new indicator
  • Verify signals are generated correctly
  • Check parameter handling and validation

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 1.0-3.0
  • 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
  • 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 */ }

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
  • BuildIndicator methods updated
  • GeneticService configurations updated
  • Compiles without errors