2025-05-28 22:37:53 +08:00

19 KiB

Random Strategy Documentation

Overview

The Random Strategy is a testing and benchmarking strategy that generates random trading signals. While it may seem counterintuitive, this strategy serves crucial purposes in algorithmic trading: providing a baseline for performance comparison, testing framework robustness, and validating backtesting systems.

Strategy Concept

Core Philosophy

  • Baseline Comparison: Provides a random baseline to compare other strategies against
  • Framework Testing: Tests the robustness of the trading framework
  • Statistical Validation: Helps validate that other strategies perform better than random chance
  • System Debugging: Useful for debugging trading systems and backtesting frameworks

Key Features

  • Configurable Randomness: Adjustable probability distributions for signal generation
  • Seed Control: Reproducible results for testing and validation
  • Signal Frequency Control: Configurable frequency of signal generation
  • Confidence Simulation: Realistic confidence levels for testing signal processing

Algorithm Details

Mathematical Foundation

The Random Strategy uses probability distributions to generate signals:

Signal Generation:
- Generate random number R ~ Uniform(0, 1)
- If R < buy_probability: Generate BUY signal
- Elif R < (buy_probability + sell_probability): Generate SELL signal
- Else: Generate HOLD signal

Confidence Generation:
- Confidence ~ Beta(alpha, beta) or Uniform(min_conf, max_conf)
- Ensures realistic confidence distributions for testing

Signal Distribution

# Default probability distribution
signal_probabilities = {
    'BUY': 0.1,    # 10% chance of BUY signal
    'SELL': 0.1,   # 10% chance of SELL signal
    'HOLD': 0.8    # 80% chance of HOLD signal
}

# Confidence distribution
confidence_range = (0.5, 0.9)  # Realistic confidence levels

Process Flow Diagram

Data Input (OHLCV)
        ↓
TimeframeAggregator
        ↓
[15min aggregated data]
        ↓
┌─────────────────────────────────────┐
│         Random Strategy             │
│                                     │
│  ┌─────────────────────────────────┐│
│  │     Random Number Generator     ││
│  │                                 ││
│  │  • Seed Control                 ││
│  │  • Probability Distribution     ││
│  │  • Signal Frequency Control     ││
│  └─────────────────────────────────┘│
│                ↓                    │
│  ┌─────────────────────────────────┐│
│  │      Signal Generation          ││
│  │                                 ││
│  │  R = random()                   ││
│  │  if R < buy_prob:               ││
│  │      signal = BUY               ││
│  │  elif R < buy_prob + sell_prob: ││
│  │      signal = SELL              ││
│  │  else:                          ││
│  │      signal = HOLD              ││
│  └─────────────────────────────────┘│
│                ↓                    │
│  ┌─────────────────────────────────┐│
│  │    Confidence Generation        ││
│  │                                 ││
│  │  confidence = random_uniform(   ││
│  │      min_confidence,            ││
│  │      max_confidence             ││
│  │  )                              ││
│  └─────────────────────────────────┘│
└─────────────────────────────────────┘
        ↓
IncStrategySignal
        ↓
Trader Execution

Implementation Architecture

Class Hierarchy

IncStrategyBase
    ↓
RandomStrategy
    ├── TimeframeAggregator (inherited)
    ├── Random Number Generator
    ├── Probability Configuration
    └── Signal Generation Logic

Key Components

1. Random Number Generator

class RandomStrategy(IncStrategyBase):
    def __init__(self, name: str, params: dict = None):
        super().__init__(name, params)
        
        # Initialize random seed for reproducibility
        if self.params.get('seed') is not None:
            random.seed(self.params['seed'])
            np.random.seed(self.params['seed'])
        
        self.signal_count = 0
        self.last_signal_time = 0

2. Signal Generation Process

