Create a New Strategy¶
Build a new trading strategy using the TradAI framework.
Prerequisites¶
- TradAI workspace set up (
just setupcompleted) - Familiarity with Python and pandas
- Basic understanding of technical indicators
Quick Start Options¶
Option A: Interactive Wizard (Recommended)¶
The easiest way to create a strategy:
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¶
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¶
- Your First Backtest - Run and analyze backtests
- Collect Market Data - Sync data for more pairs
- View reference strategy:
strategies/pascal-strategy/