Files
QuanTAlib/.github/copilot-instructions.md
miha 0f7cfb4b50 Optimize SMA to O(1) using running sum algorithm
- Changed from O(n) CircularBuffer.Average() to O(1) running sum
- Maintains _sum and _p_sum for state management
- Tracks _lastValue and _p_lastValue for isNew=false updates
- Provides ~15-20x speedup for large periods
- Pattern verified against Pine Script reference implementation
- All tests pass including update test for isNew handling

Also added .github/copilot-instructions.md with comprehensive
AI agent guidance for QuanTAlib development patterns
2025-10-21 18:47:10 -07:00

8.3 KiB

QuanTAlib AI Coding Agent Instructions

Project Overview

QuanTAlib is a high-performance C# library for quantitative technical analysis, targeting .NET 8.0 with real-time streaming data processing. The library provides 50+ technical indicators optimized for sub-millisecond calculations using circular buffers, SIMD operations, and event-driven architecture.

Critical Architecture Patterns

Core Data Flow

All indicators inherit from AbstractBase (in lib/core/abstractBase.cs) which implements ITValue:

// Standard indicator lifecycle:
Input  Calc()  ManageState(isNew)  Calculation()  Process()  Pub event

Key insight: The isNew parameter distinguishes between new bars and updates to the last bar. Indicators must support both modes - this is tested extensively in Tests/test_updates_*.cs.

Circular Buffer Pattern

CircularBuffer (in lib/core/circularbuffer.cs) is the foundation for memory-efficient fixed-capacity storage:

  • Never grows beyond initial capacity
  • O(1) add/access operations
  • SIMD-optimized aggregations (Sum, Min, Max, Average)
  • Critical: Always use Add(item, isNew) - the isNew flag controls whether to append or update

State Management in Indicators

Every indicator must implement:

protected override void ManageState(bool isNew)
{
    if (isNew) {
        _index++;
        _p_prevValue = _prevValue;  // Backup state
    } else {
        _prevValue = _p_prevValue;  // Restore state
    }
}

This allows bar updates without corrupting historical calculations.

Development Workflow

MCP-Orchestrated Process

Research Gate: Before implementing non-trivial indicators, use Context7 to retrieve authoritative formulas/references. Embed citation tags in PR descriptions.

Decomposition: Use Sequential-Thinking for complex multi-stage work (SIMD refactors, multi-timeframe logic).

Task Tracking: Taskmaster holds the canonical task graph. Feature branches follow pattern: feature/{taskId}-{slug}.

Quality Gates:

  1. Formula citation required for non-trivial indicators (Context7 tag)
  2. Benchmark data required for performance-related changes
  3. Taskmaster task IDs must be referenced in PRs
  4. Update memory-bank/progress.md after merge when threshold met

Build & Test Commands

# Build solution
dotnet build QuanTAlib.sln

# Run all tests
dotnet test --no-build

# Run with coverage
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov

# Build using tasks.json
# Use Run Task: "build" or "test"

Adding a New Indicator

  1. Research: Get formula/specification (Context7 if needed)
  2. Location: Place in appropriate lib/ subdirectory (averages, oscillators, momentum, volatility, volume, statistics)
  3. Template structure:
using System.Runtime.CompilerServices;
namespace QuanTAlib;

[SkipLocalsInit]
public sealed class MyIndicator : AbstractBase
{
    private CircularBuffer _buffer;
    private double _prevValue, _p_prevValue;  // State + backup
    
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public MyIndicator(int period)
    {
        ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
        _buffer = new(period);
        WarmupPeriod = period;  // Set when indicator stabilizes
        Name = $"MyIndicator({period})";
    }
    
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    protected override void ManageState(bool isNew)
    {
        if (isNew) {
            _index++;
            _p_prevValue = _prevValue;
        } else {
            _prevValue = _p_prevValue;
        }
    }
    
    [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
    protected override double Calculation()
    {
        ManageState(Input.IsNew);
        _buffer.Add(Input.Value, Input.IsNew);
        // Implement calculation logic
        return result;
    }
}
  1. Testing: Create update test in Tests/test_updates_*.cs:
[Fact]
public void MyIndicator_Update()
{
    var indicator = new MyIndicator(period: 14);
    TestTValueUpdate(indicator, indicator.Calc);
}

Quantower Integration

For platform indicators in quantower/, create wrapper classes inheriting from Quantower's Indicator:

