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
- Random Walk: Should approximate random walk performance
- Zero Alpha: No systematic edge over random chance
- High Volatility: Typically high volatility due to random signals
- 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
- Baseline Comparison: Compare other strategies against random performance
- Framework Testing: Test trading framework with known signal patterns
- Statistical Validation: Validate that other strategies beat random chance
- 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
-
Non-Random Results
- Check if seed is set (removes randomness)
- Verify probability parameters are correct
- Ensure random number generator is working
-
Too Many/Few Signals
- Adjust buy_probability and sell_probability
- Check signal frequency constraints
- Verify timeframe settings
-
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.