Skip to content

tradai-strategy Design Document

Overview

tradai-strategy provides the strategy framework for the TradAI platform. It extends Freqtrade's IStrategy with TradAI-specific features: standardized metadata, type-safe enums, validation, and MLflow/Docker integration.

Design Philosophy: Extend, don't wrap. TradAIStrategy IS an IStrategy - it works directly with Freqtrade without adapters.

Architecture Decisions

What We Provide

  1. TradAIStrategy (base.py) - Base class extending IStrategy
  2. Adds get_metadata() abstract method (REQUIRED)
  3. Adds validate_configuration() hook (optional)
  4. Includes StrategyDebugMixin for development
  5. Full Freqtrade compatibility (all IStrategy hooks work)

  6. StrategyMetadata (metadata.py) - Pydantic model for strategy info

  7. Required: name, version, description, timeframe, category
  8. Optional: can_short, tags, author, status, parameters
  9. Live trading fields: trading_modes, exchange, pairs, stake_currency
  10. Methods: to_mlflow_tags(), to_docker_labels()

  11. Type-Safe Enums (enums.py)

  12. SignalDirection - LONG, SHORT
  13. StrategyStatus - ACTIVE, DEPRECATED, TESTING
  14. StrategyCategory - TREND_FOLLOWING, MEAN_REVERSION, BREAKOUT, MOMENTUM, ARBITRAGE
  15. BacktestStatus - PENDING, RUNNING, COMPLETED, FAILED
  16. ValidationLevel - BASIC, STANDARD, STRICT
  17. PredictionSource - CSV, MLFLOW

  18. StrategyValidator (validation.py) - Server-side validation

  19. Validates strategy class structure
  20. Validates metadata completeness
  21. Validates Freqtrade method signatures

  22. Filters (filters.py) - Signal filtering utilities

  23. apply_filters() - Apply multiple filters to signals
  24. apply_volatility_filter() - Filter by ATR/volatility

  25. FreqAI Integration (freqai/)

  26. config_builder.py - FreqAI config construction
  27. prediction_model.py - Prediction loading utilities
  28. training_model.py - Training utilities
  29. utils.py - FreqAI helper functions

  30. Debug Utilities (debug.py)

  31. StrategyDebugMixin - Debug methods for development
  32. check_nan_indicators() - Detect NaN in indicators
  33. print_indicator_summary() - Print latest candle values

  34. Quality Checks

  35. sanity/ - Runtime sanity checks
  36. preflight/ - Pre-execution validation
  37. linting.py - Static analysis rules

What We Don't Provide

  • Strategy implementations - Live in separate tradai-strategies repo
  • Backtesting engine - Use Freqtrade directly
  • Data fetching - Use tradai-data library
  • Common utilities - Use tradai-common library

Module Organization

tradai/strategy/
├── __init__.py              # Public API exports
├── base.py                  # TradAIStrategy base class
├── metadata.py              # StrategyMetadata Pydantic model
├── enums.py                 # Type-safe enums
├── validation.py            # StrategyValidator
├── exceptions.py            # StrategyError hierarchy
├── filters.py               # Signal filtering utilities
├── dataframe_validators.py  # DataFrame validation
├── debug.py                 # StrategyDebugMixin
├── linting.py               # Static analysis rules
├── freqai/                  # FreqAI integration
│   ├── __init__.py
│   ├── config_builder.py    # FreqAI config construction
│   ├── prediction_model.py  # Prediction loading
│   ├── training_model.py    # Training utilities
│   └── utils.py             # Helper functions
├── sanity/                  # Runtime sanity checks
│   ├── __init__.py
│   ├── service.py           # Sanity check service
│   └── entities.py          # Sanity check entities
└── preflight/               # Pre-execution validation
    ├── __init__.py
    └── entities.py          # Preflight entities

Usage Patterns

Creating a Strategy

from tradai.strategy import TradAIStrategy, StrategyMetadata
from tradai.strategy.enums import StrategyCategory
import talib

class MyStrategy(TradAIStrategy):
    """Example trading strategy."""

    timeframe = '1h'
    can_short = True
    stoploss = -0.08

    def get_metadata(self) -> StrategyMetadata:
        return StrategyMetadata(
            name="MyStrategy",
            version="1.0.0",
            description="RSI mean reversion",
            timeframe=self.timeframe,
            can_short=self.can_short,
            category=StrategyCategory.MEAN_REVERSION,
            tags=["rsi", "mean-reversion"],
        )

    def populate_indicators(self, dataframe, metadata):
        dataframe['rsi'] = talib.RSI(dataframe['close'])
        return dataframe

    def populate_entry_trend(self, dataframe, metadata):
        dataframe.loc[dataframe['rsi'] < 30, 'enter_long'] = 1
        return dataframe

    def populate_exit_trend(self, dataframe, metadata):
        dataframe.loc[dataframe['rsi'] > 70, 'exit_long'] = 1
        return dataframe

Live Trading Strategy

