Skip to content

Create a New Strategy

Build a new trading strategy using the TradAI framework.

Prerequisites

  • TradAI workspace set up (just setup completed)
  • Familiarity with Python and pandas
  • Basic understanding of technical indicators

Quick Start Options

The easiest way to create a strategy:

tradai strategy wizard

The wizard guides you through: 1. Basic info (name, description, author) 2. Trading category with descriptions 3. Timeframe with recommendations 4. Exchange and trading mode 5. Trading pairs 6. Risk management (stoploss, trailing stop) 7. Advanced options (ML, shorting)

Option B: CLI with Options

# Basic
tradai strategy new MyMomentumStrategy -y

# With options
tradai strategy new MyMomentumStrategy \
  --category momentum \
  --timeframe 1h \
  --exchange binance \
  --symbols "BTC/USDT:USDT,ETH/USDT:USDT" \
  --stoploss -0.05 \
  --can-short \
  -y

See Strategy Templates for more examples.

Option C: Cookiecutter Directly

cd strategies
uv run cookiecutter ../cookiecutter-tradai-strategy

This creates:

strategies/
└── mymomentstrategy/
    ├── pyproject.toml
    ├── src/mymomentstrategy/
    │   ├── __init__.py
    │   ├── strategy.py       # Main strategy class
    │   ├── indicators.py     # Technical indicators
    │   ├── signals.py        # Entry/exit signals
    │   ├── filters.py        # Trade filters
    │   └── constants.py      # Strategy parameters
    └── tests/
        └── test_strategy.py

2. Implement Your Strategy

Edit strategy.py to implement the required methods:

"""MyMomentumStrategy - Momentum-based trading strategy."""

from typing import Any

from freqtrade.strategy import IntParameter
from pandas import DataFrame
from tradai.common.logger_mixin import LoggerMixin
from tradai.strategy import StrategyCategory, StrategyMetadata, TradAIStrategy


