This guide helps developers extend and customize ForexSmartBot.
forexsmartbot/
├── core/ # Core interfaces and base classes
├── strategies/ # Trading strategies
├── optimization/ # Optimization tools
├── monitoring/ # Monitoring and analytics
├── builder/ # Strategy builder
├── marketplace/ # Strategy marketplace
├── adapters/ # Data and broker adapters
├── services/ # Business logic services
└── ui/ # User interface
from forexsmartbot.core.interfaces import IStrategy
import pandas as pd
from typing import Dict, Any, Optional
class MyCustomStrategy(IStrategy):
"""My custom trading strategy."""
def __init__(self, param1: int = 20, param2: float = 0.5):
self._param1 = param1
self._param2 = param2
self._name = "My Custom Strategy"
@property
def name(self) -> str:
return self._name
@property
def params(self) -> Dict[str, Any]:
return {
'param1': self._param1,
'param2': self._param2
}
def set_params(self, **kwargs) -> None:
if 'param1' in kwargs:
self._param1 = int(kwargs['param1'])
if 'param2' in kwargs:
self._param2 = float(kwargs['param2'])
def indicators(self, df: pd.DataFrame) -> pd.DataFrame:
"""Calculate indicators."""
out = df.copy()
# Your indicator calculations
out['MyIndicator'] = calculate_my_indicator(out, self._param1)
return out
def signal(self, df: pd.DataFrame) -> int:
"""Generate signal: +1 (buy), -1 (sell), 0 (hold)."""
if len(df) < self._param1:
return 0
# Your signal logic
indicator = df['MyIndicator'].iloc[-1]
if indicator > self._param2:
return 1 # Buy
elif indicator < -self._param2:
return -1 # Sell
else:
return 0 # Hold
def volatility(self, df: pd.DataFrame) -> Optional[float]:
"""Calculate volatility for position sizing."""
if 'ATR' in df.columns and len(df) > 0:
atr = float(df['ATR'].iloc[-1])
price = float(df['Close'].iloc[-1])
if price > 0:
return atr / price
return None
def stop_loss(self, df: pd.DataFrame, entry_price: float, side: int) -> Optional[float]:
"""Calculate stop loss."""
if 'ATR' in df.columns and len(df) > 0:
atr = float(df['ATR'].iloc[-1])
if side > 0: # Long
return entry_price - (2 * atr)
else: # Short
return entry_price + (2 * atr)
return None
def take_profit(self, df: pd.DataFrame, entry_price: float, side: int) -> Optional[float]:
"""Calculate take profit."""
if 'ATR' in df.columns and len(df) > 0:
atr = float(df['ATR'].iloc[-1])
if side > 0: # Long
return entry_price + (4 * atr)
else: # Short
return entry_price - (4 * atr)
return NoneAdd to forexsmartbot/strategies/__init__.py:
from .my_custom_strategy import MyCustomStrategy
STRATEGIES = {
# ... existing strategies ...
'My_Custom_Strategy': MyCustomStrategy,
}from forexsmartbot.strategies import get_strategy
from forexsmartbot.services.backtest import BacktestService
from forexsmartbot.adapters.data import YFinanceProvider
strategy = get_strategy('My_Custom_Strategy', param1=20, param2=0.5)
service = BacktestService(YFinanceProvider())
results = service.run_backtest(strategy, 'EURUSD=X', '2023-01-01', '2023-12-31')from typing import Dict, Any, Callable, Tuple
import numpy as np
class MyCustomOptimizer:
"""Custom optimization algorithm."""
def __init__(self, param_bounds: Dict[str, Tuple[float, float]]):
self.param_bounds = param_bounds
def optimize(self, fitness_function: Callable[[Dict[str, float]], float]) -> Tuple[Dict[str, float], float]:
"""Run optimization."""
# Your optimization algorithm
best_params = {}
best_fitness = -float('inf')
# Example: Grid search
for param_name, (min_val, max_val) in self.param_bounds.items():
# Test values
for val in np.linspace(min_val, max_val, 10):
params = {param_name: val}
fitness = fitness_function(params)
if fitness > best_fitness:
best_fitness = fitness
best_params = params
return best_params, best_fitnessfrom forexsmartbot.core.interfaces import IDataProvider
import pandas as pd
from typing import Optional
class MyDataProvider(IDataProvider):
"""Custom data provider."""
def get_data(self, symbol: str, start_date: str, end_date: str,
interval: str = "1h") -> pd.DataFrame:
"""Fetch market data."""
# Your data fetching logic
# Return DataFrame with columns: Open, High, Low, Close, Volume
df = fetch_from_your_source(symbol, start_date, end_date, interval)
return df
def get_latest_price(self, symbol: str) -> Optional[float]:
"""Get latest price."""
# Your implementation
return get_latest_from_source(symbol)from forexsmartbot.builder.strategy_builder import ComponentType, StrategyComponent
# Add new component type
class CustomComponentType(Enum):
MY_CUSTOM_TYPE = "my_custom_type"
# Extend builder
class ExtendedStrategyBuilder(StrategyBuilder):
def add_custom_component(self, name: str, params: dict):
return self.add_component(CustomComponentType.MY_CUSTOM_TYPE, name, params)from forexsmartbot.monitoring import PerformanceTracker
class CustomPerformanceTracker(PerformanceTracker):
"""Extended performance tracker."""
def calculate_custom_metric(self, strategy_name: str) -> float:
"""Calculate custom metric."""
trades = self.get_trades(strategy_name)
# Your custom calculation
return custom_calculation(trades)Always implement required interface methods:
# Good: Implements all IStrategy methods
class MyStrategy(IStrategy):
def indicators(self, df): ...
def signal(self, df): ...
def volatility(self, df): ...
# ... all methods ...
# Bad: Missing methods
class MyStrategy(IStrategy):
def signal(self, df): ...
# Missing other methodsAlways handle errors gracefully:
def signal(self, df: pd.DataFrame) -> int:
try:
# Your logic
return calculated_signal
except Exception as e:
logger.error(f"Error in signal: {e}")
return 0 # Safe defaultUse type hints for better code clarity:
def calculate_indicator(
df: pd.DataFrame,
period: int,
multiplier: float = 2.0
) -> pd.Series:
"""Calculate indicator with type hints."""
# Implementation
return indicator_seriesDocument your code:
def my_function(param1: int, param2: float) -> float:
"""
Calculate something important.
Args:
param1: First parameter description
param2: Second parameter description
Returns:
Calculated result
Raises:
ValueError: If parameters are invalid
"""
# Implementation
passWrite tests for your code:
import unittest
class TestMyStrategy(unittest.TestCase):
def test_signal_generation(self):
strategy = MyCustomStrategy()
df = create_test_data()
signal = strategy.signal(df)
self.assertIn(signal, [-1, 0, 1])# In ui/enhanced_main_window.py
from forexsmartbot.strategies import get_strategy
# Add your strategy to the UI
strategy = get_strategy('My_Custom_Strategy')# In scripts/optimize_strategy.py
# Your strategy will automatically work with optimization toolsfrom forexsmartbot.marketplace import StrategyMarketplace, StrategyListing
marketplace = StrategyMarketplace()
listing = StrategyListing(
strategy_id="my_strategy_001",
name="My Custom Strategy",
# ... other fields ...
)
marketplace.add_listing(listing)my_extension/
├── __init__.py
├── my_strategy.py
├── my_optimizer.py
├── my_provider.py
└── tests/
└── test_my_extension.py
# In your extension
from forexsmartbot.core.interfaces import IStrategy
from forexsmartbot.optimization import GeneticOptimizer
# Your code uses ForexSmartBot interfacesimport logging
logging.basicConfig(level=logging.DEBUG)
# Your code will show debug messages# Test your strategy
from forexsmartbot.strategies import get_strategy
try:
strategy = get_strategy('My_Custom_Strategy')
print("✓ Strategy loads successfully")
except Exception as e:
print(f"✗ Error: {e}")- Follow PEP 8
- Use type hints
- Write docstrings
- Add comments for complex logic
- Write unit tests
- Test edge cases
- Test error handling
- Run validation script
- Document public APIs
- Add usage examples
- Update README if needed
- Document breaking changes
See examples/ directory for:
- Strategy implementations
- Optimization usage
- Builder examples
- Integration patterns
- Review existing code for patterns
- Check documentation
- Look at examples
- Ask questions in issues
Happy Coding! 🚀