def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
    open_price, high, low, close, volume = ohlcv
    
    # Check signal frequency constraint
    if not self._should_generate_signal(timestamp):
        return IncStrategySignal.HOLD()
    
    # Generate random signal
    rand_val = random.random()
    
    if rand_val < self.params['buy_probability']:
        signal_type = 'BUY'
    elif rand_val < (self.params['buy_probability'] + self.params['sell_probability']):
        signal_type = 'SELL'
    else:
        signal_type = 'HOLD'
    
    # Generate random confidence
    confidence = random.uniform(
        self.params['min_confidence'],
        self.params['max_confidence']
    )
    
    # Create signal with metadata
    if signal_type == 'BUY':
        self.signal_count += 1
        self.last_signal_time = timestamp
        return IncStrategySignal.BUY(
            confidence=confidence,
            metadata=self._create_metadata(timestamp, rand_val, signal_type)
        )
    elif signal_type == 'SELL':
        self.signal_count += 1
        self.last_signal_time = timestamp
        return IncStrategySignal.SELL(
            confidence=confidence,
            metadata=self._create_metadata(timestamp, rand_val, signal_type)
        )
    
    return IncStrategySignal.HOLD()

3. Signal Frequency Control

def _should_generate_signal(self, timestamp: int) -> bool:
    """Control signal generation frequency."""
    
    # Check minimum time between signals
    min_interval = self.params.get('min_signal_interval_minutes', 0) * 60 * 1000
    if timestamp - self.last_signal_time < min_interval:
        return False
    
    # Check maximum signals per day
    max_daily_signals = self.params.get('max_daily_signals', float('inf'))
    if self.signal_count >= max_daily_signals:
        # Reset counter if new day (simplified)
        if self._is_new_day(timestamp):
            self.signal_count = 0
        else:
            return False
    
    return True

Configuration Parameters

Default Parameters

default_params = {
    "timeframe": "15min",                    # Data aggregation timeframe
    "buy_probability": 0.1,                  # Probability of generating BUY signal
    "sell_probability": 0.1,                 # Probability of generating SELL signal
    "min_confidence": 0.5,                   # Minimum confidence level
    "max_confidence": 0.9,                   # Maximum confidence level
    "seed": None,                           # Random seed (None for random)
    "min_signal_interval_minutes": 0,        # Minimum minutes between signals
    "max_daily_signals": float('inf'),       # Maximum signals per day
    "signal_frequency": 1.0                  # Signal generation frequency multiplier
}

Parameter Descriptions

Parameter Type Default Description
timeframe str "15min" Data aggregation timeframe
buy_probability float 0.1 Probability of generating BUY signal (0-1)
sell_probability float 0.1 Probability of generating SELL signal (0-1)
min_confidence float 0.5 Minimum confidence level for signals
max_confidence float 0.9 Maximum confidence level for signals
seed int None Random seed for reproducible results
min_signal_interval_minutes int 0 Minimum minutes between signals
max_daily_signals int inf Maximum signals per day

Parameter Optimization Ranges

optimization_ranges = {
    "buy_probability": [0.05, 0.1, 0.15, 0.2, 0.25],
    "sell_probability": [0.05, 0.1, 0.15, 0.2, 0.25],
    "min_confidence": [0.3, 0.4, 0.5, 0.6],
    "max_confidence": [0.7, 0.8, 0.9, 1.0],
    "signal_frequency": [0.5, 1.0, 1.5, 2.0],
    "timeframe": ["5min", "15min", "30min", "1h"]
}

Signal Generation Logic

Signal Types and Probabilities

Signal Distribution:

  • BUY: Configurable probability (default 10%)
  • SELL: Configurable probability (default 10%)
  • HOLD: Remaining probability (default 80%)

Confidence Generation:

  • Uniform distribution between min_confidence and max_confidence
  • Simulates realistic confidence levels for testing

Signal Metadata

Each signal includes comprehensive metadata for testing:

metadata = {
    'random_value': 0.0847,                  # Random value that generated signal
    'signal_number': 15,                     # Sequential signal number
    'probability_used': 0.1,                 # Probability threshold used
    'confidence_range': [0.5, 0.9],         # Confidence range used
    'seed_used': 12345,                      # Random seed if specified
    'generation_method': 'uniform',          # Random generation method
    'signal_frequency': 1.0,                 # Signal frequency multiplier
    'timestamp': 1640995200000               # Signal generation timestamp
}

Performance Characteristics

Expected Performance

  1. Random Walk: Should approximate random walk performance
  2. Zero Alpha: No systematic edge over random chance
  3. High Volatility: Typically high volatility due to random signals
  4. 50% Win Rate: Expected win rate around 50% (before costs)

