investing-algorithm-framework 1.5__py3-none-any.whl → 7.25.6__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.
Files changed (276) hide show
  1. investing_algorithm_framework/__init__.py +192 -16
  2. investing_algorithm_framework/analysis/__init__.py +16 -0
  3. investing_algorithm_framework/analysis/backtest_data_ranges.py +202 -0
  4. investing_algorithm_framework/analysis/data.py +170 -0
  5. investing_algorithm_framework/analysis/markdown.py +91 -0
  6. investing_algorithm_framework/analysis/ranking.py +298 -0
  7. investing_algorithm_framework/app/__init__.py +29 -4
  8. investing_algorithm_framework/app/algorithm/__init__.py +7 -0
  9. investing_algorithm_framework/app/algorithm/algorithm.py +193 -0
  10. investing_algorithm_framework/app/algorithm/algorithm_factory.py +118 -0
  11. investing_algorithm_framework/app/app.py +2220 -379
  12. investing_algorithm_framework/app/app_hook.py +28 -0
  13. investing_algorithm_framework/app/context.py +1724 -0
  14. investing_algorithm_framework/app/eventloop.py +620 -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/time_metrics_table.py +80 -0
  31. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
  32. investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
  33. investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
  34. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
  35. investing_algorithm_framework/app/stateless/action_handlers/__init__.py +6 -3
  36. investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
  37. investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +2 -1
  38. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
  39. investing_algorithm_framework/app/strategy.py +867 -60
  40. investing_algorithm_framework/app/task.py +5 -3
  41. investing_algorithm_framework/app/web/__init__.py +2 -1
  42. investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
  43. investing_algorithm_framework/app/web/controllers/orders.py +3 -2
  44. investing_algorithm_framework/app/web/controllers/positions.py +2 -2
  45. investing_algorithm_framework/app/web/create_app.py +4 -2
  46. investing_algorithm_framework/app/web/schemas/position.py +1 -0
  47. investing_algorithm_framework/cli/__init__.py +0 -0
  48. investing_algorithm_framework/cli/cli.py +231 -0
  49. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
  50. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  51. investing_algorithm_framework/cli/initialize_app.py +603 -0
  52. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  53. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  54. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  55. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  56. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  57. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  58. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  59. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  60. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  61. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  62. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  63. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  64. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  65. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  66. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  67. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  68. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  69. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  70. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  71. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  72. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  73. investing_algorithm_framework/cli/validate_backtest_checkpoints.py +197 -0
  74. investing_algorithm_framework/create_app.py +40 -7
  75. investing_algorithm_framework/dependency_container.py +100 -47
  76. investing_algorithm_framework/domain/__init__.py +97 -30
  77. investing_algorithm_framework/domain/algorithm_id.py +69 -0
  78. investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
  79. investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
  80. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
  81. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
  82. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
  83. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  84. investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
  85. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  86. investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
  87. investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
  88. investing_algorithm_framework/domain/config.py +59 -136
  89. investing_algorithm_framework/domain/constants.py +18 -37
  90. investing_algorithm_framework/domain/data_provider.py +334 -0
  91. investing_algorithm_framework/domain/data_structures.py +42 -0
  92. investing_algorithm_framework/domain/exceptions.py +51 -1
  93. investing_algorithm_framework/domain/models/__init__.py +26 -19
  94. investing_algorithm_framework/domain/models/app_mode.py +34 -0
  95. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  96. investing_algorithm_framework/domain/models/data/data_source.py +222 -0
  97. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  98. investing_algorithm_framework/domain/models/event.py +35 -0
  99. investing_algorithm_framework/domain/models/market/__init__.py +5 -0
  100. investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
  101. investing_algorithm_framework/domain/models/order/__init__.py +3 -4
  102. investing_algorithm_framework/domain/models/order/order.py +198 -65
  103. investing_algorithm_framework/domain/models/order/order_status.py +2 -2
  104. investing_algorithm_framework/domain/models/order/order_type.py +1 -3
  105. investing_algorithm_framework/domain/models/portfolio/__init__.py +6 -2
  106. investing_algorithm_framework/domain/models/portfolio/portfolio.py +98 -3
  107. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -43
  108. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
  109. investing_algorithm_framework/domain/models/position/__init__.py +2 -1
  110. investing_algorithm_framework/domain/models/position/position.py +20 -0
  111. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  112. investing_algorithm_framework/domain/models/position/position_snapshot.py +0 -2
  113. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  114. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  115. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  116. investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
  117. investing_algorithm_framework/domain/models/strategy_profile.py +19 -141
  118. investing_algorithm_framework/domain/models/time_frame.py +94 -98
  119. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  120. investing_algorithm_framework/domain/models/time_unit.py +66 -2
  121. investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
  122. investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
  123. investing_algorithm_framework/domain/models/trade/__init__.py +11 -0
  124. investing_algorithm_framework/domain/models/trade/trade.py +389 -0
  125. investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
  126. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
  127. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
  128. investing_algorithm_framework/domain/order_executor.py +112 -0
  129. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  130. investing_algorithm_framework/domain/services/__init__.py +11 -0
  131. investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
  132. investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
  133. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
  134. investing_algorithm_framework/domain/services/rounding_service.py +27 -0
  135. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  136. investing_algorithm_framework/domain/strategy.py +1 -29
  137. investing_algorithm_framework/domain/utils/__init__.py +15 -5
  138. investing_algorithm_framework/domain/utils/csv.py +22 -0
  139. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  140. investing_algorithm_framework/domain/utils/dates.py +57 -0
  141. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  142. investing_algorithm_framework/domain/utils/polars.py +53 -0
  143. investing_algorithm_framework/domain/utils/random.py +29 -0
  144. investing_algorithm_framework/download_data.py +244 -0
  145. investing_algorithm_framework/infrastructure/__init__.py +37 -11
  146. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  147. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -0
  148. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  149. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  150. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  151. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +86 -12
  152. investing_algorithm_framework/infrastructure/models/__init__.py +7 -3
  153. investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
  154. investing_algorithm_framework/infrastructure/models/order/order.py +53 -53
  155. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  156. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  157. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +1 -1
  158. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -2
  159. investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -6
  160. investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +3 -1
  161. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  162. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  163. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
  164. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
  165. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  166. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  167. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  168. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  169. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  170. investing_algorithm_framework/infrastructure/repositories/__init__.py +10 -4
  171. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  172. investing_algorithm_framework/infrastructure/repositories/order_repository.py +16 -5
  173. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
  174. investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
  175. investing_algorithm_framework/infrastructure/repositories/repository.py +84 -30
  176. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  177. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
  178. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
  179. investing_algorithm_framework/infrastructure/services/__init__.py +9 -4
  180. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  181. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -0
  182. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  183. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  184. investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +9 -0
  185. investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
  186. investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
  187. investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
  188. investing_algorithm_framework/services/__init__.py +123 -15
  189. investing_algorithm_framework/services/configuration_service.py +77 -11
  190. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  191. investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
  192. investing_algorithm_framework/services/market_credential_service.py +40 -0
  193. investing_algorithm_framework/services/metrics/__init__.py +119 -0
  194. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  195. investing_algorithm_framework/services/metrics/beta.py +0 -0
  196. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  197. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  198. investing_algorithm_framework/services/metrics/drawdown.py +218 -0
  199. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  200. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  201. investing_algorithm_framework/services/metrics/generate.py +358 -0
  202. investing_algorithm_framework/services/metrics/mean_daily_return.py +84 -0
  203. investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
  204. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  205. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  206. investing_algorithm_framework/services/metrics/returns.py +452 -0
  207. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  208. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  209. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  210. investing_algorithm_framework/services/metrics/standard_deviation.py +156 -0
  211. investing_algorithm_framework/services/metrics/trades.py +473 -0
  212. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  213. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  214. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  215. investing_algorithm_framework/services/metrics/volatility.py +118 -0
  216. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  217. investing_algorithm_framework/services/order_service/__init__.py +9 -0
  218. investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
  219. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  220. investing_algorithm_framework/services/order_service/order_service.py +826 -0
  221. investing_algorithm_framework/services/portfolios/__init__.py +16 -0
  222. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
  223. investing_algorithm_framework/services/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
  224. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  225. investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
  226. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
  227. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
  228. investing_algorithm_framework/services/positions/__init__.py +7 -0
  229. investing_algorithm_framework/services/positions/position_service.py +210 -0
  230. investing_algorithm_framework/services/repository_service.py +8 -2
  231. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  232. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +117 -0
  233. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  234. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  235. investing_algorithm_framework/services/trade_service/__init__.py +9 -0
  236. investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
  237. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  238. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  239. investing_algorithm_framework-7.25.6.dist-info/METADATA +535 -0
  240. investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
  241. {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
  242. investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
  243. investing_algorithm_framework/app/algorithm.py +0 -630
  244. investing_algorithm_framework/domain/models/backtest_profile.py +0 -414
  245. investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
  246. investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
  247. investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -105
  248. investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
  249. investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
  250. investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
  251. investing_algorithm_framework/domain/models/trade.py +0 -78
  252. investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
  253. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
  254. investing_algorithm_framework/domain/singleton.py +0 -9
  255. investing_algorithm_framework/domain/utils/backtesting.py +0 -82
  256. investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
  257. investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
  258. investing_algorithm_framework/infrastructure/services/market_backtest_service.py +0 -360
  259. investing_algorithm_framework/infrastructure/services/market_service.py +0 -410
  260. investing_algorithm_framework/infrastructure/services/performance_service.py +0 -192
  261. investing_algorithm_framework/services/backtest_service.py +0 -268
  262. investing_algorithm_framework/services/market_data_service.py +0 -77
  263. investing_algorithm_framework/services/order_backtest_service.py +0 -122
  264. investing_algorithm_framework/services/order_service.py +0 -752
  265. investing_algorithm_framework/services/portfolio_service.py +0 -164
  266. investing_algorithm_framework/services/portfolio_snapshot_service.py +0 -68
  267. investing_algorithm_framework/services/position_cost_service.py +0 -5
  268. investing_algorithm_framework/services/position_service.py +0 -63
  269. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -225
  270. investing_algorithm_framework-1.5.dist-info/AUTHORS.md +0 -8
  271. investing_algorithm_framework-1.5.dist-info/METADATA +0 -230
  272. investing_algorithm_framework-1.5.dist-info/RECORD +0 -119
  273. investing_algorithm_framework-1.5.dist-info/top_level.txt +0 -1
  274. /investing_algorithm_framework/{infrastructure/services/performance_backtest_service.py → app/reporting/tables/stop_loss_table.py} +0 -0
  275. /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
  276. {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -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>
@@ -54,8 +54,10 @@ class ActionHandler:
54
54
  action_handler.set_strategy(payload)
55
55
  return action_handler
56
56
 
57
- def handle(self, payload, algorithm):
58
- return self.strategy.handle_event(payload=payload, algorithm=algorithm)
57
+ def handle(self, payload, context, strategy_orchestrator_service):
58
+ return self.strategy.handle_event(
59
+ payload, context, strategy_orchestrator_service
60
+ )
59
61
 
60
62
  def set_strategy(self, payload):
61
63
  action = ActionHandler.get_action_type(payload)
@@ -70,7 +72,8 @@ class ActionHandler:
70
72
  @staticmethod
71
73
  def get_action_type(payload):
72
74
 
73
- if payload is None or ("ACTION" not in payload and "action" not in payload):
75
+ if payload is None or \
76
+ ("ACTION" not in payload and "action" not in payload):
74
77
  raise OperationalException("Action type is not defined")
75
78
 
76
79
  if "action" in payload:
@@ -4,5 +4,5 @@ from abc import ABC, abstractmethod
4
4
  class ActionHandlerStrategy(ABC):
5
5
 
6
6
  @abstractmethod
7
- def handle_event(self, payload, algorithm):
7
+ def handle_event(self, payload, context, strategy_orchestrator_service):
8
8
  pass
@@ -1,4 +1,5 @@
1
1
  import json
2
+
2
3
  from investing_algorithm_framework.app.stateless.action_handlers \
3
4
  .action_handler_strategy import ActionHandlerStrategy
4
5
 
@@ -6,7 +7,7 @@ from investing_algorithm_framework.app.stateless.action_handlers \
6
7
  class CheckOnlineHandler(ActionHandlerStrategy):
7
8
  MESSAGE = {"message": "online"}
8
9
 
9
- def handle_event(self, payload, algorithm):
10
+ def handle_event(self, payload, context, strategy_orchestrator_service):
10
11
  return {
11
12
  "statusCode": 200,
12
13
  "headers": {"Content-Type": "application/json"},
@@ -5,24 +5,31 @@ from investing_algorithm_framework.app.stateless.action_handlers \
5
5
 
6
6
 
7
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
+ """
8
15
  MESSAGE = {"message": "Ok"}
9
16
 
10
- def handle_event(self, payload, algorithm):
11
- strategies = algorithm.strategy_orchestrator_service\
17
+ def handle_event(self, payload, context, strategy_orchestrator_service):
18
+ strategies = strategy_orchestrator_service\
12
19
  .get_strategies(payload.get("strategies", None))
13
- tasks = algorithm.strategy_orchestrator_service.get_tasks()
20
+ tasks = strategy_orchestrator_service.get_tasks()
14
21
 
15
22
  for strategy in strategies:
16
- algorithm.strategy_orchestrator_service.run_strategy(
23
+ strategy_orchestrator_service.run_strategy(
17
24
  strategy=strategy,
18
- algorithm=algorithm,
25
+ context=context,
19
26
  sync=True
20
27
  )
21
28
 
22
29
  for task in tasks:
23
- algorithm.strategy_orchestrator_service.run_task(
30
+ strategy_orchestrator_service.run_task(
24
31
  task=task,
25
- algorithm=algorithm,
32
+ context=context,
26
33
  sync=True
27
34
  )
28
35