class MyMomentumStrategy(TradAIStrategy, LoggerMixin):
    """Momentum strategy using EMA crossover with RSI confirmation."""

    # Trading parameters
    timeframe = "1h"
    can_short = True
    stoploss = -0.05  # 5% stoploss
    minimal_roi = {"0": 0.1}  # 10% ROI target

    # Hyperopt parameters
    ema_fast = IntParameter(5, 20, default=12, space="buy", optimize=True)
    ema_slow = IntParameter(20, 50, default=26, space="buy", optimize=True)
    rsi_period = IntParameter(10, 20, default=14, space="buy", optimize=True)

    def __init__(self, config: dict[str, Any]) -> None:
        super().__init__(config)
        LoggerMixin.__init__(self)

    def get_metadata(self) -> StrategyMetadata:
        """Return strategy metadata for TradAI registration."""
        return StrategyMetadata(
            name="MyMomentumStrategy",
            version="1.0.0",
            description="Momentum strategy using EMA crossover",
            timeframe=self.timeframe,
            category=StrategyCategory.MOMENTUM,
            can_short=self.can_short,
            tags=["ema", "rsi", "momentum"],
            author="TradAI Team",
            parameters={
                "ema_fast": self.ema_fast.value,
                "ema_slow": self.ema_slow.value,
                "rsi_period": self.rsi_period.value,
            },
        )

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Calculate technical indicators."""
        import pandas_ta as ta

        # EMA crossover
        dataframe["ema_fast"] = ta.ema(dataframe["close"], length=self.ema_fast.value)
        dataframe["ema_slow"] = ta.ema(dataframe["close"], length=self.ema_slow.value)

        # RSI for confirmation
        dataframe["rsi"] = ta.rsi(dataframe["close"], length=self.rsi_period.value)

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Generate entry signals."""
        # Long: Fast EMA crosses above slow EMA + RSI below 70
        dataframe.loc[
            (dataframe["ema_fast"] > dataframe["ema_slow"])
            & (dataframe["ema_fast"].shift(1) <= dataframe["ema_slow"].shift(1))
            & (dataframe["rsi"] < 70),
            "enter_long",
        ] = 1

        # Short: Fast EMA crosses below slow EMA + RSI above 30
        if self.can_short:
            dataframe.loc[
                (dataframe["ema_fast"] < dataframe["ema_slow"])
                & (dataframe["ema_fast"].shift(1) >= dataframe["ema_slow"].shift(1))
                & (dataframe["rsi"] > 30),
                "enter_short",
            ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Generate exit signals (optional - can use ROI/stoploss instead)."""
        # Exit long: RSI overbought
        dataframe.loc[dataframe["rsi"] > 80, "exit_long"] = 1

        # Exit short: RSI oversold
        if self.can_short:
            dataframe.loc[dataframe["rsi"] < 20, "exit_short"] = 1

        return dataframe

3. Register Strategy in Workspace

Add your strategy to the UV workspace:

# From workspace root
echo '    "strategies/mymomentstrategy",' >> pyproject.toml

# Sync workspace
uv sync --all-packages

4. Run Tests

# Run strategy tests
uv run pytest strategies/mymomentstrategy/tests/ -v

# Type check
uv run mypy --package mymomentstrategy

5. Run Backtest

# First, sync data
tradai data sync BTC/USDT:USDT --start 2024-01-01 --end 2024-03-31 --timeframe 1h

# Run backtest
tradai strategy backtest \
  --strategy MyMomentumStrategy \
  --start 2024-01-01 \
  --end 2024-03-31 \
  --symbol BTC/USDT:USDT \
  --experiment "momentum-dev"

Strategy Architecture

Required Methods

Method Purpose
get_metadata() Returns StrategyMetadata for TradAI registration
populate_indicators() Calculate technical indicators on DataFrame
populate_entry_trend() Set enter_long / enter_short signals
populate_exit_trend() Set exit_long / exit_short signals

Key Parameters

Parameter Description
timeframe Candle timeframe ("1m", "1h", "1d")
can_short Enable short positions
stoploss Stop loss ratio (-0.05 = 5% loss)
minimal_roi Take profit targets by time
trailing_stop Enable trailing stop
startup_candle_count Warmup candles for indicators

Modular Structure (Advanced)

For complex strategies, use modular structure:

strategy/
├── strategy.py      # Main class, orchestration
├── indicators.py    # calculate_indicators()
├── signals.py       # generate_entry_signals(), generate_exit_signals()
├── filters.py       # apply_filters() - volume, time, etc.
└── constants.py     # All parameter defaults

Example indicators.py:

"""Technical indicator calculations."""

from pandas import DataFrame
import pandas_ta as ta


def calculate_indicators(dataframe: DataFrame, ema_fast: int, ema_slow: int, rsi_period: int) -> DataFrame:
    """Calculate all technical indicators."""
    dataframe["ema_fast"] = ta.ema(dataframe["close"], length=ema_fast)
    dataframe["ema_slow"] = ta.ema(dataframe["close"], length=ema_slow)
    dataframe["rsi"] = ta.rsi(dataframe["close"], length=rsi_period)
    return dataframe

Testing Your Strategy

# tests/test_strategy.py
import pytest
from pandas import DataFrame
from mymomentstrategy.strategy import MyMomentumStrategy


@pytest.fixture
def strategy():
    """Create strategy instance for testing."""
    config = {"stake_currency": "USDT"}
    return MyMomentumStrategy(config)


def test_get_metadata(strategy):
    """Test metadata is correctly defined."""
    metadata = strategy.get_metadata()
    assert metadata.name == "MyMomentumStrategy"
    assert metadata.version == "1.0.0"
    assert metadata.timeframe == "1h"


def test_populate_indicators(strategy, sample_dataframe):
    """Test indicators are calculated."""
    df = strategy.populate_indicators(sample_dataframe, {})
    assert "ema_fast" in df.columns
    assert "ema_slow" in df.columns
    assert "rsi" in df.columns

Next Steps