ivolatility-backtesting 1.10.0__tar.gz → 1.12.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ivolatility-backtesting might be problematic. Click here for more details.
- {ivolatility_backtesting-1.10.0 → ivolatility_backtesting-1.12.0}/PKG-INFO +1 -1
- {ivolatility_backtesting-1.10.0 → ivolatility_backtesting-1.12.0}/ivolatility_backtesting/__init__.py +4 -2
- {ivolatility_backtesting-1.10.0 → ivolatility_backtesting-1.12.0}/ivolatility_backtesting/ivolatility_backtesting.py +68 -15
- {ivolatility_backtesting-1.10.0 → ivolatility_backtesting-1.12.0}/ivolatility_backtesting.egg-info/PKG-INFO +1 -1
- {ivolatility_backtesting-1.10.0 → ivolatility_backtesting-1.12.0}/pyproject.toml +1 -1
- {ivolatility_backtesting-1.10.0 → ivolatility_backtesting-1.12.0}/LICENSE +0 -0
- {ivolatility_backtesting-1.10.0 → ivolatility_backtesting-1.12.0}/README.md +0 -0
- {ivolatility_backtesting-1.10.0 → ivolatility_backtesting-1.12.0}/ivolatility_backtesting.egg-info/SOURCES.txt +0 -0
- {ivolatility_backtesting-1.10.0 → ivolatility_backtesting-1.12.0}/ivolatility_backtesting.egg-info/dependency_links.txt +0 -0
- {ivolatility_backtesting-1.10.0 → ivolatility_backtesting-1.12.0}/ivolatility_backtesting.egg-info/requires.txt +0 -0
- {ivolatility_backtesting-1.10.0 → ivolatility_backtesting-1.12.0}/ivolatility_backtesting.egg-info/top_level.txt +0 -0
- {ivolatility_backtesting-1.10.0 → ivolatility_backtesting-1.12.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ivolatility_backtesting
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.12.0
|
|
4
4
|
Summary: A universal backtesting framework for financial strategies using the IVolatility API.
|
|
5
5
|
Author-email: IVolatility <support@ivolatility.com>
|
|
6
6
|
Project-URL: Homepage, https://ivolatility.com
|
|
@@ -12,7 +12,8 @@ from .ivolatility_backtesting import (
|
|
|
12
12
|
preload_data_universal,
|
|
13
13
|
apply_optimization_preset, list_optimization_presets,
|
|
14
14
|
calculate_combinations_count, print_preset_info,
|
|
15
|
-
get_cache_config, UniversalCacheManager
|
|
15
|
+
get_cache_config, UniversalCacheManager,
|
|
16
|
+
_process_options_df
|
|
16
17
|
)
|
|
17
18
|
|
|
18
19
|
__all__ = [
|
|
@@ -29,5 +30,6 @@ __all__ = [
|
|
|
29
30
|
'preload_data_universal',
|
|
30
31
|
'apply_optimization_preset', 'list_optimization_presets',
|
|
31
32
|
'calculate_combinations_count', 'print_preset_info',
|
|
32
|
-
'get_cache_config', 'UniversalCacheManager'
|
|
33
|
+
'get_cache_config', 'UniversalCacheManager',
|
|
34
|
+
'_process_options_df'
|
|
33
35
|
]
|
|
@@ -19,6 +19,7 @@ import psutil
|
|
|
19
19
|
import warnings
|
|
20
20
|
from itertools import product
|
|
21
21
|
import sys
|
|
22
|
+
import gc
|
|
22
23
|
from typing import Dict, List, Optional, Tuple, Union, Any
|
|
23
24
|
warnings.filterwarnings('ignore', category=pd.errors.SettingWithCopyWarning)
|
|
24
25
|
warnings.filterwarnings('ignore', message='.*SettingWithCopyWarning.*')
|
|
@@ -1622,12 +1623,13 @@ class ChartGenerator:
|
|
|
1622
1623
|
"""Generate 6 professional charts"""
|
|
1623
1624
|
|
|
1624
1625
|
@staticmethod
|
|
1625
|
-
def create_all_charts(analyzer, filename='backtest_results.png', show_plots=True):
|
|
1626
|
+
def create_all_charts(analyzer, filename='backtest_results.png', show_plots=True, silent=False):
|
|
1626
1627
|
r = analyzer.results
|
|
1627
1628
|
|
|
1628
1629
|
if len(r.trades) == 0:
|
|
1629
|
-
|
|
1630
|
-
|
|
1630
|
+
if not silent:
|
|
1631
|
+
print("No trades to visualize")
|
|
1632
|
+
return None
|
|
1631
1633
|
|
|
1632
1634
|
trades_df = pd.DataFrame(r.trades)
|
|
1633
1635
|
fig, axes = plt.subplots(3, 2, figsize=(18, 14))
|
|
@@ -1710,7 +1712,10 @@ class ChartGenerator:
|
|
|
1710
1712
|
else:
|
|
1711
1713
|
plt.close() # Close without displaying
|
|
1712
1714
|
|
|
1713
|
-
|
|
1715
|
+
if not silent:
|
|
1716
|
+
print(f"Chart saved: {filename}")
|
|
1717
|
+
|
|
1718
|
+
return filename
|
|
1714
1719
|
|
|
1715
1720
|
|
|
1716
1721
|
def create_stoploss_charts(analyzer, filename='stoploss_analysis.png', show_plots=True):
|
|
@@ -1806,13 +1811,14 @@ class ResultsExporter:
|
|
|
1806
1811
|
"""Export results to CSV"""
|
|
1807
1812
|
|
|
1808
1813
|
@staticmethod
|
|
1809
|
-
def export_all(analyzer, prefix='backtest'):
|
|
1814
|
+
def export_all(analyzer, prefix='backtest', silent=False):
|
|
1810
1815
|
r = analyzer.results
|
|
1811
1816
|
m = analyzer.metrics
|
|
1812
1817
|
|
|
1813
1818
|
if len(r.trades) == 0:
|
|
1814
|
-
|
|
1815
|
-
|
|
1819
|
+
if not silent:
|
|
1820
|
+
print("No trades to export")
|
|
1821
|
+
return []
|
|
1816
1822
|
|
|
1817
1823
|
trades_df = pd.DataFrame(r.trades)
|
|
1818
1824
|
|
|
@@ -1882,8 +1888,12 @@ class ResultsExporter:
|
|
|
1882
1888
|
for col in numeric_columns:
|
|
1883
1889
|
trades_df[col] = trades_df[col].round(5)
|
|
1884
1890
|
|
|
1891
|
+
exported_files = []
|
|
1892
|
+
|
|
1885
1893
|
trades_df.to_csv(f'{prefix}_trades.csv', index=False)
|
|
1886
|
-
|
|
1894
|
+
exported_files.append((f'{prefix}_trades.csv', f"({len(ordered_columns)} columns)"))
|
|
1895
|
+
if not silent:
|
|
1896
|
+
print(f"Exported: {prefix}_trades.csv ({len(ordered_columns)} columns)")
|
|
1887
1897
|
|
|
1888
1898
|
equity_df = pd.DataFrame({
|
|
1889
1899
|
'date': pd.to_datetime(r.equity_dates).strftime('%Y-%m-%d'),
|
|
@@ -1891,7 +1901,9 @@ class ResultsExporter:
|
|
|
1891
1901
|
})
|
|
1892
1902
|
equity_df['equity'] = equity_df['equity'].round(5)
|
|
1893
1903
|
equity_df.to_csv(f'{prefix}_equity.csv', index=False)
|
|
1894
|
-
|
|
1904
|
+
exported_files.append((f'{prefix}_equity.csv', ""))
|
|
1905
|
+
if not silent:
|
|
1906
|
+
print(f"Exported: {prefix}_equity.csv")
|
|
1895
1907
|
|
|
1896
1908
|
with open(f'{prefix}_summary.txt', 'w') as f:
|
|
1897
1909
|
f.write("BACKTEST SUMMARY\n")
|
|
@@ -1905,7 +1917,9 @@ class ResultsExporter:
|
|
|
1905
1917
|
f.write(f"Max DD: {m['max_drawdown']:.2f}%\n")
|
|
1906
1918
|
f.write(f"Trades: {m['total_trades']}\n")
|
|
1907
1919
|
|
|
1908
|
-
|
|
1920
|
+
exported_files.append((f'{prefix}_summary.txt', ""))
|
|
1921
|
+
if not silent:
|
|
1922
|
+
print(f"Exported: {prefix}_summary.txt")
|
|
1909
1923
|
|
|
1910
1924
|
# Export metrics as JSON with rounded values
|
|
1911
1925
|
import json
|
|
@@ -1919,7 +1933,11 @@ class ResultsExporter:
|
|
|
1919
1933
|
with open(f'{prefix}_metrics.json', 'w') as f:
|
|
1920
1934
|
json.dump(metrics_rounded, f, indent=2)
|
|
1921
1935
|
|
|
1922
|
-
|
|
1936
|
+
exported_files.append((f'{prefix}_metrics.json', ""))
|
|
1937
|
+
if not silent:
|
|
1938
|
+
print(f"Exported: {prefix}_metrics.json")
|
|
1939
|
+
|
|
1940
|
+
return exported_files
|
|
1923
1941
|
|
|
1924
1942
|
|
|
1925
1943
|
# ============================================================
|
|
@@ -1961,13 +1979,22 @@ def run_backtest(strategy_function, config, print_report=True,
|
|
|
1961
1979
|
print("\n" + "="*80)
|
|
1962
1980
|
ResultsReporter.print_full_report(analyzer)
|
|
1963
1981
|
|
|
1982
|
+
# Store file info for later printing (in optimization mode)
|
|
1983
|
+
analyzer.chart_file = None
|
|
1984
|
+
analyzer.exported_files = []
|
|
1985
|
+
|
|
1964
1986
|
# Export charts during optimization if requested
|
|
1965
1987
|
if create_charts and len(results.trades) > 0:
|
|
1966
1988
|
if not is_optimization:
|
|
1967
1989
|
print(f"\n[*] Creating charts: {chart_filename}")
|
|
1968
1990
|
try:
|
|
1969
1991
|
# Don't show plots during optimization, just save them
|
|
1970
|
-
ChartGenerator.create_all_charts(
|
|
1992
|
+
chart_file = ChartGenerator.create_all_charts(
|
|
1993
|
+
analyzer, chart_filename,
|
|
1994
|
+
show_plots=not is_optimization,
|
|
1995
|
+
silent=is_optimization # ← Silent in optimization
|
|
1996
|
+
)
|
|
1997
|
+
analyzer.chart_file = chart_file
|
|
1971
1998
|
except Exception as e:
|
|
1972
1999
|
if not is_optimization:
|
|
1973
2000
|
print(f"[ERROR] Charts failed: {e}")
|
|
@@ -1977,7 +2004,11 @@ def run_backtest(strategy_function, config, print_report=True,
|
|
|
1977
2004
|
if not is_optimization:
|
|
1978
2005
|
print(f"\n[*] Exporting: {export_prefix}_*")
|
|
1979
2006
|
try:
|
|
1980
|
-
ResultsExporter.export_all(
|
|
2007
|
+
exported = ResultsExporter.export_all(
|
|
2008
|
+
analyzer, export_prefix,
|
|
2009
|
+
silent=is_optimization # ← Silent in optimization
|
|
2010
|
+
)
|
|
2011
|
+
analyzer.exported_files = exported
|
|
1981
2012
|
except Exception as e:
|
|
1982
2013
|
if not is_optimization:
|
|
1983
2014
|
print(f"[ERROR] Export failed: {e}")
|
|
@@ -3159,8 +3190,24 @@ def optimize_parameters(base_config, param_grid, strategy_function,
|
|
|
3159
3190
|
status_symbol = "✓" if is_valid else "✗"
|
|
3160
3191
|
status_color = "#00cc00" if is_valid else "#ff6666"
|
|
3161
3192
|
|
|
3162
|
-
|
|
3163
|
-
print("
|
|
3193
|
+
# Print combination header
|
|
3194
|
+
print(f"[{idx}/{total_combinations}] {param_str}")
|
|
3195
|
+
print("-" * 100)
|
|
3196
|
+
|
|
3197
|
+
# Print chart file if created
|
|
3198
|
+
if hasattr(analyzer, 'chart_file') and analyzer.chart_file:
|
|
3199
|
+
print(f"Chart saved: {analyzer.chart_file}")
|
|
3200
|
+
|
|
3201
|
+
# Print exported files
|
|
3202
|
+
if hasattr(analyzer, 'exported_files') and analyzer.exported_files:
|
|
3203
|
+
for file_path, extra_info in analyzer.exported_files:
|
|
3204
|
+
if extra_info:
|
|
3205
|
+
print(f"Exported: {file_path} {extra_info}")
|
|
3206
|
+
else:
|
|
3207
|
+
print(f"Exported: {file_path}")
|
|
3208
|
+
|
|
3209
|
+
# Print metrics with separator
|
|
3210
|
+
print("+" * 100)
|
|
3164
3211
|
if is_valid:
|
|
3165
3212
|
print(f" {status_symbol} Return: {analyzer.metrics['total_return']:>7.2f}% | "
|
|
3166
3213
|
f"Sharpe: {analyzer.metrics['sharpe']:>6.2f} | "
|
|
@@ -3170,6 +3217,7 @@ def optimize_parameters(base_config, param_grid, strategy_function,
|
|
|
3170
3217
|
f"PF: {analyzer.metrics['profit_factor']:>5.2f}")
|
|
3171
3218
|
else:
|
|
3172
3219
|
print(f" {status_symbol} INVALID: {invalid_reason}")
|
|
3220
|
+
print("+" * 100 + "\n")
|
|
3173
3221
|
|
|
3174
3222
|
# Update widget status with last result
|
|
3175
3223
|
if has_widgets:
|
|
@@ -3208,6 +3256,11 @@ def optimize_parameters(base_config, param_grid, strategy_function,
|
|
|
3208
3256
|
|
|
3209
3257
|
results.append(result)
|
|
3210
3258
|
|
|
3259
|
+
# ═══ MEMORY CLEANUP AFTER EACH TEST ═══
|
|
3260
|
+
# Delete large objects to free RAM for next iteration
|
|
3261
|
+
del analyzer, test_config
|
|
3262
|
+
gc.collect()
|
|
3263
|
+
|
|
3211
3264
|
# Show intermediate summary every 10 combinations (or at end)
|
|
3212
3265
|
if idx % 10 == 0 or idx == total_combinations:
|
|
3213
3266
|
valid_so_far = [r for r in results if r['is_valid']]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ivolatility_backtesting
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.12.0
|
|
4
4
|
Summary: A universal backtesting framework for financial strategies using the IVolatility API.
|
|
5
5
|
Author-email: IVolatility <support@ivolatility.com>
|
|
6
6
|
Project-URL: Homepage, https://ivolatility.com
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ivolatility_backtesting"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.12.0"
|
|
8
8
|
description = "A universal backtesting framework for financial strategies using the IVolatility API."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|