investing-algorithm-framework 7.19.14__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 (260) hide show
  1. investing_algorithm_framework/__init__.py +197 -0
  2. investing_algorithm_framework/app/__init__.py +47 -0
  3. investing_algorithm_framework/app/algorithm/__init__.py +7 -0
  4. investing_algorithm_framework/app/algorithm/algorithm.py +239 -0
  5. investing_algorithm_framework/app/algorithm/algorithm_factory.py +114 -0
  6. investing_algorithm_framework/app/analysis/__init__.py +15 -0
  7. investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
  8. investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
  9. investing_algorithm_framework/app/analysis/permutation.py +116 -0
  10. investing_algorithm_framework/app/analysis/ranking.py +297 -0
  11. investing_algorithm_framework/app/app.py +2204 -0
  12. investing_algorithm_framework/app/app_hook.py +28 -0
  13. investing_algorithm_framework/app/context.py +1667 -0
  14. investing_algorithm_framework/app/eventloop.py +590 -0
  15. investing_algorithm_framework/app/reporting/__init__.py +27 -0
  16. investing_algorithm_framework/app/reporting/ascii.py +921 -0
  17. investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
  18. investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
  19. investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
  20. investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
  21. investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
  22. investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
  23. investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
  24. investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
  25. investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
  26. investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
  27. investing_algorithm_framework/app/reporting/generate.py +185 -0
  28. investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
  29. investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
  30. investing_algorithm_framework/app/reporting/tables/stop_loss_table.py +0 -0
  31. investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
  32. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
  33. investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
  34. investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
  35. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
  36. investing_algorithm_framework/app/stateless/__init__.py +35 -0
  37. investing_algorithm_framework/app/stateless/action_handlers/__init__.py +84 -0
  38. investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +8 -0
  39. investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +15 -0
  40. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +40 -0
  41. investing_algorithm_framework/app/stateless/exception_handler.py +40 -0
  42. investing_algorithm_framework/app/strategy.py +675 -0
  43. investing_algorithm_framework/app/task.py +41 -0
  44. investing_algorithm_framework/app/web/__init__.py +5 -0
  45. investing_algorithm_framework/app/web/controllers/__init__.py +13 -0
  46. investing_algorithm_framework/app/web/controllers/orders.py +20 -0
  47. investing_algorithm_framework/app/web/controllers/portfolio.py +20 -0
  48. investing_algorithm_framework/app/web/controllers/positions.py +18 -0
  49. investing_algorithm_framework/app/web/create_app.py +20 -0
  50. investing_algorithm_framework/app/web/error_handler.py +59 -0
  51. investing_algorithm_framework/app/web/responses.py +20 -0
  52. investing_algorithm_framework/app/web/run_strategies.py +4 -0
  53. investing_algorithm_framework/app/web/schemas/__init__.py +12 -0
  54. investing_algorithm_framework/app/web/schemas/order.py +12 -0
  55. investing_algorithm_framework/app/web/schemas/portfolio.py +22 -0
  56. investing_algorithm_framework/app/web/schemas/position.py +15 -0
  57. investing_algorithm_framework/app/web/setup_cors.py +6 -0
  58. investing_algorithm_framework/cli/__init__.py +0 -0
  59. investing_algorithm_framework/cli/cli.py +207 -0
  60. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +499 -0
  61. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  62. investing_algorithm_framework/cli/initialize_app.py +603 -0
  63. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  64. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  65. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  66. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  67. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  68. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  69. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  70. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  71. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  72. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  73. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  74. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  75. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  76. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  77. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  78. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  79. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  80. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  81. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  82. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  83. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  84. investing_algorithm_framework/create_app.py +54 -0
  85. investing_algorithm_framework/dependency_container.py +155 -0
  86. investing_algorithm_framework/domain/__init__.py +148 -0
  87. investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
  88. investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
  89. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
  90. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
  91. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
  92. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  93. investing_algorithm_framework/domain/backtesting/backtest_run.py +435 -0
  94. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  95. investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
  96. investing_algorithm_framework/domain/config.py +111 -0
  97. investing_algorithm_framework/domain/constants.py +83 -0
  98. investing_algorithm_framework/domain/data_provider.py +334 -0
  99. investing_algorithm_framework/domain/data_structures.py +42 -0
  100. investing_algorithm_framework/domain/decimal_parsing.py +40 -0
  101. investing_algorithm_framework/domain/exceptions.py +112 -0
  102. investing_algorithm_framework/domain/models/__init__.py +43 -0
  103. investing_algorithm_framework/domain/models/app_mode.py +34 -0
  104. investing_algorithm_framework/domain/models/base_model.py +25 -0
  105. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  106. investing_algorithm_framework/domain/models/data/data_source.py +214 -0
  107. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  108. investing_algorithm_framework/domain/models/event.py +35 -0
  109. investing_algorithm_framework/domain/models/market/__init__.py +5 -0
  110. investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
  111. investing_algorithm_framework/domain/models/order/__init__.py +6 -0
  112. investing_algorithm_framework/domain/models/order/order.py +384 -0
  113. investing_algorithm_framework/domain/models/order/order_side.py +36 -0
  114. investing_algorithm_framework/domain/models/order/order_status.py +37 -0
  115. investing_algorithm_framework/domain/models/order/order_type.py +30 -0
  116. investing_algorithm_framework/domain/models/portfolio/__init__.py +9 -0
  117. investing_algorithm_framework/domain/models/portfolio/portfolio.py +169 -0
  118. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +93 -0
  119. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
  120. investing_algorithm_framework/domain/models/position/__init__.py +4 -0
  121. investing_algorithm_framework/domain/models/position/position.py +68 -0
  122. investing_algorithm_framework/domain/models/position/position_snapshot.py +47 -0
  123. investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
  124. investing_algorithm_framework/domain/models/strategy_profile.py +33 -0
  125. investing_algorithm_framework/domain/models/time_frame.py +153 -0
  126. investing_algorithm_framework/domain/models/time_interval.py +124 -0
  127. investing_algorithm_framework/domain/models/time_unit.py +149 -0
  128. investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
  129. investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
  130. investing_algorithm_framework/domain/models/trade/__init__.py +13 -0
  131. investing_algorithm_framework/domain/models/trade/trade.py +388 -0
  132. investing_algorithm_framework/domain/models/trade/trade_risk_type.py +34 -0
  133. investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
  134. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +267 -0
  135. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +303 -0
  136. investing_algorithm_framework/domain/order_executor.py +112 -0
  137. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  138. investing_algorithm_framework/domain/positions/__init__.py +4 -0
  139. investing_algorithm_framework/domain/positions/position_size.py +41 -0
  140. investing_algorithm_framework/domain/services/__init__.py +11 -0
  141. investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
  142. investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
  143. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
  144. investing_algorithm_framework/domain/services/rounding_service.py +27 -0
  145. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  146. investing_algorithm_framework/domain/stateless_actions.py +7 -0
  147. investing_algorithm_framework/domain/strategy.py +44 -0
  148. investing_algorithm_framework/domain/utils/__init__.py +27 -0
  149. investing_algorithm_framework/domain/utils/csv.py +104 -0
  150. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  151. investing_algorithm_framework/domain/utils/dates.py +57 -0
  152. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  153. investing_algorithm_framework/domain/utils/polars.py +53 -0
  154. investing_algorithm_framework/domain/utils/random.py +41 -0
  155. investing_algorithm_framework/domain/utils/signatures.py +17 -0
  156. investing_algorithm_framework/domain/utils/stoppable_thread.py +26 -0
  157. investing_algorithm_framework/domain/utils/synchronized.py +12 -0
  158. investing_algorithm_framework/download_data.py +108 -0
  159. investing_algorithm_framework/infrastructure/__init__.py +50 -0
  160. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  161. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1143 -0
  162. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  163. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  164. investing_algorithm_framework/infrastructure/database/__init__.py +10 -0
  165. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +120 -0
  166. investing_algorithm_framework/infrastructure/models/__init__.py +16 -0
  167. investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
  168. investing_algorithm_framework/infrastructure/models/model_extension.py +6 -0
  169. investing_algorithm_framework/infrastructure/models/order/__init__.py +4 -0
  170. investing_algorithm_framework/infrastructure/models/order/order.py +124 -0
  171. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  172. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  173. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +4 -0
  174. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
  175. investing_algorithm_framework/infrastructure/models/portfolio/sql_portfolio.py +114 -0
  176. investing_algorithm_framework/infrastructure/models/position/__init__.py +4 -0
  177. investing_algorithm_framework/infrastructure/models/position/position.py +63 -0
  178. investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +23 -0
  179. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  180. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  181. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +40 -0
  182. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +41 -0
  183. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  184. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  185. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  186. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  187. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  188. investing_algorithm_framework/infrastructure/repositories/__init__.py +21 -0
  189. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  190. investing_algorithm_framework/infrastructure/repositories/order_repository.py +96 -0
  191. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +30 -0
  192. investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
  193. investing_algorithm_framework/infrastructure/repositories/position_repository.py +66 -0
  194. investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
  195. investing_algorithm_framework/infrastructure/repositories/repository.py +299 -0
  196. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  197. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +23 -0
  198. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +23 -0
  199. investing_algorithm_framework/infrastructure/services/__init__.py +7 -0
  200. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  201. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +113 -0
  202. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  203. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  204. investing_algorithm_framework/services/__init__.py +132 -0
  205. investing_algorithm_framework/services/backtesting/__init__.py +5 -0
  206. investing_algorithm_framework/services/backtesting/backtest_service.py +651 -0
  207. investing_algorithm_framework/services/configuration_service.py +96 -0
  208. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  209. investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
  210. investing_algorithm_framework/services/market_credential_service.py +40 -0
  211. investing_algorithm_framework/services/metrics/__init__.py +114 -0
  212. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  213. investing_algorithm_framework/services/metrics/beta.py +0 -0
  214. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  215. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  216. investing_algorithm_framework/services/metrics/drawdown.py +181 -0
  217. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  218. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  219. investing_algorithm_framework/services/metrics/generate.py +358 -0
  220. investing_algorithm_framework/services/metrics/mean_daily_return.py +83 -0
  221. investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
  222. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  223. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  224. investing_algorithm_framework/services/metrics/returns.py +452 -0
  225. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  226. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  227. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  228. investing_algorithm_framework/services/metrics/standard_deviation.py +157 -0
  229. investing_algorithm_framework/services/metrics/trades.py +500 -0
  230. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  231. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  232. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  233. investing_algorithm_framework/services/metrics/volatility.py +97 -0
  234. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  235. investing_algorithm_framework/services/order_service/__init__.py +9 -0
  236. investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
  237. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  238. investing_algorithm_framework/services/order_service/order_service.py +826 -0
  239. investing_algorithm_framework/services/portfolios/__init__.py +16 -0
  240. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
  241. investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +75 -0
  242. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  243. investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
  244. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
  245. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
  246. investing_algorithm_framework/services/positions/__init__.py +7 -0
  247. investing_algorithm_framework/services/positions/position_service.py +210 -0
  248. investing_algorithm_framework/services/positions/position_snapshot_service.py +18 -0
  249. investing_algorithm_framework/services/repository_service.py +40 -0
  250. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  251. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +132 -0
  252. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +66 -0
  253. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +41 -0
  254. investing_algorithm_framework/services/trade_service/__init__.py +3 -0
  255. investing_algorithm_framework/services/trade_service/trade_service.py +1083 -0
  256. investing_algorithm_framework-7.19.14.dist-info/LICENSE +201 -0
  257. investing_algorithm_framework-7.19.14.dist-info/METADATA +459 -0
  258. investing_algorithm_framework-7.19.14.dist-info/RECORD +260 -0
  259. investing_algorithm_framework-7.19.14.dist-info/WHEEL +4 -0
  260. investing_algorithm_framework-7.19.14.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,147 @@
