investing-algorithm-framework 6.8.0__tar.gz → 6.8.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/PKG-INFO +1 -1
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/__init__.py +3 -1
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/app.py +6 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/trade/trade.py +6 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/repositories/order_repository.py +1 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/__init__.py +3 -1
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/cagr.py +1 -1
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/exposure.py +13 -3
- investing_algorithm_framework-6.8.2/investing_algorithm_framework/metrics/mean_daily_return.py +84 -0
- investing_algorithm_framework-6.8.2/investing_algorithm_framework/metrics/sharp_ratio.py +87 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/sortino_ratio.py +23 -14
- investing_algorithm_framework-6.8.2/investing_algorithm_framework/metrics/standard_deviation.py +159 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/win_rate.py +16 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +1 -3
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/pyproject.toml +1 -1
- investing_algorithm_framework-6.8.0/investing_algorithm_framework/metrics/sharp_ratio.py +0 -38
- investing_algorithm_framework-6.8.0/investing_algorithm_framework/metrics/standard_deviation.py +0 -92
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/LICENSE +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/README.md +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/algorithm/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/algorithm/algorithm.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/algorithm/algorithm_factory.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/app_hook.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/context.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/stateless/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/stateless/action_handlers/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/stateless/exception_handler.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/strategy.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/task.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/controllers/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/controllers/orders.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/controllers/portfolio.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/controllers/positions.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/create_app.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/error_handler.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/responses.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/run_strategies.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/schemas/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/schemas/order.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/schemas/portfolio.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/schemas/position.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/app/web/setup_cors.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/cli.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/deploy_to_azure_function.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/initialize_app.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/.gitignore.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/app-web.py.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/app.py.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/app_azure_function.py.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/app_web.py.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/azure_function_host.json.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/data_providers.py.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/env.example.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/env_azure_function.example.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/market_data_providers.py.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/readme.md.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/requirements.txt.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/requirements_azure_function.txt.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/run_backtest.py.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/cli/templates/strategy.py.template +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/create_app.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/dependency_container.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/config.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/constants.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/data_provider.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/data_structures.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/decimal_parsing.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/exceptions.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/app_mode.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/backtesting/backtest_report.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/base_model.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/data_source.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/date_range.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/event.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/market/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/market/market_credential.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/market_data_type.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/order/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/order/order.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/order/order_side.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/order/order_status.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/order/order_type.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/portfolio/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/portfolio/portfolio.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/position/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/position/position.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/position/position_snapshot.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/snapshot_interval.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/strategy_profile.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/time_frame.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/time_interval.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/time_unit.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/tracing/trace.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/trade/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/trade/trade_risk_type.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/trade/trade_status.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/trade/trade_take_profit.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/trading_data_types.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/models/trading_time_frame.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/order_executor.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/portfolio_provider.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/services/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/services/market_credential_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/services/market_data_sources.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/services/market_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/services/observable.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/services/observer.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/services/portfolios/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/services/rounding_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/services/state_handler.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/stateless_actions.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/strategy.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/utils/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/utils/backtesting.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/utils/csv.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/utils/dates.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/utils/polars.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/utils/random.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/utils/signatures.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/utils/stoppable_thread.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/domain/utils/synchronized.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/download_data.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/data_providers/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/data_providers/ccxt.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/data_providers/csv.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/database/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/database/sql_alchemy.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/decimal_parser.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/market_data_sources/pandas.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/model_extension.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/order/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/order/order.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/order/order_metadata.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/order_trade_association.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/portfolio/sql_portfolio.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/position/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/position/position.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/trades/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/trades/trade.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/order_executors/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/repositories/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/repositories/position_repository.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/repositories/repository.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/repositories/trade_repository.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/services/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/services/azure/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/services/azure/state_handler.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/calmar_ratio.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/drawdown.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/equity_curve.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/net_profit.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/price_efficiency.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/profit_factor.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/recovery.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/risk_free_rate.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/metrics/volatility.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/overfitting/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/overfitting/permutations.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/backtesting/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/backtesting/backtest_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/configuration_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/market_credential_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/market_data_source_service/data_provider_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/order_service/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/order_service/order_backtest_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/order_service/order_executor_lookup.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/order_service/order_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/portfolios/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/portfolios/portfolio_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/positions/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/positions/position_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/positions/position_snapshot_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/repository_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/trade_service/__init__.py +0 -0
- {investing_algorithm_framework-6.8.0 → investing_algorithm_framework-6.8.2}/investing_algorithm_framework/services/trade_service/trade_service.py +0 -0
|
@@ -31,7 +31,8 @@ from .metrics import get_volatility, get_sortino_ratio, get_profit_factor, \
|
|
|
31
31
|
get_drawdown_series, get_max_drawdown, get_cagr, \
|
|
32
32
|
get_standard_deviation_returns, get_standard_deviation_downside_returns, \
|
|
33
33
|
get_max_drawdown_absolute, get_exposure_time, get_average_trade_duration, \
|
|
34
|
-
get_net_profit, get_win_rate, get_win_loss_ratio, get_calmar_ratio
|
|
34
|
+
get_net_profit, get_win_rate, get_win_loss_ratio, get_calmar_ratio, \
|
|
35
|
+
get_trade_frequency
|
|
35
36
|
|
|
36
37
|
__all__ = [
|
|
37
38
|
"Algorithm",
|
|
@@ -123,4 +124,5 @@ __all__ = [
|
|
|
123
124
|
"get_win_rate",
|
|
124
125
|
"get_win_loss_ratio",
|
|
125
126
|
"get_calmar_ratio",
|
|
127
|
+
"get_trade_frequency",
|
|
126
128
|
]
|
|
@@ -426,6 +426,7 @@ class App:
|
|
|
426
426
|
Returns:
|
|
427
427
|
None
|
|
428
428
|
"""
|
|
429
|
+
strategy_orchestrator_service = None
|
|
429
430
|
|
|
430
431
|
try:
|
|
431
432
|
configuration_service = self.container.configuration_service()
|
|
@@ -466,6 +467,8 @@ class App:
|
|
|
466
467
|
data_sources=self._market_data_sources,
|
|
467
468
|
on_strategy_run_hooks=self._on_strategy_run_hooks,
|
|
468
469
|
)
|
|
470
|
+
self.initialize_data_sources(algorithm)
|
|
471
|
+
|
|
469
472
|
strategy_orchestrator_service = \
|
|
470
473
|
self.container.strategy_orchestrator_service()
|
|
471
474
|
strategy_orchestrator_service.initialize(algorithm)
|
|
@@ -1154,6 +1157,8 @@ class App:
|
|
|
1154
1157
|
None
|
|
1155
1158
|
"""
|
|
1156
1159
|
|
|
1160
|
+
logger.info("Adding strategy")
|
|
1161
|
+
|
|
1157
1162
|
if inspect.isclass(strategy):
|
|
1158
1163
|
|
|
1159
1164
|
if not issubclass(strategy, TradingStrategy):
|
|
@@ -1187,6 +1192,7 @@ class App:
|
|
|
1187
1192
|
)
|
|
1188
1193
|
|
|
1189
1194
|
if strategy.market_data_sources is not None:
|
|
1195
|
+
logger.info("Adding market data sources from strategy")
|
|
1190
1196
|
self.add_data_sources(strategy.market_data_sources)
|
|
1191
1197
|
|
|
1192
1198
|
self._strategies.append(strategy)
|
|
@@ -148,6 +148,12 @@ class Trade(BaseModel):
|
|
|
148
148
|
|
|
149
149
|
@property
|
|
150
150
|
def duration(self):
|
|
151
|
+
"""
|
|
152
|
+
Calculate the duration of the trade in hours.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
float: The duration of the trade in hours.
|
|
156
|
+
"""
|
|
151
157
|
if TradeStatus.CLOSED.equals(self.status):
|
|
152
158
|
# Get the total hours between the closed and opened datetime
|
|
153
159
|
diff = self.closed_at - self.opened_at
|
|
@@ -15,7 +15,8 @@ from .cagr import get_cagr
|
|
|
15
15
|
from .standard_deviation import get_standard_deviation_downside_returns, \
|
|
16
16
|
get_standard_deviation_returns
|
|
17
17
|
from .net_profit import get_net_profit
|
|
18
|
-
from .exposure import get_exposure_time, get_average_trade_duration
|
|
18
|
+
from .exposure import get_exposure_time, get_average_trade_duration, \
|
|
19
|
+
get_trade_frequency
|
|
19
20
|
from .win_rate import get_win_rate, get_win_loss_ratio
|
|
20
21
|
from .calmar_ratio import get_calmar_ratio
|
|
21
22
|
|
|
@@ -41,4 +42,5 @@ __all__ = [
|
|
|
41
42
|
"get_win_rate",
|
|
42
43
|
"get_win_loss_ratio",
|
|
43
44
|
"get_calmar_ratio",
|
|
45
|
+
"get_trade_frequency",
|
|
44
46
|
]
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
High exposure (>1) means you’re deploying capital aggressively, possibly with many simultaneous positions.
|
|
3
|
+
|
|
4
|
+
Exposure around 1 means capital is nearly fully invested most of the time, but not overlapping.
|
|
5
|
+
|
|
6
|
+
Low exposure (<1) means capital is mostly idle or only partially invested.
|
|
7
|
+
"""
|
|
8
|
+
|
|
1
9
|
from datetime import timedelta
|
|
2
10
|
from investing_algorithm_framework.domain import BacktestReport
|
|
3
11
|
|
|
@@ -18,13 +26,14 @@ def get_exposure_time(report: BacktestReport):
|
|
|
18
26
|
|
|
19
27
|
total_trade_duration = timedelta(0)
|
|
20
28
|
for trade in trades:
|
|
21
|
-
entry = trade.
|
|
29
|
+
entry = trade.opened_at
|
|
22
30
|
exit = trade.closed_at or report.backtest_end_date # open trades counted up to end
|
|
23
31
|
|
|
24
32
|
if exit > entry:
|
|
25
33
|
total_trade_duration += exit - entry
|
|
26
34
|
|
|
27
35
|
backtest_duration = report.backtest_end_date - report.backtest_start_date
|
|
36
|
+
|
|
28
37
|
if backtest_duration.total_seconds() == 0:
|
|
29
38
|
return 0.0
|
|
30
39
|
|
|
@@ -44,7 +53,8 @@ def get_average_trade_duration(report: BacktestReport):
|
|
|
44
53
|
if not trades:
|
|
45
54
|
return 0.0
|
|
46
55
|
|
|
47
|
-
total_duration =
|
|
56
|
+
total_duration = 0
|
|
57
|
+
|
|
48
58
|
for trade in trades:
|
|
49
59
|
trade_duration = trade.duration
|
|
50
60
|
|
|
@@ -52,7 +62,7 @@ def get_average_trade_duration(report: BacktestReport):
|
|
|
52
62
|
total_duration += trade_duration
|
|
53
63
|
|
|
54
64
|
average_trade_duration = total_duration / len(trades)
|
|
55
|
-
return average_trade_duration
|
|
65
|
+
return average_trade_duration
|
|
56
66
|
|
|
57
67
|
|
|
58
68
|
def get_trade_frequency(report: BacktestReport):
|
investing_algorithm_framework-6.8.2/investing_algorithm_framework/metrics/mean_daily_return.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from .cagr import get_cagr
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_mean_daily_return(report):
|
|
8
|
+
"""
|
|
9
|
+
Calculate the mean daily return from the total value of the snapshots.
|
|
10
|
+
|
|
11
|
+
This function computes the mean daily return based on the list of
|
|
12
|
+
snapshots in the report. If the snapshots have a granularity of less
|
|
13
|
+
than a day, the function will resample to daily frequency and compute
|
|
14
|
+
average daily returns.
|
|
15
|
+
|
|
16
|
+
If there is less data then for a year, it will use cagr to
|
|
17
|
+
calculate the mean daily return.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
report (BacktestReport): The report containing the equity curve.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
float: The mean daily return.
|
|
24
|
+
"""
|
|
25
|
+
snapshots = report.get_snapshots()
|
|
26
|
+
|
|
27
|
+
if len(snapshots) < 2:
|
|
28
|
+
return 0.0 # Not enough data
|
|
29
|
+
|
|
30
|
+
# Create DataFrame from snapshots
|
|
31
|
+
data = [(s.created_at, s.total_value) for s in snapshots]
|
|
32
|
+
df = pd.DataFrame(data, columns=["created_at", "total_value"])
|
|
33
|
+
df['created_at'] = pd.to_datetime(df['created_at'])
|
|
34
|
+
df = df.sort_values('created_at').drop_duplicates('created_at')\
|
|
35
|
+
.set_index('created_at')
|
|
36
|
+
|
|
37
|
+
start_date = df.iloc[0].name
|
|
38
|
+
end_date = df.iloc[-1].name
|
|
39
|
+
|
|
40
|
+
# Check if the period is less than a year
|
|
41
|
+
if (end_date - start_date).days < 365:
|
|
42
|
+
# Use CAGR to calculate mean daily return
|
|
43
|
+
cagr = get_cagr(report)
|
|
44
|
+
if cagr == 0.0:
|
|
45
|
+
return 0.0
|
|
46
|
+
return (1 + cagr) ** (1 / 365) - 1
|
|
47
|
+
|
|
48
|
+
# Resample to daily frequency using last value of the day
|
|
49
|
+
daily_df = df.resample('1D').last().dropna()
|
|
50
|
+
|
|
51
|
+
# Calculate daily returns
|
|
52
|
+
daily_df['return'] = daily_df['total_value'].pct_change()
|
|
53
|
+
daily_df = daily_df.dropna()
|
|
54
|
+
|
|
55
|
+
if daily_df.empty:
|
|
56
|
+
return 0.0
|
|
57
|
+
|
|
58
|
+
mean_return = daily_df['return'].mean()
|
|
59
|
+
|
|
60
|
+
if np.isnan(mean_return):
|
|
61
|
+
return 0.0
|
|
62
|
+
|
|
63
|
+
return mean_return
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_mean_yearly_return(report, periods_per_year=365):
|
|
67
|
+
"""
|
|
68
|
+
Calculate the mean yearly return from a backtest report by
|
|
69
|
+
annualizing the mean daily return.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
report (BacktestReport): The report containing the snapshots.
|
|
73
|
+
periods_per_year (int): Number of periods in a year (e.g., 365 for daily data).
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
float: The mean yearly return (annualized).
|
|
77
|
+
"""
|
|
78
|
+
mean_daily_return = get_mean_daily_return(report)
|
|
79
|
+
|
|
80
|
+
if mean_daily_return == 0.0:
|
|
81
|
+
return 0.0
|
|
82
|
+
|
|
83
|
+
return (1 + mean_daily_return) ** periods_per_year - 1
|
|
84
|
+
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The Sharpe Ratio is a widely used risk-adjusted performance metric. It
|
|
3
|
+
measures the excess return per unit of risk (volatility), where risk is
|
|
4
|
+
represented by the standard deviation of returns.
|
|
5
|
+
|
|
6
|
+
| Sharpe Ratio | Interpretation |
|
|
7
|
+
| -------------- | ------------------------------------------- |
|
|
8
|
+
| **< 0** | Bad: Underperforms risk-free asset |
|
|
9
|
+
| **0.0 – 1.0** | Suboptimal: Returns do not justify risk |
|
|
10
|
+
| **1.0 – 1.99** | Acceptable: Reasonable risk-adjusted return |
|
|
11
|
+
| **2.0 – 2.99** | Good: Strong risk-adjusted performance |
|
|
12
|
+
| **3.0+** | Excellent: Exceptional risk-adjusted return |
|
|
13
|
+
|
|
14
|
+
Sharpe Ratio is highly sensitive to the volatility estimate: Inconsistent sampling frequency, short backtests, or low trade frequency can distort it.
|
|
15
|
+
|
|
16
|
+
Different strategies have different risk profiles:
|
|
17
|
+
|
|
18
|
+
High-frequency strategies may have high Sharpe Ratios (>3).
|
|
19
|
+
|
|
20
|
+
Trend-following strategies might have lower Sharpe (1–2) but strong CAGR and Calmar.
|
|
21
|
+
|
|
22
|
+
Use risk-free rate (~4–5% annual currently) if your backtest spans long periods.
|
|
23
|
+
|
|
24
|
+
### 📌 Practical Notes about the implementation:
|
|
25
|
+
|
|
26
|
+
- Use **daily returns** for consistent Sharpe Ratio calculation and **annualize** the result using this formula:
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
Sharpe Ratio Formula:
|
|
30
|
+
Sharpe Ratio = (Mean Daily Return × Periods Per Year - Risk-Free Rate) /
|
|
31
|
+
(Standard Deviation of Daily Returns × sqrt(Periods Per Year))
|
|
32
|
+
|
|
33
|
+
- You can also calculate a **rolling Sharpe Ratio** (e.g., over a 90-day window) to detect changes in performance stability over time.
|
|
34
|
+
|
|
35
|
+
Mean daily return is either based on the real returns from the backtest or the CAGR, depending on the data duration.
|
|
36
|
+
|
|
37
|
+
When do we use actual returns vs CAGR?
|
|
38
|
+
|
|
39
|
+
| Data Duration | Use This Approach | Reason |
|
|
40
|
+
| ------------- | --------------------------------------------------------------- | ----------------------------------------------------------------- |
|
|
41
|
+
| **< 1 year** | Use **CAGR** directly and avoid Sharpe Ratio | Not enough data to estimate volatility robustly |
|
|
42
|
+
| **1–2 years** | Use **CAGR + conservative vol estimate** OR Sharpe with caution | Sharpe may be unstable, consider adding error bars or disclaimers |
|
|
43
|
+
| **> 2 years** | Use **Sharpe Ratio** based on periodic returns | Adequate data to reliably estimate risk-adjusted return |
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
from typing import Optional
|
|
48
|
+
|
|
49
|
+
import math
|
|
50
|
+
|
|
51
|
+
from investing_algorithm_framework.domain.models import BacktestReport
|
|
52
|
+
from .mean_daily_return import get_mean_daily_return
|
|
53
|
+
from .risk_free_rate import get_risk_free_rate_us
|
|
54
|
+
from .standard_deviation import get_daily_returns_std
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_sharpe_ratio(
|
|
58
|
+
backtest_report: BacktestReport, risk_free_rate: Optional[float] = None,
|
|
59
|
+
) -> float:
|
|
60
|
+
"""
|
|
61
|
+
Calculate the Sharpe Ratio from a backtest report using daily or
|
|
62
|
+
weekly returns.
|
|
63
|
+
|
|
64
|
+
The Sharpe Ratio is calculated as:
|
|
65
|
+
(Annualized Return - Risk-Free Rate) / Annualized Std Dev of Returns
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
backtest_report: Object with get_trades(trade_status=...) and
|
|
69
|
+
`number_of_days` attributes.
|
|
70
|
+
risk_free_rate (float, optional): Annual risk-free rate as a
|
|
71
|
+
decimal (e.g., 0.047 for 4.7%).
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
float: The Sharpe Ratio.
|
|
75
|
+
"""
|
|
76
|
+
snapshots = backtest_report.get_snapshots()
|
|
77
|
+
snapshots = sorted(snapshots, key=lambda s: s.created_at)
|
|
78
|
+
mean_daily_return = get_mean_daily_return(backtest_report)
|
|
79
|
+
std_daily_return = get_daily_returns_std(snapshots)
|
|
80
|
+
|
|
81
|
+
if risk_free_rate is None:
|
|
82
|
+
risk_free_rate = get_risk_free_rate_us()
|
|
83
|
+
|
|
84
|
+
# Formula: Sharpe Ratio = (Mean Daily Return × Periods Per Year - Risk-Free Rate) /
|
|
85
|
+
# (Standard Deviation of Daily Returns × sqrt(Periods Per Year))
|
|
86
|
+
return (mean_daily_return * 365 - risk_free_rate) / \
|
|
87
|
+
(std_daily_return * math.sqrt(365))
|
|
@@ -11,14 +11,21 @@ symmetrically distributed.
|
|
|
11
11
|
| **1 to 2** | ✅ Acceptable/Good — Reasonable performance for most portfolios |
|
|
12
12
|
| **2 to 3** | 💪 Strong — Very good risk-adjusted returns |
|
|
13
13
|
| **> 3** | 🌟 Excellent — Rare, may indicate exceptional strategy or overfitting |
|
|
14
|
+
|
|
15
|
+
Formula:
|
|
16
|
+
Sortino Ratio = (Mean Daily Return × Periods Per Year - Risk-Free Rate) /
|
|
17
|
+
(Downside Standard Deviation of Daily Returns × sqrt(Periods Per Year))
|
|
18
|
+
|
|
14
19
|
"""
|
|
15
20
|
|
|
16
21
|
from typing import Optional
|
|
17
22
|
|
|
23
|
+
import math
|
|
24
|
+
import numpy as np
|
|
18
25
|
from investing_algorithm_framework.domain import BacktestReport
|
|
19
|
-
from .
|
|
26
|
+
from .mean_daily_return import get_mean_daily_return
|
|
20
27
|
from .risk_free_rate import get_risk_free_rate_us
|
|
21
|
-
from .standard_deviation import
|
|
28
|
+
from .standard_deviation import get_downside_std_of_daily_returns
|
|
22
29
|
|
|
23
30
|
|
|
24
31
|
def get_sortino_ratio(
|
|
@@ -44,22 +51,24 @@ def get_sortino_ratio(
|
|
|
44
51
|
Returns:
|
|
45
52
|
float: The Sortino Ratio.
|
|
46
53
|
"""
|
|
47
|
-
|
|
54
|
+
snapshots = report.get_snapshots()
|
|
55
|
+
|
|
56
|
+
if not snapshots:
|
|
57
|
+
return float('inf')
|
|
48
58
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
get_standard_deviation_downside_returns(report)
|
|
59
|
+
snapshots = sorted(snapshots, key=lambda s: s.created_at)
|
|
60
|
+
mean_daily_return = get_mean_daily_return(report)
|
|
61
|
+
std_downside_daily_return = get_downside_std_of_daily_returns(snapshots)
|
|
53
62
|
|
|
54
63
|
if risk_free_rate is None:
|
|
55
64
|
risk_free_rate = get_risk_free_rate_us()
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
# Formula: Sharpe Ratio = (Mean Daily Return × Periods Per Year - Risk-Free Rate) /
|
|
67
|
+
# (Standard Deviation of Daily Returns × sqrt(Periods Per Year))
|
|
68
|
+
ratio = (mean_daily_return * 365 - risk_free_rate) / \
|
|
69
|
+
(std_downside_daily_return * math.sqrt(365))
|
|
60
70
|
|
|
61
|
-
if
|
|
62
|
-
return
|
|
71
|
+
if np.float64("inf") == ratio or np.float64("-inf") == ratio:
|
|
72
|
+
return float('inf')
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
return (annualized_return - risk_free_rate) / standard_deviation_downside
|
|
74
|
+
return ratio if not np.isnan(ratio) else 0.0
|
investing_algorithm_framework-6.8.2/investing_algorithm_framework/metrics/standard_deviation.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from investing_algorithm_framework.domain import BacktestReport
|
|
4
|
+
|
|
5
|
+
def get_standard_deviation_downside_returns(report: BacktestReport):
|
|
6
|
+
"""
|
|
7
|
+
Calculate the standard deviation of downside returns from the net size
|
|
8
|
+
of the reports.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
report (BacktestReport): The report containing the equity curve.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
float: The standard deviation of downside returns.
|
|
15
|
+
"""
|
|
16
|
+
snapshots = report.get_snapshots()
|
|
17
|
+
|
|
18
|
+
if len(snapshots) < 2:
|
|
19
|
+
return 0.0 # Not enough data
|
|
20
|
+
|
|
21
|
+
# Create DataFrame of net_size over time
|
|
22
|
+
data = [(s.total_value, s.created_at) for s in snapshots]
|
|
23
|
+
df = pd.DataFrame(data, columns=["total_value", "created_at"])
|
|
24
|
+
df['created_at'] = pd.to_datetime(df['created_at'])
|
|
25
|
+
df = df.sort_values('created_at').drop_duplicates('created_at').copy()
|
|
26
|
+
|
|
27
|
+
# Compute percentage returns
|
|
28
|
+
df['return'] = df['total_value'].pct_change()
|
|
29
|
+
df = df.dropna()
|
|
30
|
+
|
|
31
|
+
if df.empty:
|
|
32
|
+
return 0.0
|
|
33
|
+
|
|
34
|
+
# Filter downside returns
|
|
35
|
+
downside_returns = df['return'][df['return'] < 0]
|
|
36
|
+
|
|
37
|
+
if downside_returns.empty:
|
|
38
|
+
return 0.0
|
|
39
|
+
|
|
40
|
+
# Compute standard deviation of downside returns
|
|
41
|
+
downside_std = downside_returns.std(ddof=1) # ddof=1 for sample std dev
|
|
42
|
+
|
|
43
|
+
# Handle edge cases
|
|
44
|
+
if np.isnan(downside_std):
|
|
45
|
+
return 0.0
|
|
46
|
+
|
|
47
|
+
return downside_std
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_standard_deviation_returns(report: BacktestReport):
|
|
51
|
+
"""
|
|
52
|
+
Calculate the standard deviation of returns from the net size
|
|
53
|
+
of the reports.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
report (BacktestReport): The report containing the equity curve.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
float: The standard deviation of downside returns.
|
|
60
|
+
"""
|
|
61
|
+
snapshots = report.get_snapshots()
|
|
62
|
+
|
|
63
|
+
if len(snapshots) < 2:
|
|
64
|
+
return 0.0 # Not enough data
|
|
65
|
+
|
|
66
|
+
# Create DataFrame of net_size over time
|
|
67
|
+
data = [(s.total_value, s.created_at) for s in snapshots]
|
|
68
|
+
df = pd.DataFrame(data, columns=["total_value", "created_at"])
|
|
69
|
+
df['created_at'] = pd.to_datetime(df['created_at'])
|
|
70
|
+
df = df.sort_values('created_at').drop_duplicates('created_at').copy()
|
|
71
|
+
|
|
72
|
+
# Compute percentage returns
|
|
73
|
+
df['return'] = df['total_value'].pct_change()
|
|
74
|
+
df = df.dropna()
|
|
75
|
+
|
|
76
|
+
if df.empty:
|
|
77
|
+
return 0.0
|
|
78
|
+
|
|
79
|
+
# Filter downside returns
|
|
80
|
+
df_returns = df['return']
|
|
81
|
+
|
|
82
|
+
if df_returns.empty:
|
|
83
|
+
return 0.0
|
|
84
|
+
|
|
85
|
+
std = df_returns.std(ddof=1) # ddof=1 for sample std dev
|
|
86
|
+
|
|
87
|
+
# Handle edge cases
|
|
88
|
+
if np.isnan(std):
|
|
89
|
+
return 0.0
|
|
90
|
+
|
|
91
|
+
return std
|
|
92
|
+
|
|
93
|
+
def get_daily_returns_std(snapshots):
|
|
94
|
+
"""
|
|
95
|
+
Calculate the standard deviation of daily returns from a list of snapshots.
|
|
96
|
+
Resamples data to daily frequency using end-of-day values.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
snapshots (List[PortfolioSnapshot]): Snapshots with total_value and created_at.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
float: Standard deviation of daily returns.
|
|
103
|
+
"""
|
|
104
|
+
if len(snapshots) < 2:
|
|
105
|
+
return 0.0 # Not enough data
|
|
106
|
+
|
|
107
|
+
# Create DataFrame from snapshots
|
|
108
|
+
data = [(s.created_at, s.total_value) for s in snapshots]
|
|
109
|
+
df = pd.DataFrame(data, columns=["created_at", "total_value"])
|
|
110
|
+
df["created_at"] = pd.to_datetime(df["created_at"])
|
|
111
|
+
df = df.drop_duplicates("created_at").set_index("created_at")
|
|
112
|
+
df = df.sort_index()
|
|
113
|
+
|
|
114
|
+
# Resample to daily frequency (end of day)
|
|
115
|
+
daily_df = df.resample("D").last().dropna()
|
|
116
|
+
|
|
117
|
+
# Calculate daily returns
|
|
118
|
+
daily_df["return"] = daily_df["total_value"].pct_change().dropna()
|
|
119
|
+
|
|
120
|
+
if daily_df["return"].empty:
|
|
121
|
+
return 0.0
|
|
122
|
+
|
|
123
|
+
return daily_df["return"].std()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def get_downside_std_of_daily_returns(snapshots):
|
|
127
|
+
"""
|
|
128
|
+
Calculate the downside standard deviation of daily returns from a list of snapshots.
|
|
129
|
+
Resamples data to daily frequency using end-of-day values.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
snapshots (List[PortfolioSnapshot]): Snapshots with total_value and created_at.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
float: Downside standard deviation of daily returns.
|
|
136
|
+
"""
|
|
137
|
+
if len(snapshots) < 2:
|
|
138
|
+
return 0.0 # Not enough data
|
|
139
|
+
|
|
140
|
+
# Create DataFrame from snapshots
|
|
141
|
+
data = [(s.created_at, s.total_value) for s in snapshots]
|
|
142
|
+
df = pd.DataFrame(data, columns=["created_at", "total_value"])
|
|
143
|
+
df["created_at"] = pd.to_datetime(df["created_at"])
|
|
144
|
+
df = df.drop_duplicates("created_at").set_index("created_at")
|
|
145
|
+
df = df.sort_index()
|
|
146
|
+
|
|
147
|
+
# Resample to daily frequency (end of day)
|
|
148
|
+
daily_df = df.resample("D").last().dropna()
|
|
149
|
+
|
|
150
|
+
# Calculate daily returns
|
|
151
|
+
daily_df["return"] = daily_df["total_value"].pct_change().dropna()
|
|
152
|
+
|
|
153
|
+
# Filter only negative returns for downside deviation
|
|
154
|
+
negative_returns = daily_df["return"][daily_df["return"] < 0]
|
|
155
|
+
|
|
156
|
+
if negative_returns.empty:
|
|
157
|
+
return 0.0
|
|
158
|
+
|
|
159
|
+
return negative_returns.std()
|
|
@@ -9,6 +9,22 @@ Example of Non-Overlap:
|
|
|
9
9
|
Strategy A: 90% win rate, but average win is $1, average loss is $10 → not profitable.
|
|
10
10
|
|
|
11
11
|
Strategy B: 30% win rate, but average win is $300, average loss is $50 → highly profitable.
|
|
12
|
+
|
|
13
|
+
| Win Rate | Win/Loss Ratio | Comment |
|
|
14
|
+
| ----------------- | -------------- | ---------------------------------- |
|
|
15
|
+
| High (>60%) | <1 | Can still be profitable |
|
|
16
|
+
| Moderate (40-60%) | \~1 or >1 | Ideal sweet spot |
|
|
17
|
+
| Low (<40%) | >1 | Possible if big wins offset losses |
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
Practical Example:
|
|
21
|
+
* Win rate 40% with win/loss ratio 2: Good — you win less often but your wins are twice as big.
|
|
22
|
+
* Win rate 60% with win/loss ratio 0.7: Also good — you win often but your wins are smaller than losses.
|
|
23
|
+
|
|
24
|
+
What’s “good”?
|
|
25
|
+
* Typical win/loss ratio ranges from 0.5 to 3 depending on strategy style.
|
|
26
|
+
* Many profitable traders target win/loss ratio between 1.5 and 2.5.
|
|
27
|
+
* Very aggressive strategies might have a lower win rate but higher win/loss ratio.
|
|
12
28
|
"""
|
|
13
29
|
|
|
14
30
|
from investing_algorithm_framework import BacktestReport, TradeStatus
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from datetime import datetime, timezone
|
|
2
2
|
|
|
3
3
|
from investing_algorithm_framework.domain import Observer, Event, \
|
|
4
|
-
SNAPSHOT_INTERVAL, SnapshotInterval,
|
|
4
|
+
SNAPSHOT_INTERVAL, SnapshotInterval, OrderStatus, \
|
|
5
5
|
PortfolioSnapshot, Environment, ENVIRONMENT, BACKTESTING_INDEX_DATETIME
|
|
6
6
|
from investing_algorithm_framework.services.repository_service import \
|
|
7
7
|
RepositoryService
|
|
@@ -100,14 +100,12 @@ class PortfolioSnapshotService(RepositoryService, Observer):
|
|
|
100
100
|
pending_value = 0
|
|
101
101
|
pending_orders = self.order_repository.get_all(
|
|
102
102
|
{
|
|
103
|
-
"order_side": OrderSide.BUY.value,
|
|
104
103
|
"status": OrderStatus.OPEN.value,
|
|
105
104
|
"portfolio_id": portfolio.id
|
|
106
105
|
}
|
|
107
106
|
)
|
|
108
107
|
created_orders = self.order_repository.get_all(
|
|
109
108
|
{
|
|
110
|
-
"order_side": OrderSide.BUY.value,
|
|
111
109
|
"status": OrderStatus.CREATED.value,
|
|
112
110
|
"portfolio_id": portfolio.id
|
|
113
111
|
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
|
-
from investing_algorithm_framework.domain.models import BacktestReport
|
|
4
|
-
from .cagr import get_cagr
|
|
5
|
-
from .risk_free_rate import get_risk_free_rate_us
|
|
6
|
-
from .standard_deviation import get_standard_deviation_returns
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def get_sharpe_ratio(
|
|
10
|
-
backtest_report: BacktestReport, risk_free_rate: Optional[float] = None,
|
|
11
|
-
) -> float:
|
|
12
|
-
"""
|
|
13
|
-
Calculate the Sharpe Ratio from a backtest report using daily or
|
|
14
|
-
weekly returns.
|
|
15
|
-
|
|
16
|
-
The Sharpe Ratio is calculated as:
|
|
17
|
-
(Annualized Return - Risk-Free Rate) / Annualized Std Dev of Returns
|
|
18
|
-
|
|
19
|
-
Args:
|
|
20
|
-
backtest_report: Object with get_trades(trade_status=...) and
|
|
21
|
-
`number_of_days` attributes.
|
|
22
|
-
risk_free_rate (float, optional): Annual risk-free rate as a
|
|
23
|
-
decimal (e.g., 0.047 for 4.7%).
|
|
24
|
-
|
|
25
|
-
Returns:
|
|
26
|
-
float: The Sharpe Ratio.
|
|
27
|
-
"""
|
|
28
|
-
annualized_return = get_cagr(backtest_report)
|
|
29
|
-
# Convert annualized return to decimal
|
|
30
|
-
annualized_return = annualized_return / 100.0
|
|
31
|
-
standard_deviation_downside = \
|
|
32
|
-
get_standard_deviation_returns(backtest_report)
|
|
33
|
-
|
|
34
|
-
if risk_free_rate is None:
|
|
35
|
-
risk_free_rate = get_risk_free_rate_us()
|
|
36
|
-
|
|
37
|
-
# Calculate sharp ratio
|
|
38
|
-
return (annualized_return - risk_free_rate) / standard_deviation_downside
|