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
@@ -1,410 +0,0 @@
1
- import logging
2
- from datetime import datetime
3
- from time import sleep
4
-
5
- import ccxt
6
-
7
- from investing_algorithm_framework.domain import OperationalException, \
8
- OHLCV, AssetPrice, Order, CCXT_DATETIME_FORMAT
9
-
10
- logger = logging.getLogger("investing_algorithm_framework")
11
-
12
-
13
- class MarketService:
14
- exchange = None
15
- _market = None
16
- api_key = None,
17
- secret_key = None
18
- exchange_class = None
19
- msec = 1000
20
- minute = 60 * msec
21
-
22
- @property
23
- def market(self):
24
- return self._market
25
-
26
- @market.setter
27
- def market(self, value):
28
-
29
- if not isinstance(value, str):
30
- raise OperationalException("Market must be a string")
31
-
32
- self._market = value.lower()
33
-
34
- if not hasattr(ccxt, self._market):
35
- raise OperationalException(
36
- f"No market service found for market id {self._market}"
37
- )
38
-
39
- self.exchange_class = getattr(ccxt, self._market)
40
- self.exchange = self.exchange_class()
41
-
42
- def initialize(self, portfolio_configuration):
43
- self.api_key = portfolio_configuration.api_key
44
- self.secret_key = portfolio_configuration.secret_key
45
- self._market = portfolio_configuration.market
46
-
47
- if not hasattr(ccxt, self.market):
48
- raise OperationalException(
49
- f"No market service found for market id {self.market}"
50
- )
51
-
52
- self.exchange_class = getattr(ccxt, self.market)
53
-
54
- if self.exchange_class is None:
55
- raise OperationalException(
56
- f"No market service found for market id {self.market}"
57
- )
58
-
59
- if self.api_key is not None or self.secret_key is not None:
60
- self.exchange = self.exchange_class({
61
- 'apiKey': self.api_key,
62
- 'secret': self.secret_key,
63
- })
64
- else:
65
- self.exchange = self.exchange_class({})
66
-
67
- def pair_exists(self, target_symbol: str, trading_symbol: str):
68
-
69
- if not self.exchange.has['fetchTicker']:
70
- raise OperationalException(
71
- f"Market service {self.market} does not support "
72
- f"functionality pair_exists"
73
- )
74
-
75
- try:
76
- data = self.get_ticker(f"{target_symbol}/{trading_symbol}")
77
- return "symbol" in data
78
- except OperationalException:
79
- return False
80
-
81
- def get_ticker(self, symbol):
82
-
83
- if not self.exchange.has['fetchTicker']:
84
- raise OperationalException(
85
- f"Market service {self.market} does not support "
86
- f"functionality get_ticker"
87
- )
88
-
89
- try:
90
- return self.exchange.fetchTicker(symbol)
91
- except Exception as e:
92
- logger.exception(e)
93
- raise OperationalException(
94
- f"Could not retrieve ticker for symbol {symbol}"
95
- )
96
-
97
- def get_tickers(self, symbols):
98
-
99
- if not self.exchange.has['fetchTickers']:
100
- raise OperationalException(
101
- f"Market service {self.market} does not support "
102
- f"functionality get_tickers"
103
- )
104
-
105
- try:
106
- return self.exchange.fetchTickers(symbols)
107
- except Exception as e:
108
- logger.exception(e)
109
- raise OperationalException(
110
- "Could not retrieve selection of tickers"
111
- )
112
-
113
- def get_order_book(self, symbol):
114
-
115
- if not self.exchange.has['fetchOrderBook']:
116
- raise OperationalException(
117
- f"Market service {self.market} does not support "
118
- f"functionality get_order_book"
119
- )
120
-
121
- try:
122
- return self.exchange.fetchOrderBook(symbol)
123
- except Exception as e:
124
- logger.exception(e)
125
- raise OperationalException("Could not retrieve order book")
126
-
127
- def get_order_books(self, symbols):
128
- data = {}
129
-
130
- for symbol in symbols:
131
- try:
132
- entry = self.get_order_book(symbol)
133
- del entry['symbol']
134
- data[symbol] = entry
135
- except Exception as e:
136
- logger.exception(e)
137
-
138
- return data
139
-
140
- def get_order(self, order):
141
- symbol = f"{order.target_symbol.upper()}/" \
142
- f"{order.trading_symbol.upper()}"
143
-
144
- if not self.exchange.has['fetchOrder']:
145
- raise OperationalException(
146
- f"Market service {self.market} does not support "
147
- f"functionality get_order"
148
- )
149
-
150
- try:
151
- order = self.exchange.fetchOrder(order.external_id, symbol)
152
- return Order.from_ccxt_order(order)
153
- except Exception as e:
154
- logger.exception(e)
155
- raise OperationalException("Could not retrieve order")
156
-
157
- def get_orders(self, symbol, since: datetime = None):
158
-
159
- if not self.exchange.has['fetchOrders']:
160
- raise OperationalException(
161
- f"Market service {self.market} does not support "
162
- f"functionality get_orders"
163
- )
164
-
165
- if since is not None:
166
- since = self.exchange.parse8601(
167
- since.strftime(":%Y-%m-%d %H:%M:%S")
168
- )
169
-
170
- try:
171
- ccxt_orders = self.exchange.fetchOrders(symbol, since=since)
172
- return [Order.from_ccxt_order(order) for order in ccxt_orders]
173
- except Exception as e:
174
- logger.exception(e)
175
- raise OperationalException("Could not retrieve orders")
176
- else:
177
- try:
178
- ccxt_orders = self.exchange.fetchOrders(symbol)
179
- return [Order.from_ccxt_order(order) for order in ccxt_orders]
180
- except Exception as e:
181
- logger.exception(e)
182
- raise OperationalException("Could not retrieve orders")
183
-
184
- def get_balance(self):
185
-
186
- if not self.exchange.has['fetchBalance']:
187
- raise OperationalException(
188
- f"Market service {self.market} does not support "
189
- f"functionality get_balance"
190
- )
191
-
192
- try:
193
- return self.exchange.fetchBalance()
194
- except Exception as e:
195
- logger.exception(e)
196
- raise OperationalException("Could not retrieve balance")
197
-
198
- def create_limit_buy_order(
199
- self,
200
- target_symbol: str,
201
- trading_symbol: str,
202
- amount: float,
203
- price: float
204
- ):
205
-
206
- if not self.exchange.has['createLimitBuyOrder']:
207
- raise OperationalException(
208
- f"Market service {self.market} does not support "
209
- f"functionality create_limit_buy_order"
210
- )
211
-
212
- symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
213
-
214
- try:
215
- order = self.exchange.createLimitBuyOrder(
216
- symbol, amount, price
217
- )
218
- return Order.from_ccxt_order(order)
219
- except Exception as e:
220
- logger.exception(e)
221
- raise OperationalException("Could not create limit buy order")
222
-
223
- def create_limit_sell_order(
224
- self,
225
- target_symbol: str,
226
- trading_symbol: str,
227
- amount: float,
228
- price: float
229
- ):
230
-
231
- if not self.exchange.has['createLimitSellOrder']:
232
- raise OperationalException(
233
- f"Market service {self.market} does not support "
234
- f"functionality create_limit_sell_order"
235
- )
236
-
237
- symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
238
-
239
- try:
240
- order = self.exchange.createLimitSellOrder(
241
- symbol, amount, price
242
- )
243
- return Order.from_ccxt_order(order)
244
- except Exception as e:
245
- logger.exception(e)
246
- raise OperationalException("Could not create limit sell order")
247
-
248
- def create_market_sell_order(
249
- self,
250
- target_symbol: str,
251
- trading_symbol: str,
252
- amount: float,
253
- ):
254
-
255
- if not self.exchange.has['createMarketSellOrder']:
256
- raise OperationalException(
257
- f"Market service {self.market} does not support "
258
- f"functionality create_market_sell_order"
259
- )
260
-
261
- symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
262
-
263
- try:
264
- order = self.exchange.createMarketSellOrder(symbol, amount)
265
- return Order.from_ccxt_order(order)
266
- except Exception as e:
267
- logger.exception(e)
268
- raise OperationalException("Could not create market sell order")
269
-
270
- def cancel_order(self, order):
271
- if not self.exchange.has['cancelOrder']:
272
- raise OperationalException(
273
- f"Market service {self.market} does not support "
274
- f"functionality cancel_order"
275
- )
276
-
277
- self.exchange.cancelOrder(
278
- order.get_order_reference(),
279
- f"{order.get_target_symbol()}/{order.get_trading_symbol()}")
280
-
281
- def get_open_orders(
282
- self, target_symbol: str = None, trading_symbol: str = None
283
- ):
284
-
285
- if not self.exchange.has['fetchOpenOrders']:
286
- raise OperationalException(
287
- f"Market service {self.market} does not support "
288
- f"functionality get_open_orders"
289
- )
290
-
291
- try:
292
- if target_symbol is None or trading_symbol is None:
293
- return self.exchange.fetchOpenOrders()
294
-
295
- symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
296
- ccxt_orders = self.exchange.fetchOpenOrders(symbol)
297
- return [Order.from_ccxt_order(order) for order in ccxt_orders]
298
- except Exception as e:
299
- logger.exception(e)
300
- raise OperationalException("Could not retrieve open orders")
301
-
302
- def get_closed_orders(
303
- self, target_symbol: str = None, trading_symbol: str = None
304
- ):
305
-
306
- if not self.exchange.has['fetchClosedOrders']:
307
- raise OperationalException(
308
- f"Market service {self.market} does not support "
309
- f"functionality get_closed_orders"
310
- )
311
-
312
- try:
313
- if target_symbol is None or trading_symbol is None:
314
- return self.exchange.fetchClosedOrders()
315
-
316
- symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
317
- ccxt_orders = self.exchange.fetchClosedOrders(symbol)
318
- return [Order.from_ccxt_order(order) for order in ccxt_orders]
319
- except Exception as e:
320
- logger.exception(e)
321
- raise OperationalException("Could not retrieve closed orders")
322
-
323
- def get_prices(self, symbols):
324
- asset_prices = []
325
-
326
- try:
327
- tickers = self.exchange.fetchTickers(symbols)
328
- for ticker in tickers:
329
- asset_prices.append(
330
- AssetPrice(
331
- tickers[ticker]["symbol"],
332
- tickers[ticker]["ask"],
333
- tickers[ticker]["datetime"]
334
- )
335
- )
336
-
337
- return asset_prices
338
- except Exception as e:
339
- logger.exception(e)
340
- raise OperationalException("Could not retrieve prices")
341
-
342
- def get_ohclv(self, symbol, time_frame, from_timestamp, to_timestamp=None):
343
-
344
- if not self.exchange.has['fetchOHLCV']:
345
- raise OperationalException(
346
- f"Market service {self.market} does not support "
347
- f"functionality get_ohclvs"
348
- )
349
-
350
- time_stamp = self.exchange.parse8601(
351
- from_timestamp.strftime(CCXT_DATETIME_FORMAT)
352
- )
353
-
354
- if to_timestamp is None:
355
- to_timestamp = self.exchange.milliseconds()
356
- else:
357
- to_timestamp = self.exchange.parse8601(
358
- to_timestamp.strftime(CCXT_DATETIME_FORMAT)
359
- )
360
- data = []
361
-
362
- while time_stamp < to_timestamp:
363
- ohlcv = self.exchange.fetch_ohlcv(
364
- symbol, time_frame.to_ccxt_string(), time_stamp
365
- )
366
-
367
- if len(ohlcv) > 0:
368
- time_stamp = \
369
- ohlcv[-1][0] + \
370
- self.exchange.parse_timeframe(
371
- time_frame.to_ccxt_string()
372
- ) * 1000
373
- else:
374
- time_stamp = to_timestamp
375
-
376
- for candle in ohlcv:
377
- datetime_stamp = datetime.strptime(
378
- self.exchange.iso8601(candle[0]), CCXT_DATETIME_FORMAT
379
- )
380
- to_timestamp_datetime = datetime.strptime(
381
- self.exchange.iso8601(to_timestamp), CCXT_DATETIME_FORMAT
382
- )
383
-
384
- if datetime_stamp < to_timestamp_datetime:
385
- data.append([datetime_stamp] + candle[1:])
386
-
387
- sleep(self.exchange.rateLimit / 1000)
388
-
389
- return data
390
-
391
- def get_ohclvs(
392
- self,
393
- symbols,
394
- time_frame,
395
- from_timestamp,
396
- to_timestamp=None
397
- ):
398
- ohlcvs = {}
399
-
400
- for symbol in symbols:
401
-
402
- try:
403
- ohlcvs[symbol] = self.get_ohclv(
404
- symbol, time_frame, from_timestamp, to_timestamp
405
- )
406
- except Exception as e:
407
- logger.exception(e)
408
- logger.error(f"Could not retrieve ohclv data for {symbol}")
409
-
410
- return ohlcvs
@@ -1,192 +0,0 @@
1
- from investing_algorithm_framework.domain import OrderStatus, OrderSide
2
-
3
-
4
- class PerformanceService:
5
-
6
- def __init__(
7
- self,
8
- order_repository,
9
- position_repository,
10
- portfolio_repository,
11
- ):
12
- self.order_repository = order_repository
13
- self.position_repository = position_repository
14
- self.portfolio_repository = portfolio_repository
15
-
16
- def get_total_net_gain(self, portfolio_id):
17
- pass
18
-
19
- def get_total_size(self, portfolio_id):
20
- pass
21
-
22
- def get_percentage_change(self, portfolio_id, time_frame):
23
- pass
24
-
25
- def get_total_cost(self, portfolio_id):
26
- pass
27
-
28
- def get_number_of_trades_closed(self, portfolio_id):
29
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
30
- number_of_trades_closed = 0
31
- orders = self.order_repository.get_all(
32
- {
33
- "portfolio_id": portfolio.id,
34
- "order_side": OrderSide.BUY.value,
35
- }
36
- )
37
-
38
- for order in orders:
39
- if order.get_trade_closed_at() is not None:
40
- number_of_trades_closed += 1
41
-
42
- return number_of_trades_closed
43
-
44
- def get_number_of_trades_open(self, portfolio_id):
45
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
46
- number_of_trades_open = 0
47
- orders = self.order_repository.get_all(
48
- {
49
- "portfolio_id": portfolio.id,
50
- "order_side": OrderSide.BUY.value,
51
- }
52
- )
53
-
54
- for order in orders:
55
- if order.get_trade_closed_at() is None:
56
- number_of_trades_open += 1
57
-
58
- return number_of_trades_open
59
-
60
- def get_percentage_positive_trades(self, portfolio_id):
61
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
62
- orders = self.order_repository.get_all(
63
- {"portfolio_id": portfolio.id, "status": OrderStatus.CLOSED.value}
64
- )
65
- total_number_of_orders = len(orders)
66
-
67
- if total_number_of_orders == 0:
68
- return 0.0
69
-
70
- positive_orders = [
71
- order for order in orders if order.get_net_gain() > 0
72
- ]
73
- total_number_of_positive_orders = len(positive_orders)
74
- return total_number_of_positive_orders / total_number_of_orders * 100
75
-
76
- def get_percentage_negative_trades(self, portfolio_id):
77
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
78
- orders = self.order_repository.get_all(
79
- {"portfolio_id": portfolio.id, "status": OrderStatus.CLOSED.value}
80
- )
81
- total_number_of_orders = len(orders)
82
-
83
- if total_number_of_orders == 0:
84
- return 0.0
85
-
86
- negative_orders = [
87
- order for order in orders if order.get_net_gain() < 0
88
- ]
89
- total_number_of_negative_orders = len(negative_orders)
90
- return total_number_of_negative_orders / total_number_of_orders * 100
91
-
92
- def get_growth_rate_of_backtest(self, portfolio_id, tickers, backtest_profile):
93
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
94
- positions = self.position_repository.get_all(
95
- {"portfolio_id": portfolio.id}
96
- )
97
- allocated = 0
98
-
99
- for position in positions:
100
-
101
- if position.symbol == portfolio.trading_symbol:
102
- continue
103
-
104
- allocated += position.amount * tickers[position.symbol]["bid"]
105
-
106
- current_total_value = allocated + portfolio.unallocated
107
- gain = current_total_value - backtest_profile.initial_unallocated
108
- return gain / backtest_profile.initial_unallocated * 100
109
-
110
- def get_growth_of_backtest(self, portfolio_id, tickers, backtest_profile):
111
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
112
- positions = self.position_repository.get_all(
113
- {"portfolio_id": portfolio.id}
114
- )
115
- allocated = 0
116
-
117
- for position in positions:
118
-
119
- if position.symbol == portfolio.trading_symbol:
120
- continue
121
-
122
- allocated += position.amount * tickers[position.symbol]["bid"]
123
-
124
- current_total_value = allocated + portfolio.unallocated
125
- return current_total_value - backtest_profile.initial_unallocated
126
-
127
- def get_total_net_gain_percentage_of_backtest(self, portfolio_id, backtest_profile):
128
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
129
- return portfolio.total_net_gain \
130
- / backtest_profile.initial_unallocated * 100
131
-
132
- def get_total_value(self, portfolio_id, tickers, backtest_profile):
133
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
134
- positions = self.position_repository.get_all(
135
- {"portfolio_id": portfolio.id}
136
- )
137
- allocated = 0
138
-
139
- for position in positions:
140
-
141
- if position.symbol == portfolio.trading_symbol:
142
- continue
143
-
144
- allocated += position.amount * tickers[position.symbol]["bid"]
145
-
146
- return allocated + portfolio.unallocated
147
-
148
- def get_average_trade_duration(self, portfolio_id):
149
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
150
- buy_orders = self.order_repository.get_all(
151
- {
152
- "portfolio_id": portfolio.id,
153
- "order_side": OrderSide.BUY.value,
154
- }
155
- )
156
- buy_orders_with_trade_closed = [
157
- order for order in buy_orders if order.get_trade_closed_at() != None
158
- ]
159
-
160
- if len(buy_orders_with_trade_closed) == 0:
161
- return 0
162
-
163
- total_duration = 0
164
-
165
- for order in buy_orders_with_trade_closed:
166
- duration = order.get_trade_closed_at() - order.get_created_at()
167
- total_duration += duration.total_seconds() / 3600
168
-
169
- return total_duration / len(buy_orders_with_trade_closed)
170
-
171
- def get_average_trade_size(self, portfolio_id):
172
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
173
- buy_orders = self.order_repository.get_all(
174
- {
175
- "portfolio_id": portfolio.id,
176
- "order_side": OrderSide.BUY.value,
177
- }
178
- )
179
- closed_buy_orders = [
180
- order for order in buy_orders
181
- if order.get_trade_closed_at() != None
182
- ]
183
-
184
- if len(closed_buy_orders) == 0:
185
- return 0
186
-
187
- total_size = 0
188
-
189
- for order in closed_buy_orders:
190
- total_size += order.get_amount() * order.get_price()
191
-
192
- return total_size / len(closed_buy_orders)