investing-algorithm-framework 7.17.0__py3-none-any.whl → 7.18.0__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 +5 -3
- investing_algorithm_framework/app/__init__.py +4 -2
- investing_algorithm_framework/app/analysis/__init__.py +5 -1
- investing_algorithm_framework/app/analysis/backtest_utils.py +80 -0
- investing_algorithm_framework/app/app.py +1 -2
- investing_algorithm_framework/domain/backtesting/backtest_run.py +31 -7
- {investing_algorithm_framework-7.17.0.dist-info → investing_algorithm_framework-7.18.0.dist-info}/METADATA +1 -1
- {investing_algorithm_framework-7.17.0.dist-info → investing_algorithm_framework-7.18.0.dist-info}/RECORD +11 -10
- {investing_algorithm_framework-7.17.0.dist-info → investing_algorithm_framework-7.18.0.dist-info}/LICENSE +0 -0
- {investing_algorithm_framework-7.17.0.dist-info → investing_algorithm_framework-7.18.0.dist-info}/WHEEL +0 -0
- {investing_algorithm_framework-7.17.0.dist-info → investing_algorithm_framework-7.18.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from .app import App, Algorithm, \
|
|
2
2
|
TradingStrategy, StatelessAction, Task, AppHook, Context, \
|
|
3
|
-
add_html_report, BacktestReport, \
|
|
3
|
+
add_html_report, BacktestReport, save_backtests_to_directory, \
|
|
4
4
|
pretty_print_trades, pretty_print_positions, \
|
|
5
5
|
pretty_print_orders, pretty_print_backtest, select_backtest_date_ranges, \
|
|
6
|
-
get_equity_curve_with_drawdown_chart, \
|
|
6
|
+
get_equity_curve_with_drawdown_chart, load_backtests_from_directory, \
|
|
7
7
|
get_rolling_sharpe_ratio_chart, rank_results, \
|
|
8
8
|
get_monthly_returns_heatmap_chart, create_weights, \
|
|
9
9
|
get_yearly_returns_bar_chart, get_entry_and_exit_signals, \
|
|
@@ -189,5 +189,7 @@ __all__ = [
|
|
|
189
189
|
"get_negative_trades",
|
|
190
190
|
"get_positive_trades",
|
|
191
191
|
"get_number_of_trades",
|
|
192
|
-
"BacktestRun"
|
|
192
|
+
"BacktestRun",
|
|
193
|
+
"load_backtests_from_directory",
|
|
194
|
+
"save_backtests_to_directory"
|
|
193
195
|
]
|
|
@@ -14,7 +14,7 @@ from .reporting import add_html_report, \
|
|
|
14
14
|
get_yearly_returns_bar_chart, get_equity_curve_chart, \
|
|
15
15
|
get_ohlcv_data_completeness_chart, get_entry_and_exit_signals
|
|
16
16
|
from .analysis import select_backtest_date_ranges, rank_results, \
|
|
17
|
-
create_weights
|
|
17
|
+
create_weights, load_backtests_from_directory, save_backtests_to_directory
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
__all__ = [
|
|
@@ -41,5 +41,7 @@ __all__ = [
|
|
|
41
41
|
"rank_results",
|
|
42
42
|
"create_weights",
|
|
43
43
|
"get_entry_and_exit_signals",
|
|
44
|
-
"get_equity_curve_chart"
|
|
44
|
+
"get_equity_curve_chart",
|
|
45
|
+
"load_backtests_from_directory",
|
|
46
|
+
"save_backtests_to_directory"
|
|
45
47
|
]
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
from .backtest_data_ranges import select_backtest_date_ranges
|
|
2
2
|
from .ranking import rank_results, create_weights, combine_backtest_metrics
|
|
3
3
|
from .permutation import create_ohlcv_permutation
|
|
4
|
+
from .backtest_utils import load_backtests_from_directory, \
|
|
5
|
+
save_backtests_to_directory
|
|
4
6
|
|
|
5
7
|
__all__ = [
|
|
6
8
|
"select_backtest_date_ranges",
|
|
7
9
|
"rank_results",
|
|
8
10
|
"create_weights",
|
|
9
11
|
"create_ohlcv_permutation",
|
|
10
|
-
"combine_backtest_metrics"
|
|
12
|
+
"combine_backtest_metrics",
|
|
13
|
+
"load_backtests_from_directory",
|
|
14
|
+
"save_backtests_to_directory"
|
|
11
15
|
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import List, Union
|
|
4
|
+
from logging import getLogger
|
|
5
|
+
from random import Random
|
|
6
|
+
|
|
7
|
+
from investing_algorithm_framework.domain import Backtest
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
logger = getLogger("investing_algorithm_framework")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def save_backtests_to_directory(
|
|
14
|
+
backtests: List[Backtest],
|
|
15
|
+
directory_path: Union[str, Path]
|
|
16
|
+
) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Saves a list of Backtest objects to the specified directory.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
backtests (List[Backtest]): List of Backtest objects to save.
|
|
22
|
+
directory_path (str): Path to the directory where backtests
|
|
23
|
+
will be saved.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
None
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
if not os.path.exists(directory_path):
|
|
30
|
+
os.makedirs(directory_path)
|
|
31
|
+
|
|
32
|
+
for backtest in backtests:
|
|
33
|
+
# Check if there is an ID in the backtest metadata
|
|
34
|
+
backtest_id = backtest.metadata.get('id')
|
|
35
|
+
|
|
36
|
+
if backtest_id is None:
|
|
37
|
+
logger.warning(
|
|
38
|
+
"Backtest is missing 'id' in metadata. "
|
|
39
|
+
"Generating a random ID as name for backtest file."
|
|
40
|
+
)
|
|
41
|
+
backtest_id = str(Random().randint(100000, 999999))
|
|
42
|
+
|
|
43
|
+
backtest.save(os.path.join(directory_path, backtest_id))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def load_backtests_from_directory(
|
|
47
|
+
directory_path: Union[str, Path]
|
|
48
|
+
) -> List[Backtest]:
|
|
49
|
+
"""
|
|
50
|
+
Loads Backtest objects from the specified directory.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
directory_path (str): Path to the directory from which backtests
|
|
54
|
+
will be loaded.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
List[Backtest]: List of loaded Backtest objects.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
backtests = []
|
|
61
|
+
|
|
62
|
+
if not os.path.exists(directory_path):
|
|
63
|
+
logger.warning(
|
|
64
|
+
f"Directory {directory_path} does not exist. "
|
|
65
|
+
"No backtests loaded."
|
|
66
|
+
)
|
|
67
|
+
return backtests
|
|
68
|
+
|
|
69
|
+
for file_name in os.listdir(directory_path):
|
|
70
|
+
file_path = os.path.join(directory_path, file_name)
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
backtest = Backtest.open(file_path)
|
|
74
|
+
backtests.append(backtest)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.error(
|
|
77
|
+
f"Failed to load backtest from {file_path}: {e}"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return backtests
|
|
@@ -1070,13 +1070,12 @@ class App:
|
|
|
1070
1070
|
except Exception as e:
|
|
1071
1071
|
logger.error(
|
|
1072
1072
|
f"Error occurred during vector backtest for strategy "
|
|
1073
|
-
f"{strategy.
|
|
1073
|
+
f"{strategy.strategy_id}: {str(e)}"
|
|
1074
1074
|
)
|
|
1075
1075
|
if continue_on_error:
|
|
1076
1076
|
backtest = Backtest(
|
|
1077
1077
|
backtest_runs=[],
|
|
1078
1078
|
risk_free_rate=risk_free_rate,
|
|
1079
|
-
backtest_summary={}
|
|
1080
1079
|
)
|
|
1081
1080
|
else:
|
|
1082
1081
|
raise e
|
|
@@ -251,17 +251,41 @@ class BacktestRun:
|
|
|
251
251
|
# Remove backtest_metrics to avoid redundancy
|
|
252
252
|
data.pop("backtest_metrics", None)
|
|
253
253
|
|
|
254
|
-
|
|
254
|
+
# Ensure datetime objects are in UTC before formatting
|
|
255
|
+
backtest_start_date = self.backtest_start_date
|
|
256
|
+
|
|
257
|
+
if backtest_start_date.tzinfo is None:
|
|
258
|
+
# Naive datetime - treat as UTC
|
|
259
|
+
backtest_start_date = backtest_start_date.replace(
|
|
260
|
+
tzinfo=timezone.utc
|
|
261
|
+
)
|
|
262
|
+
else:
|
|
263
|
+
# Timezone-aware - convert to UTC
|
|
264
|
+
backtest_start_date = backtest_start_date.astimezone(
|
|
265
|
+
timezone.utc
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
backtest_end_date = self.backtest_end_date
|
|
269
|
+
if backtest_end_date.tzinfo is None:
|
|
270
|
+
backtest_end_date = backtest_end_date.replace(
|
|
271
|
+
tzinfo=timezone.utc
|
|
272
|
+
)
|
|
273
|
+
else:
|
|
274
|
+
backtest_end_date = backtest_end_date.astimezone(timezone.utc)
|
|
275
|
+
|
|
276
|
+
created_at = self.created_at
|
|
277
|
+
if created_at.tzinfo is None:
|
|
278
|
+
created_at = created_at.replace(tzinfo=timezone.utc)
|
|
279
|
+
else:
|
|
280
|
+
created_at = created_at.astimezone(timezone.utc)
|
|
281
|
+
|
|
282
|
+
data["backtest_start_date"] = backtest_start_date.strftime(
|
|
255
283
|
"%Y-%m-%d %H:%M:%S"
|
|
256
284
|
)
|
|
257
|
-
data["backtest_end_date"] =
|
|
285
|
+
data["backtest_end_date"] = backtest_end_date.strftime(
|
|
258
286
|
"%Y-%m-%d %H:%M:%S"
|
|
259
287
|
)
|
|
260
|
-
|
|
261
|
-
if self.created_at.tzinfo is None:
|
|
262
|
-
self.created_at = self.created_at.replace(tzinfo=timezone.utc)
|
|
263
|
-
|
|
264
|
-
data["created_at"] = self.created_at.strftime(
|
|
288
|
+
data["created_at"] = created_at.strftime(
|
|
265
289
|
"%Y-%m-%d %H:%M:%S"
|
|
266
290
|
)
|
|
267
291
|
json.dump(data, f, default=str)
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
investing_algorithm_framework/__init__.py,sha256=
|
|
2
|
-
investing_algorithm_framework/app/__init__.py,sha256=
|
|
1
|
+
investing_algorithm_framework/__init__.py,sha256=ACNFUkjEf2UxGoBoci9JHeT6hGFISFyy4U-rLnZW8_E,6989
|
|
2
|
+
investing_algorithm_framework/app/__init__.py,sha256=BjVkBQvVuI7ovTpR2Bn8YOHL2p5vsX-LRqpe-aS3ImM,1671
|
|
3
3
|
investing_algorithm_framework/app/algorithm/__init__.py,sha256=-a9o9bTfAhW9qSW-bKvlLQuMCf-YXxIztudo2TxMjCI,136
|
|
4
4
|
investing_algorithm_framework/app/algorithm/algorithm.py,sha256=v8AZZ7hr5ZKJbavk242xCUpGHv3mKZ4sqfGV7BwPgdU,6854
|
|
5
5
|
investing_algorithm_framework/app/algorithm/algorithm_factory.py,sha256=Z6El6ErAEEljVGEM0Hkd2VXSMM9qkSCfkp-Ht-WEy3w,3549
|
|
6
|
-
investing_algorithm_framework/app/analysis/__init__.py,sha256=
|
|
6
|
+
investing_algorithm_framework/app/analysis/__init__.py,sha256=hISKshN-FQchLBasWX5raMG-IXVJP43t5fXEdrL6M9U,508
|
|
7
7
|
investing_algorithm_framework/app/analysis/backtest_data_ranges.py,sha256=pt8vUjhyqCN5JspihlzMWW4n5BULWcy0E9SlkEM2Fo4,3960
|
|
8
|
+
investing_algorithm_framework/app/analysis/backtest_utils.py,sha256=W_X1T8f1UOllUUkaO4ZiQlQ7dSEGi9TqVo_ROyv7XNE,2121
|
|
8
9
|
investing_algorithm_framework/app/analysis/permutation.py,sha256=NHzyMQ9aCqLiLXyw1CpRZfITLzsRwHmMn8Uj8oV5_1E,3941
|
|
9
10
|
investing_algorithm_framework/app/analysis/ranking.py,sha256=-XEWmU3rxLvkC9GOW6Zci7E3Y7H2xKwU3id8ljf0n9k,10888
|
|
10
|
-
investing_algorithm_framework/app/app.py,sha256
|
|
11
|
+
investing_algorithm_framework/app/app.py,sha256=saAfkfWJg-in7qKCOIpN1HljMU2YCuvdqSTWi4a-Ub0,83223
|
|
11
12
|
investing_algorithm_framework/app/app_hook.py,sha256=cDiY4x2n06tljpx-fcbIM1oPnjTnEthibvqxUvfEppo,834
|
|
12
13
|
investing_algorithm_framework/app/context.py,sha256=kWOBZq7E45xoAPbMfn9HPhDQmEcyCqBYWi-NJK5nMXk,58874
|
|
13
14
|
investing_algorithm_framework/app/eventloop.py,sha256=w9zufVpiHrgsxuh4_AW2DXxOfI4LVcScNHTLfL9-Tws,21736
|
|
@@ -89,7 +90,7 @@ investing_algorithm_framework/domain/backtesting/backtest_date_range.py,sha256=e
|
|
|
89
90
|
investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py,sha256=D__3I_TSxDVnGtlddmWt4wHcqut8MGyYMf1IfQZXYJ0,7547
|
|
90
91
|
investing_algorithm_framework/domain/backtesting/backtest_metrics.py,sha256=gfiuNhT15UpY5l02onknf7D5wHJfeUKodlnG9FV5I1E,20120
|
|
91
92
|
investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py,sha256=8JXdu3EgFh2f2Yc41OYwIBwlYtjFiumyAJUrN5kL078,6703
|
|
92
|
-
investing_algorithm_framework/domain/backtesting/backtest_run.py,sha256=
|
|
93
|
+
investing_algorithm_framework/domain/backtesting/backtest_run.py,sha256=Pozrx4zUBDOHDn6jFY9sHUksMya3AGsgboIOUeBbKxk,15267
|
|
93
94
|
investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py,sha256=Dt3gFz-MNmxtOhYxVPN8lI_7rXtE9PK10lULDFuCHlU,7131
|
|
94
95
|
investing_algorithm_framework/domain/backtesting/combine_backtests.py,sha256=E6MHctsSaiQdDPLQtXSU9Exf_yYLUc2oAD3vKMeNM20,9963
|
|
95
96
|
investing_algorithm_framework/domain/config.py,sha256=_VkaJvrdqIKAT3_l-Y8XTEKNEaw5uVIwQ7vxomuCpUw,3003
|
|
@@ -252,8 +253,8 @@ investing_algorithm_framework/services/trade_order_evaluator/default_trade_order
|
|
|
252
253
|
investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py,sha256=pNnmgaKMR9RY6Kxq7xS0nURKoaQDe2ehrP5GfElkkcI,1328
|
|
253
254
|
investing_algorithm_framework/services/trade_service/__init__.py,sha256=AcwPyJjDRdiREnl_MWMkDSc-V-ZjXtvpHD6eQT9mc1o,68
|
|
254
255
|
investing_algorithm_framework/services/trade_service/trade_service.py,sha256=OtzIS5EebByGcqDvV2AFeBjXSarvrgubMXDaVKg6Rbw,41193
|
|
255
|
-
investing_algorithm_framework-7.
|
|
256
|
-
investing_algorithm_framework-7.
|
|
257
|
-
investing_algorithm_framework-7.
|
|
258
|
-
investing_algorithm_framework-7.
|
|
259
|
-
investing_algorithm_framework-7.
|
|
256
|
+
investing_algorithm_framework-7.18.0.dist-info/LICENSE,sha256=wbVEDvoZiMPHufRY3sLEffvAr7GH5hOIngHF8y4HFQg,11343
|
|
257
|
+
investing_algorithm_framework-7.18.0.dist-info/METADATA,sha256=_8KvJKynhbGR8VmlZzsG6tY6D2u_EzA8iApv5zCkTjE,19635
|
|
258
|
+
investing_algorithm_framework-7.18.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
259
|
+
investing_algorithm_framework-7.18.0.dist-info/entry_points.txt,sha256=jrPF0YksDs27vYzEvj3tXLe3OGWU24EJA05z5xHqmq8,91
|
|
260
|
+
investing_algorithm_framework-7.18.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|