investing-algorithm-framework 1.3.1__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 +195 -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 +31 -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 +2233 -264
- 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/stop_loss_table.py +0 -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/stateless/exception_handler.py +1 -1
- investing_algorithm_framework/app/strategy.py +873 -52
- 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 +4 -3
- investing_algorithm_framework/app/web/controllers/portfolio.py +1 -1
- investing_algorithm_framework/app/web/controllers/positions.py +3 -3
- investing_algorithm_framework/app/web/create_app.py +4 -2
- investing_algorithm_framework/app/web/error_handler.py +1 -1
- investing_algorithm_framework/app/web/schemas/order.py +2 -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 +43 -9
- investing_algorithm_framework/dependency_container.py +121 -33
- investing_algorithm_framework/domain/__init__.py +109 -22
- 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 +60 -138
- investing_algorithm_framework/domain/constants.py +23 -34
- investing_algorithm_framework/domain/data_provider.py +334 -0
- investing_algorithm_framework/domain/data_structures.py +42 -0
- investing_algorithm_framework/domain/decimal_parsing.py +40 -0
- investing_algorithm_framework/domain/exceptions.py +51 -1
- investing_algorithm_framework/domain/models/__init__.py +29 -14
- investing_algorithm_framework/domain/models/app_mode.py +34 -0
- investing_algorithm_framework/domain/models/base_model.py +3 -1
- 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 +243 -86
- 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 +7 -2
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +134 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -37
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
- investing_algorithm_framework/domain/models/position/__init__.py +3 -2
- investing_algorithm_framework/domain/models/position/position.py +29 -0
- investing_algorithm_framework/domain/models/position/position_size.py +41 -0
- investing_algorithm_framework/domain/models/position/{position_cost.py → position_snapshot.py} +16 -8
- 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 +33 -0
- 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 +111 -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 +16 -4
- 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 +39 -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 +87 -13
- investing_algorithm_framework/infrastructure/models/__init__.py +13 -4
- investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
- investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
- investing_algorithm_framework/infrastructure/models/order/order.py +73 -73
- 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 +3 -2
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
- investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +57 -3
- investing_algorithm_framework/infrastructure/models/position/__init__.py +2 -2
- investing_algorithm_framework/infrastructure/models/position/position.py +16 -11
- investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +23 -0
- 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 +13 -5
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +32 -19
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
- investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +47 -4
- investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +85 -31
- 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 -2
- 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 +127 -10
- investing_algorithm_framework/services/configuration_service.py +95 -0
- 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/positions/position_snapshot_service.py +18 -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.3.1.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 -410
- 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 -76
- 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/trading_data_types.py +0 -47
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -205
- investing_algorithm_framework/domain/singleton.py +0 -9
- investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
- investing_algorithm_framework/infrastructure/models/position/position_cost.py +0 -32
- investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
- investing_algorithm_framework/infrastructure/repositories/position_cost_repository.py +0 -16
- investing_algorithm_framework/infrastructure/services/market_service.py +0 -422
- investing_algorithm_framework/services/market_data_service.py +0 -75
- investing_algorithm_framework/services/order_service.py +0 -464
- investing_algorithm_framework/services/portfolio_service.py +0 -105
- investing_algorithm_framework/services/position_cost_service.py +0 -5
- investing_algorithm_framework/services/position_service.py +0 -50
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -219
- investing_algorithm_framework/setup_logging.py +0 -40
- investing_algorithm_framework-1.3.1.dist-info/AUTHORS.md +0 -8
- investing_algorithm_framework-1.3.1.dist-info/METADATA +0 -172
- investing_algorithm_framework-1.3.1.dist-info/RECORD +0 -103
- investing_algorithm_framework-1.3.1.dist-info/top_level.txt +0 -1
- {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
from .backtest_metrics import BacktestMetrics
|
|
5
|
+
from .backtest_summary_metrics import BacktestSummaryMetrics
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger("investing_algorithm_framework")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def safe_weighted_mean(values, weights):
|
|
11
|
+
"""
|
|
12
|
+
Calculate the weighted mean of a list of values,
|
|
13
|
+
ignoring None values and weights <= 0.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
values (List[float | None]): List of values to average.
|
|
17
|
+
weights (List[float | None]): Corresponding weights for the values.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
float | None: The weighted mean, or None if no valid values.
|
|
21
|
+
"""
|
|
22
|
+
vals = [(v, w) for v, w in zip(values, weights) if
|
|
23
|
+
v is not None and w is not None and w > 0]
|
|
24
|
+
if not vals:
|
|
25
|
+
return None
|
|
26
|
+
total_weight = sum(w for _, w in vals)
|
|
27
|
+
return sum(
|
|
28
|
+
v * w for v, w in vals
|
|
29
|
+
) / total_weight if total_weight > 0 else None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _compound_percentage_returns(percentages):
|
|
33
|
+
"""
|
|
34
|
+
Compound percentage returns across multiple periods.
|
|
35
|
+
|
|
36
|
+
For example, if period 1 has 10% return and period 2 has 5% return,
|
|
37
|
+
the compounded return is: (1 + 0.10) * (1 + 0.05) - 1 = 15.5%
|
|
38
|
+
NOT simply 10% + 5% = 15%
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
percentages (List[float | None]): List of percentage returns
|
|
42
|
+
(as whole numbers, e.g., 10 for 10%).
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
float | None: The compounded percentage return, or None if no
|
|
46
|
+
valid percentages.
|
|
47
|
+
"""
|
|
48
|
+
valid_percentages = [p for p in percentages if p is not None]
|
|
49
|
+
if not valid_percentages:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
# Convert percentages to decimals, compound, then convert back
|
|
53
|
+
compounded = 1.0
|
|
54
|
+
for pct in valid_percentages:
|
|
55
|
+
compounded *= (1 + pct / 100)
|
|
56
|
+
|
|
57
|
+
# Convert back to percentage
|
|
58
|
+
return (compounded - 1) * 100
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def combine_backtests(backtests):
|
|
62
|
+
"""
|
|
63
|
+
Combine multiple backtests into a single backtest by aggregating
|
|
64
|
+
their results.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
backtests (List[Backtest]): List of Backtest instances to combine.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Backtest: A new Backtest instance representing the combined results.
|
|
71
|
+
"""
|
|
72
|
+
backtest_metrics = []
|
|
73
|
+
backtest_runs = []
|
|
74
|
+
algorithm_id = None
|
|
75
|
+
|
|
76
|
+
for backtest in backtests:
|
|
77
|
+
if algorithm_id is None:
|
|
78
|
+
algorithm_id = backtest.algorithm_id
|
|
79
|
+
elif algorithm_id != backtest.algorithm_id:
|
|
80
|
+
raise ValueError(
|
|
81
|
+
"All backtests must belong to the same algorithm id"
|
|
82
|
+
"to be combined."
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
backtest_runs += backtest.get_all_backtest_runs()
|
|
86
|
+
backtest_metrics += backtest.get_all_backtest_metrics()
|
|
87
|
+
|
|
88
|
+
summary = generate_backtest_summary_metrics(backtest_metrics)
|
|
89
|
+
|
|
90
|
+
metadata = None
|
|
91
|
+
risk_free_rate = None
|
|
92
|
+
|
|
93
|
+
# Check if there are duplicate backtest runs
|
|
94
|
+
unique_date_ranges = set()
|
|
95
|
+
for backtest in backtests:
|
|
96
|
+
for run in backtest.get_all_backtest_runs():
|
|
97
|
+
date_range = (run.backtest_start_date, run.backtest_end_date)
|
|
98
|
+
if date_range in unique_date_ranges:
|
|
99
|
+
logger.warning(
|
|
100
|
+
"Duplicate backtest run detected for date range: "
|
|
101
|
+
f"{date_range} when combining backtests."
|
|
102
|
+
)
|
|
103
|
+
unique_date_ranges.add(date_range)
|
|
104
|
+
|
|
105
|
+
# Merge all metadata dictionaries
|
|
106
|
+
metadata = {}
|
|
107
|
+
for backtest in backtests:
|
|
108
|
+
if backtest.metadata:
|
|
109
|
+
metadata.update(backtest.metadata)
|
|
110
|
+
|
|
111
|
+
# Get the first risk-free rate
|
|
112
|
+
for backtest in backtests:
|
|
113
|
+
if backtest.risk_free_rate is not None:
|
|
114
|
+
risk_free_rate = backtest.risk_free_rate
|
|
115
|
+
break
|
|
116
|
+
from .backtest import Backtest
|
|
117
|
+
|
|
118
|
+
backtest = Backtest(
|
|
119
|
+
algorithm_id=algorithm_id,
|
|
120
|
+
backtest_summary=summary,
|
|
121
|
+
metadata=metadata,
|
|
122
|
+
risk_free_rate=risk_free_rate,
|
|
123
|
+
backtest_runs=backtest_runs
|
|
124
|
+
)
|
|
125
|
+
return backtest
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def generate_backtest_summary_metrics(
|
|
129
|
+
backtest_metrics: List[BacktestMetrics]
|
|
130
|
+
) -> BacktestSummaryMetrics:
|
|
131
|
+
"""
|
|
132
|
+
Combine multiple BacktestMetrics into a single BacktestSummaryMetrics
|
|
133
|
+
by aggregating their results.
|
|
134
|
+
|
|
135
|
+
The aggregation logic follows these principles:
|
|
136
|
+
- Absolute values (gains, losses, growth): summed across periods
|
|
137
|
+
- Percentage returns: compounded across periods (not summed)
|
|
138
|
+
- Ratios (Sharpe, Sortino, etc.): weighted average by time period
|
|
139
|
+
- Trade-based metrics (win rate, avg trade return): weighted by trade count
|
|
140
|
+
- Max drawdown: worst (minimum) value across all periods
|
|
141
|
+
- Counts (number of trades): summed
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
backtest_metrics (List[BacktestMetrics]): List of BacktestMetrics
|
|
145
|
+
instances to combine.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
BacktestSummaryMetrics: A new BacktestSummaryMetrics instance
|
|
149
|
+
representing the combined results.
|
|
150
|
+
"""
|
|
151
|
+
if not backtest_metrics:
|
|
152
|
+
return BacktestSummaryMetrics()
|
|
153
|
+
|
|
154
|
+
# Filter out None metrics
|
|
155
|
+
valid_metrics = [b for b in backtest_metrics if b is not None]
|
|
156
|
+
if not valid_metrics:
|
|
157
|
+
return BacktestSummaryMetrics()
|
|
158
|
+
|
|
159
|
+
# === ABSOLUTE VALUES (summed) ===
|
|
160
|
+
total_net_gain = sum(
|
|
161
|
+
b.total_net_gain for b in valid_metrics
|
|
162
|
+
if b.total_net_gain is not None
|
|
163
|
+
)
|
|
164
|
+
total_loss = sum(
|
|
165
|
+
b.gross_loss for b in valid_metrics
|
|
166
|
+
if b.gross_loss is not None
|
|
167
|
+
)
|
|
168
|
+
total_growth = sum(
|
|
169
|
+
b.total_growth for b in valid_metrics
|
|
170
|
+
if b.total_growth is not None
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# === PERCENTAGE RETURNS (compounded, not summed) ===
|
|
174
|
+
# Compound returns: (1 + r1) * (1 + r2) * ... - 1
|
|
175
|
+
# For percentages stored as whole numbers (e.g., 10 for 10%)
|
|
176
|
+
total_net_gain_percentage = _compound_percentage_returns(
|
|
177
|
+
[b.total_net_gain_percentage for b in valid_metrics]
|
|
178
|
+
)
|
|
179
|
+
total_loss_percentage = _compound_percentage_returns(
|
|
180
|
+
[b.total_loss_percentage for b in valid_metrics]
|
|
181
|
+
)
|
|
182
|
+
total_growth_percentage = _compound_percentage_returns(
|
|
183
|
+
[b.total_growth_percentage for b in valid_metrics]
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# === AVERAGES (weighted by time) ===
|
|
187
|
+
average_total_net_gain = safe_weighted_mean(
|
|
188
|
+
[b.total_net_gain for b in valid_metrics],
|
|
189
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
190
|
+
)
|
|
191
|
+
average_total_net_gain_percentage = safe_weighted_mean(
|
|
192
|
+
[b.total_net_gain_percentage for b in valid_metrics],
|
|
193
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
194
|
+
)
|
|
195
|
+
average_total_loss = safe_weighted_mean(
|
|
196
|
+
[b.gross_loss for b in valid_metrics],
|
|
197
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
198
|
+
)
|
|
199
|
+
average_total_loss_percentage = safe_weighted_mean(
|
|
200
|
+
[b.total_loss_percentage for b in valid_metrics],
|
|
201
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
202
|
+
)
|
|
203
|
+
average_growth = safe_weighted_mean(
|
|
204
|
+
[b.total_growth for b in valid_metrics],
|
|
205
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
206
|
+
)
|
|
207
|
+
average_growth_percentage = safe_weighted_mean(
|
|
208
|
+
[b.total_growth_percentage for b in valid_metrics],
|
|
209
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# === RISK-ADJUSTED RATIOS (weighted by time) ===
|
|
213
|
+
cagr = safe_weighted_mean(
|
|
214
|
+
[b.cagr for b in valid_metrics],
|
|
215
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
216
|
+
)
|
|
217
|
+
sharpe_ratio = safe_weighted_mean(
|
|
218
|
+
[b.sharpe_ratio for b in valid_metrics],
|
|
219
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
220
|
+
)
|
|
221
|
+
sortino_ratio = safe_weighted_mean(
|
|
222
|
+
[b.sortino_ratio for b in valid_metrics],
|
|
223
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
224
|
+
)
|
|
225
|
+
calmar_ratio = safe_weighted_mean(
|
|
226
|
+
[b.calmar_ratio for b in valid_metrics],
|
|
227
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
228
|
+
)
|
|
229
|
+
annual_volatility = safe_weighted_mean(
|
|
230
|
+
[b.annual_volatility for b in valid_metrics],
|
|
231
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# === PROFIT FACTOR (recalculated from totals, not averaged) ===
|
|
235
|
+
# profit_factor = total_gross_profit / total_gross_loss
|
|
236
|
+
total_gross_profit = sum(
|
|
237
|
+
b.gross_profit for b in valid_metrics
|
|
238
|
+
if hasattr(b, 'gross_profit') and b.gross_profit is not None
|
|
239
|
+
)
|
|
240
|
+
total_gross_loss_abs = abs(sum(
|
|
241
|
+
b.gross_loss for b in valid_metrics
|
|
242
|
+
if b.gross_loss is not None
|
|
243
|
+
))
|
|
244
|
+
if total_gross_loss_abs > 0:
|
|
245
|
+
profit_factor = total_gross_profit / total_gross_loss_abs
|
|
246
|
+
else:
|
|
247
|
+
# Fallback to weighted average if we can't calculate from totals
|
|
248
|
+
profit_factor = safe_weighted_mean(
|
|
249
|
+
[b.profit_factor for b in valid_metrics],
|
|
250
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# === MAX DRAWDOWN (worst value = minimum,
|
|
254
|
+
# since drawdowns are negative) ===
|
|
255
|
+
drawdowns = [b.max_drawdown for b in valid_metrics
|
|
256
|
+
if b.max_drawdown is not None]
|
|
257
|
+
max_drawdown = min(drawdowns) if drawdowns else None
|
|
258
|
+
|
|
259
|
+
max_drawdown_duration = max(
|
|
260
|
+
(b.max_drawdown_duration for b in valid_metrics
|
|
261
|
+
if b.max_drawdown_duration is not None), default=None
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# === TRADE FREQUENCY (weighted by time) ===
|
|
265
|
+
trades_per_year = safe_weighted_mean(
|
|
266
|
+
[b.trades_per_year for b in valid_metrics],
|
|
267
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# === WIN RATE (weighted by number of closed trades, not time) ===
|
|
271
|
+
win_rate = safe_weighted_mean(
|
|
272
|
+
[b.win_rate for b in valid_metrics],
|
|
273
|
+
[b.number_of_trades_closed for b in valid_metrics]
|
|
274
|
+
)
|
|
275
|
+
current_win_rate = safe_weighted_mean(
|
|
276
|
+
[b.current_win_rate for b in valid_metrics],
|
|
277
|
+
[b.number_of_trades_closed for b in valid_metrics]
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# === WIN/LOSS RATIO (weighted by number of closed trades) ===
|
|
281
|
+
win_loss_ratio = safe_weighted_mean(
|
|
282
|
+
[b.win_loss_ratio for b in valid_metrics],
|
|
283
|
+
[b.number_of_trades_closed for b in valid_metrics]
|
|
284
|
+
)
|
|
285
|
+
current_win_loss_ratio = safe_weighted_mean(
|
|
286
|
+
[b.current_win_loss_ratio for b in valid_metrics],
|
|
287
|
+
[b.number_of_trades_closed for b in valid_metrics]
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# === TRADE COUNTS (summed) ===
|
|
291
|
+
number_of_trades = sum(
|
|
292
|
+
b.number_of_trades for b in valid_metrics
|
|
293
|
+
if b.number_of_trades is not None
|
|
294
|
+
)
|
|
295
|
+
number_of_trades_closed = sum(
|
|
296
|
+
b.number_of_trades_closed for b in valid_metrics
|
|
297
|
+
if b.number_of_trades_closed is not None
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
# === EXPOSURE (weighted by time) ===
|
|
301
|
+
cumulative_exposure = safe_weighted_mean(
|
|
302
|
+
[b.cumulative_exposure for b in valid_metrics],
|
|
303
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
304
|
+
)
|
|
305
|
+
exposure_ratio = safe_weighted_mean(
|
|
306
|
+
[b.exposure_ratio for b in valid_metrics],
|
|
307
|
+
[b.total_number_of_days for b in valid_metrics]
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
# === AVERAGE TRADE RETURN (weighted by total trades) ===
|
|
311
|
+
average_trade_return = safe_weighted_mean(
|
|
312
|
+
[b.average_trade_return for b in valid_metrics],
|
|
313
|
+
[b.number_of_trades_closed for b in valid_metrics]
|
|
314
|
+
)
|
|
315
|
+
average_trade_return_percentage = safe_weighted_mean(
|
|
316
|
+
[b.average_trade_return_percentage for b in valid_metrics],
|
|
317
|
+
[b.number_of_trades_closed for b in valid_metrics]
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
# === AVERAGE TRADE LOSS (weighted by losing trades) ===
|
|
321
|
+
# We need to estimate losing trade count from win_rate if not available
|
|
322
|
+
losing_trade_weights = []
|
|
323
|
+
for b in valid_metrics:
|
|
324
|
+
if b.number_of_trades_closed is not None and b.win_rate is not None:
|
|
325
|
+
losing_trades = b.number_of_trades_closed * (1 - b.win_rate / 100)
|
|
326
|
+
losing_trade_weights.append(losing_trades)
|
|
327
|
+
else:
|
|
328
|
+
losing_trade_weights.append(b.number_of_trades_closed or 0)
|
|
329
|
+
|
|
330
|
+
average_trade_loss = safe_weighted_mean(
|
|
331
|
+
[b.average_trade_loss for b in valid_metrics],
|
|
332
|
+
losing_trade_weights
|
|
333
|
+
)
|
|
334
|
+
average_trade_loss_percentage = safe_weighted_mean(
|
|
335
|
+
[b.average_trade_loss_percentage for b in valid_metrics],
|
|
336
|
+
losing_trade_weights
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
# === AVERAGE TRADE GAIN (weighted by winning trades) ===
|
|
340
|
+
winning_trade_weights = []
|
|
341
|
+
for b in valid_metrics:
|
|
342
|
+
if b.number_of_trades_closed is not None and b.win_rate is not None:
|
|
343
|
+
winning_trades = b.number_of_trades_closed * (b.win_rate / 100)
|
|
344
|
+
winning_trade_weights.append(winning_trades)
|
|
345
|
+
else:
|
|
346
|
+
winning_trade_weights.append(b.number_of_trades_closed or 0)
|
|
347
|
+
|
|
348
|
+
average_trade_gain = safe_weighted_mean(
|
|
349
|
+
[b.average_trade_gain for b in valid_metrics],
|
|
350
|
+
winning_trade_weights
|
|
351
|
+
)
|
|
352
|
+
average_trade_gain_percentage = safe_weighted_mean(
|
|
353
|
+
[b.average_trade_gain_percentage for b in valid_metrics],
|
|
354
|
+
winning_trade_weights
|
|
355
|
+
)
|
|
356
|
+
return BacktestSummaryMetrics(
|
|
357
|
+
total_net_gain=total_net_gain,
|
|
358
|
+
total_net_gain_percentage=total_net_gain_percentage,
|
|
359
|
+
average_net_gain=average_total_net_gain,
|
|
360
|
+
average_net_gain_percentage=average_total_net_gain_percentage,
|
|
361
|
+
total_loss=total_loss,
|
|
362
|
+
total_loss_percentage=total_loss_percentage,
|
|
363
|
+
average_loss=average_total_loss,
|
|
364
|
+
average_loss_percentage=average_total_loss_percentage,
|
|
365
|
+
total_growth=total_growth,
|
|
366
|
+
total_growth_percentage=total_growth_percentage,
|
|
367
|
+
average_growth=average_growth,
|
|
368
|
+
average_growth_percentage=average_growth_percentage,
|
|
369
|
+
cagr=cagr,
|
|
370
|
+
sharpe_ratio=sharpe_ratio,
|
|
371
|
+
sortino_ratio=sortino_ratio,
|
|
372
|
+
calmar_ratio=calmar_ratio,
|
|
373
|
+
profit_factor=profit_factor,
|
|
374
|
+
annual_volatility=annual_volatility,
|
|
375
|
+
max_drawdown=max_drawdown,
|
|
376
|
+
max_drawdown_duration=max_drawdown_duration,
|
|
377
|
+
trades_per_year=trades_per_year,
|
|
378
|
+
win_rate=win_rate,
|
|
379
|
+
current_win_rate=current_win_rate,
|
|
380
|
+
win_loss_ratio=win_loss_ratio,
|
|
381
|
+
current_win_loss_ratio=current_win_loss_ratio,
|
|
382
|
+
number_of_trades=number_of_trades,
|
|
383
|
+
number_of_trades_closed=number_of_trades_closed,
|
|
384
|
+
cumulative_exposure=cumulative_exposure,
|
|
385
|
+
exposure_ratio=exposure_ratio,
|
|
386
|
+
average_trade_return=average_trade_return,
|
|
387
|
+
average_trade_return_percentage=average_trade_return_percentage,
|
|
388
|
+
average_trade_loss=average_trade_loss,
|
|
389
|
+
average_trade_loss_percentage=average_trade_loss_percentage,
|
|
390
|
+
average_trade_gain=average_trade_gain,
|
|
391
|
+
average_trade_gain_percentage=average_trade_gain_percentage
|
|
392
|
+
)
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
import os
|
|
3
2
|
from enum import Enum
|
|
4
3
|
|
|
5
|
-
from .constants import RESOURCE_DIRECTORY
|
|
6
4
|
from .exceptions import OperationalException
|
|
7
5
|
|
|
8
|
-
logger = logging.getLogger(
|
|
6
|
+
logger = logging.getLogger("investing_algorithm_framework")
|
|
9
7
|
|
|
10
8
|
|
|
11
9
|
class Environment(Enum):
|
|
@@ -15,6 +13,7 @@ class Environment(Enum):
|
|
|
15
13
|
DEV = 'DEV'
|
|
16
14
|
PROD = 'PROD'
|
|
17
15
|
TEST = 'TEST'
|
|
16
|
+
BACKTEST = 'BACKTEST'
|
|
18
17
|
|
|
19
18
|
# Static factory method to convert
|
|
20
19
|
# a string to environment
|
|
@@ -52,138 +51,61 @@ class Environment(Enum):
|
|
|
52
51
|
return other == self.value
|
|
53
52
|
|
|
54
53
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
'
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
'
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def update(self, *args, **kwargs):
|
|
115
|
-
return self.__dict__.update(*args, **kwargs)
|
|
116
|
-
|
|
117
|
-
def keys(self):
|
|
118
|
-
return self.__dict__.keys()
|
|
119
|
-
|
|
120
|
-
def values(self):
|
|
121
|
-
return self.__dict__.values()
|
|
122
|
-
|
|
123
|
-
def items(self):
|
|
124
|
-
return self.__dict__.items()
|
|
125
|
-
|
|
126
|
-
def pop(self, *args):
|
|
127
|
-
return self.__dict__.pop(*args)
|
|
128
|
-
|
|
129
|
-
def __cmp__(self, dict_):
|
|
130
|
-
return self.__cmp__(self.__dict__, dict_)
|
|
131
|
-
|
|
132
|
-
def __contains__(self, item):
|
|
133
|
-
return item in self.__dict__
|
|
134
|
-
|
|
135
|
-
def __iter__(self):
|
|
136
|
-
return iter(self.__dict__)
|
|
137
|
-
|
|
138
|
-
def __str__(self):
|
|
139
|
-
field_strings = []
|
|
140
|
-
|
|
141
|
-
for attribute_key in self:
|
|
142
|
-
|
|
143
|
-
if attribute_key.isupper():
|
|
144
|
-
field_strings.append(
|
|
145
|
-
f'{attribute_key}='
|
|
146
|
-
f'{self[attribute_key]!r}'
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
return f"<{self.__class__.__name__}({','.join(field_strings)})>"
|
|
150
|
-
|
|
151
|
-
def get(self, key: str, default=None):
|
|
152
|
-
"""
|
|
153
|
-
Mimics the dict get() functionality
|
|
154
|
-
"""
|
|
155
|
-
|
|
156
|
-
try:
|
|
157
|
-
return self[key]
|
|
158
|
-
# Ignore exception
|
|
159
|
-
except Exception:
|
|
160
|
-
pass
|
|
161
|
-
|
|
162
|
-
return default
|
|
163
|
-
|
|
164
|
-
def set(self, key: str, value) -> None:
|
|
165
|
-
self[key] = value
|
|
166
|
-
|
|
167
|
-
@staticmethod
|
|
168
|
-
def from_dict(dictionary):
|
|
169
|
-
config = Config()
|
|
170
|
-
|
|
171
|
-
for attribute_key in dictionary:
|
|
172
|
-
|
|
173
|
-
if attribute_key:
|
|
174
|
-
config.set(attribute_key, dictionary[attribute_key])
|
|
175
|
-
config[attribute_key] = dictionary[attribute_key]
|
|
176
|
-
return config
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
class TestConfig(Config):
|
|
180
|
-
ENVIRONMENT = Environment.TEST.value
|
|
181
|
-
TESTING = True
|
|
182
|
-
DATABASE_CONFIG = {'DATABASE_NAME': "test"}
|
|
183
|
-
LOG_LEVEL = "INFO"
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
class DevConfig(Config):
|
|
187
|
-
ENVIRONMENT = Environment.DEV.value
|
|
188
|
-
DATABASE_CONFIG = {'DATABASE_NAME': "dev"}
|
|
189
|
-
LOG_LEVEL = "INFO"
|
|
54
|
+
DEFAULT_LOGGING_CONFIG = {
|
|
55
|
+
'version': 1,
|
|
56
|
+
'disable_existing_loggers': False,
|
|
57
|
+
'formatters': {
|
|
58
|
+
'default': {
|
|
59
|
+
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
'handlers': {
|
|
63
|
+
'console': {
|
|
64
|
+
'class': 'logging.StreamHandler',
|
|
65
|
+
'formatter': 'default',
|
|
66
|
+
},
|
|
67
|
+
'file': {
|
|
68
|
+
'class': 'logging.FileHandler',
|
|
69
|
+
'formatter': 'default',
|
|
70
|
+
'filename': 'app_logs.log',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
'loggers': { # Make sure to add a 'loggers' section
|
|
74
|
+
'investing_algorithm_framework': { # Define your logger here
|
|
75
|
+
'level': 'INFO', # Set the desired level
|
|
76
|
+
'handlers': ['console', 'file'], # Use these handlers
|
|
77
|
+
'propagate': False,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
'root': { # Optional: Root logger configuration
|
|
81
|
+
'level': 'WARNING', # Root logger defaults to WARNING
|
|
82
|
+
'handlers': ['console', 'file'],
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
AWS_LAMBDA_LOGGING_CONFIG = {
|
|
87
|
+
'version': 1,
|
|
88
|
+
'disable_existing_loggers': False,
|
|
89
|
+
'formatters': {
|
|
90
|
+
'default': {
|
|
91
|
+
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
'handlers': {
|
|
95
|
+
'console': {
|
|
96
|
+
'class': 'logging.StreamHandler',
|
|
97
|
+
'formatter': 'default',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
'loggers': { # Make sure to add a 'loggers' section
|
|
101
|
+
'investing_algorithm_framework': { # Define your logger here
|
|
102
|
+
'level': 'INFO', # Set the desired level
|
|
103
|
+
'handlers': ['console'], # Use these handlers
|
|
104
|
+
'propagate': False,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
'root': { # Optional: Root logger configuration
|
|
108
|
+
'level': 'WARNING', # Root logger defaults to WARNING
|
|
109
|
+
'handlers': ['console'],
|
|
110
|
+
},
|
|
111
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
# Framework constants
|
|
2
|
-
FRAMEWORK_NAME = 'FRAMEWORK_NAME'
|
|
3
2
|
ENVIRONMENT = "ENVIRONMENT"
|
|
4
3
|
STATELESS = "STATELESS"
|
|
5
4
|
|
|
@@ -8,56 +7,46 @@ DATABASE_CONFIG = 'DATABASE_CONFIG'
|
|
|
8
7
|
DATABASE_NAME = 'DATABASE_NAME'
|
|
9
8
|
DATABASE_TYPE = 'DATABASE_TYPE'
|
|
10
9
|
DATABASE_DIRECTORY_PATH = 'DATABASE_DIRECTORY_PATH'
|
|
10
|
+
DATABASE_DIRECTORY_NAME = 'DATABASE_DIRECTORY_NAME'
|
|
11
11
|
DATABASE_URL = 'DATABASE_URL'
|
|
12
12
|
DEFAULT_DATABASE_NAME = "database"
|
|
13
13
|
|
|
14
|
+
APPLICATION_DIRECTORY = "APP_DIR"
|
|
14
15
|
RESOURCE_DIRECTORY = "RESOURCE_DIRECTORY"
|
|
16
|
+
BACKTEST_DATA_DIRECTORY_NAME = "BACKTEST_DATA_DIRECTORY_NAME"
|
|
17
|
+
DATA_DIRECTORY = "DATA_DIRECTORY"
|
|
15
18
|
LOG_LEVEL = 'LOG_LEVEL'
|
|
16
19
|
BASE_DIR = 'BASE_DIR'
|
|
17
20
|
SQLALCHEMY_DATABASE_URI = 'SQLALCHEMY_DATABASE_URI'
|
|
18
|
-
SQLITE_INITIALIZED = "SQLITE_INITIALIZED"
|
|
19
21
|
SQLALCHEMY_INITIALIZED = "SQLALCHEMY_INITIALIZED"
|
|
22
|
+
APP_MODE = "APP_MODE"
|
|
20
23
|
|
|
21
24
|
|
|
22
|
-
BINANCE = "BINANCE"
|
|
23
|
-
|
|
24
|
-
IDENTIFIER_QUERY_PARAM = "identifier"
|
|
25
|
-
TARGET_SYMBOL_QUERY_PARAM = "target_symbol"
|
|
26
|
-
TRADING_SYMBOL_QUERY_PARAM = "trading_symbol"
|
|
27
|
-
ORDER_SIDE_QUERY_PARAM = "side"
|
|
28
|
-
STATUS_QUERY_PARAM = "status"
|
|
29
|
-
POSITION_SYMBOL_QUERY_PARAM = "position"
|
|
30
|
-
SYMBOL_QUERY_PARAM = "symbol"
|
|
31
|
-
TIME_FRAME_QUERY_PARAM = "time_frame"
|
|
32
|
-
|
|
33
|
-
RESERVED_IDENTIFIERS = [
|
|
34
|
-
BINANCE
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
BINANCE_API_KEY = "binance_api_key"
|
|
38
|
-
BINANCE_SECRET_KEY = "binance_secret_key"
|
|
39
|
-
|
|
40
25
|
CHECK_PENDING_ORDERS = "CHECK_PENDING_ORDERS"
|
|
41
26
|
RUN_STRATEGY = "RUN_STRATEGY"
|
|
42
27
|
|
|
43
28
|
# Configuration
|
|
44
|
-
TRADING_SYMBOL = "TRADING_SYMBOL"
|
|
45
|
-
CCXT_ENABLED = "CCXT_ENABLED"
|
|
46
|
-
API_KEY = "API_KEY"
|
|
47
|
-
SECRET_KEY = "SECRET_KEY"
|
|
48
|
-
MARKET = "MARKET"
|
|
49
|
-
TRACK_PORTFOLIO_FROM = "TRACK_PORTFOLIO_FROM"
|
|
50
|
-
SQLITE_ENABLED = "SQLITE_ENABLED"
|
|
51
|
-
PORTFOLIOS = "PORTFOLIOS"
|
|
52
|
-
STRATEGIES = "STRATEGIES"
|
|
53
|
-
APPLICATION_CONFIGURED = "APPLICATION_CONFIGURED"
|
|
54
|
-
ACTION = "ACTION"
|
|
55
|
-
|
|
56
29
|
DEFAULT_PER_PAGE_VALUE = 10
|
|
57
30
|
DEFAULT_PAGE_VALUE = 1
|
|
58
31
|
ITEMIZE = 'itemize'
|
|
59
32
|
ITEMIZED = 'itemized'
|
|
60
33
|
PAGE = 'page'
|
|
61
34
|
PER_PAGE = 'per_page'
|
|
62
|
-
|
|
63
|
-
|
|
35
|
+
DATETIME_FORMAT_BACKTESTING = "%Y-%m-%d-%H-%M"
|
|
36
|
+
CCXT_DATETIME_FORMAT_WITH_TIMEZONE = "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
37
|
+
CCXT_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
|
|
38
|
+
DEFAULT_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
|
|
39
|
+
BACKTESTING_FLAG = "BACKTESTING"
|
|
40
|
+
INDEX_DATETIME = "INDEX_DATETIME"
|
|
41
|
+
LAST_SNAPSHOT_DATETIME = "LAST_SNAPSHOT_DATETIME"
|
|
42
|
+
BACKTESTING_START_DATE = "BACKTESTING_START_DATE"
|
|
43
|
+
BACKTESTING_END_DATE = "BACKTESTING_END_DATE"
|
|
44
|
+
BACKTESTING_INITIAL_AMOUNT = "BACKTESTING_INITIAL_AMOUNT"
|
|
45
|
+
TICKER_DATA_TYPE = "TICKER"
|
|
46
|
+
OHLCV_DATA_TYPE = "OHLCV"
|
|
47
|
+
CURRENT_UTC_DATETIME = "CURRENT_UTC_DATETIME"
|
|
48
|
+
SNAPSHOT_INTERVAL = "SNAPSHOT_INTERVAL"
|
|
49
|
+
DATETIME_FORMAT = "DATETIME_FORMAT"
|
|
50
|
+
DATETIME_FORMAT_FILE_NAME = "DATETIME_FORMAT_FILE_NAME"
|
|
51
|
+
# Deployment
|
|
52
|
+
AWS_S3_STATE_BUCKET_NAME = "AWS_S3_STATE_BUCKET_NAME"
|