Basic Examples

Simple moving average strategy to get started with RLX Backtester. This is the foundation for understanding the SDK.

Simple MA Strategy

Beginner~50 lines

A complete working example showing how to create a simple moving average crossover strategy, run a backtest, and print results. This example demonstrates the Strategy class pattern.

simple_test.pyFull example from /examples/basic/
#!/usr/bin/env python3
"""
Simple test of RLXBT SDK

Note: This example requires either:
1. Development build with `--features offline_license` (no license required)
2. Production build with valid license key (set RLX_LICENSE_KEY env var or pass to Backtester)

Get your license at https://rlxbt.com/pricing
"""

import sys
import os
import pandas as pd
import numpy as np

from rlxbt import Strategy, Backtester, load_data


class SimpleMAStrategy(Strategy):
    """Simple Moving Average Crossover Strategy"""

    def __init__(self, fast_period=10, slow_period=20):
        super().__init__()
        self.fast_period = fast_period
        self.slow_period = slow_period

    def generate_signals(self, data: pd.DataFrame) -> pd.DataFrame:
        close = data["close"]

        # Calculate indicators
        fast_ma = close.rolling(window=self.fast_period).mean()
        slow_ma = close.rolling(window=self.slow_period).mean()

        # Generate signals
        signals = np.zeros(len(data), dtype=int)

        # Long when fast > slow
        signals[fast_ma > slow_ma] = 1
        # Short when fast < slow
        signals[fast_ma < slow_ma] = -1

        df = pd.DataFrame(index=data.index)
        df["signal"] = signals

        return df


def run_test():
    print("šŸš€ Running Simple Test")

    # Check for license key (optional in dev builds)
    license_key = os.environ.get("RLX_LICENSE_KEY")
    if license_key:
        print(f"šŸ”‘ Using license key: {license_key[:20]}...")
    else:
        print("ā„¹ļø  No license key set (OK for development builds)")

    # Load data
    data_path = "data/BTCUSDT_1h_with_indicators.csv"
    data = load_data(data_path)
    print(f"šŸ“Š Loaded {len(data)} bars")

    # Create strategy
    strategy = SimpleMAStrategy(fast_period=10, slow_period=20)

    # Create backtester
    backtester = Backtester(
        initial_capital=10000.0,
        license_key=license_key,
    )

    # Run backtest
    results = backtester.run(strategy, data)

    # Print results
    print("\nšŸ“Š Results:")
    print(f"Total Return: {results['total_return']:.2%}")
    print(f"Sharpe Ratio: {results['sharpe_ratio']:.2f}")
    print(f"Max Drawdown: {results['max_drawdown']:.2%}")
    print(f"Total Trades: {results['total_trades']}")
    print(f"Win Rate: {results['win_rate']:.2%}")


if __name__ == "__main__":
    run_test()

Key Concepts

  • •Strategy class - Inherit and implement generate_signals()
  • •Signal values - 1 = Long, -1 = Short, 0 = Flat
  • •Backtester - Handles execution, position management
  • •Results dict - Contains all metrics automatically

Expected Output

šŸš€ Running Simple Test
ā„¹ļø No license key set (OK for development builds)
šŸ“Š Loaded 8760 bars
šŸ“Š Results:
Total Return: 45.23%
Sharpe Ratio: 1.42
Max Drawdown: -12.34%
Total Trades: 156
Win Rate: 52.56%

Variations

With Stop Loss & Take Profit

with_stops.py
class MAWithStops(Strategy):
    def generate_signals(self, data: pd.DataFrame) -> pd.DataFrame:
        close = data["close"]
        fast_ma = close.rolling(10).mean()
        slow_ma = close.rolling(20).mean()

        df = pd.DataFrame(index=data.index)
        df["signal"] = np.where(fast_ma > slow_ma, 1, 
                         np.where(fast_ma < slow_ma, -1, 0))
        
        # Add dynamic stop loss and take profit
        df["stop_loss"] = np.where(
            df["signal"] == 1,
            close * 0.98,  # 2% below for longs
            close * 1.02   # 2% above for shorts
        )
        df["take_profit"] = np.where(
            df["signal"] == 1,
            close * 1.04,  # 4% above for longs
            close * 0.96   # 4% below for shorts
        )
        
        return df

Using TradingEngine Directly

For more control, use the Rust engine directly:

engine_direct.py
from rlxbt import rlx, load_data

# Load and prepare data
data = load_data("data/BTCUSDT_1h.csv")
data["signal"] = 0
data.loc[data["SMA_20"] > data["SMA_50"], "signal"] = 1
data.loc[data["SMA_20"] < data["SMA_50"], "signal"] = -1

# Create Rust engine directly
engine = rlx.TradingEngine(
    initial_capital=100_000.0,
    commission=0.001,
    slippage=0.0005,
    contract_size=1.0,
    enable_dynamic_tp_sl=False,
)

# Run with signal column
result = engine.run_with_signals(data, "signal")

print(f"Total Return: {result.total_return * 100:.2f}%")
print(f"Trades: {len(result.trades)}")