Statistical Properties

  • Sharpe Ratio: Expected to be around 0 (random performance)
  • Maximum Drawdown: Highly variable, can be significant
  • Return Distribution: Should approximate normal distribution over time
  • Signal Distribution: Follows configured probability distribution

Use Cases

  1. Baseline Comparison: Compare other strategies against random performance
  2. Framework Testing: Test trading framework with known signal patterns
  3. Statistical Validation: Validate that other strategies beat random chance
  4. System Debugging: Debug backtesting and trading systems

Usage Examples

Basic Usage

from IncrementalTrader import RandomStrategy, IncTrader

# Create strategy with default parameters
strategy = RandomStrategy("random")

# Create trader
trader = IncTrader(strategy, initial_usd=10000)

# Process data
for timestamp, ohlcv in data_stream:
    signal = trader.process_data_point(timestamp, ohlcv)
    if signal.signal_type != 'HOLD':
        print(f"Random Signal: {signal.signal_type} (confidence: {signal.confidence:.2f})")

Reproducible Testing

# Create strategy with fixed seed for reproducible results
strategy = RandomStrategy("random_test", {
    "seed": 12345,
    "buy_probability": 0.15,
    "sell_probability": 0.15,
    "min_confidence": 0.6,
    "max_confidence": 0.8
})

Controlled Signal Frequency

# Create strategy with controlled signal frequency
strategy = RandomStrategy("random_controlled", {
    "buy_probability": 0.2,
    "sell_probability": 0.2,
    "min_signal_interval_minutes": 60,  # At least 1 hour between signals
    "max_daily_signals": 5              # Maximum 5 signals per day
})

Advanced Features

Custom Probability Distributions

def custom_signal_generation(self, timestamp: int) -> str:
    """Custom signal generation with time-based probabilities."""
    
    # Vary probabilities based on time of day
    hour = datetime.fromtimestamp(timestamp / 1000).hour
    
    if 9 <= hour <= 16:  # Market hours
        buy_prob = 0.15
        sell_prob = 0.15
    else:  # After hours
        buy_prob = 0.05
        sell_prob = 0.05
    
    rand_val = random.random()
    if rand_val < buy_prob:
        return 'BUY'
    elif rand_val < buy_prob + sell_prob:
        return 'SELL'
    return 'HOLD'

Confidence Distribution Modeling

def generate_realistic_confidence(self) -> float:
    """Generate confidence using beta distribution for realism."""
    
    # Beta distribution parameters for realistic confidence
    alpha = 2.0  # Shape parameter
    beta = 2.0   # Shape parameter
    
    # Generate beta-distributed confidence
    beta_sample = np.random.beta(alpha, beta)
    
    # Scale to desired range
    min_conf = self.params['min_confidence']
    max_conf = self.params['max_confidence']
    
    return min_conf + beta_sample * (max_conf - min_conf)

Market Regime Simulation