1
+ import pandas as pd
2
+
3
+ from investing_algorithm_framework.domain import DEFAULT_DATETIME_FORMAT
4
+ from .utils import safe_format, safe_format_date, safe_format_percentage
5
+
6
+
7
+ def highlight_win_rate(row):
8
+ """
9
+ | **Winning Percentage** | **Interpretation** |
10
+ |-------------------------|----------------------------------------------------------------------|
11
+ | **< 40%** | 🟥 Poor — Strategy may not be viable |
12
+ | **40% to 50%** | 🟧 Weak — Needs improvement, often breakeven or slightly negative |
13
+ | **50% to 60%** | 🟨 Average — Acceptable for many strategies, especially with high PF |
14
+ | **60% to 70%** | 🟩 Good — Solid performance, often with good risk/reward |
15
+ | **> 70%** | 🟩 Excellent — High win rate, typically indicates a strong edge |
16
+ """
17
+ metric = row['Metric']
18
+ try:
19
+ value = float(row['Value'].strip('%'))
20
+ except Exception:
21
+ value = None
22
+ styles = pd.Series('', index=row.index)
23
+ if metric == 'Win Rate' and value is not None:
24
+ if value < 40:
25
+ styles['Value'] = 'color: #B22222; font-weight: bold;' # red
26
+ elif 40 <= value < 50:
27
+ styles['Value'] = 'color: #FFA500; font-weight: bold;' # orange
28
+ elif 50 <= value < 60:
29
+ styles['Value'] = 'color: #FFD700; font-weight: bold;' # gold
30
+ elif 60 <= value < 70:
31
+ styles['Value'] = 'color: #32CD32; font-weight: bold;' # lime green
32
+ elif value >= 70:
33
+ styles['Value'] = 'color: #228B22; font-weight: bold;' # dark green
34
+ return styles
35
+
36
+
37
+ def highlight_win_loss_ratio(row):
38
+ """
39
+ | **Win/Loss Ratio** | **Interpretation** |
40
+ |---------------------|----------------------------------------------------------------------|
41
+ | **< 0.5** | 🟥 Poor — Strategy may not be viable |
42
+ | **0.5 to 1.0** | 🟧 Weak — Needs improvement, often breakeven or slightly negative |
43
+ | **1.0 to 1.5** | 🟨 Average — Acceptable for many strategies, especially with high PF |
44
+ | **1.5 to 2.0** | 🟩 Good — Solid performance, often with good risk/reward |
45
+ | **> 2.0** | 🟩 Excellent — High win/loss ratio, typically indicates a strong edge |
46
+ """
47
+ metric = row['Metric']
48
+ try:
49
+ value = float(row['Value'])
50
+ except Exception:
51
+ value = None
52
+ styles = pd.Series('', index=row.index)
53
+ if metric == 'Win/Loss Ratio' and value is not None:
54
+ if value < 0.5:
55
+ styles['Value'] = 'color: #B22222; font-weight: bold;' # red
56
+ elif 0.5 <= value < 1.0:
57
+ styles['Value'] = 'color: #FFA500; font-weight: bold;' # orange
58
+ elif 1.0 <= value < 1.5:
59
+ styles['Value'] = 'color: #FFD700; font-weight: bold;' # gold
60
+ elif 1.5 <= value < 2.0:
61
+ styles['Value'] = 'color: #32CD32; font-weight: bold;' # lime green
62
+ elif value >= 2.0:
63
+ styles['Value'] = 'color: #228B22; font-weight: bold;' # dark green
64
+ return styles
65
+
66
+
67
+ def create_html_trade_metrics_table(results, report):
68
+ copy_results = results.to_dict().copy()
69
+ string_format = "{:.2f}"
70
+ copy_results['Trades per Year'] = safe_format(copy_results['trades_per_year'], string_format)
71
+ copy_results['Trades per Day'] = safe_format(copy_results['trade_per_day'], string_format)
72
+ copy_results['Exposure Ratio'] = safe_format(copy_results['exposure_ratio'],string_format)
73
+ copy_results["Cumulative Exposure"] = safe_format(copy_results['cumulative_exposure'],string_format)
74
+ best_trade = copy_results['best_trade']
75
+
76
+ if best_trade is None:
77
+ copy_results["Best Trade"] = "N/A"
78
+ copy_results['Best Trade Date'] = "N/A"
79
+ else:
80
+ copy_results['Best Trade'] = f"{best_trade['net_gain']:.2f} {report.trading_symbol}"
81
+ copy_results['Best Trade Date'] = safe_format_date(best_trade["opened_at"], format_str=DEFAULT_DATETIME_FORMAT)
82
+ worst_trade = copy_results['worst_trade']
83
+
84
+ if worst_trade is None:
85
+ copy_results["Worst Trade"] = "N/A"
86
+ copy_results['Worst Trade Date'] = "N/A"
87
+ else:
88
+ copy_results['Worst Trade'] = f"{worst_trade['net_gain']:.2f} {report.trading_symbol}"
89
+ copy_results['Worst Trade Date'] = safe_format_date(worst_trade['opened_at'], format_str=DEFAULT_DATETIME_FORMAT)
90
+
91
+ copy_results['Trades Average Gain'] = f"{safe_format(copy_results['average_trade_gain'], string_format)} {report.trading_symbol} {copy_results['trades_average_gain_percentage']:.2f}%"
92
+ copy_results['Trades Average Loss'] = f"{safe_format(copy_results['average_trade_loss'], string_format)} {report.trading_symbol} {copy_results['trades_average_loss_percentage']:.2f}%"
93
+ copy_results['Average Trade Duration'] = f"{copy_results['average_trade_duration']:.2f} hours"
94
+ copy_results['Number of Trades'] = f"{copy_results['number_of_trades']}"
95
+ copy_results['Win Rate'] = f"{copy_results['win_rate']:.2f}%"
96
+ copy_results['Win/Loss Ratio'] = f"{copy_results['win_loss_ratio']:.2f}"
97
+
98
+ stats = {
99
+ "Metric": [
100
+ "Trades per Year",
101
+ "Trade per Day",
102
+ "Exposure Ratio",
103
+ "Cumulative Exposure",
104
+ "Trades Average Gain",
105
+ "Trades Average Loss",
106
+ "Best Trade",
107
+ "Best Trade Date",
108
+ "Worst Trade",
109
+ "Worst Trade Date",
110
+ "Average Trade Duration",
111
+ "Number of Trades",
112
+ "Win Rate",
113
+ "Win/Loss Ratio"
114
+ ],
115
+ "Value": [
116
+ copy_results['Trades per Year'],
117
+ copy_results['Trades per Day'],
118
+ copy_results['Exposure Ratio'],
119
+ copy_results['Cumulative Exposure'],
120
+ copy_results['Trades Average Gain'],
121
+ copy_results['Trades Average Loss'],
122
+ copy_results['Best Trade'],
123
+ copy_results['Best Trade Date'],
124
+ copy_results['Worst Trade'],
125
+ copy_results['Worst Trade Date'],
126
+ copy_results['Average Trade Duration'],
127
+ copy_results['Number of Trades'],
128
+ copy_results['Win Rate'],
129
+ copy_results['Win/Loss Ratio']
130
+ ]
131
+ }
132
+
133
+ df_stats = pd.DataFrame(stats)
134
+
135
+ table_html = (
136
+ df_stats.style
137
+ .apply(highlight_win_rate, axis=1)
138
+ .apply(highlight_win_loss_ratio, axis=1)
139
+ .set_table_styles([
140
+ {'selector': 'th', 'props': [('background-color', '#f2f2f2'), ('font-weight', 'bold')]},
141
+ {'selector': 'td', 'props': [('font-size', '14px')]},
142
+ {'selector': 'tr:nth-child(even)', 'props': [('background-color', '#fafafa')]}
143
+ ])
144
+ .hide(axis='index')
145
+ .to_html()
146
+ )
147
+ return table_html
@@ -0,0 +1,75 @@
1
+ import pandas as pd
2
+
3
+
4
+ def highlight_net_gain(row):
5
+ """
6
+ Apply conditional formatting to the 'Net Gain' column based on numeric value.
7
+ """
8
+ try:
9
+ # Extract numeric value before the first space (assumes format like "123.45 USDT (10.23%)")
10
+ value_str = row['Net Gain'].split()[2]
11
+ value_str = value_str.split('(')[1]
12
+ value_str = value_str.replace(')', '').strip()
13
+ if '%' in value_str:
14
+ value_str = value_str.replace('%', '')
15
+ value = float(value_str)
16
+
17
+ except Exception:
18
+ value = None
19
+
20
+ styles = pd.Series('', index=row.index)
21
+
22
+ if value is not None:
23
+ if value < -5:
24
+ styles['Net Gain'] = 'color: #B22222; font-weight: bold;' # red
25
+ elif -5 <= value < -2:
26
+ styles['Net Gain'] = 'color: #FFA500; font-weight: bold;' # orange
27
+ elif -2 <= value < 0:
28
+ styles['Net Gain'] = 'color: #FFD700; font-weight: bold;' # gold
29
+ elif 0 <= value < 5:
30
+ styles['Net Gain'] = 'color: #32CD32; font-weight: bold;' # lime green
31
+ elif value >= 5:
32
+ styles['Net Gain'] = 'color: #228B22; font-weight: bold;' # dark green
33
+
34
+ return styles
35
+
36
+ def get_exit(trade):
37
+ """
38
+ Returns the exit price of a trade if it is closed, otherwise returns "open".
39
+ This is used to display the exit price in the trades table.
40
+
41
+ A trade can have multiple exits and close prices.
42
+ """
43
+
44
+ if trade.closed_at:
45
+ return f"{trade.closed_at.strftime('%Y-%m-%d %H:%M:%S')}"
46
+ else:
47
+ return "open"
48
+
49
+
50
+ def create_html_trades_table(report):
51
+ trades = report.get_trades()
52
+
53
+ # Create a DataFrame from the trades, with the attributes id, net_gain, net_gain_percentage, entry_time, exit_time, duration, and symbol
54
+ selection = {
55
+ 'Trade': [f"{trade.id} {trade.target_symbol}/{trade.trading_symbol}" for trade in trades],
56
+ 'Net Gain': [f"{trade.change:.2f} {report.trading_symbol} ({trade.percentage_change:.2f}%)" for trade in trades],
57
+ 'Entry (Price, Date)': [f"{trade.open_price} {trade.opened_at.strftime('%Y-%m-%d %H:%M:%S')}" for trade in trades],
58
+ 'Exit (Price, Date)': [get_exit(trade) for trade in trades],
59
+ 'Duration': [f"{trade.duration:.2f} hours" for trade in trades],
60
+ }
61
+ trades_df = pd.DataFrame(selection)
62
+
63
+ table_html = (
64
+ trades_df.style
65
+ .apply(highlight_net_gain, axis=1)
66
+ # .apply(highlight_win_loss_ratio, axis=1)
67
+ .set_table_styles([
68
+ {'selector': 'th', 'props': [('background-color', '#f2f2f2'), ('font-weight', 'bold')]},
69
+ {'selector': 'td', 'props': [('font-size', '14px')]},
70
+ {'selector': 'tr:nth-child(even)', 'props': [('background-color', '#fafafa')]}
71
+ ])
72
+ .hide(axis='index')
73
+ .to_html(classes='display', index=False, table_id='tradesTable')
74
+ )
75
+ return table_html
@@ -0,0 +1,29 @@
1
+ import pandas as pd
2
+
3
+
4
+ def safe_format(value, format_str, default_value='N/A'):
5
+ if value is None:
6
+ return default_value
7
+
8
+ if isinstance(value, (int, float)):
9
+ return format_str.format(value)
10
+ return value
11
+
12
+
13
+ def safe_format_percentage(value, format_str, default_value='N/A'):
14
+ if value is None:
15
+ return default_value
16
+
17
+ if isinstance(value, (int, float)):
18
+ return format_str.format(value * 100)
19
+
20
+ return value
21
+
22
+
23
+ def safe_format_date(value, format_str, default_value='N/A'):
24
+ if value is None:
25
+ return default_value
26
+
27
+ if isinstance(value, pd.Timestamp):
28
+ return value.strftime(format_str)
29
+ return value
@@ -0,0 +1,154 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Aluminium Smelting Strategy Report</title>
5
+ <style>
6
+ body {{ '{' }}
7
+ font-family: Arial, sans-serif;
8
+ margin: 40px;
9
+ {{ '}' }}
10
+ h1 {{ '{' }}
11
+ color: #333;
12
+ text-align: center;
13
+ {{ '}' }}
14
+ .tab {{ '{' }}
15
+ overflow: hidden;
16
+ border-bottom: 1px solid #ccc;
17
+ margin-bottom: 20px;
18
+ {{ '}' }}
19
+ .tab button {{ '{' }}
20
+ background-color: #f1f1f1;
21
+ float: left;
22
+ border: none;
23
+ outline: none;
24
+ cursor: pointer;
25
+ padding: 10px 20px;
26
+ transition: 0.3s;
27
+ font-size: 17px;
28
+ {{ '}' }}
29
+ .tab button:hover {{ '{' }}
30
+ background-color: #ddd;
31
+ {{ '}' }}
32
+ .tab button.active {{ '{' }}
33
+ background-color: #ccc;
34
+ {{ '}' }}
35
+ .tabcontent {{ '{' }}
36
+ display: none;
37
+ {{ '}' }}
38
+ .tabcontent.active {{ '{' }}
39
+ display: block;
40
+ {{ '}' }}
41
+ .stats-table, .metrics-table {{ '{' }}
42
+ border-collapse: collapse;
43
+ width: 50%;
44
+ margin-top: 20px;
45
+ {{ '}' }}
46
+ .stats-table th, .stats-table td {{ '{' }}
47
+ border: 1px solid #ddd;
48
+ padding: 8px;
49
+ text-align: left;
50
+ {{ '}' }}
51
+ .stats-table th {{ '{' }}
52
+ background-color: #f2f2f2;
53
+ {{ '}' }}
54
+ .grid-container {{ '{' }}
55
+ display: flex;
56
+ gap: 10px;
57
+ align-items: flex-start;
58
+ margin-top: 20px;
59
+ {{ '}' }}
60
+ .heatmap-cell {{ '{' }}
61
+ padding: 5px;
62
+ width: 60%;
63
+ {{ '}' }}
64
+ .yearly-returns-cell {{ '{' }}
65
+ padding: 5px;
66
+ width: 30%;
67
+ {{ '}' }}
68
+ .metrics-table {{ '{' }}
69
+ padding: 5px;
70
+ {{ '}' }}
71
+ </style>
72
+ <script>
73
+ function openTab(evt, tabName) {{ '{' }}
74
+ var i, tabcontent, tablinks;
75
+ tabcontent = document.getElementsByClassName("tabcontent");
76
+ for (i = 0; i < tabcontent.length; i++) {{ '{' }}
77
+ tabcontent[i].classList.remove("active");
78
+ {{ '}' }}
79
+ tablinks = document.getElementsByClassName("tablink");
80
+ for (i = 0; i < tablinks.length; i++) {{ '{' }}
81
+ tablinks[i].classList.remove("active");
82
+ {{ '}' }}
83
+ document.getElementById(tabName).classList.add("active");
84
+ evt.currentTarget.classList.add("active");
85
+ {{ '}' }}
86
+ </script>
87
+
88
+ <!-- DataTables CSS -->
89
+ <link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css">
90
+ <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css"/>
91
+ <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
92
+ <script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
93
+ </head>
94
+ <body>
95
+ <h1>Backtest report: {{ report.name }}</h1>
96
+
97
+ <div class="tab">
98
+ <button class="tablink active" onclick="openTab(event, 'Overview')">Overview</button>
99
+ <button class="tablink" onclick="openTab(event, 'Trades')">Trades</button>
100
+ <button class="tablink" onclick="openTab(event, 'Positions and orders')">Positions and Orders</button>
101
+ <button class="tablink" onclick="openTab(event, 'DataAnalysis')">Data Analysis</button>
102
+
103
+ </div>
104
+
105
+ <div id="Overview" class="tabcontent active">
106
+ <div>
107
+ {{ equity_with_drawdown_plot_html | safe }}
108
+ </div>
109
+ <div>
110
+ {{ rolling_sharpe_ratio_plot_html | safe }}
111
+ </div>
112
+ <div class="grid-container">
113
+ <div class="heatmap-cell">
114
+ {{ monthly_returns_heatmap_html | safe }}
115
+ </div>
116
+ <div class="yearly-returns-cell">
117
+ {{ yearly_returns_histogram_html | safe }}
118
+ </div>
119
+ </div>
120
+ <div class="grid-container">
121
+ <div class="metrics-table">
122
+ <h2>Time Metrics</h2>
123
+ {{ time_metrics_table_html | safe }}
124
+ </div>
125
+ <div class="metrics-table">
126
+ <h2>Performance Metrics</h2>
127
+ {{ key_metrics_table_html | safe }}
128
+ </div>
129
+ <div class="metrics-table">
130
+ <h2>Trade Metrics</h2>
131
+ {{ trades_metrics_table_html | safe }}
132
+ </div>
133
+ </div>
134
+ </div>
135
+ <div id="Trades" class="tabcontent">
136
+ {{ trades_table_html | safe }}
137
+ {% raw %}
138
+ <script>
139
+ $(document).ready(function() {
140
+ $('.stats-table').DataTable({
141
+ paging: true,
142
+ pageLength: 10,
143
+ searching: true
144
+ });
145
+ });
146
+ </script>
147
+ {% endraw %}
148
+ </div>
149
+ <div id="DataAnalysis" class="tabcontent">
150
+ <h2>OHLCV Data Completeness Charts</h2>
151
+ {{ data_completeness_charts_html | safe }}
152
+ </div>
153
+ </body>
154
+ </html>
@@ -0,0 +1,35 @@
1
+ from investing_algorithm_framework.app.stateless.action_handlers \
2
+ import ActionHandler
3
+ from investing_algorithm_framework.app.stateless.action_handlers import \
4
+ StatelessAction
5
+ from investing_algorithm_framework.app.stateless.exception_handler import \
6
+ handle_exception
7
+ from investing_algorithm_framework.domain.exceptions import \
8
+ OperationalException
9
+
10
+
11
+ class StatelessHandler:
12
+
13
+ def handler(self, payload, algorithm):
14
+ action = StatelessHandler.get_action_type(payload)
15
+
16
+ try:
17
+ # Handle the action
18
+ action_handler = ActionHandler.of(StatelessAction
19
+ .from_string(action))
20
+ return action_handler.handle(payload)
21
+ except Exception as e:
22
+ return handle_exception(e)
23
+
24
+ @staticmethod
25
+ def get_action_type(payload):
26
+
27
+ if "action" in payload:
28
+ action = payload["action"]
29
+ else:
30
+ action = payload["ACTION"]
31
+
32
+ if action is None:
33
+ raise OperationalException("Action type not supported")
34
+
35
+ return action
@@ -0,0 +1,84 @@
1
+ from enum import Enum
2
+
3
+ from investing_algorithm_framework.app.stateless.action_handlers \
4
+ .check_online_handler import CheckOnlineHandler
5
+ from investing_algorithm_framework.app.stateless.action_handlers \
6
+ .run_strategy_handler import RunStrategyHandler
7
+ from investing_algorithm_framework.domain.exceptions import \
8
+ OperationalException
9
+
10
+
11
+ class StatelessAction(Enum):
12
+ RUN_STRATEGY = 'RUN_STRATEGY'
13
+ PING = "PING"
14
+
15
+ @staticmethod
16
+ def from_value(value: str):
17
+ if isinstance(value, StatelessAction):
18
+ for action in StatelessAction:
19
+
20
+ if value == action:
21
+ return action
22
+
23
+ if isinstance(value, str):
24
+ return StatelessAction.from_string(value)
25
+
26
+ raise ValueError("Could not convert value to stateless action")
27
+
28
+ @staticmethod
29
+ def from_string(value: str):
30
+
31
+ if isinstance(value, str):
32
+ for action in StatelessAction:
33
+
34
+ if value.upper() == action.value:
35
+ return action
36
+
37
+ raise ValueError("Could not convert value to stateless action")
38
+
39
+ def equals(self, other):
40
+
41
+ if isinstance(other, Enum):
42
+ return self.value == other.value
43
+
44
+ else:
45
+ return StatelessAction.from_string(other) == self
46
+
47
+
48
+ class ActionHandler:
49
+ strategy = None
50
+
51
+ @staticmethod
52
+ def of(payload: dict):
53
+ action_handler = ActionHandler()
54
+ action_handler.set_strategy(payload)
55
+ return action_handler
56
+
57
+ def handle(self, payload, context, strategy_orchestrator_service):
58
+ return self.strategy.handle_event(
59
+ payload, context, strategy_orchestrator_service
60
+ )
61
+
62
+ def set_strategy(self, payload):
63
+ action = ActionHandler.get_action_type(payload)
64
+
65
+ if StatelessAction.RUN_STRATEGY.equals(action):
66
+ self.strategy = RunStrategyHandler()
67
+ elif StatelessAction.PING.equals(action):
68
+ self.strategy = CheckOnlineHandler()
69
+ else:
70
+ raise OperationalException("Action not supported")
71
+
72
+ @staticmethod
73
+ def get_action_type(payload):
74
+
75
+ if payload is None or \
76
+ ("ACTION" not in payload and "action" not in payload):
77
+ raise OperationalException("Action type is not defined")
78
+
79
+ if "action" in payload:
80
+ action = payload["action"]
81
+ else:
82
+ action = payload["ACTION"]
83
+
84
+ return action
@@ -0,0 +1,8 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class ActionHandlerStrategy(ABC):
5
+
6
+ @abstractmethod
7
+ def handle_event(self, payload, context, strategy_orchestrator_service):
8
+ pass
@@ -0,0 +1,15 @@
1
+ import json
2
+
3
+ from investing_algorithm_framework.app.stateless.action_handlers \
4
+ .action_handler_strategy import ActionHandlerStrategy
5
+
6
+
7
+ class CheckOnlineHandler(ActionHandlerStrategy):
8
+ MESSAGE = {"message": "online"}
9
+
10
+ def handle_event(self, payload, context, strategy_orchestrator_service):
11
+ return {
12
+ "statusCode": 200,
13
+ "headers": {"Content-Type": "application/json"},
14
+ "body": json.dumps(CheckOnlineHandler.MESSAGE)
15
+ }
@@ -0,0 +1,40 @@
1
+ import json
2
+
3
+ from investing_algorithm_framework.app.stateless.action_handlers \
4
+ .action_handler_strategy import ActionHandlerStrategy
5
+
6
+
7
+ class RunStrategyHandler(ActionHandlerStrategy):
8
+ """
9
+ RunStrategyHandler is an action handler that runs a strategy and its tasks
10
+ synchronously.
11
+
12
+ If the run was successful, it returns a 200 OK response with a message
13
+ "OK".
14
+ """
15
+ MESSAGE = {"message": "Ok"}
16
+
17
+ def handle_event(self, payload, context, strategy_orchestrator_service):
18
+ strategies = strategy_orchestrator_service\
19
+ .get_strategies(payload.get("strategies", None))
20
+ tasks = strategy_orchestrator_service.get_tasks()
21
+
22
+ for strategy in strategies:
23
+ strategy_orchestrator_service.run_strategy(
24
+ strategy=strategy,
25
+ context=context,
26
+ sync=True
27
+ )
28
+
29
+ for task in tasks:
30
+ strategy_orchestrator_service.run_task(
31
+ task=task,
32
+ context=context,
33
+ sync=True
34
+ )
35
+
36
+ return {
37
+ "statusCode": 200,
38
+ "headers": {"Content-Type": "application/json"},
39
+ "body": json.dumps({"message": RunStrategyHandler.MESSAGE})
40
+ }
@@ -0,0 +1,40 @@
1
+ import json
2
+ import logging
3
+ from typing import Dict, List
4
+
5
+ from investing_algorithm_framework.domain import OperationalException
6
+
7
+ logger = logging.getLogger("investing_algorithm_framework")
8
+
9
+
10
+ def create_error_response(error_message, status_code: int = 400):
11
+ response = json.dumps({"error_message": error_message})
12
+ return response, status_code
13
+
14
+
15
+ def format_marshmallow_validation_error(errors):
16
+ errors_message = {}
17
+
18
+ for key in errors:
19
+
20
+ if isinstance(errors[key], Dict):
21
+ errors_message[key] = \
22
+ format_marshmallow_validation_error(errors[key])
23
+
24
+ if isinstance(errors[key], List):
25
+ errors_message[key] = errors[key][0].lower()
26
+ return errors_message
27
+
28
+
29
+ def handle_exception(error):
30
+ logger.error("exception of type {} occurred".format(type(error)))
31
+ logger.exception(error)
32
+
33
+ if isinstance(error, OperationalException):
34
+ return error.to_response()
35
+ else:
36
+ # Internal error happened that was unknown
37
+ return {
38
+ "status": "error",
39
+ "message": str(error)
40
+ }