investing-algorithm-framework 3.7.0__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 (256) hide show
  1. investing_algorithm_framework/__init__.py +168 -45
  2. investing_algorithm_framework/app/__init__.py +32 -1
  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 +1933 -589
  12. investing_algorithm_framework/app/app_hook.py +28 -0
  13. investing_algorithm_framework/app/context.py +1725 -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/action_handlers/__init__.py +4 -2
  37. investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
  38. investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +1 -1
  39. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
  40. investing_algorithm_framework/app/strategy.py +664 -84
  41. investing_algorithm_framework/app/task.py +5 -3
  42. investing_algorithm_framework/app/web/__init__.py +2 -1
  43. investing_algorithm_framework/app/web/create_app.py +4 -2
  44. investing_algorithm_framework/cli/__init__.py +0 -0
  45. investing_algorithm_framework/cli/cli.py +226 -0
  46. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
  47. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  48. investing_algorithm_framework/cli/initialize_app.py +603 -0
  49. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  50. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  51. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  52. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  53. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  54. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  55. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  56. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  57. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  58. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  59. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  60. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  61. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  62. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  63. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  64. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  65. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  66. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  67. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  68. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  69. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  70. investing_algorithm_framework/create_app.py +40 -6
  71. investing_algorithm_framework/dependency_container.py +72 -56
  72. investing_algorithm_framework/domain/__init__.py +71 -47
  73. investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
  74. investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
  75. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
  76. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
  77. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
  78. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  79. investing_algorithm_framework/domain/backtesting/backtest_run.py +605 -0
  80. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  81. investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
  82. investing_algorithm_framework/domain/config.py +59 -91
  83. investing_algorithm_framework/domain/constants.py +13 -38
  84. investing_algorithm_framework/domain/data_provider.py +334 -0
  85. investing_algorithm_framework/domain/data_structures.py +3 -2
  86. investing_algorithm_framework/domain/exceptions.py +51 -1
  87. investing_algorithm_framework/domain/models/__init__.py +17 -12
  88. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  89. investing_algorithm_framework/domain/models/data/data_source.py +214 -0
  90. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  91. investing_algorithm_framework/domain/models/event.py +35 -0
  92. investing_algorithm_framework/domain/models/market/market_credential.py +55 -1
  93. investing_algorithm_framework/domain/models/order/order.py +77 -83
  94. investing_algorithm_framework/domain/models/order/order_status.py +2 -2
  95. investing_algorithm_framework/domain/models/order/order_type.py +1 -3
  96. investing_algorithm_framework/domain/models/portfolio/portfolio.py +81 -3
  97. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +26 -3
  98. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
  99. investing_algorithm_framework/domain/models/position/__init__.py +2 -1
  100. investing_algorithm_framework/domain/models/position/position.py +12 -0
  101. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  102. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  103. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  104. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  105. investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
  106. investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
  107. investing_algorithm_framework/domain/models/time_frame.py +37 -0
  108. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  109. investing_algorithm_framework/domain/models/time_unit.py +66 -2
  110. investing_algorithm_framework/domain/models/trade/__init__.py +8 -1
  111. investing_algorithm_framework/domain/models/trade/trade.py +295 -171
  112. investing_algorithm_framework/domain/models/trade/trade_status.py +9 -2
  113. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
  114. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
  115. investing_algorithm_framework/domain/order_executor.py +112 -0
  116. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  117. investing_algorithm_framework/domain/services/__init__.py +2 -9
  118. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +0 -6
  119. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  120. investing_algorithm_framework/domain/strategy.py +1 -29
  121. investing_algorithm_framework/domain/utils/__init__.py +12 -7
  122. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  123. investing_algorithm_framework/domain/utils/dates.py +57 -0
  124. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  125. investing_algorithm_framework/domain/utils/polars.py +53 -0
  126. investing_algorithm_framework/domain/utils/random.py +29 -0
  127. investing_algorithm_framework/download_data.py +108 -0
  128. investing_algorithm_framework/infrastructure/__init__.py +31 -18
  129. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  130. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1143 -0
  131. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  132. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  133. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  134. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +86 -12
  135. investing_algorithm_framework/infrastructure/models/__init__.py +6 -11
  136. investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -1
  137. investing_algorithm_framework/infrastructure/models/order/order.py +35 -49
  138. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  139. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  140. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +1 -1
  141. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -0
  142. investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -5
  143. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  144. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  145. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
  146. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
  147. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  148. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  149. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  150. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  151. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  152. investing_algorithm_framework/infrastructure/repositories/__init__.py +8 -0
  153. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  154. investing_algorithm_framework/infrastructure/repositories/order_repository.py +5 -0
  155. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +1 -1
  156. investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
  157. investing_algorithm_framework/infrastructure/repositories/repository.py +81 -27
  158. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  159. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
  160. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
  161. investing_algorithm_framework/infrastructure/services/__init__.py +4 -4
  162. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  163. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +113 -0
  164. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  165. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  166. investing_algorithm_framework/services/__init__.py +113 -16
  167. investing_algorithm_framework/services/backtesting/__init__.py +0 -7
  168. investing_algorithm_framework/services/backtesting/backtest_service.py +566 -359
  169. investing_algorithm_framework/services/configuration_service.py +77 -11
  170. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  171. investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
  172. investing_algorithm_framework/services/market_credential_service.py +16 -1
  173. investing_algorithm_framework/services/metrics/__init__.py +114 -0
  174. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  175. investing_algorithm_framework/services/metrics/beta.py +0 -0
  176. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  177. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  178. investing_algorithm_framework/services/metrics/drawdown.py +181 -0
  179. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  180. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  181. investing_algorithm_framework/services/metrics/generate.py +358 -0
  182. investing_algorithm_framework/services/metrics/mean_daily_return.py +83 -0
  183. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  184. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  185. investing_algorithm_framework/services/metrics/returns.py +452 -0
  186. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  187. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  188. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  189. investing_algorithm_framework/services/metrics/standard_deviation.py +157 -0
  190. investing_algorithm_framework/services/metrics/trades.py +500 -0
  191. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  192. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  193. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  194. investing_algorithm_framework/services/metrics/volatility.py +97 -0
  195. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  196. investing_algorithm_framework/services/order_service/__init__.py +3 -1
  197. investing_algorithm_framework/services/order_service/order_backtest_service.py +76 -89
  198. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  199. investing_algorithm_framework/services/order_service/order_service.py +407 -326
  200. investing_algorithm_framework/services/portfolios/__init__.py +3 -1
  201. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +37 -3
  202. investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +22 -8
  203. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  204. investing_algorithm_framework/services/portfolios/portfolio_service.py +96 -28
  205. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +97 -28
  206. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +116 -313
  207. investing_algorithm_framework/services/positions/__init__.py +7 -0
  208. investing_algorithm_framework/services/positions/position_service.py +210 -0
  209. investing_algorithm_framework/services/repository_service.py +8 -2
  210. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  211. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +113 -0
  212. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  213. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  214. investing_algorithm_framework/services/trade_service/__init__.py +7 -1
  215. investing_algorithm_framework/services/trade_service/trade_service.py +1013 -315
  216. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  217. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  218. investing_algorithm_framework-7.19.15.dist-info/METADATA +537 -0
  219. investing_algorithm_framework-7.19.15.dist-info/RECORD +263 -0
  220. investing_algorithm_framework-7.19.15.dist-info/entry_points.txt +3 -0
  221. investing_algorithm_framework/app/algorithm.py +0 -1105
  222. investing_algorithm_framework/domain/graphs.py +0 -382
  223. investing_algorithm_framework/domain/metrics/__init__.py +0 -6
  224. investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -11
  225. investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -43
  226. investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
  227. investing_algorithm_framework/domain/models/backtesting/backtest_report.py +0 -580
  228. investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -243
  229. investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
  230. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
  231. investing_algorithm_framework/domain/services/market_data_sources.py +0 -344
  232. investing_algorithm_framework/domain/services/market_service.py +0 -153
  233. investing_algorithm_framework/domain/singleton.py +0 -9
  234. investing_algorithm_framework/domain/utils/backtesting.py +0 -472
  235. investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -12
  236. investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -559
  237. investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -254
  238. investing_algorithm_framework/infrastructure/models/market_data_sources/us_treasury_yield.py +0 -47
  239. investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
  240. investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -455
  241. investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
  242. investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
  243. investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -350
  244. investing_algorithm_framework/services/backtesting/backtest_report_writer_service.py +0 -53
  245. investing_algorithm_framework/services/backtesting/graphs.py +0 -61
  246. investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -8
  247. investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -150
  248. investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -189
  249. investing_algorithm_framework/services/position_service.py +0 -31
  250. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -264
  251. investing_algorithm_framework-3.7.0.dist-info/METADATA +0 -339
  252. investing_algorithm_framework-3.7.0.dist-info/RECORD +0 -147
  253. /investing_algorithm_framework/{domain → services}/metrics/price_efficiency.py +0 -0
  254. /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
  255. {investing_algorithm_framework-3.7.0.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
  256. {investing_algorithm_framework-3.7.0.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
@@ -1,559 +0,0 @@
1
- import datetime
2
- import logging
3
- import os
4
- from datetime import timedelta
5
- from dateutil.parser import parse
6
- import polars
7
- from dateutil import parser
8
-
9
- from investing_algorithm_framework.domain import RESOURCE_DIRECTORY, \
10
- BACKTEST_DATA_DIRECTORY_NAME, DATETIME_FORMAT_BACKTESTING, \
11
- OperationalException, DATETIME_FORMAT, OHLCVMarketDataSource, \
12
- BacktestMarketDataSource, OrderBookMarketDataSource, \
13
- TickerMarketDataSource, TimeFrame
14
- from investing_algorithm_framework.infrastructure.services import \
15
- CCXTMarketService
16
-
17
- logger = logging.getLogger(__name__)
18
-
19
-
20
- class CCXTOHLCVBacktestMarketDataSource(
21
- OHLCVMarketDataSource, BacktestMarketDataSource
22
- ):
23
- """
24
- CCXTOHLCVBacktestMarketDataSource implementation using ccxt to download
25
- all data sources.
26
-
27
- This class will determine the start and end date of the data range by
28
- taking the backtest start date (e.g. 01-01-2024) and the backtest
29
- end date (e.g. 31-12-2024) in combination with the difference between
30
- start and end date. The reason for this is that the data source needs
31
- to have data on the first run (e.g. an algorithm starting on
32
- 01-01-2024 that requires 2h data for the last 17 days will
33
- need to have pulled data from 15-12-2023)
34
-
35
- To achieve this, a backtest_data_start_date attribute is used. This
36
- attribute is indexed on this calculated date.
37
- """
38
- backtest_data_directory = None
39
- backtest_data_end_date = None
40
- total_minutes_timeframe = None
41
- column_names = ["Datetime", "Open", "High", "Low", "Close", "Volume"]
42
-
43
- def __init__(
44
- self,
45
- identifier,
46
- market,
47
- symbol,
48
- timeframe,
49
- window_size=None,
50
- ):
51
- super().__init__(
52
- identifier=identifier,
53
- market=market,
54
- symbol=symbol,
55
- timeframe=timeframe,
56
- window_size=window_size,
57
- )
58
- self.data = None
59
- self._start_date_data_source = None
60
- self._end_date_data_source = None
61
-
62
- def prepare_data(
63
- self,
64
- config,
65
- backtest_start_date,
66
- backtest_end_date,
67
- **kwargs
68
- ):
69
- """
70
- Prepare data implementation of ccxt based ohlcv backtest market
71
- data source
72
-
73
- This implementation will check if the data source already exists before
74
- pulling all the data. This optimization will prevent downloading
75
- of unnecessary resources.
76
-
77
- When downloading the data it will use the ccxt library.
78
- """
79
- # Calculating the backtest data start date
80
- backtest_data_start_date = \
81
- backtest_start_date - timedelta(
82
- minutes=(
83
- (self.window_size + 1) *
84
- TimeFrame.from_value(self.timeframe).amount_of_minutes
85
- )
86
- )
87
- self.backtest_data_start_date = backtest_data_start_date\
88
- .replace(microsecond=0)
89
- self.backtest_data_index_date = backtest_data_start_date\
90
- .replace(microsecond=0)
91
- self.backtest_data_end_date = backtest_end_date.replace(microsecond=0)
92
-
93
- # Creating the backtest data directory and file
94
- self.backtest_data_directory = os.path.join(
95
- config.get(RESOURCE_DIRECTORY),
96
- config.get(BACKTEST_DATA_DIRECTORY_NAME)
97
- )
98
-
99
- if not os.path.isdir(self.backtest_data_directory):
100
- os.mkdir(self.backtest_data_directory)
101
-
102
- file_path = self._create_file_path()
103
-
104
- if not self._data_source_exists(file_path):
105
- if not os.path.isfile(file_path):
106
- try:
107
- with open(file_path, 'w') as _:
108
- pass
109
- except Exception as e:
110
- logger.error(e)
111
- raise OperationalException(
112
- f"Could not create backtest data file {file_path}"
113
- )
114
-
115
- # Get the OHLCV data from the ccxt market service
116
- market_service = CCXTMarketService(
117
- market_credential_service=self.market_credential_service,
118
- )
119
- market_service.config = config
120
- ohlcv = market_service.get_ohlcv(
121
- symbol=self.symbol,
122
- time_frame=self.timeframe.value,
123
- from_timestamp=backtest_data_start_date,
124
- to_timestamp=backtest_end_date,
125
- market=self.market
126
- )
127
- self.write_data_to_file_path(file_path, ohlcv)
128
-
129
- self.load_data()
130
-
131
- def load_data(self):
132
- file_path = self._create_file_path()
133
- self.data = polars.read_csv(file_path)
134
- first_row = self.data.head(1)
135
- last_row = self.data.tail(1)
136
- self._start_date_data_source = parse(first_row["Datetime"][0])
137
- self._end_date_data_source = parse(last_row["Datetime"][0])
138
-
139
- def _create_file_path(self):
140
- """
141
- Function to create a filename in the following format:
142
- OHLCV_{symbol}_{market}_{timeframe}_{start_date}_{end_date}.csv
143
- """
144
- symbol_string = self.symbol.replace("/", "-")
145
- time_frame_string = self.timeframe.value.replace("_", "")
146
- backtest_data_start_date = \
147
- self.backtest_data_start_date.strftime(DATETIME_FORMAT_BACKTESTING)
148
- backtest_data_end_date = \
149
- self.backtest_data_end_date.strftime(DATETIME_FORMAT_BACKTESTING)
150
- return os.path.join(
151
- self.backtest_data_directory,
152
- os.path.join(
153
- f"OHLCV_"
154
- f"{symbol_string}_"
155
- f"{self.market}_"
156
- f"{time_frame_string}_"
157
- f"{backtest_data_start_date}_"
158
- f"{backtest_data_end_date}.csv"
159
- )
160
- )
161
-
162
- def get_data(self, **kwargs):
163
- """
164
- Get data implementation of ccxt based ohlcv backtest market data
165
- source. This implementation will use polars to load and filter the
166
- data.
167
- """
168
- start_date = kwargs.get("start_date")
169
- end_date = kwargs.get("end_date")
170
- backtest_index_date = kwargs.get("backtest_index_date")
171
-
172
- if self.data is None:
173
- self.load_data()
174
-
175
- if start_date is None \
176
- and end_date is None \
177
- and backtest_index_date is None:
178
- return self.data
179
-
180
- if backtest_index_date is not None:
181
- end_date = backtest_index_date
182
- start_date = self.create_start_date(
183
- end_date, self.timeframe, self.window_size
184
- )
185
- else:
186
- if start_date is None:
187
- start_date = self.create_start_date(
188
- end_date, self.timeframe, self.window_size
189
- )
190
-
191
- if end_date is None:
192
- end_date = self.create_end_date(
193
- start_date, self.timeframe, self.window_size
194
- )
195
-
196
- if start_date < self._start_date_data_source:
197
- raise OperationalException(
198
- f"Start date {start_date} is before the start date "
199
- f"of the data source {self._start_date_data_source}"
200
- )
201
-
202
- if end_date > self._end_date_data_source:
203
- raise OperationalException(
204
- f"End date {end_date} is after the end date "
205
- f"of the data source {self._end_date_data_source}"
206
- )
207
-
208
- selection = self.data.filter(
209
- (self.data['Datetime'] >= start_date.strftime(DATETIME_FORMAT))
210
- & (self.data['Datetime'] <= end_date.strftime(DATETIME_FORMAT))
211
- )
212
- return selection
213
-
214
- def to_backtest_market_data_source(self) -> BacktestMarketDataSource:
215
- # Ignore this method for now
216
- pass
217
-
218
- def empty(self):
219
- return False
220
-
221
- @property
222
- def file_name(self):
223
- return self._create_file_path().split("/")[-1]
224
-
225
- def write_data_to_file_path(self, data_file, data: polars.DataFrame):
226
- data.write_csv(data_file)
227
-
228
-
229
- class CCXTTickerBacktestMarketDataSource(
230
- TickerMarketDataSource, BacktestMarketDataSource
231
- ):
232
- """
233
- CCXTTickerBacktestMarketDataSource implementation using ccxt to download
234
- all data sources.
235
-
236
- This class will determine the start and end date of the data range by
237
- taking the start date of the backtest minus 1 day and the end date of the
238
- backtest. The reason for this is that the data source needs
239
- to have data on the first run (e.g. an algorithm starting on
240
- 01-01-2024 that requires ticker data will need to have pulled data from
241
- 01-01-2024 - amount of minutes of the provided timeframe)
242
-
243
- To achieve this, a backtest_data_start_date attribute is used. This
244
- attribute is indexed on this calculated date.
245
- """
246
- backtest_data_directory = None
247
- backtest_data_start_date = None
248
- backtest_data_end_date = None
249
- timeframe = None
250
- column_names = ["Datetime", "Open", "High", "Low", "Close", "Volume"]
251
-
252
- def __init__(
253
- self,
254
- identifier,
255
- market,
256
- symbol=None,
257
- timeframe=None,
258
- ):
259
- super().__init__(
260
- identifier=identifier,
261
- market=market,
262
- symbol=symbol,
263
- )
264
-
265
- if timeframe is not None:
266
- self.timeframe = timeframe
267
-
268
- if self.timeframe is None:
269
- raise OperationalException(
270
- "timeframe should be set for "
271
- "CCXTTickerBacktestMarketDataSource"
272
- )
273
-
274
- def prepare_data(
275
- self,
276
- config,
277
- backtest_start_date,
278
- backtest_end_date,
279
- **kwargs
280
- ):
281
- """
282
- Prepare data implementation of ccxt based ticker backtest market
283
- data source
284
-
285
- This implementation will check if the data source already exists before
286
- pulling all the data. This optimization will prevent downloading
287
- of unnecessary resources.
288
-
289
- When downloading the data it will use the ccxt library.
290
- """
291
- total_minutes = TimeFrame.from_string(self.timeframe).amount_of_minutes
292
- self.backtest_data_start_date = \
293
- backtest_start_date - timedelta(minutes=total_minutes)
294
- self.backtest_data_end_date = backtest_end_date
295
-
296
- # Creating the backtest data directory and file
297
- self.backtest_data_directory = os.path.join(
298
- config.get(RESOURCE_DIRECTORY),
299
- config.get(BACKTEST_DATA_DIRECTORY_NAME)
300
- )
301
-
302
- if not os.path.isdir(self.backtest_data_directory):
303
- os.mkdir(self.backtest_data_directory)
304
-
305
- file_path = self._create_file_path()
306
-
307
- if not os.path.isfile(file_path):
308
- try:
309
- with open(file_path, 'w') as _:
310
- pass
311
- except Exception as e:
312
- logger.error(e)
313
- raise OperationalException(
314
- f"Could not create backtest data file {file_path}"
315
- )
316
-
317
- # Check if the data source already exists, if not download the data
318
- if not self._data_source_exists(file_path):
319
- if not os.path.isfile(file_path):
320
- try:
321
- with open(file_path, 'w') as _:
322
- pass
323
- except Exception as e:
324
- logger.error(e)
325
- raise OperationalException(
326
- f"Could not create backtest data file {file_path}"
327
- )
328
-
329
- # Get the OHLCV data from the ccxt market service
330
- market_service = CCXTMarketService(
331
- market_credential_service=self.market_credential_service
332
- )
333
- market_service.config = config
334
- ohlcv = market_service.get_ohlcv(
335
- symbol=self.symbol,
336
- time_frame=self.timeframe,
337
- from_timestamp=self.backtest_data_start_date,
338
- to_timestamp=backtest_end_date,
339
- market=self.market
340
- )
341
- self.write_data_to_file_path(file_path, ohlcv)
342
-
343
- def _create_file_path(self):
344
-
345
- if self.symbol is None or self.market is None:
346
- return None
347
-
348
- symbol_string = self.symbol.replace("/", "-")
349
- market_string = self.market.replace("/", "-")
350
- backtest_data_start_date = \
351
- self.backtest_data_start_date.strftime(DATETIME_FORMAT_BACKTESTING)
352
- backtest_data_end_date = \
353
- self.backtest_data_end_date.strftime(DATETIME_FORMAT_BACKTESTING)
354
- return os.path.join(
355
- self.backtest_data_directory,
356
- os.path.join(
357
- f"TICKER_"
358
- f"{symbol_string}_"
359
- f"{market_string}_"
360
- f"{backtest_data_start_date}_"
361
- f"{backtest_data_end_date}.csv"
362
- )
363
- )
364
-
365
- def to_backtest_market_data_source(self) -> BacktestMarketDataSource:
366
- # Ignore this method for now
367
- pass
368
-
369
- def empty(self):
370
- return False
371
-
372
- def get_data(self, **kwargs):
373
- """
374
- Get data implementation of ccxt based ticker backtest market data
375
- source
376
- """
377
- if "backtest_index_date" not in kwargs:
378
- raise OperationalException(
379
- "backtest_index_date should be passed as a parameter "
380
- "for CCXTTickerBacktestMarketDataSource"
381
- )
382
-
383
- file_path = self._create_file_path()
384
- backtest_index_date = kwargs["backtest_index_date"]
385
-
386
- # Filter the data based on the backtest index date and the end date
387
- df = polars.read_csv(file_path)
388
- filtered_df = df.filter(
389
- (df['Datetime'] >= backtest_index_date.strftime(DATETIME_FORMAT))
390
- )
391
-
392
- # If nothing is found, get all dates before the index date
393
- if len(filtered_df) == 0:
394
- filtered_df = df.filter(
395
- (df['Datetime'] <= backtest_index_date.strftime(
396
- DATETIME_FORMAT))
397
- )
398
- first_row = filtered_df.tail(1)[0]
399
- else:
400
- first_row = filtered_df.head(1)[0]
401
-
402
- first_row_datetime = parser.parse(first_row["Datetime"][0])
403
-
404
- # Calculate the bid and ask price based on the high and low price
405
- return {
406
- "symbol": self.symbol,
407
- "bid": float((first_row["Low"][0])
408
- + float(first_row["High"][0]))/2,
409
- "ask": float((first_row["Low"][0])
410
- + float(first_row["High"][0]))/2,
411
- "datetime": first_row_datetime,
412
- }
413
-
414
- def write_data_to_file_path(self, data_file, data: polars.DataFrame):
415
- data.write_csv(data_file)
416
-
417
-
418
- class CCXTOHLCVMarketDataSource(OHLCVMarketDataSource):
419
- """
420
- CCXTOHLCVMarketDataSource implementation of OHLCVMarketDataSource using
421
- ccxt to download all ohlcv data sources.
422
- """
423
-
424
- def get_data(self, **kwargs):
425
- """
426
- Implementation of get_data for CCXTOHLCVMarketDataSource.
427
- This implementation uses the CCXTMarketService to get the OHLCV data.
428
-
429
- In the kwargs, the start_date should be set as a datetime object.
430
-
431
- returns a polars.DataFrame with the OHLCV data
432
- """
433
- market_service = CCXTMarketService(
434
- market_credential_service=self.market_credential_service,
435
- )
436
-
437
- # Add config if present
438
- if self.config is not None:
439
- market_service.config = self.config
440
-
441
- if "start_date" in kwargs:
442
- start_date = kwargs["start_date"]
443
-
444
- if not isinstance(start_date, datetime.datetime):
445
- raise OperationalException(
446
- "start_date should be a datetime object"
447
- )
448
- else:
449
- raise OperationalException(
450
- "start_date should be set for CCXTOHLCVMarketDataSource"
451
- )
452
-
453
- if "end_date" not in kwargs:
454
- end_date = self.create_end_date(
455
- start_date, self.timeframe, self.window_size
456
- )
457
- else:
458
- end_date = kwargs["end_date"]
459
-
460
- if not isinstance(end_date, datetime.datetime):
461
- raise OperationalException(
462
- "end_date should be a datetime object"
463
- )
464
-
465
- if not isinstance(start_date, datetime.datetime):
466
- raise OperationalException(
467
- "start_date should be a datetime object"
468
- )
469
-
470
- return market_service.get_ohlcv(
471
- symbol=self.symbol,
472
- time_frame=self.timeframe,
473
- from_timestamp=start_date,
474
- to_timestamp=end_date,
475
- market=self.market
476
- )
477
-
478
- def to_backtest_market_data_source(self) -> BacktestMarketDataSource:
479
- return CCXTOHLCVBacktestMarketDataSource(
480
- identifier=self.identifier,
481
- market=self.market,
482
- symbol=self.symbol,
483
- timeframe=self.timeframe,
484
- window_size=self.window_size
485
- )
486
-
487
-
488
- class CCXTOrderBookMarketDataSource(OrderBookMarketDataSource):
489
-
490
- def get_data(self, **kwargs):
491
- market_service = CCXTMarketService(
492
- market_credential_service=self.market_credential_service
493
- )
494
- market_service.config = self.config
495
- return market_service.get_order_book(
496
- symbol=self.symbol, market=self.market
497
- )
498
-
499
- def to_backtest_market_data_source(self) -> BacktestMarketDataSource:
500
- pass
501
-
502
-
503
- class CCXTTickerMarketDataSource(TickerMarketDataSource):
504
-
505
- def __init__(
506
- self,
507
- identifier,
508
- market,
509
- symbol=None,
510
- backtest_timeframe=None,
511
-
512
- ):
513
- super().__init__(
514
- identifier=identifier,
515
- market=market,
516
- symbol=symbol,
517
- )
518
- self._backtest_timeframe = backtest_timeframe
519
-
520
- def get_data(self, **kwargs):
521
- market_service = CCXTMarketService(
522
- market_credential_service=self.market_credential_service
523
- )
524
- market_service.config = self.config
525
-
526
- if self.market is None:
527
-
528
- if "market" not in kwargs:
529
- raise OperationalException(
530
- "Either market or market should be "
531
- "passed as a parameter"
532
- )
533
- else:
534
- market = kwargs["market"]
535
- else:
536
- market = self.market
537
-
538
- market_service.market = market
539
-
540
- if self.symbol is None:
541
-
542
- if "symbol" not in kwargs:
543
- raise OperationalException(
544
- "Either symbol or symbol should be passed as a parameter"
545
- )
546
- else:
547
- symbol = kwargs["symbol"]
548
- else:
549
- symbol = self.symbol
550
-
551
- return market_service.get_ticker(symbol=symbol, market=market)
552
-
553
- def to_backtest_market_data_source(self) -> BacktestMarketDataSource:
554
- return CCXTTickerBacktestMarketDataSource(
555
- identifier=self.identifier,
556
- market=self.market,
557
- symbol=self.symbol,
558
- timeframe=self._backtest_timeframe,
559
- )