def get_metadata(self) -> StrategyMetadata:
    return StrategyMetadata(
        name="LiveMomentumBot",
        version="2.1.0",
        description="Production momentum strategy",
        timeframe="4h",
        category=StrategyCategory.MOMENTUM,
        # Live trading fields (LM001)
        trading_modes=["backtest", "dry-run", "live"],
        exchange="binance",
        pairs=["BTC/USDT:USDT", "ETH/USDT:USDT"],
        stake_currency="USDT",
        status=StrategyStatus.ACTIVE,
    )

With FreqAI

from tradai.strategy.freqai import FreqAIConfigBuilder

class MLStrategy(TradAIStrategy):
    """FreqAI-enabled strategy."""

    def get_metadata(self) -> StrategyMetadata:
        return StrategyMetadata(
            name="MLStrategy",
            version="1.0.0",
            description="ML-based predictions",
            timeframe="1h",
            category=StrategyCategory.TREND_FOLLOWING,
            tags=["freqai", "ml", "gradient-boosting"],
        )

    @property
    def freqai_config(self) -> dict:
        return FreqAIConfigBuilder(
            model_name="LightGBMClassifier",
            label_period_candles=24,
            feature_parameters={
                "include_corr_pairlist": ["BTC/USDT:USDT"],
                "indicator_periods_candles": [10, 20, 50],
            },
        ).build()

Validation

from tradai.strategy import StrategyValidator
from tradai.strategy.enums import ValidationLevel

validator = StrategyValidator(level=ValidationLevel.STANDARD)
result = validator.validate(MyStrategy)

if not result.is_valid:
    for error in result.errors:
        print(f"Error: {error}")

Relationship to tradai-strategies

tradai-strategy (this library) provides the framework: - TradAIStrategy base class - Metadata schema - Validation utilities

tradai-strategies (separate private repo) contains implementations: - Production strategies (PascalStrategy, RadStrategy, etc.) - Example strategies for learning - Strategy-specific tests

tradai-uv/libs/tradai-strategy/    # Framework (public in workspace)
    └── src/tradai/strategy/

tradai-strategies/                  # Implementations (private repo)
    ├── strategies/                 # Production strategies
    │   ├── pascal-strategy/
    │   ├── rad-strategy/
    │   └── stochastic-strategy/
    └── examples/                   # Learning examples
        ├── minimal_strategy/
        └── momentum_strategy/

Design Patterns Applied

1. Extension Pattern (not Adapter)

# TradAIStrategy IS an IStrategy
class TradAIStrategy(IStrategy, StrategyDebugMixin):
    # All IStrategy methods work directly
    # Additional TradAI features added on top

2. Mixin Pattern (Composition)

class StrategyDebugMixin:
    """Debug utilities mixed into TradAIStrategy."""

    def check_nan_indicators(self, df, pair): ...
    def print_indicator_summary(self, df, pair): ...

class TradAIStrategy(IStrategy, StrategyDebugMixin):
    # Gets debug methods without inheritance hierarchy

3. Frozen Entities (Immutability)

class StrategyMetadata(BaseModel):
    model_config = ConfigDict(frozen=True)
    # All fields immutable after creation

4. Type-Safe Enums

class StrategyCategory(str, Enum):
    """str inheritance for JSON serialization."""
    TREND_FOLLOWING = "trend-following"
    MEAN_REVERSION = "mean-reversion"

Dependencies

Core: - freqtrade>=2024.1 (strategy framework) - pydantic>=2.0.0 (metadata validation) - pandas>=2.0.0 (dataframe operations)

Optional: - ta-lib (technical indicators) - pandas-ta (additional indicators)

Testing Strategy

  1. Unit Tests (tests/unit/)
  2. Test metadata validation
  3. Test enum serialization
  4. Test validator logic

  5. Integration Tests (tests/integration/)

  6. Test strategy loading with Freqtrade
  7. Test FreqAI integration

  8. Example Tests (tests/examples/)

  9. Verify example strategies work
  10. Validate with sample data

Test structure:

tests/
├── unit/
│   ├── test_metadata.py
│   ├── test_enums.py
│   ├── test_validation.py
│   ├── sanity/
│   │   └── test_service.py
│   └── preflight/
│       └── test_entities.py
├── freqai/
│   ├── test_config_builder.py
│   ├── test_prediction_model.py
│   └── test_training_model.py
├── test_base.py
├── test_filters.py
├── test_debug.py
└── test_linting.py

Code Quality Standards

  1. Type Hints: 100% coverage (strict mypy)
  2. Docstrings: Google style for all public APIs
  3. Test Coverage: 85%+ target
  4. Absolute Imports: Enforced via Ruff
  5. Frozen Entities: All Pydantic models use frozen=True

Success Criteria

  • [ ] All strategies extend TradAIStrategy
  • [ ] All strategies implement get_metadata()
  • [ ] 85%+ test coverage
  • [ ] 100% type hint coverage
  • [ ] Server-side validation works
  • [ ] MLflow tags generate correctly
  • [ ] Docker labels generate correctly
  • [ ] FreqAI integration works