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.

Files changed (192) hide show
  1. investing_algorithm_framework/__init__.py +147 -44
  2. investing_algorithm_framework/app/__init__.py +23 -6
  3. investing_algorithm_framework/app/algorithm/algorithm.py +5 -41
  4. investing_algorithm_framework/app/algorithm/algorithm_factory.py +17 -10
  5. investing_algorithm_framework/app/analysis/__init__.py +15 -0
  6. investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
  7. investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
  8. investing_algorithm_framework/app/analysis/permutation.py +116 -0
  9. investing_algorithm_framework/app/analysis/ranking.py +297 -0
  10. investing_algorithm_framework/app/app.py +1322 -707
  11. investing_algorithm_framework/app/context.py +196 -88
  12. investing_algorithm_framework/app/eventloop.py +590 -0
  13. investing_algorithm_framework/app/reporting/__init__.py +16 -5
  14. investing_algorithm_framework/app/reporting/ascii.py +57 -202
  15. investing_algorithm_framework/app/reporting/backtest_report.py +284 -170
  16. investing_algorithm_framework/app/reporting/charts/__init__.py +10 -2
  17. investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
  18. investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
  19. investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +11 -26
  20. investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
  21. investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
  22. investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +1 -1
  23. investing_algorithm_framework/app/reporting/generate.py +100 -114
  24. investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +40 -32
  25. investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +34 -27
  26. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +23 -19
  27. investing_algorithm_framework/app/reporting/tables/trades_table.py +1 -1
  28. investing_algorithm_framework/app/reporting/tables/utils.py +1 -0
  29. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +10 -16
  30. investing_algorithm_framework/app/strategy.py +315 -175
  31. investing_algorithm_framework/app/task.py +5 -3
  32. investing_algorithm_framework/cli/cli.py +30 -12
  33. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +131 -34
  34. investing_algorithm_framework/cli/initialize_app.py +20 -1
  35. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +18 -6
  36. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  37. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  38. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -2
  39. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +1 -1
  40. investing_algorithm_framework/create_app.py +3 -5
  41. investing_algorithm_framework/dependency_container.py +25 -39
  42. investing_algorithm_framework/domain/__init__.py +45 -38
  43. investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
  44. investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
  45. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
  46. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
  47. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
  48. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  49. investing_algorithm_framework/domain/backtesting/backtest_run.py +605 -0
  50. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  51. investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
  52. investing_algorithm_framework/domain/config.py +27 -0
  53. investing_algorithm_framework/domain/constants.py +6 -34
  54. investing_algorithm_framework/domain/data_provider.py +200 -56
  55. investing_algorithm_framework/domain/exceptions.py +34 -1
  56. investing_algorithm_framework/domain/models/__init__.py +10 -19
  57. investing_algorithm_framework/domain/models/base_model.py +0 -6
  58. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  59. investing_algorithm_framework/domain/models/data/data_source.py +214 -0
  60. investing_algorithm_framework/domain/models/{market_data_type.py → data/data_type.py} +7 -7
  61. investing_algorithm_framework/domain/models/market/market_credential.py +6 -0
  62. investing_algorithm_framework/domain/models/order/order.py +34 -13
  63. investing_algorithm_framework/domain/models/order/order_status.py +1 -1
  64. investing_algorithm_framework/domain/models/order/order_type.py +1 -1
  65. investing_algorithm_framework/domain/models/portfolio/portfolio.py +14 -1
  66. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +5 -1
  67. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +51 -11
  68. investing_algorithm_framework/domain/models/position/__init__.py +2 -1
  69. investing_algorithm_framework/domain/models/position/position.py +9 -0
  70. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  71. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  72. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  73. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  74. investing_algorithm_framework/domain/models/snapshot_interval.py +0 -1
  75. investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
  76. investing_algorithm_framework/domain/models/time_frame.py +7 -0
  77. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  78. investing_algorithm_framework/domain/models/time_unit.py +63 -1
  79. investing_algorithm_framework/domain/models/trade/__init__.py +0 -2
  80. investing_algorithm_framework/domain/models/trade/trade.py +56 -32
  81. investing_algorithm_framework/domain/models/trade/trade_status.py +8 -2
  82. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +106 -41
  83. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +161 -99
  84. investing_algorithm_framework/domain/order_executor.py +19 -0
  85. investing_algorithm_framework/domain/portfolio_provider.py +20 -1
  86. investing_algorithm_framework/domain/services/__init__.py +0 -13
  87. investing_algorithm_framework/domain/strategy.py +1 -29
  88. investing_algorithm_framework/domain/utils/__init__.py +5 -1
  89. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  90. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  91. investing_algorithm_framework/domain/utils/polars.py +17 -14
  92. investing_algorithm_framework/download_data.py +40 -10
  93. investing_algorithm_framework/infrastructure/__init__.py +13 -25
  94. investing_algorithm_framework/infrastructure/data_providers/__init__.py +7 -4
  95. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +811 -546
  96. investing_algorithm_framework/infrastructure/data_providers/csv.py +433 -122
  97. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  98. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  99. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +81 -0
  100. investing_algorithm_framework/infrastructure/models/__init__.py +0 -13
  101. investing_algorithm_framework/infrastructure/models/order/order.py +9 -3
  102. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +27 -8
  103. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +21 -7
  104. investing_algorithm_framework/infrastructure/order_executors/__init__.py +2 -0
  105. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  106. investing_algorithm_framework/infrastructure/repositories/repository.py +16 -2
  107. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +2 -2
  108. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +6 -0
  109. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +6 -0
  110. investing_algorithm_framework/infrastructure/services/__init__.py +0 -4
  111. investing_algorithm_framework/services/__init__.py +105 -8
  112. investing_algorithm_framework/services/backtesting/backtest_service.py +536 -476
  113. investing_algorithm_framework/services/configuration_service.py +14 -4
  114. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  115. investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
  116. investing_algorithm_framework/{app/reporting → services}/metrics/__init__.py +48 -17
  117. investing_algorithm_framework/{app/reporting → services}/metrics/drawdown.py +10 -10
  118. investing_algorithm_framework/{app/reporting → services}/metrics/equity_curve.py +2 -2
  119. investing_algorithm_framework/{app/reporting → services}/metrics/exposure.py +60 -2
  120. investing_algorithm_framework/services/metrics/generate.py +358 -0
  121. investing_algorithm_framework/{app/reporting → services}/metrics/profit_factor.py +36 -0
  122. investing_algorithm_framework/{app/reporting → services}/metrics/recovery.py +2 -2
  123. investing_algorithm_framework/{app/reporting → services}/metrics/returns.py +146 -147
  124. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  125. investing_algorithm_framework/{app/reporting/metrics/sharp_ratio.py → services/metrics/sharpe_ratio.py} +6 -10
  126. investing_algorithm_framework/{app/reporting → services}/metrics/sortino_ratio.py +3 -7
  127. investing_algorithm_framework/services/metrics/trades.py +500 -0
  128. investing_algorithm_framework/services/metrics/volatility.py +97 -0
  129. investing_algorithm_framework/{app/reporting → services}/metrics/win_rate.py +70 -3
  130. investing_algorithm_framework/services/order_service/order_backtest_service.py +21 -31
  131. investing_algorithm_framework/services/order_service/order_service.py +9 -71
  132. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +0 -2
  133. investing_algorithm_framework/services/portfolios/portfolio_service.py +3 -13
  134. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +62 -96
  135. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +0 -3
  136. investing_algorithm_framework/services/repository_service.py +5 -2
  137. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  138. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +113 -0
  139. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  140. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  141. investing_algorithm_framework/services/trade_service/__init__.py +7 -1
  142. investing_algorithm_framework/services/trade_service/trade_service.py +51 -29
  143. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  144. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  145. investing_algorithm_framework-7.19.15.dist-info/METADATA +537 -0
  146. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/RECORD +159 -148
  147. investing_algorithm_framework/app/reporting/evaluation.py +0 -243
  148. investing_algorithm_framework/app/reporting/metrics/risk_free_rate.py +0 -8
  149. investing_algorithm_framework/app/reporting/metrics/volatility.py +0 -69
  150. investing_algorithm_framework/cli/templates/requirements_azure_function.txt.template +0 -3
  151. investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -9
  152. investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -47
  153. investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
  154. investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -0
  155. investing_algorithm_framework/domain/models/backtesting/backtest_results.py +0 -440
  156. investing_algorithm_framework/domain/models/data_source.py +0 -21
  157. investing_algorithm_framework/domain/models/date_range.py +0 -64
  158. investing_algorithm_framework/domain/models/trade/trade_risk_type.py +0 -34
  159. investing_algorithm_framework/domain/models/trading_data_types.py +0 -48
  160. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
  161. investing_algorithm_framework/domain/services/market_data_sources.py +0 -543
  162. investing_algorithm_framework/domain/services/market_service.py +0 -153
  163. investing_algorithm_framework/domain/services/observable.py +0 -51
  164. investing_algorithm_framework/domain/services/observer.py +0 -19
  165. investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -16
  166. investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -746
  167. investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -270
  168. investing_algorithm_framework/infrastructure/models/market_data_sources/pandas.py +0 -312
  169. investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
  170. investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -471
  171. investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
  172. investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
  173. investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -322
  174. investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -10
  175. investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -269
  176. investing_algorithm_framework/services/market_data_source_service/data_provider_service.py +0 -350
  177. investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -377
  178. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -296
  179. investing_algorithm_framework-6.9.1.dist-info/METADATA +0 -440
  180. /investing_algorithm_framework/{app/reporting → services}/metrics/alpha.py +0 -0
  181. /investing_algorithm_framework/{app/reporting → services}/metrics/beta.py +0 -0
  182. /investing_algorithm_framework/{app/reporting → services}/metrics/cagr.py +0 -0
  183. /investing_algorithm_framework/{app/reporting → services}/metrics/calmar_ratio.py +0 -0
  184. /investing_algorithm_framework/{app/reporting → services}/metrics/mean_daily_return.py +0 -0
  185. /investing_algorithm_framework/{app/reporting → services}/metrics/price_efficiency.py +0 -0
  186. /investing_algorithm_framework/{app/reporting → services}/metrics/standard_deviation.py +0 -0
  187. /investing_algorithm_framework/{app/reporting → services}/metrics/treynor_ratio.py +0 -0
  188. /investing_algorithm_framework/{app/reporting → services}/metrics/ulcer.py +0 -0
  189. /investing_algorithm_framework/{app/reporting → services}/metrics/value_at_risk.py +0 -0
  190. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
  191. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
  192. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/entry_points.txt +0 -0