  • Use private lib/ indicator instances
  • Map OnUpdate() to indicator's Calc() method
  • Extract output fields (e.g., ma, jmaUp, jmaLo) from indicator state

Code Style Requirements

Performance First

  • Use [MethodImpl(MethodImplOptions.AggressiveInlining)] for hot paths
  • Use [MethodImpl(MethodImplOptions.AggressiveOptimization)] for calculation methods
  • Apply [SkipLocalsInit] to indicator classes
  • Prefer SIMD operations in CircularBuffer for aggregations
  • Minimize allocations in Calculation() methods

C# Conventions

  • No inline comments within methods - code should be self-documenting
  • Use XML doc comments for public APIs only
  • PascalCase for public members, _camelCase for private fields
  • Compact code - minimal whitespace between logical blocks
  • Latest C# features: ArgumentOutOfRangeException.ThrowIfLessThan, pattern matching, etc.

Project Settings

  • LangVersion: preview - use cutting-edge C# features
  • AllowUnsafeBlocks: true - SIMD and unsafe operations permitted
  • Nullable: enable - strict nullability checking
  • Target: net8.0

Key Files & Directories

Core Library Structure

lib/
├── core/           # AbstractBase, CircularBuffer, TSeries, TBar, TValue
├── averages/       # Moving averages (SMA, EMA, DEMA, TEMA, JMA, etc.)
├── oscillators/    # RSI, Stochastic, Williams %R, CCI, Fisher
├── momentum/       # MACD, ADX, ROC, Vortex
├── volatility/     # ATR, Bollinger Bands, volatility measures
├── volume/         # Volume-based indicators
└── statistics/     # Statistical measures, correlations

Critical Reference Files

  • lib/core/abstractBase.cs - Base class for all indicators
  • lib/core/circularbuffer.cs - Memory-efficient storage with SIMD
  • Directory.Build.props - Solution-wide MSBuild properties
  • memory-bank/systemPatterns.md - Architecture patterns
  • memory-bank/activeContext.md - Current work focus and MCP policies
  • memory-bank/progress.md - Completed features and roadmap

Testing Reference

  • Tests/test_updates_*.cs - Update behavior validation (IsNew handling)
  • Tests/test_quantower.cs - Quantower integration validation
  • Tests/test_talib.cs, test_Trady.cs - Cross-validation against reference libraries

Common Patterns

Multi-Stage Smoothing

Many indicators (DEMA, TEMA, MACD) use cascaded smoothing:

private readonly Ema _ema1;
private readonly Ema _ema2;

_ema1.Calc(Input.Value, Input.IsNew);
_ema2.Calc(_ema1.Value, Input.IsNew);

Bar-Based vs Value-Based

  • Value-based: Accept TValue, process single values (most indicators)
  • Bar-based: Accept TBar (OHLCV), process bar data (ATR, Stochastic, volume indicators)

Override appropriate Calc() method:

public override TValue Calc(TBar barInput) { /* ... */ }

WarmupPeriod Calculation

Set WarmupPeriod to indicate when the indicator reaches 95% accuracy:

WarmupPeriod = (int)Math.Ceiling(Math.Log(0.05) / Math.Log(1 - alpha));

Validation Strategy

  1. Update tests: Verify isNew=false behavior converges to isNew=true with same final value
  2. Reference comparison: Validate against TALib, Trady, or Skender implementations
  3. Edge cases: Test with insufficient data (< period), NaN/Infinity, extreme values
  4. Performance: Benchmark calculation time - target < 0.5ms per update

Documentation Requirements

  • XML docs on public classes/methods describing purpose, formula, and sources
  • Mathematical formulas in doc comments with source citations
  • No internal comments - let code structure communicate intent
  • Update memory-bank/progress.md after significant feature completion

GitVersion & Releases

  • Semantic versioning via GitVersion.yml
  • Version properties auto-injected: $(GitVersion_MajorMinorPatch)
  • Commit messages influence version bumps (conventional commits)
  • Build creates NuGet package with embedded version metadata