From b0e13f94679b5e6c385d14aaaa06e705659085e2 Mon Sep 17 00:00:00 2001 From: marcvanduyn Date: Sun, 28 Dec 2025 10:51:42 +0100 Subject: [PATCH 1/6] Fix doc page references --- .../docs/Contributing Guide/contributing.md | 638 +++++++++++++ .../docs/Contributing Guide/style-guide.md | 900 ++++++++++++++++++ docusaurus/docs/Data/download.md | 554 +++++++++++ docusaurus/docs/Data/market-data-sources.md | 556 +++++++++++ .../docs/Data/multiple-market-data-sources.md | 856 +++++++++++++++++ .../application-setup/index.html | 25 - .../Getting Started/backtesting/index.html | 23 - docusaurus/docs/Getting Started/deployment.md | 520 ++++++++++ .../Getting Started/deployment/index.html | 17 - .../docs/Getting Started/installation.md | 52 + .../Getting Started/installation/index.html | 17 - docusaurus/docs/Getting Started/orders.md | 362 +++++++ .../docs/Getting Started/orders/index.html | 33 - .../Getting Started/performance/index.html | 17 - .../portfolio-configuration.md | 124 +++ .../portfolio-configuration/index.html | 21 - docusaurus/docs/Getting Started/positions.md | 404 ++++++++ .../docs/Getting Started/positions/index.html | 22 - .../docs/Getting Started/simple-example.md | 626 ++++++++++++ docusaurus/docs/Getting Started/strategies.md | 291 ++++++ .../Getting Started/strategies/index.html | 19 - docusaurus/docs/Getting Started/tasks.md | 607 ++++++++++++ .../docs/Getting Started/tasks/index.html | 17 - docusaurus/docs/Getting Started/trades.md | 472 +++++++++ .../docs/Getting Started/trades/index.html | 17 - docusaurus/docs/introduction.md | 50 + docusaurus/sidebar.js | 4 + 27 files changed, 7016 insertions(+), 228 deletions(-) create mode 100644 docusaurus/docs/Contributing Guide/contributing.md create mode 100644 docusaurus/docs/Contributing Guide/style-guide.md create mode 100644 docusaurus/docs/Data/download.md create mode 100644 docusaurus/docs/Data/market-data-sources.md create mode 100644 docusaurus/docs/Data/multiple-market-data-sources.md delete mode 100644 docusaurus/docs/Getting Started/application-setup/index.html delete mode 100644 docusaurus/docs/Getting Started/backtesting/index.html create mode 100644 docusaurus/docs/Getting Started/deployment.md delete mode 100644 docusaurus/docs/Getting Started/deployment/index.html create mode 100644 docusaurus/docs/Getting Started/installation.md delete mode 100644 docusaurus/docs/Getting Started/installation/index.html create mode 100644 docusaurus/docs/Getting Started/orders.md delete mode 100644 docusaurus/docs/Getting Started/orders/index.html delete mode 100644 docusaurus/docs/Getting Started/performance/index.html create mode 100644 docusaurus/docs/Getting Started/portfolio-configuration.md delete mode 100644 docusaurus/docs/Getting Started/portfolio-configuration/index.html create mode 100644 docusaurus/docs/Getting Started/positions.md delete mode 100644 docusaurus/docs/Getting Started/positions/index.html create mode 100644 docusaurus/docs/Getting Started/simple-example.md create mode 100644 docusaurus/docs/Getting Started/strategies.md delete mode 100644 docusaurus/docs/Getting Started/strategies/index.html create mode 100644 docusaurus/docs/Getting Started/tasks.md delete mode 100644 docusaurus/docs/Getting Started/tasks/index.html create mode 100644 docusaurus/docs/Getting Started/trades.md delete mode 100644 docusaurus/docs/Getting Started/trades/index.html create mode 100644 docusaurus/docs/introduction.md diff --git a/docusaurus/docs/Contributing Guide/contributing.md b/docusaurus/docs/Contributing Guide/contributing.md new file mode 100644 index 00000000..5ba9d9dc --- /dev/null +++ b/docusaurus/docs/Contributing Guide/contributing.md @@ -0,0 +1,638 @@ +--- +sidebar_position: 1 +--- + +# Contributing + +Welcome to the Investing Algorithm Framework contributing guide! We appreciate your interest in contributing to the project. + +## Getting Started + +### Prerequisites + +Before contributing, ensure you have: + +- **Python 3.8+** installed +- **Git** for version control +- **Poetry** for dependency management (recommended) +- A **GitHub account** + +### Setting Up Development Environment + +1. **Fork the Repository** + ```bash + # Go to GitHub and fork the repository + # https://github.com/coding-kitties/investing-algorithm-framework + ``` + +2. **Clone Your Fork** + ```bash + git clone https://github.com/YOUR_USERNAME/investing-algorithm-framework.git + cd investing-algorithm-framework + ``` + +3. **Add Upstream Remote** + ```bash + git remote add upstream https://github.com/coding-kitties/investing-algorithm-framework.git + ``` + +4. **Install Dependencies** + ```bash + # Using Poetry (recommended) + poetry install --with dev,test + poetry shell + + # Or using pip + pip install -e .[dev,test] + ``` + +5. **Install Pre-commit Hooks** + ```bash + pre-commit install + ``` + +## Development Workflow + +### Creating a Feature Branch + +```bash +# Make sure you're on main and up to date +git checkout main +git pull upstream main + +# Create a new feature branch +git checkout -b feature/your-feature-name + +# Or for bug fixes +git checkout -b fix/issue-description +``` + +### Making Changes + +1. **Write Tests First** (TDD approach recommended) + ```python + # tests/test_your_feature.py + import pytest + from investing_algorithm_framework import YourNewFeature + + class TestYourNewFeature: + def test_basic_functionality(self): + feature = YourNewFeature() + result = feature.do_something() + assert result == expected_value + ``` + +2. **Implement Your Feature** + ```python + # investing_algorithm_framework/your_module.py + class YourNewFeature: + def do_something(self): + # Your implementation + pass + ``` + +3. **Run Tests** + ```bash + # Run specific tests + pytest tests/test_your_feature.py + + # Run all tests + pytest + + # Run with coverage + pytest --cov=investing_algorithm_framework + ``` + +4. **Check Code Quality** + ```bash + # Linting + flake8 investing_algorithm_framework tests + + # Type checking + mypy investing_algorithm_framework + + # Format code + black investing_algorithm_framework tests + isort investing_algorithm_framework tests + ``` + +### Committing Changes + +1. **Stage Your Changes** + ```bash + git add . + ``` + +2. **Commit with Descriptive Message** + ```bash + git commit -m "feat: add new portfolio rebalancing strategy + + - Implement automatic portfolio rebalancing + - Add threshold-based rebalancing triggers + - Include comprehensive tests for edge cases + - Update documentation with usage examples + + Fixes #123" + ``` + +### Submitting a Pull Request + +1. **Push Your Branch** + ```bash + git push origin feature/your-feature-name + ``` + +2. **Create Pull Request** + - Go to GitHub and create a Pull Request + - Use the PR template + - Link related issues + - Provide clear description of changes + +3. **Address Review Feedback** + - Make requested changes + - Push additional commits to your branch + - Respond to reviewer comments + +## Contribution Types + +### Bug Reports + +When reporting bugs, please include: + +```markdown +**Bug Description** +A clear description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Create app with configuration X +2. Add strategy Y +3. Run backtest with parameters Z +4. See error + +**Expected Behavior** +A clear description of what you expected to happen. + +**Environment** +- OS: [e.g., macOS, Ubuntu 20.04] +- Python version: [e.g., 3.9.7] +- Framework version: [e.g., 1.2.3] + +**Additional Context** +- Error logs +- Configuration files +- Screenshots (if applicable) +``` + +### Feature Requests + +For feature requests, please provide: + +```markdown +**Feature Description** +A clear description of the feature you'd like to see. + +**Use Case** +Describe the problem this feature would solve. + +**Proposed Solution** +If you have ideas on implementation, please share them. + +**Alternatives Considered** +Any alternative solutions or workarounds you've considered. + +**Additional Context** +Any other context, screenshots, or examples. +``` + +### Code Contributions + +#### New Features + +```python +# Example: Adding a new indicator + +class RSIIndicator: + """Relative Strength Index technical indicator.""" + + def __init__(self, period: int = 14): + """ + Initialize RSI indicator. + + Args: + period: Number of periods for RSI calculation + """ + self.period = period + + def calculate(self, prices: List[float]) -> List[float]: + """ + Calculate RSI values. + + Args: + prices: List of closing prices + + Returns: + List of RSI values + + Raises: + ValueError: If insufficient data points + """ + if len(prices) < self.period + 1: + raise ValueError(f"Need at least {self.period + 1} data points") + + # Implementation here + pass +``` + +#### Documentation + +```python +def create_trading_strategy( + name: str, + symbols: List[str], + timeframe: str = "1d" +) -> TradingStrategy: + """ + Create a new trading strategy. + + This function helps create a trading strategy with the specified + parameters. The strategy will be configured to trade the given + symbols on the specified timeframe. + + Args: + name: Name of the trading strategy + symbols: List of symbols to trade (e.g., ["BTC/USDT", "ETH/USDT"]) + timeframe: Trading timeframe (default: "1d") + + Returns: + TradingStrategy: Configured trading strategy instance + + Example: + >>> strategy = create_trading_strategy( + ... name="MA Crossover", + ... symbols=["BTC/USDT"], + ... timeframe="1h" + ... ) + >>> app.add_strategy(strategy) + + Raises: + ValueError: If invalid symbols or timeframe provided + """ + # Implementation + pass +``` + +## Code Style Guidelines + +### Python Code Style + +We follow [PEP 8](https://www.python.org/dev/peps/pep-0008/) with some modifications: + +```python +# Good: Clear, descriptive names +class MovingAverageStrategy(TradingStrategy): + def __init__(self, short_period: int = 20, long_period: int = 50): + self.short_period = short_period + self.long_period = long_period + + def calculate_moving_average(self, prices: List[float], period: int) -> float: + """Calculate simple moving average.""" + return sum(prices[-period:]) / period + +# Bad: Unclear names, no type hints +class MAStrat: + def __init__(self, s=20, l=50): + self.s = s + self.l = l + + def calc_ma(self, p, per): + return sum(p[-per:]) / per +``` + +### Import Organization + +```python +# Standard library imports +import logging +import os +from datetime import datetime, timedelta +from typing import List, Optional, Dict + +# Third-party imports +import pandas as pd +import numpy as np +from ccxt import Exchange + +# Local imports +from investing_algorithm_framework.domain import TradingStrategy +from investing_algorithm_framework.infrastructure import CCXTOrderExecutor +``` + +### Error Handling + +```python +# Good: Specific exceptions with helpful messages +def get_market_data(symbol: str, timeframe: str) -> pd.DataFrame: + try: + data = self.data_provider.get_data(symbol, timeframe) + if not data: + raise DataError(f"No data available for {symbol} on {timeframe}") + return pd.DataFrame(data) + except ConnectionError as e: + raise DataError(f"Failed to fetch data for {symbol}: {e}") from e + except Exception as e: + logger.error(f"Unexpected error fetching {symbol} data: {e}") + raise + +# Bad: Broad exception handling +def get_market_data(symbol, timeframe): + try: + data = self.data_provider.get_data(symbol, timeframe) + return pd.DataFrame(data) + except: + return None +``` + +## Testing Guidelines + +### Unit Tests + +```python +import pytest +from unittest.mock import Mock, patch +from investing_algorithm_framework import TradingStrategy + +class TestTradingStrategy: + def test_strategy_initialization(self): + """Test strategy can be initialized with default parameters.""" + strategy = TradingStrategy() + assert strategy is not None + + def test_strategy_with_custom_parameters(self): + """Test strategy initialization with custom parameters.""" + strategy = TradingStrategy(risk_per_trade=0.02) + assert strategy.risk_per_trade == 0.02 + + @patch('investing_algorithm_framework.MarketDataProvider') + def test_strategy_execution_with_mock_data(self, mock_data_provider): + """Test strategy execution with mocked market data.""" + # Setup + mock_data_provider.get_data.return_value = [ + {"open": 100, "high": 105, "low": 95, "close": 102} + ] + + strategy = TradingStrategy() + algorithm = Mock() + + # Execute + strategy.apply_strategy(algorithm, mock_data_provider) + + # Assert + mock_data_provider.get_data.assert_called() + + def test_strategy_handles_no_data(self): + """Test strategy handles case when no market data is available.""" + strategy = TradingStrategy() + algorithm = Mock() + market_data = Mock() + market_data.get_data.return_value = None + + # Should not raise exception + strategy.apply_strategy(algorithm, market_data) +``` + +### Integration Tests + +```python +class TestTradingIntegration: + @pytest.fixture + def app(self): + """Create test app with minimal configuration.""" + from investing_algorithm_framework import create_app + return create_app(config={'testing': True}) + + def test_end_to_end_trading_flow(self, app): + """Test complete trading flow from strategy to order execution.""" + # Add test strategy + strategy = TestStrategy() + app.add_strategy(strategy) + + # Add test data + app.add_test_data("BTC/USDT", test_market_data) + + # Run backtest + results = app.run_backtest( + start_date="2023-01-01", + end_date="2023-01-31" + ) + + # Verify results + assert results.total_trades > 0 + assert results.final_portfolio_value > 0 +``` + +### Performance Tests + +```python +import pytest +import time + +class TestPerformance: + def test_strategy_execution_performance(self): + """Test strategy executes within acceptable time limits.""" + strategy = TradingStrategy() + algorithm = Mock() + market_data = Mock() + market_data.get_data.return_value = generate_large_dataset(10000) + + start_time = time.time() + strategy.apply_strategy(algorithm, market_data) + execution_time = time.time() - start_time + + # Should complete within 1 second for 10k data points + assert execution_time < 1.0 + + @pytest.mark.benchmark + def test_backtest_performance(self, benchmark): + """Benchmark backtest execution speed.""" + app = create_test_app() + + result = benchmark( + app.run_backtest, + start_date="2023-01-01", + end_date="2023-12-31" + ) + + assert result.total_trades >= 0 +``` + +## Documentation Standards + +### Docstring Format + +We use Google-style docstrings: + +```python +def calculate_portfolio_metrics( + portfolio_value: float, + benchmark_value: float, + risk_free_rate: float = 0.02 +) -> Dict[str, float]: + """ + Calculate portfolio performance metrics. + + This function calculates common portfolio performance metrics + including Sharpe ratio, alpha, and beta relative to a benchmark. + + Args: + portfolio_value: Current portfolio value in base currency + benchmark_value: Current benchmark value + risk_free_rate: Risk-free rate for Sharpe ratio calculation. + Defaults to 2% (0.02). + + Returns: + Dict containing calculated metrics: + - sharpe_ratio: Risk-adjusted return metric + - alpha: Excess return over benchmark + - beta: Portfolio sensitivity to benchmark + + Raises: + ValueError: If portfolio_value or benchmark_value is negative + + Example: + >>> metrics = calculate_portfolio_metrics(10000, 9500, 0.025) + >>> print(f"Sharpe Ratio: {metrics['sharpe_ratio']:.2f}") + Sharpe Ratio: 1.25 + """ + if portfolio_value < 0 or benchmark_value < 0: + raise ValueError("Portfolio and benchmark values must be non-negative") + + # Implementation here + return { + "sharpe_ratio": 0.0, + "alpha": 0.0, + "beta": 0.0 + } +``` + +### README Updates + +When adding new features, update relevant documentation: + +```markdown +## New Portfolio Rebalancing Feature + +The framework now supports automatic portfolio rebalancing: + +```python +from investing_algorithm_framework import PortfolioRebalancer + +# Configure rebalancing strategy +rebalancer = PortfolioRebalancer( + target_weights={"BTC": 0.6, "ETH": 0.3, "ADA": 0.1}, + rebalance_threshold=0.05, # 5% deviation triggers rebalance + rebalance_frequency="weekly" +) + +app.add_rebalancer(rebalancer) +``` + +This feature helps maintain target asset allocation automatically. +``` + +## Pull Request Process + +### PR Template + +```markdown +## Description +Brief description of changes and motivation. + +## Type of Change +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Documentation update + +## Testing +- [ ] New tests added for new functionality +- [ ] All existing tests pass +- [ ] Manual testing completed + +## Checklist +- [ ] Code follows project style guidelines +- [ ] Self-review completed +- [ ] Code is properly commented +- [ ] Corresponding documentation updated +- [ ] No new warnings introduced + +## Related Issues +Fixes #(issue number) +``` + +### Review Criteria + +Pull requests will be reviewed for: + +1. **Functionality**: Does it work as intended? +2. **Testing**: Are there adequate tests? +3. **Code Quality**: Is the code readable and maintainable? +4. **Documentation**: Is it properly documented? +5. **Performance**: Does it introduce performance regressions? +6. **Breaking Changes**: Are breaking changes properly documented? + +## Release Process + +### Versioning + +We follow [Semantic Versioning](https://semver.org/): + +- **MAJOR**: Breaking changes +- **MINOR**: New features, backwards compatible +- **PATCH**: Bug fixes, backwards compatible + +### Changelog Format + +```markdown +## [1.2.0] - 2024-01-15 + +### Added +- New portfolio rebalancing feature +- Support for additional exchanges (Kraken, Bitfinex) +- Advanced risk management tools + +### Changed +- Improved performance of backtesting engine +- Updated API for strategy configuration + +### Deprecated +- Old configuration format (will be removed in v2.0) + +### Fixed +- Bug in order execution timing +- Memory leak in data provider +``` + +## Community Guidelines + +### Code of Conduct + +- Be respectful and inclusive +- Provide constructive feedback +- Help others learn and grow +- Follow project guidelines + +### Getting Help + +- **GitHub Issues**: Bug reports and feature requests +- **GitHub Discussions**: Questions and community discussions +- **Documentation**: Check docs for answers first + +### Recognition + +Contributors will be recognized in: +- Project README +- Release notes +- Annual contributor highlights + +Thank you for contributing to the Investing Algorithm Framework! 🚀 diff --git a/docusaurus/docs/Contributing Guide/style-guide.md b/docusaurus/docs/Contributing Guide/style-guide.md new file mode 100644 index 00000000..54828734 --- /dev/null +++ b/docusaurus/docs/Contributing Guide/style-guide.md @@ -0,0 +1,900 @@ +--- +sidebar_position: 2 +--- + +# Style Guide + +This style guide ensures consistency and readability across the Investing Algorithm Framework codebase. + +## Python Style Guidelines + +### Code Formatting + +We use **Black** for code formatting with the following configuration: + +```toml +# pyproject.toml +[tool.black] +line-length = 88 +target-version = ['py38'] +include = '\.pyi?$' +extend-exclude = ''' +/( + # directories + \.eggs + | \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist +)/ +''' +``` + +### Import Style + +#### Import Order + +1. Standard library imports +2. Related third-party imports +3. Local application/library imports + +```python +# Standard library +import logging +import os +from datetime import datetime, timedelta +from typing import Dict, List, Optional, Union + +# Third-party +import numpy as np +import pandas as pd +import ccxt +from sqlalchemy import Column, Integer, String + +# Local +from investing_algorithm_framework.domain import TradingStrategy +from investing_algorithm_framework.infrastructure import CCXTOrderExecutor +from investing_algorithm_framework.services import PortfolioService +``` + +#### Import Guidelines + +```python +# Good: Explicit imports +from investing_algorithm_framework.domain import ( + TradingStrategy, + BacktestDateRange, + PortfolioConfiguration +) + +# Avoid: Wildcard imports +from investing_algorithm_framework.domain import * + +# Good: Relative imports for same package +from .portfolio_service import PortfolioService +from ..domain.models import Order + +# Avoid: Long relative imports +from ....some.deep.nested.module import SomeClass +``` + +### Variable Naming + +#### General Rules + +```python +# Good: Descriptive names +portfolio_total_value = 10000.0 +moving_average_period = 20 +btc_current_price = 45000.0 + +# Bad: Abbreviated or unclear names +ptv = 10000.0 +map = 20 +bcp = 45000.0 + +# Good: Boolean variables +is_backtesting = True +has_open_positions = False +should_rebalance = True + +# Bad: Ambiguous boolean names +backtesting = True +positions = False +rebalance = True +``` + +#### Class Naming + +```python +# Good: PascalCase for classes +class TradingStrategy: + pass + +class MovingAverageCrossoverStrategy(TradingStrategy): + pass + +class CCXTDataProvider: + pass + +# Bad: Snake_case or unclear names +class trading_strategy: + pass + +class Strategy1: + pass + +class MAStrat: + pass +``` + +#### Method and Function Naming + +```python +class PortfolioManager: + # Good: Verb-based method names + def calculate_total_value(self) -> float: + pass + + def get_positions(self) -> List[Position]: + pass + + def create_buy_order(self, symbol: str, amount: float) -> Order: + pass + + def is_position_open(self, symbol: str) -> bool: + pass + + # Bad: Unclear or noun-based names + def total(self) -> float: + pass + + def positions(self) -> List[Position]: + pass + + def order(self, symbol: str, amount: float) -> Order: + pass +``` + +#### Constants + +```python +# Good: UPPERCASE with underscores +DEFAULT_TIMEFRAME = "1d" +MAX_POSITION_SIZE = 0.25 +API_RATE_LIMIT = 1200 # requests per minute +SUPPORTED_EXCHANGES = ["binance", "coinbase", "kraken"] + +# Bad: Mixed case or unclear names +defaultTimeframe = "1d" +maxPos = 0.25 +limit = 1200 +exchanges = ["binance", "coinbase", "kraken"] +``` + +### Type Hints + +#### Basic Type Hints + +```python +from typing import Dict, List, Optional, Union +from datetime import datetime + +def calculate_moving_average( + prices: List[float], + period: int +) -> float: + return sum(prices[-period:]) / period + +def get_portfolio_value( + positions: Dict[str, float], + prices: Dict[str, float] +) -> Optional[float]: + if not positions: + return None + return sum(positions[symbol] * prices.get(symbol, 0) + for symbol in positions) +``` + +#### Complex Type Hints + +```python +from typing import Callable, TypeVar, Generic, Protocol + +# Type variables +T = TypeVar('T') +StrategyType = TypeVar('StrategyType', bound='TradingStrategy') + +# Generic classes +class DataProvider(Generic[T]): + def get_data(self, symbol: str) -> T: + pass + +# Protocols for structural typing +class Tradeable(Protocol): + def get_current_price(self) -> float: ... + def create_order(self, side: str, amount: float) -> Order: ... + +# Complex function signatures +def backtest_strategy( + strategy: TradingStrategy, + data_provider: DataProvider[pd.DataFrame], + date_range: BacktestDateRange, + callback: Optional[Callable[[BacktestRun], None]] = None +) -> BacktestResults: + pass +``` + +### Documentation Style + +#### Class Documentation + +```python +class MovingAverageStrategy(TradingStrategy): + """ + Trading strategy based on moving average crossover signals. + + This strategy generates buy signals when a short-term moving average + crosses above a long-term moving average, and sell signals when the + short-term average crosses below the long-term average. + + Attributes: + short_period: Number of periods for short moving average + long_period: Number of periods for long moving average + symbol: Trading symbol to apply strategy to + + Example: + >>> strategy = MovingAverageStrategy( + ... short_period=20, + ... long_period=50, + ... symbol="BTC/USDT" + ... ) + >>> app.add_strategy(strategy) + """ + + def __init__( + self, + short_period: int = 20, + long_period: int = 50, + symbol: str = "BTC/USDT" + ): + """ + Initialize the moving average strategy. + + Args: + short_period: Period for short moving average. Must be less + than long_period. Defaults to 20. + long_period: Period for long moving average. Must be greater + than short_period. Defaults to 50. + symbol: Trading symbol to monitor. Defaults to "BTC/USDT". + + Raises: + ValueError: If short_period >= long_period + ValueError: If periods are not positive integers + """ + if short_period >= long_period: + raise ValueError("Short period must be less than long period") + + if short_period <= 0 or long_period <= 0: + raise ValueError("Periods must be positive integers") + + self.short_period = short_period + self.long_period = long_period + self.symbol = symbol +``` + +#### Function Documentation + +```python +def calculate_sharpe_ratio( + returns: List[float], + risk_free_rate: float = 0.02, + periods_per_year: int = 252 +) -> float: + """ + Calculate the Sharpe ratio for a series of returns. + + The Sharpe ratio measures risk-adjusted return by comparing the excess + return of an investment over a risk-free rate to the volatility of the + investment. + + Args: + returns: List of periodic returns (e.g., daily returns) + risk_free_rate: Annual risk-free rate. Defaults to 2% (0.02). + periods_per_year: Number of return periods per year. Defaults to + 252 (trading days). + + Returns: + The Sharpe ratio as a float. Higher values indicate better + risk-adjusted returns. + + Raises: + ValueError: If returns list is empty + ValueError: If standard deviation is zero (no volatility) + + Example: + >>> daily_returns = [0.01, -0.02, 0.015, 0.008, -0.005] + >>> sharpe = calculate_sharpe_ratio(daily_returns, 0.025) + >>> print(f"Sharpe Ratio: {sharpe:.2f}") + Sharpe Ratio: 1.25 + + Note: + This function assumes the returns are already in decimal format + (e.g., 0.01 for 1% return). + """ + if not returns: + raise ValueError("Returns list cannot be empty") + + mean_return = sum(returns) / len(returns) + std_return = (sum((r - mean_return) ** 2 for r in returns) / len(returns)) ** 0.5 + + if std_return == 0: + raise ValueError("Cannot calculate Sharpe ratio with zero volatility") + + # Annualize the Sharpe ratio + excess_return = mean_return - (risk_free_rate / periods_per_year) + annualized_excess_return = excess_return * periods_per_year + annualized_volatility = std_return * (periods_per_year ** 0.5) + + return annualized_excess_return / annualized_volatility +``` + +### Error Handling Style + +#### Exception Hierarchy + +```python +# Base framework exception +class InvestingAlgorithmFrameworkError(Exception): + """Base exception for all framework-related errors.""" + pass + +# Domain-specific exceptions +class TradingError(InvestingAlgorithmFrameworkError): + """Base exception for trading-related errors.""" + pass + +class OrderError(TradingError): + """Exception raised for order-related errors.""" + pass + +class InsufficientFundsError(OrderError): + """Exception raised when insufficient funds for order.""" + pass + +class DataError(InvestingAlgorithmFrameworkError): + """Exception raised for data-related errors.""" + pass + +class ConnectionError(DataError): + """Exception raised for connection-related errors.""" + pass +``` + +#### Exception Usage + +```python +def create_buy_order( + self, + symbol: str, + amount: float, + price: Optional[float] = None +) -> Order: + """ + Create a buy order for the specified symbol and amount. + + Args: + symbol: Trading symbol (e.g., "BTC/USDT") + amount: Amount to buy in base currency + price: Limit price (optional, uses market price if None) + + Returns: + Created order object + + Raises: + ValueError: If amount is not positive + InsufficientFundsError: If account balance is insufficient + ConnectionError: If exchange connection fails + """ + # Input validation + if amount <= 0: + raise ValueError(f"Order amount must be positive, got {amount}") + + if not symbol: + raise ValueError("Symbol cannot be empty") + + try: + # Check available balance + balance = self._get_available_balance() + if balance < amount: + raise InsufficientFundsError( + f"Insufficient balance: {balance} < {amount}" + ) + + # Create order + order = self._create_order_on_exchange(symbol, amount, price) + return order + + except ConnectionError as e: + # Re-raise connection errors with context + raise ConnectionError( + f"Failed to create order for {symbol}: {e}" + ) from e + + except Exception as e: + # Log unexpected errors and re-raise + logger.error(f"Unexpected error creating order: {e}", exc_info=True) + raise TradingError(f"Failed to create buy order: {e}") from e +``` + +### Logging Style + +#### Logger Setup + +```python +import logging + +# Good: Module-level logger +logger = logging.getLogger(__name__) + +class TradingStrategy: + def __init__(self): + # Good: Class-specific logger + self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}") + + def apply_strategy(self, algorithm, market_data): + self.logger.info("Executing trading strategy") + + try: + current_price = market_data.get_last_price("BTC/USDT") + self.logger.debug(f"Current BTC price: ${current_price:.2f}") + + # Strategy logic here + + except Exception as e: + self.logger.error(f"Strategy execution failed: {e}", exc_info=True) + raise +``` + +#### Logging Messages + +```python +# Good: Informative log messages +logger.info("Starting portfolio rebalancing", extra={ + 'portfolio_value': portfolio.total_value, + 'target_weights': target_weights, + 'current_weights': current_weights +}) + +logger.warning( + "Position size exceeds risk limit", + extra={ + 'symbol': symbol, + 'position_size': position_size, + 'risk_limit': risk_limit, + 'portfolio_percentage': position_size / portfolio.total_value + } +) + +# Bad: Vague or missing information +logger.info("Rebalancing") +logger.warning("Risk limit exceeded") + +# Good: Structured logging for production +logger.info("Order executed", extra={ + 'event_type': 'order_filled', + 'order_id': order.id, + 'symbol': order.symbol, + 'side': order.side, + 'amount': order.amount, + 'price': order.price, + 'timestamp': datetime.utcnow().isoformat() +}) +``` + +## Testing Style + +### Test Organization + +```python +# tests/test_trading_strategy.py +import pytest +from unittest.mock import Mock, patch +from investing_algorithm_framework import TradingStrategy + +class TestTradingStrategy: + """Test suite for TradingStrategy class.""" + + @pytest.fixture + def strategy(self): + """Fixture providing a basic strategy instance.""" + return TradingStrategy() + + @pytest.fixture + def mock_market_data(self): + """Fixture providing mock market data.""" + mock_data = Mock() + mock_data.get_last_price.return_value = 50000.0 + mock_data.get_data.return_value = [ + {"open": 49000, "high": 51000, "low": 48000, "close": 50000} + ] + return mock_data + + def test_strategy_initialization(self, strategy): + """Test strategy can be initialized with default parameters.""" + assert strategy is not None + assert hasattr(strategy, 'apply_strategy') + + def test_strategy_with_market_data(self, strategy, mock_market_data): + """Test strategy execution with mock market data.""" + algorithm = Mock() + + # Should not raise exception + strategy.apply_strategy(algorithm, mock_market_data) + + # Verify market data was accessed + mock_market_data.get_last_price.assert_called() + + @pytest.mark.parametrize("price,expected_signal", [ + (45000, "buy"), + (55000, "sell"), + (50000, "hold") + ]) + def test_strategy_signals_at_different_prices( + self, + strategy, + price, + expected_signal + ): + """Test strategy generates correct signals at different price levels.""" + # Test implementation here + pass + + def test_strategy_handles_missing_data(self, strategy): + """Test strategy handles case when market data is unavailable.""" + algorithm = Mock() + market_data = Mock() + market_data.get_last_price.return_value = None + + # Should handle gracefully without crashing + strategy.apply_strategy(algorithm, market_data) + + def test_strategy_error_handling(self, strategy): + """Test strategy handles exceptions in market data access.""" + algorithm = Mock() + market_data = Mock() + market_data.get_last_price.side_effect = ConnectionError("Network error") + + with pytest.raises(ConnectionError): + strategy.apply_strategy(algorithm, market_data) +``` + +### Assertion Style + +```python +# Good: Clear, specific assertions +def test_portfolio_value_calculation(): + portfolio = Portfolio() + portfolio.add_position("BTC", 0.1, 50000) + portfolio.add_position("ETH", 2.0, 3000) + + expected_value = (0.1 * 50000) + (2.0 * 3000) + actual_value = portfolio.calculate_total_value() + + assert actual_value == expected_value + assert portfolio.get_position_count() == 2 + +# Good: Using pytest.approx for floating point comparisons +def test_sharpe_ratio_calculation(): + returns = [0.01, -0.02, 0.015, 0.008, -0.005] + sharpe = calculate_sharpe_ratio(returns, risk_free_rate=0.02) + + assert sharpe == pytest.approx(1.25, abs=0.01) + +# Good: Testing exception messages +def test_invalid_order_amount(): + portfolio = Portfolio() + + with pytest.raises(ValueError, match="Order amount must be positive"): + portfolio.create_buy_order("BTC/USDT", -100) +``` + +## Configuration Style + +### Environment Configuration + +```python +# config/development.py +class DevelopmentConfig: + """Development environment configuration.""" + + DEBUG = True + TESTING = False + + # Database + DATABASE_URI = "sqlite:///dev.db" + + # Logging + LOG_LEVEL = "DEBUG" + LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + + # Trading + PAPER_TRADING = True + INITIAL_BALANCE = 10000.0 + + # API Keys (should be loaded from environment) + EXCHANGE_API_KEY = os.getenv("DEV_EXCHANGE_API_KEY") + EXCHANGE_API_SECRET = os.getenv("DEV_EXCHANGE_API_SECRET") + +# config/production.py +class ProductionConfig: + """Production environment configuration.""" + + DEBUG = False + TESTING = False + + # Database + DATABASE_URI = os.getenv("DATABASE_URI") + + # Logging + LOG_LEVEL = "INFO" + LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s" + + # Trading + PAPER_TRADING = False + INITIAL_BALANCE = float(os.getenv("INITIAL_BALANCE", "1000")) + + # Security + SECRET_KEY = os.getenv("SECRET_KEY") + EXCHANGE_API_KEY = os.getenv("EXCHANGE_API_KEY") + EXCHANGE_API_SECRET = os.getenv("EXCHANGE_API_SECRET") +``` + +### YAML Configuration Style + +```yaml +# config.yaml +app: + name: "Trading Bot" + version: "1.0.0" + environment: "production" + +database: + uri: "${DATABASE_URI}" + pool_size: 10 + max_overflow: 20 + +exchanges: + binance: + api_key: "${BINANCE_API_KEY}" + api_secret: "${BINANCE_API_SECRET}" + sandbox: false + rate_limit: 1200 + +portfolio: + initial_balance: 10000.0 + trading_symbol: "USDT" + max_position_size: 0.25 + risk_per_trade: 0.02 + +strategies: + - name: "MA Crossover" + class: "MovingAverageStrategy" + parameters: + short_period: 20 + long_period: 50 + symbols: ["BTC/USDT", "ETH/USDT"] + +logging: + level: "INFO" + format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + handlers: + - type: "file" + filename: "logs/trading.log" + max_size: "10MB" + backup_count: 5 + - type: "console" + stream: "stdout" +``` + +## File Organization + +### Directory Structure + +``` +investing_algorithm_framework/ +├── __init__.py +├── app/ # Application layer +│ ├── __init__.py +│ ├── app.py +│ ├── algorithm/ +│ ├── analysis/ +│ └── strategy/ +├── domain/ # Domain models and business logic +│ ├── __init__.py +│ ├── models/ +│ ├── services/ +│ └── exceptions/ +├── infrastructure/ # External integrations +│ ├── __init__.py +│ ├── exchanges/ +│ ├── data_providers/ +│ └── databases/ +├── cli/ # Command-line interface +│ ├── __init__.py +│ └── commands/ +└── utils/ # Utility functions + ├── __init__.py + ├── datetime_utils.py + └── math_utils.py + +tests/ +├── __init__.py +├── unit/ +├── integration/ +├── fixtures/ +└── conftest.py + +docs/ +├── api/ +├── guides/ +└── examples/ + +examples/ +├── basic_trading_bot.py +├── advanced_strategies/ +└── data_analysis/ +``` + +### Module Structure + +```python +# investing_algorithm_framework/domain/models/order.py +""" +Order domain model. + +This module contains the Order class and related enums for representing +trading orders within the framework. +""" + +from enum import Enum +from datetime import datetime +from typing import Optional +from dataclasses import dataclass + +__all__ = ['Order', 'OrderStatus', 'OrderSide', 'OrderType'] + + +class OrderStatus(Enum): + """Enumeration of possible order statuses.""" + PENDING = "pending" + OPEN = "open" + FILLED = "filled" + CANCELLED = "cancelled" + REJECTED = "rejected" + + +class OrderSide(Enum): + """Enumeration of order sides.""" + BUY = "buy" + SELL = "sell" + + +class OrderType(Enum): + """Enumeration of order types.""" + MARKET = "market" + LIMIT = "limit" + STOP = "stop" + STOP_LIMIT = "stop_limit" + + +@dataclass +class Order: + """ + Represents a trading order. + + This class encapsulates all information about a trading order, + including its type, status, and execution details. + + Attributes: + id: Unique identifier for the order + symbol: Trading pair symbol (e.g., "BTC/USDT") + side: Order side (buy or sell) + type: Order type (market, limit, etc.) + amount: Order amount in base currency + price: Order price (None for market orders) + status: Current order status + created_at: Order creation timestamp + updated_at: Last update timestamp + """ + id: str + symbol: str + side: OrderSide + type: OrderType + amount: float + price: Optional[float] = None + status: OrderStatus = OrderStatus.PENDING + created_at: Optional[datetime] = None + updated_at: Optional[datetime] = None + + def __post_init__(self): + """Initialize timestamps if not provided.""" + if self.created_at is None: + self.created_at = datetime.utcnow() + if self.updated_at is None: + self.updated_at = self.created_at +``` + +## Git Commit Style + +### Commit Message Format + +``` +(): + + + +