investing-algorithm-framework 1.5__py3-none-any.whl → 7.25.6__py3-none-any.whl
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/__init__.py +192 -16
- investing_algorithm_framework/analysis/__init__.py +16 -0
- investing_algorithm_framework/analysis/backtest_data_ranges.py +202 -0
- investing_algorithm_framework/analysis/data.py +170 -0
- investing_algorithm_framework/analysis/markdown.py +91 -0
- investing_algorithm_framework/analysis/ranking.py +298 -0
- investing_algorithm_framework/app/__init__.py +29 -4
- investing_algorithm_framework/app/algorithm/__init__.py +7 -0
- investing_algorithm_framework/app/algorithm/algorithm.py +193 -0
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +118 -0
- investing_algorithm_framework/app/app.py +2220 -379
- investing_algorithm_framework/app/app_hook.py +28 -0
- investing_algorithm_framework/app/context.py +1724 -0
- investing_algorithm_framework/app/eventloop.py +620 -0
- investing_algorithm_framework/app/reporting/__init__.py +27 -0
- investing_algorithm_framework/app/reporting/ascii.py +921 -0
- investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
- investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
- investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
- investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
- investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
- investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
- investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
- investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
- investing_algorithm_framework/app/reporting/generate.py +185 -0
- investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
- investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
- investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
- investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
- investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
- investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
- investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
- investing_algorithm_framework/app/stateless/action_handlers/__init__.py +6 -3
- investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
- investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +2 -1
- investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
- investing_algorithm_framework/app/strategy.py +867 -60
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/app/web/__init__.py +2 -1
- investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
- investing_algorithm_framework/app/web/controllers/orders.py +3 -2
- investing_algorithm_framework/app/web/controllers/positions.py +2 -2
- investing_algorithm_framework/app/web/create_app.py +4 -2
- investing_algorithm_framework/app/web/schemas/position.py +1 -0
- investing_algorithm_framework/cli/__init__.py +0 -0
- investing_algorithm_framework/cli/cli.py +231 -0
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
- investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
- investing_algorithm_framework/cli/initialize_app.py +603 -0
- investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
- investing_algorithm_framework/cli/templates/app.py.template +18 -0
- investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
- investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
- investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
- investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
- investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
- investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
- investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
- investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
- investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
- investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
- investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
- investing_algorithm_framework/cli/templates/env.example.template +2 -0
- investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
- investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
- investing_algorithm_framework/cli/templates/readme.md.template +135 -0
- investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
- investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
- investing_algorithm_framework/cli/validate_backtest_checkpoints.py +197 -0
- investing_algorithm_framework/create_app.py +40 -7
- investing_algorithm_framework/dependency_container.py +100 -47
- investing_algorithm_framework/domain/__init__.py +97 -30
- investing_algorithm_framework/domain/algorithm_id.py +69 -0
- investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
- investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
- investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
- investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
- investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
- investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
- investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
- investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
- investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
- investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
- investing_algorithm_framework/domain/config.py +59 -136
- investing_algorithm_framework/domain/constants.py +18 -37
- investing_algorithm_framework/domain/data_provider.py +334 -0
- investing_algorithm_framework/domain/data_structures.py +42 -0
- investing_algorithm_framework/domain/exceptions.py +51 -1
- investing_algorithm_framework/domain/models/__init__.py +26 -19
- investing_algorithm_framework/domain/models/app_mode.py +34 -0
- investing_algorithm_framework/domain/models/data/__init__.py +7 -0
- investing_algorithm_framework/domain/models/data/data_source.py +222 -0
- investing_algorithm_framework/domain/models/data/data_type.py +46 -0
- investing_algorithm_framework/domain/models/event.py +35 -0
- investing_algorithm_framework/domain/models/market/__init__.py +5 -0
- investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
- investing_algorithm_framework/domain/models/order/__init__.py +3 -4
- investing_algorithm_framework/domain/models/order/order.py +198 -65
- investing_algorithm_framework/domain/models/order/order_status.py +2 -2
- investing_algorithm_framework/domain/models/order/order_type.py +1 -3
- investing_algorithm_framework/domain/models/portfolio/__init__.py +6 -2
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +98 -3
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -43
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
- investing_algorithm_framework/domain/models/position/__init__.py +2 -1
- investing_algorithm_framework/domain/models/position/position.py +20 -0
- investing_algorithm_framework/domain/models/position/position_size.py +41 -0
- investing_algorithm_framework/domain/models/position/position_snapshot.py +0 -2
- investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
- investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
- investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
- investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
- investing_algorithm_framework/domain/models/strategy_profile.py +19 -141
- investing_algorithm_framework/domain/models/time_frame.py +94 -98
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +66 -2
- investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
- investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
- investing_algorithm_framework/domain/models/trade/__init__.py +11 -0
- investing_algorithm_framework/domain/models/trade/trade.py +389 -0
- investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
- investing_algorithm_framework/domain/order_executor.py +112 -0
- investing_algorithm_framework/domain/portfolio_provider.py +118 -0
- investing_algorithm_framework/domain/services/__init__.py +11 -0
- investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
- investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
- investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
- investing_algorithm_framework/domain/services/rounding_service.py +27 -0
- investing_algorithm_framework/domain/services/state_handler.py +38 -0
- investing_algorithm_framework/domain/strategy.py +1 -29
- investing_algorithm_framework/domain/utils/__init__.py +15 -5
- investing_algorithm_framework/domain/utils/csv.py +22 -0
- investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
- investing_algorithm_framework/domain/utils/dates.py +57 -0
- investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
- investing_algorithm_framework/domain/utils/polars.py +53 -0
- investing_algorithm_framework/domain/utils/random.py +29 -0
- investing_algorithm_framework/download_data.py +244 -0
- investing_algorithm_framework/infrastructure/__init__.py +37 -11
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -0
- investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
- investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
- investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
- investing_algorithm_framework/infrastructure/database/sql_alchemy.py +86 -12
- investing_algorithm_framework/infrastructure/models/__init__.py +7 -3
- investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
- investing_algorithm_framework/infrastructure/models/order/order.py +53 -53
- investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
- investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
- investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +1 -1
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -2
- investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -6
- investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +3 -1
- investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
- investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
- investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
- investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
- investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
- investing_algorithm_framework/infrastructure/repositories/__init__.py +10 -4
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +16 -5
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +84 -30
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +9 -4
- investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
- investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -0
- investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
- investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
- investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +9 -0
- investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
- investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
- investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
- investing_algorithm_framework/services/__init__.py +123 -15
- investing_algorithm_framework/services/configuration_service.py +77 -11
- investing_algorithm_framework/services/data_providers/__init__.py +5 -0
- investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
- investing_algorithm_framework/services/market_credential_service.py +40 -0
- investing_algorithm_framework/services/metrics/__init__.py +119 -0
- investing_algorithm_framework/services/metrics/alpha.py +0 -0
- investing_algorithm_framework/services/metrics/beta.py +0 -0
- investing_algorithm_framework/services/metrics/cagr.py +60 -0
- investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
- investing_algorithm_framework/services/metrics/drawdown.py +218 -0
- investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
- investing_algorithm_framework/services/metrics/exposure.py +210 -0
- investing_algorithm_framework/services/metrics/generate.py +358 -0
- investing_algorithm_framework/services/metrics/mean_daily_return.py +84 -0
- investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
- investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
- investing_algorithm_framework/services/metrics/recovery.py +113 -0
- investing_algorithm_framework/services/metrics/returns.py +452 -0
- investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
- investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
- investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
- investing_algorithm_framework/services/metrics/standard_deviation.py +156 -0
- investing_algorithm_framework/services/metrics/trades.py +473 -0
- investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
- investing_algorithm_framework/services/metrics/ulcer.py +0 -0
- investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
- investing_algorithm_framework/services/metrics/volatility.py +118 -0
- investing_algorithm_framework/services/metrics/win_rate.py +177 -0
- investing_algorithm_framework/services/order_service/__init__.py +9 -0
- investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
- investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
- investing_algorithm_framework/services/order_service/order_service.py +826 -0
- investing_algorithm_framework/services/portfolios/__init__.py +16 -0
- investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
- investing_algorithm_framework/services/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
- investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
- investing_algorithm_framework/services/positions/__init__.py +7 -0
- investing_algorithm_framework/services/positions/position_service.py +210 -0
- investing_algorithm_framework/services/repository_service.py +8 -2
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +117 -0
- investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
- investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
- investing_algorithm_framework/services/trade_service/__init__.py +9 -0
- investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
- investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
- investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
- investing_algorithm_framework-7.25.6.dist-info/METADATA +535 -0
- investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
- {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
- investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
- investing_algorithm_framework/app/algorithm.py +0 -630
- investing_algorithm_framework/domain/models/backtest_profile.py +0 -414
- investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
- investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
- investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -105
- investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
- investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
- investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
- investing_algorithm_framework/domain/models/trade.py +0 -78
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
- investing_algorithm_framework/domain/singleton.py +0 -9
- investing_algorithm_framework/domain/utils/backtesting.py +0 -82
- investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
- investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
- investing_algorithm_framework/infrastructure/services/market_backtest_service.py +0 -360
- investing_algorithm_framework/infrastructure/services/market_service.py +0 -410
- investing_algorithm_framework/infrastructure/services/performance_service.py +0 -192
- investing_algorithm_framework/services/backtest_service.py +0 -268
- investing_algorithm_framework/services/market_data_service.py +0 -77
- investing_algorithm_framework/services/order_backtest_service.py +0 -122
- investing_algorithm_framework/services/order_service.py +0 -752
- investing_algorithm_framework/services/portfolio_service.py +0 -164
- investing_algorithm_framework/services/portfolio_snapshot_service.py +0 -68
- investing_algorithm_framework/services/position_cost_service.py +0 -5
- investing_algorithm_framework/services/position_service.py +0 -63
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -225
- investing_algorithm_framework-1.5.dist-info/AUTHORS.md +0 -8
- investing_algorithm_framework-1.5.dist-info/METADATA +0 -230
- investing_algorithm_framework-1.5.dist-info/RECORD +0 -119
- investing_algorithm_framework-1.5.dist-info/top_level.txt +0 -1
- /investing_algorithm_framework/{infrastructure/services/performance_backtest_service.py → app/reporting/tables/stop_loss_table.py} +0 -0
- /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
- {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pandas import DataFrame, DatetimeIndex
|
|
3
|
+
from investing_algorithm_framework.domain.exceptions import \
|
|
4
|
+
OperationalException
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_price_efficiency_ratio(data: DataFrame):
|
|
10
|
+
"""
|
|
11
|
+
Calculate the price efficiency ratio (noise) for each symbol.
|
|
12
|
+
|
|
13
|
+
The price efficiency ratio is calculated as follows:
|
|
14
|
+
|
|
15
|
+
1. Calculate the net price change over the period
|
|
16
|
+
2. Calculate the sum of absolute daily price changes
|
|
17
|
+
3. Calculate Efficiency Ratio = Net Price Change / Sum of Absolute
|
|
18
|
+
Daily Price Changes
|
|
19
|
+
|
|
20
|
+
The price efficiency ratio is a measure of the efficiency of the
|
|
21
|
+
price movement over the period. A higher efficiency ratio indicates
|
|
22
|
+
a more efficient price movement.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
data (dict): A pandas DataFrame containing a column with either a
|
|
26
|
+
'Close' or 'Price' label and a datetime index.
|
|
27
|
+
|
|
28
|
+
returns:
|
|
29
|
+
float: The price efficiency ratio
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
# Check if close value and index is a datetime object
|
|
33
|
+
if 'Close' not in data.columns:
|
|
34
|
+
raise OperationalException(
|
|
35
|
+
"Close column not found in data, "
|
|
36
|
+
"required for price efficiency ratio calculation"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if not isinstance(data.index, DatetimeIndex):
|
|
40
|
+
raise OperationalException(
|
|
41
|
+
"Index is not a datetime object,"
|
|
42
|
+
"required for price efficiency ratio calculation"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Calculate daily price changes
|
|
46
|
+
data['Daily Change'] = data['Close'].diff()
|
|
47
|
+
|
|
48
|
+
# Calculate net price change over the period
|
|
49
|
+
net_price_change = abs(
|
|
50
|
+
data['Close'].iloc[-1] - data['Close'].iloc[0])
|
|
51
|
+
|
|
52
|
+
# Calculate the sum of absolute daily price changes
|
|
53
|
+
sum_absolute_changes = data['Daily Change'] \
|
|
54
|
+
.abs().sum()
|
|
55
|
+
|
|
56
|
+
# Calculate Efficiency Ratio
|
|
57
|
+
return net_price_change / sum_absolute_changes
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for calculating profit factor metrics from backtest trades.
|
|
3
|
+
|
|
4
|
+
| **Profit Factor** | **Interpretation** |
|
|
5
|
+
| ----------------- | ----------------------------------------------------------------------------------------- |
|
|
6
|
+
| **< 1.0** | **Losing strategy** — losses outweigh profits |
|
|
7
|
+
| **1.0 – 1.3** | Weak or barely breakeven — needs improvement or may not be sustainable |
|
|
8
|
+
| **1.3 – 1.6** | Average — possibly profitable but sensitive to market regime changes |
|
|
9
|
+
| **1.6 – 2.0** | Good — generally indicates a solid, sustainable edge |
|
|
10
|
+
| **2.0 – 3.0** | Very good — strong edge with lower drawdown risk |
|
|
11
|
+
| **> 3.0** | Excellent — rare in real markets; often associated with low-frequency or niche strategies |
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from collections import deque
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
from typing import List, Tuple
|
|
18
|
+
|
|
19
|
+
from investing_algorithm_framework.domain.models import Trade
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_cumulative_profit_factor_series(
|
|
23
|
+
trades: List[Trade]
|
|
24
|
+
) -> list[tuple[datetime, float]]:
|
|
25
|
+
"""
|
|
26
|
+
Calculates the cumulative profit factor over time from a backtest report.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
trades (List[Trade]): List of closed trades from the backtest report.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
List of (datetime, float) tuples: (timestamp, cumulative profit factor)
|
|
33
|
+
"""
|
|
34
|
+
results = []
|
|
35
|
+
gross_profit = 0.0
|
|
36
|
+
gross_loss = 0.0
|
|
37
|
+
|
|
38
|
+
for trade in trades:
|
|
39
|
+
close_time = trade.closed_at
|
|
40
|
+
profit = trade.net_gain
|
|
41
|
+
|
|
42
|
+
if profit >= 0:
|
|
43
|
+
gross_profit += profit
|
|
44
|
+
else:
|
|
45
|
+
gross_loss += abs(profit)
|
|
46
|
+
|
|
47
|
+
# Calculate profit factor with division-by-zero protection
|
|
48
|
+
if gross_loss > 0:
|
|
49
|
+
profit_factor = gross_profit / gross_loss
|
|
50
|
+
else:
|
|
51
|
+
profit_factor = float('inf') if gross_profit > 0 else 0.0
|
|
52
|
+
|
|
53
|
+
results.append((close_time, profit_factor))
|
|
54
|
+
|
|
55
|
+
return results
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_rolling_profit_factor_series(
|
|
59
|
+
trades: List[Trade], window_size: int = 20
|
|
60
|
+
) -> List[Tuple[datetime, float]]:
|
|
61
|
+
"""
|
|
62
|
+
Calculates the rolling profit factor over time from a backtest report.
|
|
63
|
+
|
|
64
|
+
The rolling profit factor is computed using the most recent
|
|
65
|
+
`window_size` trades and updated after each closed trade.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
backtest_report (BacktestReport): A instance of BacktestReport
|
|
69
|
+
containing closed trades.
|
|
70
|
+
window_size: The number of most recent trades to include in
|
|
71
|
+
each rolling calculation.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
A list of tuples, where each tuple contains:
|
|
75
|
+
- datetime: The close time of the trade (or aligned date).
|
|
76
|
+
- float: The rolling profit factor at that time.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
results = []
|
|
80
|
+
trade_window = deque(maxlen=window_size)
|
|
81
|
+
|
|
82
|
+
for trade in trades:
|
|
83
|
+
close_time = trade.closed_at
|
|
84
|
+
profit = trade.net_gain
|
|
85
|
+
|
|
86
|
+
trade_window.append(profit)
|
|
87
|
+
|
|
88
|
+
gross_profit = sum(p for p in trade_window if p >= 0)
|
|
89
|
+
gross_loss = sum(abs(p) for p in trade_window if p < 0)
|
|
90
|
+
|
|
91
|
+
if gross_loss > 0:
|
|
92
|
+
profit_factor = gross_profit / gross_loss
|
|
93
|
+
else:
|
|
94
|
+
profit_factor = float('inf') if gross_profit > 0 else 0.0
|
|
95
|
+
|
|
96
|
+
results.append((close_time, profit_factor))
|
|
97
|
+
|
|
98
|
+
return results
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def get_profit_factor(trades: List[Trade]) -> float:
|
|
102
|
+
"""
|
|
103
|
+
Calculates the total profit factor at the end of the backtest.
|
|
104
|
+
|
|
105
|
+
The profit factor is defined as:
|
|
106
|
+
Total Gross Profit / Total Gross Loss
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
trades (List[Trade]): List of closed trades from the backtest report.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
float: The profit factor at the end of the backtest.
|
|
113
|
+
Returns float('inf') if there are no losses,
|
|
114
|
+
and 0.0 if there are no profits and losses.
|
|
115
|
+
"""
|
|
116
|
+
gross_profit = 0.0
|
|
117
|
+
gross_loss = 0.0
|
|
118
|
+
|
|
119
|
+
for trade in trades:
|
|
120
|
+
profit = trade.net_gain
|
|
121
|
+
if profit > 0:
|
|
122
|
+
gross_profit += profit
|
|
123
|
+
elif profit < 0:
|
|
124
|
+
gross_loss += abs(profit)
|
|
125
|
+
|
|
126
|
+
if gross_loss == 0:
|
|
127
|
+
return float('inf') if gross_profit > 0 else 0.0
|
|
128
|
+
|
|
129
|
+
return gross_profit / gross_loss
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def get_gross_profit(trades: List[Trade]) -> float:
|
|
133
|
+
"""
|
|
134
|
+
Function to calculate the total gross profit from a list of trades.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
trades (List[Trade]): List of closed trades from the backtest report.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
float: The total gross profit from the trades.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
gross_profit = 0.0
|
|
144
|
+
for trade in trades:
|
|
145
|
+
if trade.net_gain > 0:
|
|
146
|
+
gross_profit += trade.net_gain
|
|
147
|
+
return gross_profit
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def get_gross_loss(trades: List[Trade]) -> float:
|
|
151
|
+
"""
|
|
152
|
+
Function to calculate the total gross loss from a list of trades.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
trades (List[Trade]): List of closed trades from the backtest report.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
float: The total gross loss from the trades.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
gross_loss = 0.0
|
|
162
|
+
for trade in trades:
|
|
163
|
+
if trade.net_gain < 0:
|
|
164
|
+
gross_loss += abs(trade.net_gain)
|
|
165
|
+
return gross_loss
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
🔍 What is Recovery Factor:
|
|
3
|
+
Recovery Factor =
|
|
4
|
+
🔹 Total Net Profit (how much you made overall)
|
|
5
|
+
➗
|
|
6
|
+
🔹 Max Drawdown (worst capital drop at any point)
|
|
7
|
+
|
|
8
|
+
This metric answers:
|
|
9
|
+
|
|
10
|
+
💡 "How much return did I get per unit of worst-case risk?"
|
|
11
|
+
|
|
12
|
+
🎯 Why this matters:
|
|
13
|
+
Even if a strategy has many small drawdowns, only the worst one is used.
|
|
14
|
+
|
|
15
|
+
A strategy that earns €100k total, but once dropped €50k from peak, has a
|
|
16
|
+
recovery factor of 2.0 — suggesting that it made 2x the worst pain it endured.
|
|
17
|
+
|
|
18
|
+
✅ Pros:
|
|
19
|
+
Simple and interpretable.
|
|
20
|
+
|
|
21
|
+
Useful when comparing strategies with very different drawdown profiles.
|
|
22
|
+
|
|
23
|
+
⚠️ Limitations:
|
|
24
|
+
It ignores the frequency or duration of drawdowns.
|
|
25
|
+
|
|
26
|
+
It can be misleading if net profit is inflated due to one lucky trend or
|
|
27
|
+
max drawdown happened early and was never tested again.
|
|
28
|
+
|
|
29
|
+
| **Recovery Factor** | **Interpretation** | **Comments** |
|
|
30
|
+
| ------------------- | ---------------------------------------------------- | ---------------------------------------------------------------- |
|
|
31
|
+
| **< 1.0** | ❌ *Poor* – Risk outweighs reward | Losing more in drawdowns than you're making overall |
|
|
32
|
+
| **1.0 – 1.5** | ⚠️ *Weak* – Barely recovering from drawdowns | Net profit is only slightly higher than worst drawdown |
|
|
33
|
+
| **1.5 – 2.0** | 🤔 *Moderate* – Acceptable, but not robust | Strategy is viable but may lack strong risk-adjusted performance |
|
|
34
|
+
| **2.0 – 3.0** | ✅ *Good* – Solid recovery from worst drawdowns | Indicates decent resilience and profitability |
|
|
35
|
+
| **3.0 – 5.0** | 🚀 *Very Good* – Strong risk-adjusted performance | Shows good ability to recover and generate consistent profit |
|
|
36
|
+
| **> 5.0** | 🏆 *Excellent* – Exceptional recovery vs. risk taken | Often seen in highly optimized or low-volatility strategies |
|
|
37
|
+
| **∞ (Infinity)** | 💡 *No drawdown* – Unrealistic or incomplete data | Can happen if drawdown is zero — investigate carefully |
|
|
38
|
+
"""
|
|
39
|
+
from typing import List
|
|
40
|
+
|
|
41
|
+
from investing_algorithm_framework.domain import PortfolioSnapshot
|
|
42
|
+
from .drawdown import get_max_drawdown_absolute
|
|
43
|
+
from .returns import get_total_return
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_recovery_factor(snapshots: List[PortfolioSnapshot]) -> float:
|
|
47
|
+
"""
|
|
48
|
+
Calculate the recovery factor of a backtest report.
|
|
49
|
+
|
|
50
|
+
The recovery factor is defined as:
|
|
51
|
+
Recovery Factor = Total Net Profit / Maximum Drawdown
|
|
52
|
+
|
|
53
|
+
This metric indicates how efficiently a trading strategy recovers
|
|
54
|
+
from drawdowns. A higher recovery factor implies better
|
|
55
|
+
risk-adjusted performance.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
|
|
59
|
+
from the backtest report.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
float: The recovery factor. Returns 0.0 if max drawdown is
|
|
63
|
+
zero or undefined.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
if not snapshots:
|
|
67
|
+
return 0.0
|
|
68
|
+
|
|
69
|
+
max_drawdown_absolute = get_max_drawdown_absolute(snapshots)
|
|
70
|
+
net_profit, _ = get_total_return(snapshots)
|
|
71
|
+
|
|
72
|
+
if max_drawdown_absolute == 0:
|
|
73
|
+
return float('inf') if net_profit > 0 else 0.0
|
|
74
|
+
|
|
75
|
+
return net_profit / max_drawdown_absolute
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_recovery_time(snapshots: List[PortfolioSnapshot]) -> float:
|
|
79
|
+
"""
|
|
80
|
+
Calculate the recovery time of a backtest report.
|
|
81
|
+
|
|
82
|
+
The recovery time is defined as the number of days it takes to recover
|
|
83
|
+
from the maximum drawdown.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
|
|
87
|
+
from the backtest report.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
float: The recovery time in days. Returns 0.0 if no drawdown
|
|
91
|
+
occurred.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
if not snapshots:
|
|
95
|
+
return 0.0
|
|
96
|
+
|
|
97
|
+
max_drawdown_absolute = get_max_drawdown_absolute(snapshots)
|
|
98
|
+
|
|
99
|
+
if max_drawdown_absolute == 0:
|
|
100
|
+
return 0.0
|
|
101
|
+
|
|
102
|
+
# Find the first snapshot after the maximum drawdown
|
|
103
|
+
first_snapshot_after_drawdown = next(
|
|
104
|
+
(s for s in snapshots if s.net_size >= max_drawdown_absolute), None)
|
|
105
|
+
|
|
106
|
+
if not first_snapshot_after_drawdown:
|
|
107
|
+
return 0.0
|
|
108
|
+
|
|
109
|
+
# Calculate recovery time in days
|
|
110
|
+
recovery_time = (first_snapshot_after_drawdown.created_at -
|
|
111
|
+
snapshots[0].created_at).days
|
|
112
|
+
|
|
113
|
+
return recovery_time if recovery_time > 0 else 1.0
|