@@ -5,19 +5,16 @@ from plotly import graph_objects as go
5
5
 
6
6
  def get_equity_curve_with_drawdown_chart(equity_curve_series, drawdown_series):
7
7
  equity_curve_df = pd.DataFrame(
8
- equity_curve_series, columns=["datetime", "value"]
8
+ equity_curve_series, columns=["value", "datetime"]
9
9
  )
10
10
  drawdown_df = pd.DataFrame(
11
- drawdown_series, columns=["datetime", "value"]
11
+ drawdown_series, columns=["value", "datetime"]
12
12
  )
13
13
 
14
14
  # Normalize equity to start at 1
15
- equity_curve_df["value"] = (equity_curve_df["value"] /
16
- equity_curve_df["value"].iloc[0])
17
-
18
- # Split into above and below 1
19
- above_1 = equity_curve_df[equity_curve_df["value"] >= 1]
20
- below_1 = equity_curve_df[equity_curve_df["value"] < 1]
15
+ equity_curve_df["value"] = (
16
+ equity_curve_df["value"] / equity_curve_df["value"].iloc[0]
17
+ )
21
18
 
22
19
  fig = make_subplots(
23
20
  rows=2,
@@ -28,27 +25,15 @@ def get_equity_curve_with_drawdown_chart(equity_curve_series, drawdown_series):
28
25
  subplot_titles=["", ""]
29
26
  )
