- 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
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)- theisNewflag 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:
- Formula citation required for non-trivial indicators (Context7 tag)
- Benchmark data required for performance-related changes
- Taskmaster task IDs must be referenced in PRs
- Update
memory-bank/progress.mdafter 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
- Research: Get formula/specification (Context7 if needed)
- Location: Place in appropriate
lib/subdirectory (averages, oscillators, momentum, volatility, volume, statistics) - 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;
}
}
- 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'sCalc()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
CircularBufferfor 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# featuresAllowUnsafeBlocks: true- SIMD and unsafe operations permittedNullable: 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 indicatorslib/core/circularbuffer.cs- Memory-efficient storage with SIMDDirectory.Build.props- Solution-wide MSBuild propertiesmemory-bank/systemPatterns.md- Architecture patternsmemory-bank/activeContext.md- Current work focus and MCP policiesmemory-bank/progress.md- Completed features and roadmap
Testing Reference
Tests/test_updates_*.cs- Update behavior validation (IsNew handling)Tests/test_quantower.cs- Quantower integration validationTests/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
- Update tests: Verify
isNew=falsebehavior converges toisNew=truewith same final value - Reference comparison: Validate against TALib, Trady, or Skender implementations
- Edge cases: Test with insufficient data (< period), NaN/Infinity, extreme values
- 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.mdafter 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