investing-algorithm-framework 6.9.1__py3-none-any.whl → 7.19.15__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of investing-algorithm-framework might be problematic. Click here for more details.

Files changed (192) hide show
  1. investing_algorithm_framework/__init__.py +147 -44
  2. investing_algorithm_framework/app/__init__.py +23 -6
  3. investing_algorithm_framework/app/algorithm/algorithm.py +5 -41
  4. investing_algorithm_framework/app/algorithm/algorithm_factory.py +17 -10
  5. investing_algorithm_framework/app/analysis/__init__.py +15 -0
  6. investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
  7. investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
  8. investing_algorithm_framework/app/analysis/permutation.py +116 -0
  9. investing_algorithm_framework/app/analysis/ranking.py +297 -0
  10. investing_algorithm_framework/app/app.py +1322 -707
  11. investing_algorithm_framework/app/context.py +196 -88
  12. investing_algorithm_framework/app/eventloop.py +590 -0
  13. investing_algorithm_framework/app/reporting/__init__.py +16 -5
  14. investing_algorithm_framework/app/reporting/ascii.py +57 -202
  15. investing_algorithm_framework/app/reporting/backtest_report.py +284 -170
  16. investing_algorithm_framework/app/reporting/charts/__init__.py +10 -2
  17. investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
  18. investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
  19. investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +11 -26
  20. investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
  21. investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
  22. investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +1 -1
  23. investing_algorithm_framework/app/reporting/generate.py +100 -114
  24. investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +40 -32
  25. investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +34 -27
  26. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +23 -19
  27. investing_algorithm_framework/app/reporting/tables/trades_table.py +1 -1
  28. investing_algorithm_framework/app/reporting/tables/utils.py +1 -0
  29. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +10 -16
  30. investing_algorithm_framework/app/strategy.py +315 -175
  31. investing_algorithm_framework/app/task.py +5 -3
  32. investing_algorithm_framework/cli/cli.py +30 -12
  33. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +131 -34
  34. investing_algorithm_framework/cli/initialize_app.py +20 -1
  35. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +18 -6
  36. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  37. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  38. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -2
  39. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +1 -1
  40. investing_algorithm_framework/create_app.py +3 -5
  41. investing_algorithm_framework/dependency_container.py +25 -39
  42. investing_algorithm_framework/domain/__init__.py +45 -38
  43. investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
  44. investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
  45. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
  46. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
  47. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
  48. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  49. investing_algorithm_framework/domain/backtesting/backtest_run.py +605 -0
  50. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  51. investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
  52. investing_algorithm_framework/domain/config.py +27 -0
  53. investing_algorithm_framework/domain/constants.py +6 -34
  54. investing_algorithm_framework/domain/data_provider.py +200 -56
  55. investing_algorithm_framework/domain/exceptions.py +34 -1
  56. investing_algorithm_framework/domain/models/__init__.py +10 -19
  57. investing_algorithm_framework/domain/models/base_model.py +0 -6
  58. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  59. investing_algorithm_framework/domain/models/data/data_source.py +214 -0
  60. investing_algorithm_framework/domain/models/{market_data_type.py → data/data_type.py} +7 -7
  61. investing_algorithm_framework/domain/models/market/market_credential.py +6 -0
  62. investing_algorithm_framework/domain/models/order/order.py +34 -13
  63. investing_algorithm_framework/domain/models/order/order_status.py +1 -1
  64. investing_algorithm_framework/domain/models/order/order_type.py +1 -1
  65. investing_algorithm_framework/domain/models/portfolio/portfolio.py +14 -1
  66. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +5 -1
  67. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +51 -11
  68. investing_algorithm_framework/domain/models/position/__init__.py +2 -1
  69. investing_algorithm_framework/domain/models/position/position.py +9 -0
  70. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  71. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  72. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  73. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  74. investing_algorithm_framework/domain/models/snapshot_interval.py +0 -1
  75. investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
  76. investing_algorithm_framework/domain/models/time_frame.py +7 -0
  77. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  78. investing_algorithm_framework/domain/models/time_unit.py +63 -1
  79. investing_algorithm_framework/domain/models/trade/__init__.py +0 -2
  80. investing_algorithm_framework/domain/models/trade/trade.py +56 -32
  81. investing_algorithm_framework/domain/models/trade/trade_status.py +8 -2
  82. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +106 -41
  83. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +161 -99
  84. investing_algorithm_framework/domain/order_executor.py +19 -0
  85. investing_algorithm_framework/domain/portfolio_provider.py +20 -1
  86. investing_algorithm_framework/domain/services/__init__.py +0 -13
  87. investing_algorithm_framework/domain/strategy.py +1 -29
  88. investing_algorithm_framework/domain/utils/__init__.py +5 -1
  89. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  90. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  91. investing_algorithm_framework/domain/utils/polars.py +17 -14
  92. investing_algorithm_framework/download_data.py +40 -10
  93. investing_algorithm_framework/infrastructure/__init__.py +13 -25
  94. investing_algorithm_framework/infrastructure/data_providers/__init__.py +7 -4
  95. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +811 -546
  96. investing_algorithm_framework/infrastructure/data_providers/csv.py +433 -122
  97. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  98. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  99. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +81 -0
  100. investing_algorithm_framework/infrastructure/models/__init__.py +0 -13
  101. investing_algorithm_framework/infrastructure/models/order/order.py +9 -3
  102. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +27 -8
  103. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +21 -7
  104. investing_algorithm_framework/infrastructure/order_executors/__init__.py +2 -0
  105. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  106. investing_algorithm_framework/infrastructure/repositories/repository.py +16 -2
  107. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +2 -2
  108. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +6 -0
  109. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +6 -0
  110. investing_algorithm_framework/infrastructure/services/__init__.py +0 -4
  111. investing_algorithm_framework/services/__init__.py +105 -8
  112. investing_algorithm_framework/services/backtesting/backtest_service.py +536 -476
  113. investing_algorithm_framework/services/configuration_service.py +14 -4
  114. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  115. investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
  116. investing_algorithm_framework/{app/reporting → services}/metrics/__init__.py +48 -17
  117. investing_algorithm_framework/{app/reporting → services}/metrics/drawdown.py +10 -10
  118. investing_algorithm_framework/{app/reporting → services}/metrics/equity_curve.py +2 -2
  119. investing_algorithm_framework/{app/reporting → services}/metrics/exposure.py +60 -2
  120. investing_algorithm_framework/services/metrics/generate.py +358 -0
  121. investing_algorithm_framework/{app/reporting → services}/metrics/profit_factor.py +36 -0
  122. investing_algorithm_framework/{app/reporting → services}/metrics/recovery.py +2 -2
  123. investing_algorithm_framework/{app/reporting → services}/metrics/returns.py +146 -147
  124. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  125. investing_algorithm_framework/{app/reporting/metrics/sharp_ratio.py → services/metrics/sharpe_ratio.py} +6 -10
  126. investing_algorithm_framework/{app/reporting → services}/metrics/sortino_ratio.py +3 -7
  127. investing_algorithm_framework/services/metrics/trades.py +500 -0
  128. investing_algorithm_framework/services/metrics/volatility.py +97 -0
  129. investing_algorithm_framework/{app/reporting → services}/metrics/win_rate.py +70 -3
  130. investing_algorithm_framework/services/order_service/order_backtest_service.py +21 -31
  131. investing_algorithm_framework/services/order_service/order_service.py +9 -71
  132. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +0 -2
  133. investing_algorithm_framework/services/portfolios/portfolio_service.py +3 -13
  134. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +62 -96
  135. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +0 -3
  136. investing_algorithm_framework/services/repository_service.py +5 -2
  137. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  138. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +113 -0
  139. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  140. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  141. investing_algorithm_framework/services/trade_service/__init__.py +7 -1
  142. investing_algorithm_framework/services/trade_service/trade_service.py +51 -29
  143. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  144. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  145. investing_algorithm_framework-7.19.15.dist-info/METADATA +537 -0
  146. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/RECORD +159 -148
  147. investing_algorithm_framework/app/reporting/evaluation.py +0 -243
  148. investing_algorithm_framework/app/reporting/metrics/risk_free_rate.py +0 -8
  149. investing_algorithm_framework/app/reporting/metrics/volatility.py +0 -69
  150. investing_algorithm_framework/cli/templates/requirements_azure_function.txt.template +0 -3
  151. investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -9
  152. investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -47
  153. investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
  154. investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -0
  155. investing_algorithm_framework/domain/models/backtesting/backtest_results.py +0 -440
  156. investing_algorithm_framework/domain/models/data_source.py +0 -21
  157. investing_algorithm_framework/domain/models/date_range.py +0 -64
  158. investing_algorithm_framework/domain/models/trade/trade_risk_type.py +0 -34
  159. investing_algorithm_framework/domain/models/trading_data_types.py +0 -48
  160. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
  161. investing_algorithm_framework/domain/services/market_data_sources.py +0 -543
  162. investing_algorithm_framework/domain/services/market_service.py +0 -153
  163. investing_algorithm_framework/domain/services/observable.py +0 -51
  164. investing_algorithm_framework/domain/services/observer.py +0 -19
  165. investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -16
  166. investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -746
  167. investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -270
  168. investing_algorithm_framework/infrastructure/models/market_data_sources/pandas.py +0 -312
  169. investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
  170. investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -471
  171. investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
  172. investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
  173. investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -322
  174. investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -10
  175. investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -269
  176. investing_algorithm_framework/services/market_data_source_service/data_provider_service.py +0 -350
  177. investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -377
  178. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -296
  179. investing_algorithm_framework-6.9.1.dist-info/METADATA +0 -440
  180. /investing_algorithm_framework/{app/reporting → services}/metrics/alpha.py +0 -0
  181. /investing_algorithm_framework/{app/reporting → services}/metrics/beta.py +0 -0
  182. /investing_algorithm_framework/{app/reporting → services}/metrics/cagr.py +0 -0
  183. /investing_algorithm_framework/{app/reporting → services}/metrics/calmar_ratio.py +0 -0
  184. /investing_algorithm_framework/{app/reporting → services}/metrics/mean_daily_return.py +0 -0
  185. /investing_algorithm_framework/{app/reporting → services}/metrics/price_efficiency.py +0 -0
  186. /investing_algorithm_framework/{app/reporting → services}/metrics/standard_deviation.py +0 -0
  187. /investing_algorithm_framework/{app/reporting → services}/metrics/treynor_ratio.py +0 -0
  188. /investing_algorithm_framework/{app/reporting → services}/metrics/ulcer.py +0 -0
  189. /investing_algorithm_framework/{app/reporting → services}/metrics/value_at_risk.py +0 -0
  190. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
  191. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
  192. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/entry_points.txt +0 -0