def simulate_market_regimes(self, timestamp: int) -> dict:
    """Simulate different market regimes for testing."""
    
    # Simple regime switching based on time
    regime_cycle = (timestamp // (24 * 60 * 60 * 1000)) % 3
    
    if regime_cycle == 0:  # Bull market
        return {
            'buy_probability': 0.2,
            'sell_probability': 0.05,
            'confidence_boost': 0.1
        }
    elif regime_cycle == 1:  # Bear market
        return {
            'buy_probability': 0.05,
            'sell_probability': 0.2,
            'confidence_boost': 0.1
        }
    else:  # Sideways market
        return {
            'buy_probability': 0.1,
            'sell_probability': 0.1,
            'confidence_boost': 0.0
        }

Backtesting Results

Expected Performance Metrics

Timeframe: 15min
Period: 2024-01-01 to 2024-12-31
Initial Capital: $10,000

Expected Results:
Total Return: ~0% (random walk)
Sharpe Ratio: ~0.0
Max Drawdown: Variable (10-30%)
Win Rate: ~50%
Profit Factor: ~1.0 (before costs)
Total Trades: Variable based on probabilities

Statistical Analysis

Signal Distribution Analysis:
BUY Signals: ~10% of total data points
SELL Signals: ~10% of total data points
HOLD Signals: ~80% of total data points

Confidence Distribution:
Mean Confidence: 0.7 (midpoint of range)
Std Confidence: Varies by distribution type
Min Confidence: 0.5
Max Confidence: 0.9

Implementation Notes

Memory Efficiency

  • Minimal State: Only tracks signal count and timing
  • No Indicators: No technical indicators to maintain
  • Constant Memory: O(1) memory usage

Real-time Capability

  • Ultra-Fast: Minimal processing per data point
  • No Dependencies: No indicator calculations required
  • Immediate Signals: Instant signal generation

Error Handling

def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
    try:
        # Validate basic data
        if not self._validate_ohlcv(ohlcv):
            self.logger.warning(f"Invalid OHLCV data: {ohlcv}")
            return IncStrategySignal.HOLD()
        
        # Generate random signal
        return self._generate_random_signal(timestamp)
        
    except Exception as e:
        self.logger.error(f"Error in Random strategy: {e}")
        return IncStrategySignal.HOLD()

Testing and Validation

Framework Testing

def test_signal_distribution():
    """Test that signal distribution matches expected probabilities."""
    
    strategy = RandomStrategy("test", {"seed": 12345})
    signals = []
    
    # Generate many signals
    for i in range(10000):
        signal = strategy._generate_random_signal(i)
        signals.append(signal.signal_type)
    
    # Analyze distribution
    buy_ratio = signals.count('BUY') / len(signals)
    sell_ratio = signals.count('SELL') / len(signals)
    hold_ratio = signals.count('HOLD') / len(signals)
    
    assert abs(buy_ratio - 0.1) < 0.02   # Within 2% of expected
    assert abs(sell_ratio - 0.1) < 0.02  # Within 2% of expected
    assert abs(hold_ratio - 0.8) < 0.02  # Within 2% of expected

Reproducibility Testing

def test_reproducibility():
    """Test that same seed produces same results."""
    
    strategy1 = RandomStrategy("test1", {"seed": 12345})
    strategy2 = RandomStrategy("test2", {"seed": 12345})
    
    signals1 = []
    signals2 = []
    
    # Generate signals with both strategies
    for i in range(1000):
        sig1 = strategy1._generate_random_signal(i)
        sig2 = strategy2._generate_random_signal(i)
        signals1.append((sig1.signal_type, sig1.confidence))
        signals2.append((sig2.signal_type, sig2.confidence))
    
    # Should be identical
    assert signals1 == signals2

Troubleshooting

Common Issues

  1. Non-Random Results

    • Check if seed is set (removes randomness)
    • Verify probability parameters are correct
    • Ensure random number generator is working
  2. Too Many/Few Signals

    • Adjust buy_probability and sell_probability
    • Check signal frequency constraints
    • Verify timeframe settings
  3. Unrealistic Performance

    • Random strategy should perform around 0% return
    • If significantly positive/negative, check for bugs
    • Verify transaction costs are included

Debug Information

# Enable debug logging
strategy.logger.setLevel(logging.DEBUG)

# Check signal statistics
print(f"Total signals generated: {strategy.signal_count}")
print(f"Buy probability: {strategy.params['buy_probability']}")
print(f"Sell probability: {strategy.params['sell_probability']}")
print(f"Current seed: {strategy.params.get('seed', 'None (random)')}")

Integration with Testing Framework

Benchmark Comparison

def compare_with_random_baseline(strategy_results, random_results):
    """Compare strategy performance against random baseline."""
    
    strategy_return = strategy_results['total_return']
    random_return = random_results['total_return']
    
    # Calculate excess return over random
    excess_return = strategy_return - random_return
    
    # Statistical significance test
    t_stat, p_value = stats.ttest_ind(
        strategy_results['daily_returns'],
        random_results['daily_returns']
    )
    
    return {
        'excess_return': excess_return,
        'statistical_significance': p_value < 0.05,
        't_statistic': t_stat,
        'p_value': p_value
    }

The Random Strategy serves as a crucial testing and benchmarking tool, providing a baseline for performance comparison and validating that other strategies perform better than random chance. While it generates no alpha by design, it's invaluable for framework testing and statistical validation.