30
27
 
31
- # Equity curve - gains (green)
32
- fig.add_trace(
33
- go.Scatter(
34
- x=above_1["datetime"],
35
- y=above_1["value"],
36
- mode="lines",
37
- line=dict(color="rgba(34, 139, 34, 0.9)", width=1.5),
38
- name="Cumulative Equity (Gains)"
39
- ),
40
- row=1,
41
- col=1
42
- )
43
-
44
- # Equity curve - losses (red)
28
+ # Draw equity curve
45
29
  fig.add_trace(
46
30
  go.Scatter(
47
- x=below_1["datetime"],
48
- y=below_1["value"],
31
+ x=equity_curve_df["datetime"],
32
+ y=equity_curve_df["value"],
49
33
  mode="lines",
50
- line=dict(color="rgba(220, 20, 60, 0.9)", width=1.5),
51
- name="Cumulative Equity (Loss)"
34
+ line=dict(color="rgba(0, 128, 0, 0.8)", width=1),
35
+ name="Equity Curve",
36
+ hovertemplate="<b>Equity</b><br>%{x}<br>Value: %{y:.2f}<extra></extra>"
52
37
  ),
53
38
  row=1,
54
39
  col=1
@@ -0,0 +1,11 @@
1
+ import plotly.graph_objects as go
2
+
3
+
4
+ def create_line_scatter(x, y, name, colour = 'blue'):
5
+ return go.Scatter(
6
+ x=x,
7
+ y=y,
8
+ mode='lines',
9
+ name=name,
10
+ line=dict(color=colour)
11
+ )
@@ -0,0 +1,51 @@
1
+ import plotly.graph_objects as go
2
+ import pandas as pd
3
+ import plotly.io as pio
4
+
5
+ def get_ohlcv_data_completeness_chart(
6
+ df,
7
+ timeframe='1min',
8
+ windowsize=100,
9
+ title="OHLCV Data completenes"
10
+ ):
11
+ df = df.copy()
12
+ df['Datetime'] = pd.to_datetime(df['Datetime'])
13
+ df = df.sort_values('Datetime').tail(windowsize)
14
+ start = df['Datetime'].iloc[0]
15
+ end = df['Datetime'].iloc[-1]
16
+ freq = pd.to_timedelta(timeframe)
17
+ expected = pd.date_range(start, end, freq=freq)
18
+ actual = df['Datetime']
19
+ missing = expected.difference(actual)
20
+
21
+ # Calculte the percentage completeness
22
+ completeness = len(actual) / len(expected) * 100
23
+ title += f" ({completeness:.2f}% complete)"
24
+ fig = go.Figure()
25
+ fig.add_trace(
26
+ go.Scatter(
27
+ x=actual,
28
+ y=[1]*len(actual),
29
+ mode='markers',
30
+ name='Present',
31
+ marker=dict(color='green', size=6)
32
+ )
33
+ )
34
+ fig.add_trace(
35
+ go.Scatter(
36
+ x=missing,
37
+ y=[1]*len(missing),
38
+ mode='markers',
39
+ name='Missing',
40
+ marker=dict(color='red', size=6, symbol='x')
41
+ )
42
+ )
43
+ fig.update_layout(
44
+ title=title,
45
+ xaxis_title='Datetime',
46
+ yaxis=dict(showticklabels=False),
47
+ height=300,
48
+ showlegend=True
49
+ )
50
+
51
+ return pio.to_html(fig, full_html=False, include_plotlyjs='cdn')
@@ -2,7 +2,7 @@ import pandas as pd
2
2
  import plotly.graph_objects as go
3
3
 
4
4
 
5
- def get_rolling_sharp_ratio_chart(rolling_sharpe_ratio_series):
5
+ def get_rolling_sharpe_ratio_chart(rolling_sharpe_ratio_series):
6
6
  """
7
7
  Generates a Plotly figure showing the rolling Sharpe ratio series.
8
8
 
@@ -1,126 +1,77 @@
1
1
  import os
2
+ import logging
3
+ import pandas as pd
2
4
  from jinja2 import Environment, FileSystemLoader
3
5
 
4
6
  from .tables import create_html_time_metrics_table, \
5
7
  create_html_trade_metrics_table, create_html_key_metrics_table, \
6
8
  create_html_trades_table
7
9
  from .charts import get_equity_curve_with_drawdown_chart, \
8
- get_rolling_sharp_ratio_chart, get_monthly_returns_heatmap_chart, \
9
- get_yearly_returns_bar_chart
10
- from .metrics import get_equity_curve, get_total_return, get_cagr, \
11
- get_sharpe_ratio, get_rolling_sharpe_ratio, get_sortino_ratio, \
12
- get_profit_factor, get_calmar_ratio, get_annual_volatility, \
13
- get_monthly_returns, get_yearly_returns, get_drawdown_series, \
14
- get_max_drawdown, get_max_drawdown_absolute, get_max_daily_drawdown, \
15
- get_max_drawdown_duration, get_trade_frequency, \
16
- get_exposure, get_win_rate, get_average_gain, get_average_loss, \
17
- get_best_trade, get_best_trade_date, get_worst_trade, \
18
- get_worst_trade_date, get_average_trade_duration, \
19
- get_percentage_winning_months, get_percentage_winning_years, \
20
- get_average_monthly_return, \
21
- get_average_monthly_return_losing_months, get_win_loss_ratio, \
22
- get_average_monthly_return_winning_months, get_best_month, \
23
- get_best_year, get_worst_month, get_worst_year, get_trades_per_year
24
-
25
-
26
- def add_metrics(report, risk_free_rate=None) -> "BacktestReport":
10
+ get_rolling_sharpe_ratio_chart, get_monthly_returns_heatmap_chart, \
11
+ get_yearly_returns_bar_chart, get_ohlcv_data_completeness_chart
12
+ from investing_algorithm_framework.domain import TimeFrame
13
+
14
+
15
+ logger = logging.getLogger("investing_algorithm_framework")
16
+
17
+
18
+ def get_symbol_from_file_name(file_name: str) -> str:
27
19
  """
28
- Add metrics to the report.results.
20
+ Extract the symbol from the file name.
29
21
 
30
22
  Args:
31
- report (BacktestReport): The backtest report.results to
32
- which the metrics will be added.
33
- risk_free_rate (float, optional): The risk-free rate to be used in
34
- the report.results calculations. Defaults to None. If none, the
35
- risk-free rate will be retrieved from the US Treasury (10-year
36
- yield) using the `get_risk_free_rate` function.
23
+ file_name (str): The file name from which to extract the symbol.
24
+
25
+ Returns:
26
+ str: The extracted symbol.
37
27
  """
38
- results = {
39
- "backtest_start_date": report.results.backtest_start_date,
40
- "backtest_end_date": report.results.backtest_end_date,
41
- "Equity Curve": get_equity_curve(report.results.portfolio_snapshots),
42
- "Total Return": get_total_return(report.results.portfolio_snapshots),
43
- "CAGR": get_cagr(report.results.portfolio_snapshots),
44
- "Sharpe Ratio": get_sharpe_ratio(report.results.portfolio_snapshots),
45
- "Rolling Sharpe Ratio": get_rolling_sharpe_ratio(
46
- report.results.portfolio_snapshots, risk_free_rate=risk_free_rate
47
- ),
48
- "Sortino Ratio": get_sortino_ratio(
49
- report.results.portfolio_snapshots, risk_free_rate=risk_free_rate
50
- ),
51
- "Profit Factor": get_profit_factor(report.results.get_trades()),
52
- "Calmar Ratio": get_calmar_ratio(report.results.portfolio_snapshots),
53
- "Annual Volatility": get_annual_volatility(
54
- report.results.portfolio_snapshots
55
- ),
56
- "Monthly Returns": get_monthly_returns(
57
- report.results.portfolio_snapshots
58
- ),
59
- "Yearly Returns": get_yearly_returns(
60
- report.results.portfolio_snapshots
61
- ),
62
- "Drawdown series": get_drawdown_series(
63
- report.results.portfolio_snapshots
64
- ),
65
- "Max Drawdown": get_max_drawdown(
66
- report.results.portfolio_snapshots
67
- ),
68
- "Max Drawdown Absolute": get_max_drawdown_absolute(
69
- report.results.portfolio_snapshots),
70
- "Max Daily Drawdown": get_max_daily_drawdown(
71
- report.results.portfolio_snapshots
72
- ),
73
- "Max Drawdown Duration": get_max_drawdown_duration(
74
- report.results.portfolio_snapshots
75
- ),
76
- "Trades per Year": get_trades_per_year(
77
- report.results.get_trades(),
78
- report.results.backtest_start_date,
79
- report.results.backtest_end_date
80
- ),
81
- "Trade per day": get_trade_frequency(
82
- report.results.get_trades(),
83
- report.results.backtest_start_date,
84
- report.results.backtest_end_date
85
- ),
86
- "Exposure": get_exposure(
87
- report.results.get_trades(),
88
- report.results.backtest_start_date,
89
- report.results.backtest_end_date
90
- ),
91
- "Trades winning percentage": get_win_rate(report.results.get_trades()),
92
- "Trades average gain": get_average_gain(report.results.get_trades()),
93
- "Trades average loss": get_average_loss(report.results.get_trades()),
94
- "Best Trade": get_best_trade(report.results.get_trades()),
95
- "Best Trade Date": get_best_trade_date(report.results.get_trades()),
96
- "Worst Trade": get_worst_trade(report.results.get_trades()),
97
- "Worst Trade Date": get_worst_trade_date(report.results.get_trades()),
98
- "Average Trade Duration": get_average_trade_duration(
99
- report.results.get_trades()),
100
- "Number of Trades": len(report.results.get_trades()),
101
- "Win Rate": get_win_rate(report.results.get_trades()),
102
- "Win/Loss Ratio": get_win_loss_ratio(report.results.get_trades()),
103
- "Percentage Winning Months": get_percentage_winning_months(
104
- report.results.portfolio_snapshots),
105
- "Percentage Winning Years": get_percentage_winning_years(
106
- report.results.portfolio_snapshots),
107
- "Average Monthly Return": get_average_monthly_return(
108
- report.results.portfolio_snapshots),
109
- "Average Monthly Return (Losing Months)":
110
- get_average_monthly_return_losing_months(
111
- report.results.portfolio_snapshots
112
- ),
113
- "Average Monthly Return (Winning Months)":
114
- get_average_monthly_return_winning_months(
115
- report.results.portfolio_snapshots
116
- ),
117
- "Best Month": get_best_month(report.results.portfolio_snapshots),
118
- "Best Year": get_best_year(report.results.portfolio_snapshots),
119
- "Worst Month": get_worst_month(report.results.portfolio_snapshots),
120
- "Worst Year": get_worst_year(report.results.portfolio_snapshots),
121
- }
122
- report.metrics = results
123
- return report
28
+ # Assuming the file name format is "symbol_timeframe.csv"
29
+ return file_name.split('_')[0].upper()
30
+
31
+ def get_market_from_file_name(file_name: str) -> str:
32
+ """
33
+ Extract the market from the file name.
34
+
35
+ Args:
36
+ file_name (str): The file name from which to extract the market.
37
+
38
+ Returns:
39
+ str: The extracted market.
40
+ """
41
+ # Assuming the file name format is "symbol_market_timeframe.csv"
42
+ parts = file_name.split('_')
43
+ if len(parts) < 2:
44
+ raise ValueError("File name does not contain a valid market.")
45
+ return parts[1].upper()
46
+
47
+
48
+ def get_time_frame_from_file_name(file_name: str) -> TimeFrame:
49
+ """
50
+ Extract the time frame from the file name.
51
+
52
+ Args:
53
+ file_name (str): The file name from which to extract the time frame.
54
+
55
+ Returns:
56
+ TimeFrame: The extracted time frame.
57
+ """
58
+ parts = file_name.split('_')
59
+
60
+ if len(parts) < 3:
61
+ raise ValueError(
62
+ "File name does not contain a valid time frame."
63
+ )
64
+ time_frame_str = parts[3]
65
+
66
+ try:
67
+ return TimeFrame.from_string(time_frame_str)
68
+ except ValueError:
69
+ raise ValueError(
70
+ f"Could not extract time frame from file name: {file_path}. "
71
+ f"Expected format 'OHLCV_<SYMBOL>_<MARKET>_<TIME_FRAME>_<START_DATE>_<END_DATE>.csv', "
72
+ f"got '{time_frame_str}'."
73
+ )
74
+
124
75
 
125
76
  def add_html_report(report) -> "BacktestReport":
126
77
  """
@@ -131,7 +82,7 @@ def add_html_report(report) -> "BacktestReport":
131
82
  content will be added.
132
83
 
133
84
  Returns:
134
- Backtestreport.results: The updated report.results with HTML content.
85
+ Backtestreport: The updated report with HTML content.
135
86
  """
136
87
  metrics = report.metrics
137
88
  # Create plots
@@ -142,7 +93,7 @@ def add_html_report(report) -> "BacktestReport":
142
93
  full_html=False, include_plotlyjs='cdn',
143
94
  config={'responsive': True}, default_width="90%"
144
95
  )
145
- rolling_sharpe_ratio_fig = get_rolling_sharp_ratio_chart(
96
+ rolling_sharpe_ratio_fig = get_rolling_sharpe_ratio_chart(
146
97
  metrics["Rolling Sharpe Ratio"]
147
98
  )
148
99
  rolling_sharpe_ratio_plot_html = rolling_sharpe_ratio_fig.to_html(
@@ -164,6 +115,40 @@ def add_html_report(report) -> "BacktestReport":
164
115
  config={'responsive': True}
165
116
  )
166
117
 
118
+ # Create OHLCV data completeness charts
119
+ data_files = report.data_files
120
+ ohlcv_data_completeness_charts_html = ""
121
+
122
+ for file in data_files:
123
+ try:
124
+ if file.endswith('.csv'):
125
+ df = pd.read_csv(file, parse_dates=['Datetime'])
126
+ file_name = os.path.basename(file)
127
+ symbol = get_symbol_from_file_name(file_name)
128
+ market = get_market_from_file_name(file_name)
129
+ time_frame = get_time_frame_from_file_name(file_name)
130
+ title = f"OHLCV Data Completeness for {market} - {symbol} - {time_frame.value}"
131
+ ohlcv_data_completeness_chart_html = \
132
+ get_ohlcv_data_completeness_chart(
133
+ df,
134
+ timeframe=time_frame.value,
135
+ windowsize=200,
136
+ title=title
137
+ )
138
+
139
+ ohlcv_data_completeness_charts_html += (
140
+ '<div class="ohlcv-data-completeness-chart">'
141
+ f'{ohlcv_data_completeness_chart_html}'
142
+ '</div>'
143
+ )
144
+
145
+ except Exception as e:
146
+ logger.warning(
147
+ "Error creating OHLCV data completeness " +
148
+ f"chart for {file}: {e}"
149
+ )
150
+ continue
151
+
167
152
  # Create HTML tables
168
153
  key_metrics_table_html = create_html_key_metrics_table(
169
154
  metrics, report.results
@@ -194,6 +179,7 @@ def add_html_report(report) -> "BacktestReport":
194
179
  trades_metrics_table_html=trades_metrics_table_html,
195
180
  time_metrics_table_html=time_metrics_table_html,
196
181
  trades_table_html=trades_table_html,
182
+ data_completeness_charts_html=ohlcv_data_completeness_charts_html
197
183
  )
198
184
  report.html_report = html_rendered
199
185
  return report
@@ -1,6 +1,6 @@
1
1
  import pandas as pd
2
2
 
3
- from .utils import safe_format, safe_format_date, safe_format_percentage
3
+ from .utils import safe_format, safe_format_percentage
4
4
 
5
5
 
6
6
  def highlight_sharpe_and_sortino(row):
@@ -138,7 +138,6 @@ def highlight_max_drawdown(row):
138
138
  if value < 5:
139
139
  styles['Value'] = 'color: #006400; font-weight: bold;' # dark green
140
140
  elif 10 >= value > 5:
141
- print("10 >= value > 5")
142
141
  styles['Value'] = 'color: #32CD32; font-weight: bold;' # lime green
143
142
  elif 20 >= value > 10:
144
143
  styles['Value'] = 'color: #FFD700; font-weight: bold;' # gold
@@ -150,42 +149,51 @@ def highlight_max_drawdown(row):
150
149
 
151
150
 
152
151
  def create_html_key_metrics_table(results, report):
153
- copy_results = results.copy()
152
+ copy_results = results.to_dict().copy()
153
+ format_str = "{:.2f}"
154
+
154
155
  # Format some values to percentages and floats
155
- copy_results['Total Return'] = safe_format_percentage(copy_results['Total Return'], "{:.2f}%")
156
- copy_results['CAGR'] = safe_format_percentage(copy_results['CAGR'], "{:.2f}%")
157
- copy_results['Sharpe Ratio'] = safe_format(copy_results['Sharpe Ratio'], "{:.2f}")
158
- copy_results['Sortino Ratio'] = safe_format(copy_results['Sortino Ratio'], "{:.2f}")
159
- copy_results['Profit Factor'] = safe_format(copy_results['Profit Factor'], "{:.2f}")
160
- copy_results['Calmar Ratio'] = safe_format(copy_results['Calmar Ratio'], "{:.2f}")
161
- copy_results['Annual Volatility'] = safe_format_percentage(copy_results['Annual Volatility'], "{:.2f}%")
162
- copy_results['Max Drawdown'] = safe_format_percentage(copy_results['Max Drawdown'], "{:.2f}%")
163
- copy_results['Max Drawdown Absolute'] = safe_format(copy_results['Max Drawdown Absolute'], "{:.2f} " + report.trading_symbol)
164
- copy_results['Max Daily Drawdown'] = safe_format_percentage(copy_results['Max Daily Drawdown'], "{:.2f}%")
165
- copy_results['Max Drawdown Duration'] = f"{copy_results['Max Drawdown Duration']} hours - {copy_results['Max Drawdown Duration'] // 24} days"
166
-
167
- # copy_results['Total Return'] = f"{copy_results['Total Return']:.2f}%"
168
- # copy_results['CAGR'] = f"{copy_results['CAGR'] * 100:.2f}%"
169
- # copy_results['Sharpe Ratio'] = f"{copy_results['Sharpe Ratio']:.2f}"
170
- # copy_results['Sortino Ratio'] = f"{copy_results['Sortino Ratio']:.2f}"
171
- # copy_results['Profit Factor'] = f"{copy_results['Profit Factor']:.2f}"
172
- # copy_results['Calmar Ratio'] = f"{copy_results['Calmar Ratio']:.2f}"
173
- # copy_results['Annual Volatility'] = f"{copy_results['Annual Volatility'] * 100:.2f}%"
174
- # copy_results['Max Drawdown'] = f"{copy_results['Max Drawdown'] * 100:.2f}%"
175
- # copy_results['Max Drawdown Absolute'] = f"{copy_results['Max Drawdown Absolute']:.2f} {report.trading_symbol}"
176
- # copy_results['Max Daily Drawdown'] = f"{copy_results['Max Daily Drawdown'] * 100:.2f}%"
177
- # copy_results['Max Drawdown Duration'] = f"{copy_results['Max Drawdown Duration']} hours - {copy_results['Max Drawdown Duration'] // 24} days"
156
+ copy_results['Total Return'] = (
157
+ f"{safe_format(copy_results['total_net_gain'], format_str)} "
158
+ f"({safe_format_percentage(copy_results['total_net_gain_percentage'], format_str)}%)"
159
+ )
160
+ copy_results['CAGR'] = f"{safe_format_percentage(copy_results['cagr'],format_str)}%"
161
+ copy_results['Sharpe Ratio'] = safe_format(copy_results['sharpe_ratio'], format_str)
162
+ copy_results['Sortino Ratio'] = safe_format(copy_results['sortino_ratio'], format_str)
163
+ copy_results['Profit Factor'] = safe_format(copy_results['profit_factor'], format_str)
164
+ copy_results['Calmar Ratio'] = safe_format(copy_results['calmar_ratio'], format_str)
165
+ copy_results['Annual Volatility'] = f"{safe_format_percentage(copy_results['annual_volatility'], format_str)}%"
166
+ copy_results['Max Drawdown'] = f"{safe_format_percentage(copy_results['max_drawdown'], format_str)}%"
167
+ copy_results['Max Drawdown Absolute'] = f"{safe_format(copy_results['max_drawdown_absolute'], format_str)} {report.trading_symbol}"
168
+ copy_results['Max Daily Drawdown'] = f"{safe_format_percentage(copy_results['max_daily_drawdown'], format_str)}%"
169
+ copy_results['Max Drawdown Duration'] = f"{copy_results['max_drawdown_duration']} hours - {copy_results['max_drawdown_duration'] // 24} days"
178
170
 
179
171
  stats = {
180
172
  "Metric": [
181
- "Total Return", "CAGR", "Sharpe Ratio", "Sortino Ratio",
182
- "Profit Factor", "Calmar Ratio", "Annual Volatility", "Max Drawdown", "Max Drawdown Absolute", "Max Daily Drawdown", "Max Drawdown Duration"
173
+ "Total Return",
174
+ "CAGR",
175
+ "Sharpe Ratio",
176
+ "Sortino Ratio",
177
+ "Profit Factor",
178
+ "Calmar Ratio",
179
+ "Annual Volatility",
180
+ "Max Drawdown",
181
+ "Max Drawdown Absolute",
182
+ "Max Daily Drawdown",
183
+ "Max Drawdown Duration"
183
184
  ],
184
185
  "Value": [
185
- copy_results['Total Return'], copy_results['CAGR'], copy_results['Sharpe Ratio'],
186
- copy_results['Sortino Ratio'], copy_results['Profit Factor'], copy_results['Calmar Ratio'],
187
- copy_results['Annual Volatility'], copy_results['Max Drawdown'], copy_results['Max Drawdown Absolute'],
188
- copy_results['Max Daily Drawdown'], copy_results['Max Drawdown Duration']
186
+ copy_results['Total Return'],
187
+ copy_results['CAGR'],
188
+ copy_results['Sharpe Ratio'],
189
+ copy_results['Sortino Ratio'],
190
+ copy_results['Profit Factor'],
191
+ copy_results['Calmar Ratio'],
192
+ copy_results['Annual Volatility'],
193
+ copy_results['Max Drawdown'],
194
+ copy_results['Max Drawdown Absolute'],
195
+ copy_results['Max Daily Drawdown'],
196
+ copy_results['Max Drawdown Duration']
189
197
  ]
190
198
  }
191
199
 
@@ -1,40 +1,45 @@
1
1
  import pandas as pd
2
2
 
3
- from .utils import safe_format, safe_format_percentage, safe_format_date
3
+ from .utils import safe_format_percentage, safe_format_date
4
4
 
5
5
 
6
6
  def create_html_time_metrics_table(results, report):
7
- copy_results = results.copy()
8
- copy_results['Percentage Winning Months'] = safe_format(copy_results['Percentage Winning Months'], "{:.2f}")
9
- copy_results['Percentage Winning Years'] = safe_format(copy_results['Percentage Winning Years'], "{:.2f}")
10
- copy_results['Average Monthly Return'] = safe_format(copy_results['Average Monthly Return'], "{:.2f}")
11
- copy_results['Average Monthly Return (Losing Months)'] = safe_format(copy_results['Average Monthly Return (Losing Months)'], "{:.2f}%")
12
- copy_results['Average Monthly Return (Winning Months)'] = safe_format(copy_results['Average Monthly Return (Winning Months)'], "{:.2f}%")
7
+ copy_results = results.to_dict().copy()
8
+ start_date = report.backtest_start_date
9
+ end_date = report.backtest_end_date
10
+ string_format = "{:.2f}"
11
+ # Format dates
12
+ copy_results['Start Date'] = safe_format_date(start_date, "%Y-%m-%d %H:%M")
13
+ copy_results['End Date'] = safe_format_date(end_date, "%Y-%m-%d %H:%M")
14
+ copy_results['Percentage Winning Months'] = f"{safe_format_percentage(copy_results['percentage_winning_months'], string_format)}%"
15
+ copy_results['Percentage Winning Years'] = f"{safe_format_percentage(copy_results['percentage_winning_years'], string_format)}%"
16
+ copy_results['Average Monthly Return'] = f"{safe_format_percentage(copy_results['average_monthly_return'], string_format)}%"
17
+ copy_results['Average Monthly Return (Losing Months)'] = f"{safe_format_percentage(copy_results['average_monthly_return_losing_months'], string_format)}%"
18
+ copy_results['Average Monthly Return (Winning Months)'] = f"{safe_format_percentage(copy_results['average_monthly_return_winning_months'], string_format)}%"
13
19
 
14
- if isinstance(copy_results['Best Month'], tuple):
15
- copy_results['Best Month'] = (
16
- safe_format_percentage(copy_results['Best Month'][0], "{:.2f}%"),
17
- safe_format_date(copy_results['Best Month'][1], "%b %Y")
18
- )
20
+ if isinstance(copy_results['best_month'], tuple):
21
+ percentage = copy_results['best_month'][0]
22
+ date = copy_results['best_month'][1]
23
+ copy_results['Best Month'] = f"{safe_format_percentage(percentage, string_format)}% {safe_format_date(date, '%b %Y')}"
19
24
 
20
- if isinstance(copy_results['Worst Month'], tuple):
21
- copy_results['Worst Month'] = (
22
- safe_format_percentage(copy_results['Worst Month'][0], "{:.2f}%"),
23
- safe_format_date(copy_results['Worst Month'][1], "%b %Y")
24
- )
25
- if isinstance(copy_results['Best Year'], tuple):
26
- copy_results['Best Year'] = (
27
- safe_format_percentage(copy_results['Best Year'][0], "{:.2f}%"),
28
- safe_format_date(copy_results['Best Year'][1], "%Y")
29
- )
30
- if isinstance(copy_results['Worst Year'], tuple):
31
- copy_results['Worst Year'] = (
32
- safe_format_percentage(copy_results['Worst Year'][0], "{:.2f}%"),
33
- safe_format_date(copy_results['Worst Year'][1], "%Y")
34
- )
25
+ if isinstance(copy_results['worst_month'], tuple):
26
+ percentage = copy_results['worst_month'][0]
27
+ date = copy_results['worst_month'][1]
28
+ copy_results['Worst Month'] = f"{safe_format_percentage(percentage, string_format)}% {safe_format_date(date, '%b %Y')}"
29
+
30
+ if isinstance(copy_results['best_year'], tuple):
31
+ percentage = copy_results['best_year'][0]
32
+ date = copy_results['best_year'][1]
33
+ copy_results['Best Year'] = f"{safe_format_percentage(percentage, string_format)}% {safe_format_date(date, '%b %Y')}"
34
+ if isinstance(copy_results['worst_year'], tuple):
35
+ percentage = copy_results['worst_year'][0]
36
+ date = copy_results['worst_year'][1]
37
+ copy_results['Worst Year'] = f"{safe_format_percentage(percentage, string_format)}% {safe_format_date(date, '%b %Y')}"
35
38
 
36
39
  stats = {
37
40
  "Metric": [
41
+ "Start Date",
42
+ "End Date",
38
43
  "% Winning Months",
39
44
  "% Winning Years",
40
45
  "AVG Mo Return",
@@ -46,6 +51,8 @@ def create_html_time_metrics_table(results, report):
46
51
  "Worst Year",
47
52
  ],
48
53
  "Value": [
54
+ copy_results['Start Date'],
55
+ copy_results['End Date'],
49
56
  copy_results['Percentage Winning Months'],
50
57
  copy_results['Percentage Winning Years'],
51
58
  copy_results['Average Monthly Return'],