@@ -1,322 +0,0 @@
1
- import logging
2
-
3
- from investing_algorithm_framework.domain import OrderStatus, OrderSide, \
4
- TradeStatus
5
-
6
- logger = logging.getLogger(__name__)
7
-
8
-
9
- class PerformanceService:
10
- """
11
- Service to calculate the performance of a portfolio.
12
- """
13
-
14
- def __init__(
15
- self,
16
- order_repository,
17
- position_repository,
18
- portfolio_repository,
19
- trade_repository,
20
- ):
21
- self.order_repository = order_repository
22
- self.position_repository = position_repository
23
- self.portfolio_repository = portfolio_repository
24
- self.trade_repository = trade_repository
25
-
26
- def get_total_net_gain(self, portfolio_id):
27
- pass
28
-
29
- def get_total_size(self, portfolio_id):
30
- pass
31
-
32
- def get_percentage_change(self, portfolio_id, time_frame):
33
- pass
34
-
35
- def get_total_cost(self, portfolio_id):
36
- pass
37
-
38
- def get_number_of_trades_closed(self, portfolio_id):
39
- """"
40
- Get the number of trades closed. This function will
41
- return the number of trades that are already closed.
42
-
43
- param portfolio_id: The id of the portfolio
44
- type portfolio_id: str
45
-
46
- return: The number of trades closed
47
- """
48
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
49
- return self.trade_repository.count(
50
- {"portfolio_id": portfolio.id, "status": TradeStatus.OPEN.value}
51
- )
52
-
53
- def get_number_of_trades_open(self, portfolio_id):
54
- """
55
- Get the number of trades open. This function will
56
- return the number of trades that are still open.
57
-
58
- param portfolio_id: The id of the portfolio
59
- type portfolio_id: str
60
-
61
- return: The number of trades open
62
- """
63
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
64
- return self.trade_repository.count(
65
- {"portfolio_id": portfolio.id, "status": TradeStatus.OPEN.value}
66
- )
67
-
68
- def get_percentage_positive_trades(self, portfolio_id):
69
- """
70
- Get the percentage of positive trades. This function will
71
- calculate the percentage of positive trades by dividing the
72
- total number of positive trades by the total number of trades
73
- and then multiplying it by 100.
74
-
75
- param portfolio_id: The id of the portfolio
76
- type portfolio_id: str
77
-
78
- return: The percentage of positive trades
79
- """
80
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
81
- trades = self.trade_repository.get_all(
82
- {"portfolio_id": portfolio.id, "status": OrderStatus.CLOSED.value}
83
- )
84
- total_number_of_trades = len(trades)
85
-
86
- if total_number_of_trades == 0:
87
- return 0.0
88
-
89
- positive_trades = [
90
- trade for trade in trades if trade.net_gain > 0
91
- ]
92
- total_number_of_positive_trades = len(positive_trades)
93
- return total_number_of_positive_trades / total_number_of_trades * 100
94
-
95
- def get_percentage_negative_trades(self, portfolio_id):
96
- """
97
- Get the percentage of negative trades. This function will
98
- calculate the percentage of negative trades by dividing the
99
- total number of negative trades by the total number of trades
100
- and then multiplying it by 100.
101
-
102
- param portfolio_id: The id of the portfolio
103
- type portfolio_id: str
104
-
105
- return: The percentage of negative trades
106
- """
107
-
108
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
109
- trades = self.trade_repository.get_all(
110
- {"portfolio_id": portfolio.id, "status": TradeStatus.CLOSED.value}
111
- )
112
- total_number_of_trades = len(trades)
113
-
114
- if total_number_of_trades == 0:
115
- return 0.0
116
-
117
- negative_trades = [
118
- trade for trade in trades if trade.net_gain < 0
119
- ]
120
- total_number_of_negative_trades = len(negative_trades)
121
- return total_number_of_negative_trades / total_number_of_trades * 100
122
-
123
- def get_growth_rate_of_backtest(
124
- self, portfolio_id, tickers, backtest_profile
125
- ):
126
- """
127
- Get the growth rate of the backtest. This function will
128
- calculate the total value of the portfolio and then
129
- calculate the growth rate of the portfolio.
130
-
131
- param portfolio_id: The id of the portfolio
132
- type portfolio_id: str
133
- param tickers: list of tickers of all the used symbols
134
- type tickers: dict
135
-
136
- return: The growth rate of the backtest
137
- """
138
- total_value = self.get_total_value(
139
- portfolio_id, tickers, backtest_profile
140
- )
141
- gain = total_value - backtest_profile.initial_unallocated
142
- return gain / backtest_profile.initial_unallocated * 100
143
-
144
- def get_growth_of_backtest(self, portfolio_id, tickers, backtest_profile):
145
- """
146
- Get the growth of the backtest. This function will
147
- calculate the total value of the portfolio and then
148
- calculate the growth of the portfolio.
149
-
150
- param portfolio_id: The id of the portfolio
151
- type portfolio_id: str
152
- param tickers: The tickers of the market
153
- type tickers: dict
154
-
155
- return: The growth of the backtest
156
- """
157
- total_value = self.get_total_value(
158
- portfolio_id, tickers, backtest_profile
159
- )
160
- return total_value - backtest_profile.initial_unallocated
161
-
162
- def get_total_net_gain_percentage_of_backtest(
163
- self, portfolio_id, backtest_profile
164
- ):
165
- """
166
- Get the total net gain percentage of the backtest. This function
167
- will calculate the total net gain percentage of the portfolio
168
- by dividing the total net gain by the initial unallocated value
169
- of the portfolio and then multiplying it by 100.
170
-
171
- param portfolio_id: The id of the portfolio
172
- type portfolio_id: str
173
- param backtest_profile: The backtest profile
174
- type backtest_profile: BacktestProfile
175
-
176
- return: The total net gain percentage of the backtest
177
- """
178
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
179
-
180
- if portfolio.total_net_gain == 0:
181
- return 0
182
-
183
- return portfolio.total_net_gain \
184
- / backtest_profile.initial_unallocated * 100
185
-
186
- def get_total_value(self, portfolio_id, tickers, backtest_profile):
187
- """
188
- Get the total value of the portfolio. This functions
189
- will calculate the allocated value, pending buy value,
190
- pending sell value and unallocated value.
191
-
192
- At the end, it will sum all these values and return the
193
- total value of the portfolio.
194
-
195
- param portfolio_id: The id of the portfolio
196
- type portfolio_id: str
197
- param tickers: The tickers of the market
198
- type tickers: dict
199
- param backtest_profile: The backtest profile
200
- type backtest_profile: BacktestProfile
201
-
202
- return: The total value of the portfolio
203
- rtype: float
204
- """
205
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
206
- positions = self.position_repository.get_all(
207
- {"portfolio_id": portfolio.id}
208
- )
209
- allocated = 0
210
-
211
- for position in positions:
212
-
213
- if position.symbol == portfolio.trading_symbol:
214
- continue
215
-
216
- ticker_symbol = f"{position.symbol.upper()}" \
217
- f"/{portfolio.trading_symbol.upper()}"
218
- if ticker_symbol not in tickers:
219
- logger.warning(
220
- f"Symbol {position.symbol} not found in tickers, "
221
- f"cannot calculate the total value of the position"
222
- )
223
- continue
224
-
225
- allocated += position.amount * tickers[ticker_symbol]["bid"]
226
-
227
- # Calculate the pending sell value
228
- pending_sell_orders = self.order_repository.get_all(
229
- {
230
- "portfolio_id": portfolio.id,
231
- "status": OrderStatus.OPEN.value,
232
- "order_side": OrderSide.SELL.value,
233
- }
234
- )
235
-
236
- for order in pending_sell_orders:
237
-
238
- if order.get_symbol() in tickers:
239
- allocated += order.get_amount() \
240
- * tickers[order.get_symbol()]["bid"]
241
- else:
242
- logger.warning(
243
- f"Symbol {order.get_symbol()} not found in tickers, "
244
- f"cannot calculate the total value of sell orders"
245
- )
246
-
247
- # Calculate the unallocated value by summing the unallocated and
248
- # pending buy value
249
- unallocated = portfolio.unallocated
250
- pending_buy_orders = self.order_repository.get_all(
251
- {
252
- "portfolio_id": portfolio.id,
253
- "status": OrderStatus.OPEN.value,
254
- "order_side": OrderSide.BUY.value,
255
- }
256
- )
257
-
258
- for order in pending_buy_orders:
259
- if order.get_symbol() in tickers:
260
- unallocated += order.get_amount() \
261
- * tickers[order.get_symbol()]["ask"]
262
- else:
263
- logger.warning(
264
- f"Symbol {order.get_symbol()} not found in tickers, "
265
- f"cannot calculate the total value of buy orders"
266
- )
267
-
268
- # Add everything together
269
- return allocated + unallocated
270
-
271
- def get_average_trade_duration(self, portfolio_id):
272
- """
273
- Get the average trade duration. This function will
274
- calculate the average trade duration by summing the
275
- duration of all the trades and then dividing it by the
276
- total number of trades.
277
-
278
- param portfolio_id: The id of the portfolio
279
- type portfolio_id: str
280
-
281
- return: The average trade duration
282
- """
283
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
284
- trades = self.trade_repository.get_all(
285
- {"portfolio_id": portfolio.id, "status": TradeStatus.CLOSED.value}
286
- )
287
-
288
- if len(trades) == 0:
289
- return 0
290
-
291
- total_duration = 0
292
-
293
- for trade in trades:
294
- duration = trade.closed_at - trade.opened_at
295
- total_duration += duration.total_seconds() / 3600
296
-
297
- return total_duration / len(trades)
298
-
299
- def get_average_trade_size(self, portfolio_id):
300
- """
301
- Get the average trade size. This function will calculate
302
- the average trade size by summing the size of all the trades
303
- and then dividing it by the total number of trades.
304
-
305
- param portfolio_id: The id of the portfolio
306
- type portfolio_id: str
307
-
308
- return: The average trade size
309
- """
310
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
311
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
312
- trades = self.trade_repository.get_all({"portfolio_id": portfolio.id})
313
-
314
- if len(trades) == 0:
315
- return 0
316
-
317
- total_size = 0
318
-
319
- for trade in trades:
320
- total_size += trade.amount * trade.open_price
321
-
322
- return total_size / len(trades)
@@ -1,10 +0,0 @@
1
- from .backtest_market_data_source_service import \
2
- BacktestMarketDataSourceService
3
- from .market_data_source_service import MarketDataSourceService
4
- from .data_provider_service import DataProviderService
5
-
6
- __all__ = [
7
- "MarketDataSourceService",
8
- "BacktestMarketDataSourceService",
9
- "DataProviderService"
10
- ]
@@ -1,269 +0,0 @@
1
- from typing import List
2
-
3
- from tqdm import tqdm
4
-
5
- from investing_algorithm_framework.domain import MarketService, \
6
- BacktestMarketDataSource, BACKTESTING_END_DATE, BACKTESTING_START_DATE, \
7
- BACKTESTING_INDEX_DATETIME, OperationalException, OHLCVMarketDataSource, \
8
- TickerMarketDataSource, OrderBookMarketDataSource, MarketDataType, \
9
- TimeFrame
10
- from investing_algorithm_framework.services.configuration_service import \
11
- ConfigurationService
12
- from investing_algorithm_framework.services.market_credential_service \
13
- import MarketCredentialService
14
- from .market_data_source_service import MarketDataSourceService
15
-
16
-
17
- class BacktestMarketDataSourceService(MarketDataSourceService):
18
- """
19
- BacktestMarketDataSourceService is a subclass of MarketDataSourceService.
20
- It is used to create market data sources for backtesting.
21
-
22
- In the constructor, it takes a list of BacktestMarketDataSource objects.
23
- These objects are used to prepare the data for backtesting.
24
-
25
- The prepare_data method of BacktestMarketDataSource is called in the
26
- constructor.
27
-
28
- The main difference between MarketDataSourceService and
29
- the BacktestMarketDataSourceService is that it will
30
- prepare the data for backtesting in the constructor.
31
- """
32
- def __init__(
33
- self,
34
- market_service: MarketService,
35
- market_credential_service: MarketCredentialService,
36
- configuration_service: ConfigurationService,
37
- market_data_sources: List[BacktestMarketDataSource] = None
38
- ):
39
- super().__init__(
40
- market_service=market_service,
41
- market_data_sources=None,
42
- market_credential_service=market_credential_service,
43
- configuration_service=configuration_service
44
- )
45
- self.market_data_sources = []
46
-
47
- # Add all market data sources to the list
48
- if market_data_sources is not None:
49
- for market_data_source in market_data_sources:
50
- self.add(market_data_source)
51
-
52
- def initialize_market_data_sources(self):
53
- config = self._configuration_service.get_config()
54
- backtest_start_date = config[BACKTESTING_START_DATE]
55
- backtest_end_date = config[BACKTESTING_END_DATE]
56
- backtest_market_data_sources = [
57
- market_data_source.to_backtest_market_data_source() for
58
- market_data_source in self._market_data_sources
59
- ]
60
-
61
- # Filter out the None values
62
- backtest_market_data_sources = [
63
- market_data_source for market_data_source in
64
- backtest_market_data_sources if market_data_source is not None
65
- ]
66
-
67
- for backtest_market_data_source in tqdm(
68
- backtest_market_data_sources,
69
- total=len(self._market_data_sources),
70
- desc="Preparing backtest market data",
71
- colour="GREEN"
72
- ):
73
- backtest_market_data_source.market_credential_service = \
74
- self._market_credential_service
75
- backtest_market_data_source.prepare_data(
76
- config=config,
77
- backtest_start_date=backtest_start_date,
78
- backtest_end_date=backtest_end_date
79
- )
80
-
81
- self.clear_market_data_sources()
82
- self.market_data_sources = backtest_market_data_sources
83
-
84
- def get_data(self, identifier):
85
- """
86
- This method is used to get the data for backtesting. It loops
87
- over all the backtest market data sources and returns the data
88
- for the given identifier (If there is a match).
89
-
90
- If there is no match, it raises an OperationalException.
91
-
92
- Args:
93
- identifier: The identifier of the market data source
94
-
95
- Returns:
96
- The data for the given identifier
97
- """
98
- config = self._configuration_service.get_config()
99
- backtest_index_date = config[BACKTESTING_INDEX_DATETIME]
100
-
101
- for market_data_source in self._market_data_sources:
102
-
103
- if market_data_source.get_identifier() == identifier:
104
- config = self._configuration_service.get_config()
105
- backtest_index_date = config[BACKTESTING_INDEX_DATETIME]
106
- data = market_data_source.get_data(
107
- date=backtest_index_date, config=config
108
- )
109
-
110
- result = {
111
- "data": data,
112
- "type": None,
113
- "symbol": None,
114
- "time_frame": None
115
- }
116
-
117
- # Add metadata to the data
118
- if isinstance(market_data_source, OHLCVMarketDataSource):
119
- result["type"] = MarketDataType.OHLCV
120
- time_frame = TimeFrame.from_value(
121
- market_data_source.time_frame
122
- )
123
- result["time_frame"] = time_frame.value
124
- result["symbol"] = market_data_source.symbol
125
- return result
126
-
127
- if isinstance(market_data_source, TickerMarketDataSource):
128
- result["type"] = MarketDataType.TICKER
129
- result["time_frame"] = TimeFrame.CURRENT
130
- result["symbol"] = market_data_source.symbol
131
- return result
132
-
133
- if isinstance(market_data_source, OrderBookMarketDataSource):
134
- result["type"] = MarketDataType.ORDER_BOOK
135
- result["time_frame"] = TimeFrame.CURRENT
136
- result["symbol"] = market_data_source.symbol
137
- return result
138
-
139
- result["type"] = MarketDataType.CUSTOM
140
- result["time_frame"] = TimeFrame.CURRENT
141
- result["symbol"] = market_data_source.symbol
142
- return result
143
-
144
- raise OperationalException(
145
- f"Backtest market data source not found for {identifier}"
146
- )
147
-
148
- def get_ticker(self, symbol, market=None):
149
- ticker_market_data_source = self.get_ticker_market_data_source(
150
- symbol=symbol, market=market
151
- )
152
- ohlcv_market_data_source = self.get_ohlcv_market_data_source(
153
- symbol=symbol, market=market
154
- )
155
- if ohlcv_market_data_source is not None and \
156
- ticker_market_data_source is None:
157
- data = ohlcv_market_data_source.get_data(
158
- date=self._configuration_service.get_config()[
159
- BACKTESTING_INDEX_DATETIME
160
- ],
161
- config=self._configuration_service.get_config()
162
- )
163
-
164
- # Get last row of the OHLCV data polars dataframe
165
- close = data.tail(1)["Close"][0]
166
- timestamp = data.tail(1)["Datetime"][0]
167
- open = data.tail(1)["Open"][0]
168
- high = data.tail(1)["High"][0]
169
- low = data.tail(1)["Low"][0]
170
- volume = data.tail(1)["Volume"][0]
171
-
172
- spread = close * 0.001
173
- bid = close - spread
174
- ask = close + spread
175
-
176
- ticker_data = {
177
- "symbol": symbol,
178
- "market": market,
179
- "timestamp": timestamp,
180
- # or the appropriate time column
181
- "open": open,
182
- "high": high,
183
- "low": low,
184
- "close": close,
185
- "last": close,
186
- "volume": volume,
187
- "bid": bid,
188
- "ask": ask,
189
- }
190
- return ticker_data
191
-
192
- if ticker_market_data_source is None:
193
- identifiers = self.get_market_data_source_identifiers()
194
- raise OperationalException(
195
- "Backtest ticker data source "
196
- f"not found for {symbol} and market {market}, the available "
197
- f"data sources are: {identifiers}"
198
- )
199
-
200
- config = self._configuration_service.get_config()
201
- backtest_index_date = config[BACKTESTING_INDEX_DATETIME]
202
- return ticker_market_data_source.get_data(
203
- date=backtest_index_date, config=config
204
- )
205
-
206
- def get_order_book(self, symbol, market):
207
- market_data_source = self.get_order_book_market_data_source(
208
- symbol=symbol, market=market
209
- )
210
- market_data_source.market_credential_service = \
211
- self._market_credential_service
212
- config = self._configuration_service.get_config()
213
- backtest_index_date = config[BACKTESTING_INDEX_DATETIME]
214
- return market_data_source.get_data(
215
- date=backtest_index_date, config=config
216
- )
217
-
218
- def get_ohlcv(
219
- self,
220
- symbol,
221
- from_timestamp,
222
- time_frame=None,
223
- market=None,
224
- to_timestamp=None
225
- ):
226
- market_data_source = self.get_ohlcv_market_data_source(
227
- symbol=symbol, market=market, time_frame=time_frame
228
- )
229
- market_data_source.market_credential_service = \
230
- self._market_credential_service
231
- config = self._configuration_service.get_config()
232
- backtest_index_date = config[BACKTESTING_INDEX_DATETIME]
233
- return market_data_source.get_data(
234
- date=backtest_index_date, config=config
235
- )
236
-
237
- def is_ohlcv_data_source_present(self, symbol, time_frame, market):
238
- market_data_source = self.get_ohlcv_market_data_source(
239
- symbol=symbol, market=market, time_frame=time_frame
240
- )
241
- return market_data_source is not None
242
-
243
- def add(self, market_data_source):
244
-
245
- if market_data_source is None:
246
- return
247
-
248
- # Check if there is already a market data source with the same
249
- # identifier
250
- for existing_market_data_source in self._market_data_sources:
251
- if existing_market_data_source.get_identifier() == \
252
- market_data_source.get_identifier():
253
- return
254
-
255
- self._market_data_sources.append(market_data_source)
256
-
257
- def has_ticker_market_data_source(self, symbol, market=None):
258
- ticker_market_data_source = self.get_ticker_market_data_source(
259
- symbol=symbol, market=market
260
- )
261
- ohlcv_market_data_source = self.get_ohlcv_market_data_source(
262
- symbol=symbol, market=market
263
- )
264
-
265
- if ohlcv_market_data_source is not None or \
266
- ticker_market_data_source is not None:
267
- return True
268
-
269
- return False