investing-algorithm-framework 6.9.1__py3-none-any.whl → 7.19.15__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.
Potentially problematic release.
This version of investing-algorithm-framework might be problematic. Click here for more details.
- investing_algorithm_framework/__init__.py +147 -44
- investing_algorithm_framework/app/__init__.py +23 -6
- investing_algorithm_framework/app/algorithm/algorithm.py +5 -41
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +17 -10
- investing_algorithm_framework/app/analysis/__init__.py +15 -0
- investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
- investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
- investing_algorithm_framework/app/analysis/permutation.py +116 -0
- investing_algorithm_framework/app/analysis/ranking.py +297 -0
- investing_algorithm_framework/app/app.py +1322 -707
- investing_algorithm_framework/app/context.py +196 -88
- investing_algorithm_framework/app/eventloop.py +590 -0
- investing_algorithm_framework/app/reporting/__init__.py +16 -5
- investing_algorithm_framework/app/reporting/ascii.py +57 -202
- investing_algorithm_framework/app/reporting/backtest_report.py +284 -170
- investing_algorithm_framework/app/reporting/charts/__init__.py +10 -2
- 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 +11 -26
- investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
- investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
- investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +1 -1
- investing_algorithm_framework/app/reporting/generate.py +100 -114
- investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +40 -32
- investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +34 -27
- investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +23 -19
- investing_algorithm_framework/app/reporting/tables/trades_table.py +1 -1
- investing_algorithm_framework/app/reporting/tables/utils.py +1 -0
- investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +10 -16
- investing_algorithm_framework/app/strategy.py +315 -175
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/cli/cli.py +30 -12
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +131 -34
- investing_algorithm_framework/cli/initialize_app.py +20 -1
- investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +18 -6
- 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_requirements.txt.template +2 -2
- investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +1 -1
- investing_algorithm_framework/create_app.py +3 -5
- investing_algorithm_framework/dependency_container.py +25 -39
- investing_algorithm_framework/domain/__init__.py +45 -38
- investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
- investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
- investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
- investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
- investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
- investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
- investing_algorithm_framework/domain/backtesting/backtest_run.py +605 -0
- investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
- investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
- investing_algorithm_framework/domain/config.py +27 -0
- investing_algorithm_framework/domain/constants.py +6 -34
- investing_algorithm_framework/domain/data_provider.py +200 -56
- investing_algorithm_framework/domain/exceptions.py +34 -1
- investing_algorithm_framework/domain/models/__init__.py +10 -19
- investing_algorithm_framework/domain/models/base_model.py +0 -6
- investing_algorithm_framework/domain/models/data/__init__.py +7 -0
- investing_algorithm_framework/domain/models/data/data_source.py +214 -0
- investing_algorithm_framework/domain/models/{market_data_type.py → data/data_type.py} +7 -7
- investing_algorithm_framework/domain/models/market/market_credential.py +6 -0
- investing_algorithm_framework/domain/models/order/order.py +34 -13
- investing_algorithm_framework/domain/models/order/order_status.py +1 -1
- investing_algorithm_framework/domain/models/order/order_type.py +1 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +14 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +5 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +51 -11
- investing_algorithm_framework/domain/models/position/__init__.py +2 -1
- investing_algorithm_framework/domain/models/position/position.py +9 -0
- investing_algorithm_framework/domain/models/position/position_size.py +41 -0
- 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 +0 -1
- investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
- investing_algorithm_framework/domain/models/time_frame.py +7 -0
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +63 -1
- investing_algorithm_framework/domain/models/trade/__init__.py +0 -2
- investing_algorithm_framework/domain/models/trade/trade.py +56 -32
- investing_algorithm_framework/domain/models/trade/trade_status.py +8 -2
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +106 -41
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +161 -99
- investing_algorithm_framework/domain/order_executor.py +19 -0
- investing_algorithm_framework/domain/portfolio_provider.py +20 -1
- investing_algorithm_framework/domain/services/__init__.py +0 -13
- investing_algorithm_framework/domain/strategy.py +1 -29
- investing_algorithm_framework/domain/utils/__init__.py +5 -1
- investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
- investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
- investing_algorithm_framework/domain/utils/polars.py +17 -14
- investing_algorithm_framework/download_data.py +40 -10
- investing_algorithm_framework/infrastructure/__init__.py +13 -25
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +7 -4
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +811 -546
- investing_algorithm_framework/infrastructure/data_providers/csv.py +433 -122
- 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 +81 -0
- investing_algorithm_framework/infrastructure/models/__init__.py +0 -13
- investing_algorithm_framework/infrastructure/models/order/order.py +9 -3
- investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +27 -8
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +21 -7
- investing_algorithm_framework/infrastructure/order_executors/__init__.py +2 -0
- investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +16 -2
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +2 -2
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +6 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +6 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +0 -4
- investing_algorithm_framework/services/__init__.py +105 -8
- investing_algorithm_framework/services/backtesting/backtest_service.py +536 -476
- investing_algorithm_framework/services/configuration_service.py +14 -4
- investing_algorithm_framework/services/data_providers/__init__.py +5 -0
- investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/__init__.py +48 -17
- investing_algorithm_framework/{app/reporting → services}/metrics/drawdown.py +10 -10
- investing_algorithm_framework/{app/reporting → services}/metrics/equity_curve.py +2 -2
- investing_algorithm_framework/{app/reporting → services}/metrics/exposure.py +60 -2
- investing_algorithm_framework/services/metrics/generate.py +358 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/profit_factor.py +36 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/recovery.py +2 -2
- investing_algorithm_framework/{app/reporting → services}/metrics/returns.py +146 -147
- investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
- investing_algorithm_framework/{app/reporting/metrics/sharp_ratio.py → services/metrics/sharpe_ratio.py} +6 -10
- investing_algorithm_framework/{app/reporting → services}/metrics/sortino_ratio.py +3 -7
- investing_algorithm_framework/services/metrics/trades.py +500 -0
- investing_algorithm_framework/services/metrics/volatility.py +97 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/win_rate.py +70 -3
- investing_algorithm_framework/services/order_service/order_backtest_service.py +21 -31
- investing_algorithm_framework/services/order_service/order_service.py +9 -71
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +0 -2
- investing_algorithm_framework/services/portfolios/portfolio_service.py +3 -13
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +62 -96
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +0 -3
- investing_algorithm_framework/services/repository_service.py +5 -2
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +113 -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 +7 -1
- investing_algorithm_framework/services/trade_service/trade_service.py +51 -29
- 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.19.15.dist-info/METADATA +537 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/RECORD +159 -148
- investing_algorithm_framework/app/reporting/evaluation.py +0 -243
- investing_algorithm_framework/app/reporting/metrics/risk_free_rate.py +0 -8
- investing_algorithm_framework/app/reporting/metrics/volatility.py +0 -69
- investing_algorithm_framework/cli/templates/requirements_azure_function.txt.template +0 -3
- investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -9
- investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -47
- investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
- investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -0
- investing_algorithm_framework/domain/models/backtesting/backtest_results.py +0 -440
- investing_algorithm_framework/domain/models/data_source.py +0 -21
- investing_algorithm_framework/domain/models/date_range.py +0 -64
- investing_algorithm_framework/domain/models/trade/trade_risk_type.py +0 -34
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -48
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
- investing_algorithm_framework/domain/services/market_data_sources.py +0 -543
- investing_algorithm_framework/domain/services/market_service.py +0 -153
- investing_algorithm_framework/domain/services/observable.py +0 -51
- investing_algorithm_framework/domain/services/observer.py +0 -19
- investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -16
- investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -746
- investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -270
- investing_algorithm_framework/infrastructure/models/market_data_sources/pandas.py +0 -312
- investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
- investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -471
- investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
- investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
- investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -322
- investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -10
- investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -269
- investing_algorithm_framework/services/market_data_source_service/data_provider_service.py +0 -350
- investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -377
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -296
- investing_algorithm_framework-6.9.1.dist-info/METADATA +0 -440
- /investing_algorithm_framework/{app/reporting → services}/metrics/alpha.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/beta.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/cagr.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/calmar_ratio.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/mean_daily_return.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/price_efficiency.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/standard_deviation.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/treynor_ratio.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/ulcer.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/value_at_risk.py +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from logging import getLogger
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
logger = getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class BacktestSummaryMetrics:
|
|
12
|
+
"""
|
|
13
|
+
Represents the summarized results of a backtest,
|
|
14
|
+
focusing on key headline performance and risk metrics.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
total_net_gain (float): Total net gain from the backtest.
|
|
18
|
+
total_net_gain_percentage (float): Total net gain percentage
|
|
19
|
+
from the backtest.
|
|
20
|
+
total_loss (float): Total gross loss from all trades.
|
|
21
|
+
total_loss_percentage (float): Total gross loss percentage.
|
|
22
|
+
total_growth (float): Total growth from the backtest.
|
|
23
|
+
total_growth_percentage (float): Total growth percentage
|
|
24
|
+
from the backtest.
|
|
25
|
+
average_net_gain (float): Average returns across multiple backtests.
|
|
26
|
+
average_net_gain_percentage (float): Average return percentage across
|
|
27
|
+
multiple backtests.
|
|
28
|
+
average_growth (float): Average growth across multiple backtests.
|
|
29
|
+
average_growth_percentage (float): Average growth percentage across
|
|
30
|
+
multiple backtests.
|
|
31
|
+
average_loss (float): Average loss across multiple backtests.
|
|
32
|
+
average_loss_percentage (float): Average loss percentage across
|
|
33
|
+
multiple backtests.
|
|
34
|
+
average_trade_return (float): Average return per trade.
|
|
35
|
+
average_trade_return_percentage (float): Average return percentage
|
|
36
|
+
per trade.
|
|
37
|
+
average_trade_loss (float): Total gross loss from all trades.
|
|
38
|
+
average_trade_loss_percentage (float): Average trade loss percentage.
|
|
39
|
+
average_trade_gain (float): Average gain from winning trades.
|
|
40
|
+
average_trade_gain_percentage (float): Average gain percentage
|
|
41
|
+
cagr (float): Compound annual growth rate of the backtest.
|
|
42
|
+
sharpe_ratio (float): Sharpe ratio, risk-adjusted return.
|
|
43
|
+
sortino_ratio (float): Sortino ratio, downside-risk adjusted return.
|
|
44
|
+
calmar_ratio (float): CAGR relative to max drawdown.
|
|
45
|
+
profit_factor (float): Total profit / total loss.
|
|
46
|
+
annual_volatility (float): Annualized volatility of returns.
|
|
47
|
+
max_drawdown (float): Maximum drawdown observed.
|
|
48
|
+
max_drawdown_duration (int): Duration of the maximum drawdown.
|
|
49
|
+
trades_per_year (float): Average trades executed per year.
|
|
50
|
+
win_rate (float): Percentage of winning trades.
|
|
51
|
+
current_win_rate (float): Win rate over recent trades.
|
|
52
|
+
win_loss_ratio (float): Ratio of average win to average loss.
|
|
53
|
+
current_win_loss_ratio (float): Win/loss ratio over recent trades.
|
|
54
|
+
number_of_trades (int): Total number of trades executed.
|
|
55
|
+
cumulative_exposure (float): Total exposure over the backtest period.
|
|
56
|
+
exposure_ratio (float): Ratio of exposure to available capital.
|
|
57
|
+
"""
|
|
58
|
+
total_net_gain: float = None
|
|
59
|
+
total_net_gain_percentage: float = None
|
|
60
|
+
total_growth: float = None
|
|
61
|
+
total_growth_percentage: float = None
|
|
62
|
+
total_loss: float = None
|
|
63
|
+
total_loss_percentage: float = None
|
|
64
|
+
average_net_gain: float = None
|
|
65
|
+
average_net_gain_percentage: float = None
|
|
66
|
+
average_growth: float = None
|
|
67
|
+
average_growth_percentage: float = None
|
|
68
|
+
average_loss: float = None
|
|
69
|
+
average_loss_percentage: float = None
|
|
70
|
+
average_trade_return: float = None
|
|
71
|
+
average_trade_return_percentage: float = None
|
|
72
|
+
average_trade_loss: float = None
|
|
73
|
+
average_trade_loss_percentage: float = None
|
|
74
|
+
average_trade_gain: float = None
|
|
75
|
+
average_trade_gain_percentage: float = None
|
|
76
|
+
cagr: float = None
|
|
77
|
+
sharpe_ratio: float = None
|
|
78
|
+
sortino_ratio: float = None
|
|
79
|
+
calmar_ratio: float = None
|
|
80
|
+
profit_factor: float = None
|
|
81
|
+
annual_volatility: float = None
|
|
82
|
+
max_drawdown: float = None
|
|
83
|
+
max_drawdown_duration: int = None
|
|
84
|
+
trades_per_year: float = None
|
|
85
|
+
win_rate: float = None
|
|
86
|
+
current_win_rate: float = None
|
|
87
|
+
win_loss_ratio: float = None
|
|
88
|
+
current_win_loss_ratio: float = None
|
|
89
|
+
number_of_trades: int = None
|
|
90
|
+
number_of_trades_closed: int = None
|
|
91
|
+
cumulative_exposure: float = None
|
|
92
|
+
exposure_ratio: float = None
|
|
93
|
+
|
|
94
|
+
def to_dict(self) -> dict:
|
|
95
|
+
"""
|
|
96
|
+
Convert the BacktestSummaryMetrics instance to a dictionary.
|
|
97
|
+
"""
|
|
98
|
+
return {
|
|
99
|
+
"total_net_gain": self.total_net_gain,
|
|
100
|
+
"total_net_gain_percentage": self.total_net_gain_percentage,
|
|
101
|
+
"total_growth": self.total_growth,
|
|
102
|
+
"total_growth_percentage": self.total_growth_percentage,
|
|
103
|
+
"total_loss": self.total_loss,
|
|
104
|
+
"total_loss_percentage": self.total_loss_percentage,
|
|
105
|
+
"average_loss": self.average_loss,
|
|
106
|
+
"average_loss_percentage": self.average_loss_percentage,
|
|
107
|
+
"average_net_gain": self.average_net_gain,
|
|
108
|
+
"average_net_gain_percentage": self.average_net_gain_percentage,
|
|
109
|
+
"average_growth": self.average_growth,
|
|
110
|
+
"average_growth_percentage": self.average_growth_percentage,
|
|
111
|
+
"average_trade_return": self.average_trade_return,
|
|
112
|
+
"average_trade_return_percentage":
|
|
113
|
+
self.average_trade_return_percentage,
|
|
114
|
+
"average_trade_loss": self.average_trade_loss,
|
|
115
|
+
"average_trade_loss_percentage":
|
|
116
|
+
self.average_trade_loss_percentage,
|
|
117
|
+
"average_trade_gain": self.average_trade_gain,
|
|
118
|
+
"average_trade_gain_percentage":
|
|
119
|
+
self.average_trade_gain_percentage,
|
|
120
|
+
"cagr": self.cagr,
|
|
121
|
+
"sharpe_ratio": self.sharpe_ratio,
|
|
122
|
+
"sortino_ratio": self.sortino_ratio,
|
|
123
|
+
"calmar_ratio": self.calmar_ratio,
|
|
124
|
+
"profit_factor": self.profit_factor,
|
|
125
|
+
"annual_volatility": self.annual_volatility,
|
|
126
|
+
"max_drawdown": self.max_drawdown,
|
|
127
|
+
"max_drawdown_duration": self.max_drawdown_duration,
|
|
128
|
+
"trades_per_year": self.trades_per_year,
|
|
129
|
+
"win_rate": self.win_rate,
|
|
130
|
+
"current_win_rate": self.current_win_rate,
|
|
131
|
+
"win_loss_ratio": self.win_loss_ratio,
|
|
132
|
+
"current_win_loss_ratio": self.current_win_loss_ratio,
|
|
133
|
+
"number_of_trades": self.number_of_trades,
|
|
134
|
+
"number_of_trades_closed": self.number_of_trades_closed,
|
|
135
|
+
"cumulative_exposure": self.cumulative_exposure,
|
|
136
|
+
"exposure_ratio": self.exposure_ratio,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
def save(self, file_path: str | Path) -> None:
|
|
140
|
+
"""
|
|
141
|
+
Save the summary metrics to a JSON file.
|
|
142
|
+
"""
|
|
143
|
+
with open(file_path, 'w') as file:
|
|
144
|
+
json.dump(self.to_dict(), file, indent=4, default=str)
|
|
145
|
+
|
|
146
|
+
@staticmethod
|
|
147
|
+
def open(file_path: str | Path) -> 'BacktestSummaryMetrics':
|
|
148
|
+
"""
|
|
149
|
+
Load summary metrics from a JSON file.
|
|
150
|
+
"""
|
|
151
|
+
if not os.path.exists(file_path):
|
|
152
|
+
raise FileNotFoundError(f"Metrics file not found at {file_path}")
|
|
153
|
+
|
|
154
|
+
with open(file_path, 'r') as file:
|
|
155
|
+
data = json.load(file)
|
|
156
|
+
|
|
157
|
+
return BacktestSummaryMetrics(**data)
|
|
158
|
+
|
|
159
|
+
def __repr__(self):
|
|
160
|
+
return json.dumps(
|
|
161
|
+
self.to_dict(), indent=4, sort_keys=True, default=str
|
|
162
|
+
)
|
|
@@ -0,0 +1,280 @@
|
|
|
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 combine_backtests(backtests):
|
|
33
|
+
"""
|
|
34
|
+
Combine multiple backtests into a single backtest by aggregating
|
|
35
|
+
their results.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
backtests (List[Backtest]): List of Backtest instances to combine.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Backtest: A new Backtest instance representing the combined results.
|
|
42
|
+
"""
|
|
43
|
+
backtest_metrics = []
|
|
44
|
+
backtest_runs = []
|
|
45
|
+
|
|
46
|
+
for backtest in backtests:
|
|
47
|
+
backtest_runs += backtest.get_all_backtest_runs()
|
|
48
|
+
backtest_metrics += backtest.get_all_backtest_metrics()
|
|
49
|
+
|
|
50
|
+
summary = generate_backtest_summary_metrics(backtest_metrics)
|
|
51
|
+
|
|
52
|
+
metadata = None
|
|
53
|
+
risk_free_rate = None
|
|
54
|
+
|
|
55
|
+
# Check if there are duplicate backtest runs
|
|
56
|
+
unique_date_ranges = set()
|
|
57
|
+
for backtest in backtests:
|
|
58
|
+
for run in backtest.get_all_backtest_runs():
|
|
59
|
+
date_range = (run.backtest_start_date, run.backtest_end_date)
|
|
60
|
+
if date_range in unique_date_ranges:
|
|
61
|
+
logger.warning(
|
|
62
|
+
"Duplicate backtest run detected for date range: "
|
|
63
|
+
f"{date_range} when combining backtests."
|
|
64
|
+
)
|
|
65
|
+
unique_date_ranges.add(date_range)
|
|
66
|
+
|
|
67
|
+
# Merge all metadata dictionaries
|
|
68
|
+
metadata = {}
|
|
69
|
+
for backtest in backtests:
|
|
70
|
+
if backtest.metadata:
|
|
71
|
+
metadata.update(backtest.metadata)
|
|
72
|
+
|
|
73
|
+
# Get the first risk-free rate
|
|
74
|
+
for backtest in backtests:
|
|
75
|
+
if backtest.risk_free_rate is not None:
|
|
76
|
+
risk_free_rate = backtest.risk_free_rate
|
|
77
|
+
break
|
|
78
|
+
from .backtest import Backtest
|
|
79
|
+
|
|
80
|
+
backtest = Backtest(
|
|
81
|
+
backtest_summary=summary,
|
|
82
|
+
metadata=metadata,
|
|
83
|
+
risk_free_rate=risk_free_rate,
|
|
84
|
+
backtest_runs=backtest_runs
|
|
85
|
+
)
|
|
86
|
+
return backtest
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def generate_backtest_summary_metrics(
|
|
90
|
+
backtest_metrics: List[BacktestMetrics]
|
|
91
|
+
) -> BacktestSummaryMetrics:
|
|
92
|
+
"""
|
|
93
|
+
Combine multiple BacktestMetrics into a single BacktestMetrics
|
|
94
|
+
by aggregating their results.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
backtest_metrics (List[BacktestMetrics]): List of BacktestMetrics
|
|
98
|
+
instances to combine.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
BacktestMetrics: A new BacktestMetrics instance representing the
|
|
102
|
+
combined results.
|
|
103
|
+
"""
|
|
104
|
+
total_net_gain = sum(
|
|
105
|
+
b.total_net_gain for b in backtest_metrics
|
|
106
|
+
if b.total_net_gain is not None
|
|
107
|
+
)
|
|
108
|
+
total_net_gain_percentage = sum(
|
|
109
|
+
b.total_net_gain_percentage for b in backtest_metrics
|
|
110
|
+
if b.total_net_gain_percentage is not None
|
|
111
|
+
)
|
|
112
|
+
average_total_net_gain = safe_weighted_mean(
|
|
113
|
+
[b.total_net_gain for b in backtest_metrics],
|
|
114
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
115
|
+
)
|
|
116
|
+
average_total_net_gain_percentage = safe_weighted_mean(
|
|
117
|
+
[b.total_net_gain_percentage for b in backtest_metrics],
|
|
118
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
119
|
+
)
|
|
120
|
+
total_loss = sum(
|
|
121
|
+
b.gross_loss for b in backtest_metrics
|
|
122
|
+
if b.gross_loss is not None
|
|
123
|
+
)
|
|
124
|
+
total_loss_percentage = sum(
|
|
125
|
+
b.total_loss_percentage for b in backtest_metrics
|
|
126
|
+
if b.total_loss_percentage is not None
|
|
127
|
+
)
|
|
128
|
+
average_total_loss = safe_weighted_mean(
|
|
129
|
+
[b.gross_loss for b in backtest_metrics],
|
|
130
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
131
|
+
)
|
|
132
|
+
average_total_loss_percentage = safe_weighted_mean(
|
|
133
|
+
[b.total_loss_percentage for b in backtest_metrics],
|
|
134
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
135
|
+
)
|
|
136
|
+
total_growth = sum(
|
|
137
|
+
b.total_growth for b in backtest_metrics
|
|
138
|
+
if b.total_growth is not None
|
|
139
|
+
)
|
|
140
|
+
total_growth_percentage = sum(
|
|
141
|
+
b.total_growth_percentage for b in backtest_metrics
|
|
142
|
+
if b.total_growth_percentage is not None
|
|
143
|
+
)
|
|
144
|
+
average_growth = safe_weighted_mean(
|
|
145
|
+
[b.total_growth for b in backtest_metrics],
|
|
146
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
147
|
+
)
|
|
148
|
+
average_growth_percentage = safe_weighted_mean(
|
|
149
|
+
[b.total_growth_percentage for b in backtest_metrics],
|
|
150
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
151
|
+
)
|
|
152
|
+
cagr = safe_weighted_mean(
|
|
153
|
+
[b.cagr for b in backtest_metrics],
|
|
154
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
155
|
+
)
|
|
156
|
+
sharp_ratio = safe_weighted_mean(
|
|
157
|
+
[b.sharpe_ratio for b in backtest_metrics],
|
|
158
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
159
|
+
)
|
|
160
|
+
sortino_ratio = safe_weighted_mean(
|
|
161
|
+
[b.sortino_ratio for b in backtest_metrics],
|
|
162
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
163
|
+
)
|
|
164
|
+
calmar_ratio = safe_weighted_mean(
|
|
165
|
+
[b.calmar_ratio for b in backtest_metrics],
|
|
166
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
167
|
+
)
|
|
168
|
+
profit_factor = safe_weighted_mean(
|
|
169
|
+
[b.profit_factor for b in backtest_metrics],
|
|
170
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
171
|
+
)
|
|
172
|
+
annual_volatility = safe_weighted_mean(
|
|
173
|
+
[b.annual_volatility for b in backtest_metrics],
|
|
174
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
175
|
+
)
|
|
176
|
+
max_drawdown = max(
|
|
177
|
+
(b.max_drawdown for b in backtest_metrics
|
|
178
|
+
if b.max_drawdown is not None), default=None
|
|
179
|
+
)
|
|
180
|
+
max_drawdown_duration = max(
|
|
181
|
+
(b.max_drawdown_duration for b in backtest_metrics
|
|
182
|
+
if b.max_drawdown_duration is not None), default=None
|
|
183
|
+
)
|
|
184
|
+
trades_per_year = safe_weighted_mean(
|
|
185
|
+
[b.trades_per_year for b in backtest_metrics],
|
|
186
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
187
|
+
)
|
|
188
|
+
win_rate = safe_weighted_mean(
|
|
189
|
+
[b.win_rate for b in backtest_metrics],
|
|
190
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
191
|
+
)
|
|
192
|
+
current_win_rate = safe_weighted_mean(
|
|
193
|
+
[b.current_win_rate for b in backtest_metrics],
|
|
194
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
195
|
+
)
|
|
196
|
+
win_loss_ratio = safe_weighted_mean(
|
|
197
|
+
[b.win_loss_ratio for b in backtest_metrics],
|
|
198
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
199
|
+
)
|
|
200
|
+
current_win_loss_ratio = safe_weighted_mean(
|
|
201
|
+
[b.current_win_loss_ratio for b in backtest_metrics],
|
|
202
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
203
|
+
)
|
|
204
|
+
number_of_trades = sum(
|
|
205
|
+
b.number_of_trades for b in backtest_metrics
|
|
206
|
+
if b.number_of_trades is not None
|
|
207
|
+
)
|
|
208
|
+
number_of_trades_closed = sum(
|
|
209
|
+
b.number_of_trades_closed for b in backtest_metrics
|
|
210
|
+
if b.number_of_trades_closed is not None
|
|
211
|
+
)
|
|
212
|
+
cumulative_exposure = safe_weighted_mean(
|
|
213
|
+
[b.cumulative_exposure for b in backtest_metrics],
|
|
214
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
215
|
+
)
|
|
216
|
+
exposure_ratio = safe_weighted_mean(
|
|
217
|
+
[b.exposure_ratio for b in backtest_metrics],
|
|
218
|
+
[b.total_number_of_days for b in backtest_metrics]
|
|
219
|
+
)
|
|
220
|
+
average_trade_return = safe_weighted_mean(
|
|
221
|
+
[b.average_trade_return for b in backtest_metrics],
|
|
222
|
+
[b.number_of_trades for b in backtest_metrics]
|
|
223
|
+
)
|
|
224
|
+
average_trade_return_percentage = safe_weighted_mean(
|
|
225
|
+
[b.average_trade_return_percentage for b in backtest_metrics],
|
|
226
|
+
[b.number_of_trades for b in backtest_metrics]
|
|
227
|
+
)
|
|
228
|
+
average_trade_loss = safe_weighted_mean(
|
|
229
|
+
[b.average_trade_loss for b in backtest_metrics],
|
|
230
|
+
[b.number_of_trades for b in backtest_metrics]
|
|
231
|
+
)
|
|
232
|
+
average_trade_loss_percentage = safe_weighted_mean(
|
|
233
|
+
[b.average_trade_loss_percentage for b in backtest_metrics],
|
|
234
|
+
[b.number_of_trades for b in backtest_metrics]
|
|
235
|
+
)
|
|
236
|
+
average_trade_gain = safe_weighted_mean(
|
|
237
|
+
[b.average_trade_gain for b in backtest_metrics],
|
|
238
|
+
[b.number_of_trades for b in backtest_metrics]
|
|
239
|
+
)
|
|
240
|
+
average_trade_gain_percentage = safe_weighted_mean(
|
|
241
|
+
[b.average_trade_gain_percentage for b in backtest_metrics],
|
|
242
|
+
[b.number_of_trades for b in backtest_metrics]
|
|
243
|
+
)
|
|
244
|
+
return BacktestSummaryMetrics(
|
|
245
|
+
total_net_gain=total_net_gain,
|
|
246
|
+
total_net_gain_percentage=total_net_gain_percentage,
|
|
247
|
+
average_net_gain=average_total_net_gain,
|
|
248
|
+
average_net_gain_percentage=average_total_net_gain_percentage,
|
|
249
|
+
total_loss=total_loss,
|
|
250
|
+
total_loss_percentage=total_loss_percentage,
|
|
251
|
+
average_loss=average_total_loss,
|
|
252
|
+
average_loss_percentage=average_total_loss_percentage,
|
|
253
|
+
total_growth=total_growth,
|
|
254
|
+
total_growth_percentage=total_growth_percentage,
|
|
255
|
+
average_growth=average_growth,
|
|
256
|
+
average_growth_percentage=average_growth_percentage,
|
|
257
|
+
cagr=cagr,
|
|
258
|
+
sharpe_ratio=sharp_ratio,
|
|
259
|
+
sortino_ratio=sortino_ratio,
|
|
260
|
+
calmar_ratio=calmar_ratio,
|
|
261
|
+
profit_factor=profit_factor,
|
|
262
|
+
annual_volatility=annual_volatility,
|
|
263
|
+
max_drawdown=max_drawdown,
|
|
264
|
+
max_drawdown_duration=max_drawdown_duration,
|
|
265
|
+
trades_per_year=trades_per_year,
|
|
266
|
+
win_rate=win_rate,
|
|
267
|
+
current_win_rate=current_win_rate,
|
|
268
|
+
win_loss_ratio=win_loss_ratio,
|
|
269
|
+
current_win_loss_ratio=current_win_loss_ratio,
|
|
270
|
+
number_of_trades=number_of_trades,
|
|
271
|
+
number_of_trades_closed=number_of_trades_closed,
|
|
272
|
+
cumulative_exposure=cumulative_exposure,
|
|
273
|
+
exposure_ratio=exposure_ratio,
|
|
274
|
+
average_trade_return=average_trade_return,
|
|
275
|
+
average_trade_return_percentage=average_trade_return_percentage,
|
|
276
|
+
average_trade_loss=average_trade_loss,
|
|
277
|
+
average_trade_loss_percentage=average_trade_loss_percentage,
|
|
278
|
+
average_trade_gain=average_trade_gain,
|
|
279
|
+
average_trade_gain_percentage=average_trade_gain_percentage
|
|
280
|
+
)
|
|
@@ -82,3 +82,30 @@ DEFAULT_LOGGING_CONFIG = {
|
|
|
82
82
|
'handlers': ['console', 'file'],
|
|
83
83
|
},
|
|
84
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
|
|
|
@@ -12,62 +11,34 @@ DATABASE_DIRECTORY_NAME = 'DATABASE_DIRECTORY_NAME'
|
|
|
12
11
|
DATABASE_URL = 'DATABASE_URL'
|
|
13
12
|
DEFAULT_DATABASE_NAME = "database"
|
|
14
13
|
|
|
15
|
-
SYMBOLS = "SYMBOLS"
|
|
16
14
|
APPLICATION_DIRECTORY = "APP_DIR"
|
|
17
15
|
RESOURCE_DIRECTORY = "RESOURCE_DIRECTORY"
|
|
18
16
|
BACKTEST_DATA_DIRECTORY_NAME = "BACKTEST_DATA_DIRECTORY_NAME"
|
|
17
|
+
DATA_DIRECTORY = "DATA_DIRECTORY"
|
|
19
18
|
LOG_LEVEL = 'LOG_LEVEL'
|
|
20
19
|
BASE_DIR = 'BASE_DIR'
|
|
21
20
|
SQLALCHEMY_DATABASE_URI = 'SQLALCHEMY_DATABASE_URI'
|
|
22
|
-
SQLITE_INITIALIZED = "SQLITE_INITIALIZED"
|
|
23
21
|
SQLALCHEMY_INITIALIZED = "SQLALCHEMY_INITIALIZED"
|
|
24
|
-
RESERVED_BALANCES = "RESERVED_BALANCES"
|
|
25
22
|
APP_MODE = "APP_MODE"
|
|
26
|
-
BINANCE = "BINANCE"
|
|
27
23
|
|
|
28
|
-
IDENTIFIER_QUERY_PARAM = "identifier"
|
|
29
|
-
TARGET_SYMBOL_QUERY_PARAM = "target_symbol"
|
|
30
|
-
TRADING_SYMBOL_QUERY_PARAM = "trading_symbol"
|
|
31
|
-
ORDER_SIDE_QUERY_PARAM = "order_size"
|
|
32
|
-
STATUS_QUERY_PARAM = "status"
|
|
33
|
-
POSITION_SYMBOL_QUERY_PARAM = "position"
|
|
34
|
-
SYMBOL_QUERY_PARAM = "symbol"
|
|
35
|
-
TIME_FRAME_QUERY_PARAM = "time_frame"
|
|
36
|
-
|
|
37
|
-
RESERVED_IDENTIFIERS = [
|
|
38
|
-
BINANCE
|
|
39
|
-
]
|
|
40
|
-
|
|
41
|
-
BINANCE_API_KEY = "binance_api_key"
|
|
42
|
-
BINANCE_SECRET_KEY = "binance_secret_key"
|
|
43
24
|
|
|
44
25
|
CHECK_PENDING_ORDERS = "CHECK_PENDING_ORDERS"
|
|
45
26
|
RUN_STRATEGY = "RUN_STRATEGY"
|
|
46
27
|
|
|
47
28
|
# Configuration
|
|
48
|
-
TRADING_SYMBOL = "TRADING_SYMBOL"
|
|
49
|
-
CCXT_ENABLED = "CCXT_ENABLED"
|
|
50
|
-
API_KEY = "API_KEY"
|
|
51
|
-
SECRET_KEY = "SECRET_KEY"
|
|
52
|
-
MARKET = "MARKET"
|
|
53
|
-
TRACK_PORTFOLIO_FROM = "TRACK_PORTFOLIO_FROM"
|
|
54
|
-
SQLITE_ENABLED = "SQLITE_ENABLED"
|
|
55
|
-
PORTFOLIOS = "PORTFOLIOS"
|
|
56
|
-
STRATEGIES = "STRATEGIES"
|
|
57
|
-
APPLICATION_CONFIGURED = "APPLICATION_CONFIGURED"
|
|
58
|
-
ACTION = "ACTION"
|
|
59
29
|
DEFAULT_PER_PAGE_VALUE = 10
|
|
60
30
|
DEFAULT_PAGE_VALUE = 1
|
|
61
31
|
ITEMIZE = 'itemize'
|
|
62
32
|
ITEMIZED = 'itemized'
|
|
63
33
|
PAGE = 'page'
|
|
64
34
|
PER_PAGE = 'per_page'
|
|
65
|
-
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
|
|
66
35
|
DATETIME_FORMAT_BACKTESTING = "%Y-%m-%d-%H-%M"
|
|
67
36
|
CCXT_DATETIME_FORMAT_WITH_TIMEZONE = "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
68
37
|
CCXT_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
|
|
38
|
+
DEFAULT_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
|
|
69
39
|
BACKTESTING_FLAG = "BACKTESTING"
|
|
70
|
-
|
|
40
|
+
INDEX_DATETIME = "INDEX_DATETIME"
|
|
41
|
+
LAST_SNAPSHOT_DATETIME = "LAST_SNAPSHOT_DATETIME"
|
|
71
42
|
BACKTESTING_START_DATE = "BACKTESTING_START_DATE"
|
|
72
43
|
BACKTESTING_END_DATE = "BACKTESTING_END_DATE"
|
|
73
44
|
BACKTESTING_INITIAL_AMOUNT = "BACKTESTING_INITIAL_AMOUNT"
|
|
@@ -75,6 +46,7 @@ TICKER_DATA_TYPE = "TICKER"
|
|
|
75
46
|
OHLCV_DATA_TYPE = "OHLCV"
|
|
76
47
|
CURRENT_UTC_DATETIME = "CURRENT_UTC_DATETIME"
|
|
77
48
|
SNAPSHOT_INTERVAL = "SNAPSHOT_INTERVAL"
|
|
78
|
-
|
|
49
|
+
DATETIME_FORMAT = "DATETIME_FORMAT"
|
|
50
|
+
DATETIME_FORMAT_FILE_NAME = "DATETIME_FORMAT_FILE_NAME"
|
|
79
51
|
# Deployment
|
|
80
52
|
AWS_S3_STATE_BUCKET_NAME = "AWS_S3_STATE_BUCKET_NAME"
|