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,1105 +0,0 @@
1
- import inspect
2
- import logging
3
- from typing import List, Dict
4
-
5
- from investing_algorithm_framework.domain import OrderStatus, \
6
- Position, Order, Portfolio, OrderType, OrderSide, \
7
- BACKTESTING_FLAG, BACKTESTING_INDEX_DATETIME, MarketService, TimeUnit, \
8
- OperationalException, random_string, RoundingService
9
- from investing_algorithm_framework.services import MarketCredentialService, \
10
- MarketDataSourceService, PortfolioService, PositionService, TradeService, \
11
- OrderService, ConfigurationService, StrategyOrchestratorService, \
12
- PortfolioConfigurationService
13
- from .task import Task
14
-
15
- logger = logging.getLogger("investing_algorithm_framework")
16
-
17
-
18
- class Algorithm:
19
- """
20
- Class to represent an algorithm. An algorithm is a collection of
21
- strategies that are executed in a specific order. The algorithm
22
- class is responsible for managing the strategies and executing
23
- them in the correct order.
24
-
25
- :param (optional) name: The name of the algorithm
26
- :param (optional) description: The description of the algorithm
27
- :param (optional) context: The context of the algorithm,
28
- for backtest references
29
- :param (optional) strategy: A single strategy to add to the algorithm
30
- :param (optional) data_sources: The list of data sources to
31
- add to the algorithm
32
- """
33
- def __init__(
34
- self,
35
- name: str = None,
36
- description: str = None,
37
- context: Dict = None,
38
- strategy=None,
39
- data_sources=None
40
- ):
41
- self._name = name
42
- self._context = {}
43
-
44
- if name is None:
45
- self._name = f"algorithm_{random_string(10)}"
46
-
47
- self._description = None
48
-
49
- if description is not None:
50
- self._description = description
51
-
52
- if context is not None:
53
- self.add_context(context)
54
-
55
- self._strategies = []
56
- self._tasks = []
57
- self.portfolio_service: PortfolioService
58
- self.position_service: PositionService
59
- self.order_service: OrderService
60
- self.market_service: MarketService
61
- self.configuration_service: ConfigurationService
62
- self.portfolio_configuration_service: PortfolioConfigurationService
63
- self.strategy_orchestrator_service: StrategyOrchestratorService
64
- self._data_sources = {}
65
- self._strategies = []
66
- self._market_credential_service: MarketCredentialService
67
- self._market_data_source_service: MarketDataSourceService
68
- self.trade_service: TradeService
69
-
70
- if strategy is not None:
71
- self.add_strategy(strategy)
72
-
73
- if data_sources is not None:
74
- self.add_data_sources(data_sources)
75
-
76
- def initialize_services(
77
- self,
78
- configuration_service,
79
- portfolio_configuration_service,
80
- portfolio_service,
81
- position_service,
82
- order_service,
83
- market_service,
84
- strategy_orchestrator_service,
85
- market_credential_service,
86
- market_data_source_service,
87
- trade_service
88
- ):
89
- self.portfolio_service: PortfolioService = portfolio_service
90
- self.position_service: PositionService = position_service
91
- self.order_service: OrderService = order_service
92
- self.market_service: MarketService = market_service
93
- self.configuration_service: ConfigurationService \
94
- = configuration_service
95
- self.portfolio_configuration_service: PortfolioConfigurationService \
96
- = portfolio_configuration_service
97
- self.strategy_orchestrator_service: StrategyOrchestratorService \
98
- = strategy_orchestrator_service
99
- self._data_sources = {}
100
- self._market_credential_service: MarketCredentialService \
101
- = market_credential_service
102
- self._market_data_source_service: MarketDataSourceService \
103
- = market_data_source_service
104
- self.trade_service: TradeService = trade_service
105
-
106
- # Add all registered strategies to the orchestrator
107
- self.strategy_orchestrator_service.add_strategies(
108
- self._strategies
109
- )
110
-
111
- def start(self, number_of_iterations=None, stateless=False):
112
-
113
- if not stateless:
114
- self.strategy_orchestrator_service.start(
115
- algorithm=self,
116
- number_of_iterations=number_of_iterations
117
- )
118
-
119
- @property
120
- def name(self):
121
- return self._name
122
-
123
- @property
124
- def data_sources(self):
125
- return self._data_sources
126
-
127
- @property
128
- def identifier(self):
129
- """
130
- Function to get a config instance. This allows users when
131
- having access to the algorithm instance also to read the
132
- configs of the app.
133
- """
134
- return self.configuration_service.config
135
-
136
- @property
137
- def config(self):
138
- """
139
- Function to get a config instance. This allows users when
140
- having access to the algorithm instance also to read the
141
- configs of the app.
142
- """
143
- return self.configuration_service.config
144
-
145
- @property
146
- def description(self):
147
- """
148
- Function to get the description of the algorithm
149
- """
150
- return self._description
151
-
152
- @property
153
- def context(self):
154
- """
155
- Function to get the context of the algorithm
156
- """
157
- return self._context
158
-
159
- def add_context(self, context: Dict):
160
- # Check if the context is a dictionary with only string,
161
- # float or int values
162
- for key, value in self.context.items():
163
- if not isinstance(key, str) or \
164
- not isinstance(value, (str, float, int)):
165
- raise OperationalException(
166
- "The context for an algorithm must be a dictionary with "
167
- "only string, float or int values."
168
- )
169
-
170
- self._context = context
171
-
172
- @property
173
- def running(self) -> bool:
174
- """
175
- Returns True if the algorithm is running, False otherwise.
176
-
177
- The algorithm is considered to be running if has strategies
178
- scheduled to run in the strategy orchestrator service.
179
- """
180
- return self.strategy_orchestrator_service.running
181
-
182
- def run_jobs(self):
183
- """
184
- Function run all pending jobs in the strategy orchestrator
185
- """
186
- self.strategy_orchestrator_service.run_pending_jobs()
187
-
188
- def create_order(
189
- self,
190
- target_symbol,
191
- price,
192
- order_type,
193
- order_side,
194
- amount,
195
- market=None,
196
- execute=True,
197
- validate=True,
198
- sync=True
199
- ):
200
- """
201
- Function to create an order. This function will create an order
202
- and execute it if the execute parameter is set to True. If the
203
- validate parameter is set to True, the order will be validated
204
-
205
- :param target_symbol: The symbol of the asset to trade
206
- :param price: The price of the asset
207
- :param order_type: The type of the order
208
- :param order_side: The side of the order
209
- :param amount: The amount of the asset to trade
210
- :param market: The market to trade the asset
211
- :param execute: If set to True, the order will be executed
212
- :param validate: If set to True, the order will be validated
213
- :param sync: If set to True, the created order will be synced
214
- with the portfolio of the algorithm.
215
- :return: The order created
216
- """
217
- portfolio = self.portfolio_service.find({"market": market})
218
- order_data = {
219
- "target_symbol": target_symbol,
220
- "price": price,
221
- "amount": amount,
222
- "order_type": order_type,
223
- "order_side": order_side,
224
- "portfolio_id": portfolio.id,
225
- "status": OrderStatus.CREATED.value,
226
- "trading_symbol": portfolio.trading_symbol,
227
- }
228
-
229
- if BACKTESTING_FLAG in self.configuration_service.config \
230
- and self.configuration_service.config[BACKTESTING_FLAG]:
231
- order_data["created_at"] = \
232
- self.configuration_service.config[BACKTESTING_INDEX_DATETIME]
233
-
234
- return self.order_service.create(
235
- order_data, execute=execute, validate=validate, sync=sync
236
- )
237
-
238
- def create_limit_order(
239
- self,
240
- target_symbol,
241
- price,
242
- order_side,
243
- amount=None,
244
- percentage_of_portfolio=None,
245
- percentage_of_position=None,
246
- precision=None,
247
- market=None,
248
- execute=True,
249
- validate=True,
250
- sync=True
251
- ):
252
- portfolio = self.portfolio_service.find({"market": market})
253
-
254
- if percentage_of_portfolio is not None:
255
- if not OrderSide.BUY.equals(order_side):
256
- raise OperationalException(
257
- "Percentage of portfolio is only supported for BUY orders."
258
- )
259
-
260
- percentage_of_portfolio = percentage_of_portfolio
261
- net_size = portfolio.get_net_size()
262
- size = net_size * percentage_of_portfolio / 100
263
- amount = size / price
264
-
265
- elif percentage_of_position is not None:
266
-
267
- if not OrderSide.SELL.equals(order_side):
268
- raise OperationalException(
269
- "Percentage of position is only supported for SELL orders."
270
- )
271
-
272
- position = self.position_service.find(
273
- {
274
- "symbol": target_symbol,
275
- "portfolio": portfolio.id
276
- }
277
- )
278
- amount = position.get_amount() * (percentage_of_position / 100)
279
-
280
- if precision is not None:
281
- amount = RoundingService.round_down(amount, precision)
282
-
283
- order_data = {
284
- "target_symbol": target_symbol,
285
- "price": price,
286
- "amount": amount,
287
- "order_type": OrderType.LIMIT.value,
288
- "order_side": OrderSide.from_value(order_side).value,
289
- "portfolio_id": portfolio.id,
290
- "status": OrderStatus.CREATED.value,
291
- "trading_symbol": portfolio.trading_symbol,
292
- }
293
-
294
- if BACKTESTING_FLAG in self.configuration_service.config \
295
- and self.configuration_service.config[BACKTESTING_FLAG]:
296
- order_data["created_at"] = \
297
- self.configuration_service.config[BACKTESTING_INDEX_DATETIME]
298
-
299
- return self.order_service.create(
300
- order_data, execute=execute, validate=validate, sync=sync
301
- )
302
-
303
- def create_market_order(
304
- self,
305
- target_symbol,
306
- order_side,
307
- amount,
308
- market=None,
309
- execute=False,
310
- validate=False,
311
- sync=True
312
- ):
313
-
314
- if market is None:
315
- portfolio = self.portfolio_service.get_all()[0]
316
- else:
317
- portfolio = self.portfolio_service.find({"market": market})
318
- order_data = {
319
- "target_symbol": target_symbol,
320
- "amount": amount,
321
- "order_type": OrderType.MARKET.value,
322
- "order_side": OrderSide.from_value(order_side).value,
323
- "portfolio_id": portfolio.id,
324
- "status": OrderStatus.CREATED.value,
325
- "trading_symbol": portfolio.trading_symbol,
326
- }
327
-
328
- if BACKTESTING_FLAG in self.configuration_service.config \
329
- and self.configuration_service.config[BACKTESTING_FLAG]:
330
- order_data["created_at"] = \
331
- self.configuration_service.config[BACKTESTING_INDEX_DATETIME]
332
-
333
- return self.order_service.create(
334
- order_data, execute=execute, validate=validate, sync=sync
335
- )
336
-
337
- def get_portfolio(self, market=None) -> Portfolio:
338
-
339
- if market is None:
340
- return self.portfolio_service.find({})
341
-
342
- return self.portfolio_service.find({{"market": market}})
343
-
344
- def get_unallocated(self, market=None) -> float:
345
-
346
- if market:
347
- portfolio = self.portfolio_service.find({{"market": market}})
348
- else:
349
- portfolio = self.portfolio_service.find({})
350
-
351
- trading_symbol = portfolio.trading_symbol
352
- return self.position_service.find(
353
- {"portfolio": portfolio.id, "symbol": trading_symbol}
354
- ).get_amount()
355
-
356
- def get_total_size(self):
357
- """
358
- Returns the total size of the portfolio.
359
- """
360
- return self.get_unallocated() + self.get_allocated()
361
-
362
- def reset(self):
363
- self._workers = []
364
- self._running_workers = []
365
-
366
- def get_order(
367
- self,
368
- reference_id=None,
369
- market=None,
370
- target_symbol=None,
371
- trading_symbol=None,
372
- order_side=None,
373
- order_type=None
374
- ) -> Order:
375
- query_params = {}
376
-
377
- if reference_id:
378
- query_params["reference_id"] = reference_id
379
-
380
- if target_symbol:
381
- query_params["target_symbol"] = target_symbol
382
-
383
- if trading_symbol:
384
- query_params["trading_symbol"] = trading_symbol
385
-
386
- if order_side:
387
- query_params["order_side"] = order_side
388
-
389
- if order_type:
390
- query_params["order_type"] = order_type
391
-
392
- if market:
393
- portfolio = self.portfolio_service.find({"market": market})
394
- positions = self.position_service.get_all(
395
- {"portfolio": portfolio.id}
396
- )
397
- query_params["position"] = [position.id for position in positions]
398
-
399
- return self.order_service.find(query_params)
400
-
401
- def get_orders(
402
- self,
403
- target_symbol=None,
404
- status=None,
405
- order_type=None,
406
- order_side=None,
407
- market=None
408
- ) -> List[Order]:
409
-
410
- if market is None:
411
- portfolio = self.portfolio_service.get_all()[0]
412
- else:
413
- portfolio = self.portfolio_service.find({"market": market})
414
-
415
- positions = self.position_service.get_all({"portfolio": portfolio.id})
416
- return self.order_service.get_all(
417
- {
418
- "position": [position.id for position in positions],
419
- "target_symbol": target_symbol,
420
- "status": status,
421
- "order_type": order_type,
422
- "order_side": order_side
423
- }
424
- )
425
-
426
- def get_positions(
427
- self,
428
- market=None,
429
- identifier=None,
430
- amount_gt=None,
431
- amount_gte=None,
432
- amount_lt=None,
433
- amount_lte=None
434
- ) -> List[Position]:
435
- query_params = {}
436
-
437
- if market is not None:
438
- query_params["market"] = market
439
-
440
- if identifier is not None:
441
- query_params["identifier"] = identifier
442
-
443
- if amount_gt is not None:
444
- query_params["amount_gt"] = amount_gt
445
-
446
- if amount_gte is not None:
447
- query_params["amount_gte"] = amount_gte
448
-
449
- if amount_lt is not None:
450
- query_params["amount_lt"] = amount_lt
451
-
452
- if amount_lte is not None:
453
- query_params["amount_lte"] = amount_lte
454
-
455
- portfolios = self.portfolio_service.get_all(query_params)
456
-
457
- if not portfolios:
458
- raise OperationalException("No portfolio found.")
459
-
460
- portfolio = portfolios[0]
461
- return self.position_service.get_all(
462
- {"portfolio": portfolio.id}
463
- )
464
-
465
- def get_position(self, symbol, market=None, identifier=None) -> Position:
466
- query_params = {}
467
-
468
- if market is not None:
469
- query_params["market"] = market
470
-
471
- if identifier is not None:
472
- query_params["identifier"] = identifier
473
-
474
- portfolios = self.portfolio_service.get_all(query_params)
475
-
476
- if not portfolios:
477
- raise OperationalException("No portfolio found.")
478
-
479
- portfolio = portfolios[0]
480
-
481
- try:
482
- return self.position_service.find(
483
- {"portfolio": portfolio.id, "symbol": symbol}
484
- )
485
- except OperationalException:
486
- return None
487
-
488
- def has_position(
489
- self,
490
- symbol,
491
- market=None,
492
- identifier=None,
493
- amount_gt=0,
494
- amount_gte=None,
495
- amount_lt=None,
496
- amount_lte=None
497
- ):
498
- """
499
- Function to check if a position exists. This function will return
500
- True if a position exists, False otherwise. This function will check
501
- if the amount > 0 condition by default.
502
-
503
- param symbol: The symbol of the asset
504
- param market: The market of the asset
505
- param identifier: The identifier of the portfolio
506
- param amount_gt: The amount of the asset must be greater than this
507
- param amount_gte: The amount of the asset must be greater than
508
- or equal to this
509
- param amount_lt: The amount of the asset must be less than this
510
- param amount_lte: The amount of the asset must be less than
511
- or equal to this
512
-
513
- return: True if a position exists, False otherwise
514
- """
515
-
516
- return self.position_exists(
517
- symbol=symbol,
518
- market=market,
519
- identifier=identifier,
520
- amount_gt=amount_gt,
521
- amount_gte=amount_gte,
522
- amount_lt=amount_lt,
523
- amount_lte=amount_lte
524
- )
525
-
526
- def position_exists(
527
- self,
528
- symbol,
529
- market=None,
530
- identifier=None,
531
- amount_gt=None,
532
- amount_gte=None,
533
- amount_lt=None,
534
- amount_lte=None
535
- ) -> bool:
536
- """
537
- Function to check if a position exists. This function will return
538
- True if a position exists, False otherwise. This function will
539
- not check the amount > 0 condition by default. If you want to
540
- check if a position exists with an amount greater than 0, you
541
- can use the amount_gt parameter. If you want to check if a
542
- position exists with an amount greater than or equal to a
543
- certain amount, you can use the amount_gte parameter. If you
544
- want to check if a position exists with an amount less than a
545
- certain amount, you can use the amount_lt parameter. If you want
546
- to check if a position exists with an amount less than or equal
547
- to a certain amount, you can use the amount_lte parameter.
548
-
549
- It is not recommended to use this method directly because it can
550
- have adverse effects on the algorithm. It is recommended to use
551
- the has_position method instead.
552
-
553
- param symbol: The symbol of the asset
554
- param market: The market of the asset
555
- param identifier: The identifier of the portfolio
556
- param amount_gt: The amount of the asset must be greater than this
557
- param amount_gte: The amount of the asset must be greater than
558
- or equal to this
559
- param amount_lt: The amount of the asset must be less than this
560
- param amount_lte: The amount of the asset must be less than
561
- or equal to this
562
-
563
- return: True if a position exists, False otherwise
564
- """
565
- query_params = {}
566
-
567
- if market is not None:
568
- query_params["market"] = market
569
-
570
- if identifier is not None:
571
- query_params["identifier"] = identifier
572
-
573
- if amount_gt is not None:
574
- query_params["amount_gt"] = amount_gt
575
-
576
- if amount_gte is not None:
577
- query_params["amount_gte"] = amount_gte
578
-
579
- if amount_lt is not None:
580
- query_params["amount_lt"] = amount_lt
581
-
582
- if amount_lte is not None:
583
- query_params["amount_lte"] = amount_lte
584
-
585
- query_params["symbol"] = symbol
586
- return self.position_service.exists(query_params)
587
-
588
- def get_position_percentage_of_portfolio(
589
- self, symbol, market=None, identifier=None
590
- ) -> float:
591
- """
592
- Returns the percentage of the current total value of the portfolio
593
- that is allocated to a position. This is calculated by dividing
594
- the current value of the position by the total current value
595
- of the portfolio.
596
- """
597
-
598
- query_params = {}
599
-
600
- if market is not None:
601
- query_params["market"] = market
602
-
603
- if identifier is not None:
604
- query_params["identifier"] = identifier
605
-
606
- portfolios = self.portfolio_service.get_all(query_params)
607
-
608
- if not portfolios:
609
- raise OperationalException("No portfolio found.")
610
-
611
- portfolio = portfolios[0]
612
- position = self.position_service.find(
613
- {"portfolio": portfolio.id, "symbol": symbol}
614
- )
615
- full_symbol = f"{position.symbol.upper()}/" \
616
- f"{portfolio.trading_symbol.upper()}"
617
- ticker = self._market_data_source_service.get_ticker(
618
- symbol=full_symbol, market=market
619
- )
620
- total = self.get_unallocated() + self.get_allocated()
621
- return (position.amount * ticker["bid"] / total) * 100
622
-
623
- def get_position_percentage_of_portfolio_by_net_size(
624
- self, symbol, market=None, identifier=None
625
- ) -> float:
626
- """
627
- Returns the percentage of the portfolio that is allocated to a
628
- position. This is calculated by dividing the cost of the position
629
- by the total net size of the portfolio.
630
-
631
- The total net size of the portfolio is the initial balance of the
632
- portfolio plus the all the net gains of your trades.
633
- """
634
- query_params = {}
635
-
636
- if market is not None:
637
- query_params["market"] = market
638
-
639
- if identifier is not None:
640
- query_params["identifier"] = identifier
641
-
642
- portfolios = self.portfolio_service.get_all(query_params)
643
-
644
- if not portfolios:
645
- raise OperationalException("No portfolio found.")
646
-
647
- portfolio = portfolios[0]
648
- position = self.position_service.find(
649
- {"portfolio": portfolio.id, "symbol": symbol}
650
- )
651
- net_size = portfolio.get_net_size()
652
- return (position.cost / net_size) * 100
653
-
654
- def close_position(
655
- self, symbol, market=None, identifier=None, precision=None
656
- ):
657
- portfolio = self.portfolio_service.find(
658
- {"market": market, "identifier": identifier}
659
- )
660
- position = self.position_service.find(
661
- {"portfolio": portfolio.id, "symbol": symbol}
662
- )
663
-
664
- if position.get_amount() == 0:
665
- return
666
-
667
- for order in self.order_service \
668
- .get_all(
669
- {
670
- "position": position.id,
671
- "status": OrderStatus.OPEN.value
672
- }
673
- ):
674
- self.order_service.cancel_order(order)
675
-
676
- symbol = f"{symbol.upper()}/{portfolio.trading_symbol.upper()}"
677
- ticker = self._market_data_source_service.get_ticker(
678
- symbol=symbol, market=market
679
- )
680
- self.create_limit_order(
681
- target_symbol=position.symbol,
682
- amount=position.get_amount(),
683
- order_side=OrderSide.SELL.value,
684
- price=ticker["bid"],
685
- precision=precision,
686
- )
687
-
688
- def add_strategies(self, strategies):
689
- """
690
- Function to add multiple strategies to the algorithm.
691
- This function will check if there are any duplicate strategies
692
- with the same name and raise an exception if there are.
693
- """
694
- has_duplicates = False
695
-
696
- for strategy in strategies:
697
- from .strategy import TradingStrategy
698
- if not issubclass(strategy, TradingStrategy):
699
- raise OperationalException(
700
- "The strategy must be a subclass of TradingStrategy"
701
- )
702
-
703
- if inspect.isclass(strategy):
704
- strategy = strategy()
705
-
706
- assert isinstance(strategy, TradingStrategy), \
707
- OperationalException(
708
- "Strategy object is not an instance of a TradingStrategy"
709
- )
710
-
711
- # Check if there are any duplicate strategies
712
- for i in range(len(strategies)):
713
- for j in range(i + 1, len(strategies)):
714
- if strategies[i].worker_id == strategies[j].worker_id:
715
- has_duplicates = True
716
- break
717
-
718
- if has_duplicates:
719
- raise OperationalException(
720
- "There are duplicate strategies with the same name"
721
- )
722
-
723
- self._strategies = strategies
724
-
725
- def add_strategy(self, strategy):
726
- """
727
- Function to add multiple strategies to the algorithm.
728
- This function will check if there are any duplicate strategies
729
- with the same name and raise an exception if there are.
730
- """
731
- has_duplicates = False
732
- from .strategy import TradingStrategy
733
-
734
- if inspect.isclass(strategy):
735
-
736
- if not issubclass(strategy, TradingStrategy):
737
- raise OperationalException(
738
- "The strategy must be a subclass of TradingStrategy"
739
- )
740
-
741
- strategy = strategy()
742
-
743
- assert isinstance(strategy, TradingStrategy), \
744
- OperationalException(
745
- "Strategy object is not an instance of a TradingStrategy"
746
- )
747
-
748
- for i in range(len(self._strategies)):
749
- for j in range(i + 1, len(self._strategies)):
750
- if self._strategies[i].worker_id == strategy.worker_id:
751
- has_duplicates = True
752
- break
753
-
754
- if has_duplicates:
755
- raise OperationalException(
756
- "Can't add strategy, there already exists a strategy "
757
- "with the same id in the algorithm"
758
- )
759
-
760
- if strategy.market_data_sources is not None:
761
- self.add_data_sources(strategy.market_data_sources)
762
-
763
- self._strategies.append(strategy)
764
-
765
- @property
766
- def strategies(self):
767
- return self._strategies
768
-
769
- def get_strategy(self, strategy_id):
770
- for strategy in self.strategy_orchestrator_service.get_strategies():
771
- if strategy.worker_id == strategy_id:
772
- return strategy
773
-
774
- return None
775
-
776
- def add_tasks(self, tasks):
777
- self.strategy_orchestrator_service.add_tasks(tasks)
778
-
779
- def get_allocated(self, market=None, identifier=None) -> float:
780
-
781
- if self.portfolio_configuration_service.count() > 1 \
782
- and identifier is None and market is None:
783
- raise OperationalException(
784
- "Multiple portfolios found. Please specify a "
785
- "portfolio identifier."
786
- )
787
-
788
- if market is not None and identifier is not None:
789
- portfolio_configurations = self.portfolio_configuration_service \
790
- .get_all()
791
-
792
- else:
793
- query_params = {"market": market, "identifier": identifier}
794
- portfolio_configuration = self.portfolio_configuration_service \
795
- .find(query_params)
796
-
797
- if not portfolio_configuration:
798
- raise OperationalException("No portfolio found.")
799
-
800
- portfolio_configurations = [portfolio_configuration]
801
-
802
- if len(portfolio_configurations) == 0:
803
- raise OperationalException("No portfolio found.")
804
-
805
- portfolios = []
806
-
807
- for portfolio_configuration in portfolio_configurations:
808
- portfolio = self.portfolio_service.find(
809
- {"identifier": portfolio_configuration.identifier}
810
- )
811
- portfolio.configuration = portfolio_configuration
812
- portfolios.append(portfolio)
813
-
814
- allocated = 0
815
-
816
- for portfolio in portfolios:
817
- positions = self.position_service.get_all(
818
- {"portfolio": portfolio.id}
819
- )
820
-
821
- for position in positions:
822
- if portfolio.trading_symbol == position.symbol:
823
- continue
824
-
825
- symbol = f"{position.symbol.upper()}/" \
826
- f"{portfolio.trading_symbol.upper()}"
827
- ticker = self._market_data_source_service.get_ticker(
828
- symbol=symbol, market=market,
829
- )
830
- allocated = allocated + \
831
- (position.get_amount() * ticker["bid"])
832
-
833
- return allocated
834
-
835
- def get_unfilled(self, market=None, identifier=None) -> float:
836
-
837
- if self.portfolio_configuration_service.count() > 1 \
838
- and identifier is None and market is None:
839
- raise OperationalException(
840
- "Multiple portfolios found. Please specify a "
841
- "portfolio identifier."
842
- )
843
-
844
- if market is not None and identifier is not None:
845
- portfolio_configurations = self.portfolio_configuration_service \
846
- .get_all()
847
-
848
- else:
849
- query_params = {
850
- "market": market,
851
- "identifier": identifier
852
- }
853
- portfolio_configurations = [self.portfolio_configuration_service
854
- .find(query_params)]
855
-
856
- portfolios = []
857
-
858
- for portfolio_configuration in portfolio_configurations:
859
- portfolio = self.portfolio_service.find(
860
- {"identifier": portfolio_configuration.identifier}
861
- )
862
- portfolios.append(portfolio)
863
-
864
- unfilled = 0
865
-
866
- for portfolio in portfolios:
867
- orders = self.order_service.get_all(
868
- {"status": OrderStatus.OPEN.value, "portfolio": portfolio.id}
869
- )
870
- unfilled = unfilled + sum(
871
- [order.get_amount() * order.get_price() for order in orders]
872
- )
873
-
874
- return unfilled
875
-
876
- def get_portfolio_configurations(self):
877
- return self.portfolio_configuration_service.get_all()
878
-
879
- def has_open_buy_orders(self, target_symbol, identifier=None, market=None):
880
- query_params = {}
881
-
882
- if identifier is not None:
883
- portfolio = self.portfolio_service.find(
884
- {"identifier": identifier}
885
- )
886
- query_params["portfolio"] = portfolio.id
887
-
888
- if market is not None:
889
- portfolio = self.portfolio_service.find(
890
- {"market": market}
891
- )
892
- query_params["portfolio"] = portfolio.id
893
-
894
- query_params["target_symbol"] = target_symbol
895
- query_params["order_side"] = OrderSide.BUY.value
896
- query_params["status"] = OrderStatus.OPEN.value
897
- return self.order_service.exists(query_params)
898
-
899
- def has_open_sell_orders(self, target_symbol, identifier=None,
900
- market=None):
901
- query_params = {}
902
-
903
- if identifier is not None:
904
- portfolio = self.portfolio_service.find(
905
- {"identifier": identifier}
906
- )
907
- query_params["portfolio"] = portfolio.id
908
-
909
- if market is not None:
910
- portfolio = self.portfolio_service.find(
911
- {"market": market}
912
- )
913
- query_params["portfolio"] = portfolio.id
914
-
915
- query_params["target_symbol"] = target_symbol
916
- query_params["order_side"] = OrderSide.SELL.value
917
- query_params["status"] = OrderStatus.OPEN.value
918
- return self.order_service.exists(query_params)
919
-
920
- def has_open_orders(
921
- self, target_symbol=None, identifier=None, market=None
922
- ):
923
- query_params = {}
924
-
925
- if identifier is not None:
926
- portfolio = self.portfolio_service.find(
927
- {"identifier": identifier}
928
- )
929
- query_params["portfolio"] = portfolio.id
930
-
931
- if market is not None:
932
- portfolio = self.portfolio_service.find(
933
- {"market": market}
934
- )
935
- query_params["portfolio"] = portfolio.id
936
-
937
- if target_symbol is not None:
938
- query_params["target_symbol"] = target_symbol
939
-
940
- query_params["status"] = OrderStatus.OPEN.value
941
- return self.order_service.exists(query_params)
942
-
943
- def check_pending_orders(self):
944
- self.order_service.check_pending_orders()
945
-
946
- def get_trades(self, market=None):
947
- return self.trade_service.get_trades(market)
948
-
949
- def get_closed_trades(self):
950
- return self.trade_service.get_closed_trades()
951
-
952
- def get_open_trades(self, target_symbol=None, market=None):
953
- return self.trade_service.get_open_trades(target_symbol, market)
954
-
955
- def close_trade(self, trade, market=None, precision=None) -> None:
956
- self.trade_service.close_trade(
957
- trade=trade, market=market, precision=precision
958
- )
959
-
960
- def get_number_of_positions(self):
961
- """
962
- Returns the number of positions that have a positive amount.
963
- """
964
- return self.position_service.count({"amount_gt": 0})
965
-
966
- def has_trading_symbol_position_available(
967
- self,
968
- amount_gt=None,
969
- amount_gte=None,
970
- percentage_of_portfolio=None,
971
- market=None
972
- ):
973
- """
974
- Checks if there is a position available for the trading symbol of the
975
- portfolio. If the amount_gt or amount_gte parameters are specified,
976
- the amount of the position must be greater than the specified amount.
977
- If the percentage_of_portfolio parameter is specified, the amount of
978
- the position must be greater than the net_size of the
979
- portfolio.
980
-
981
- :param amount_gt: The amount of the position must be greater than
982
- this amount.
983
- :param amount_gte: The amount of the position must be greater than
984
- or equal to this amount.
985
- :param percentage_of_portfolio: The amount of the position must be
986
- greater than the net_size of the portfolio.
987
- :param market: The market of the portfolio.
988
- :return: True if there is a trading symbol position available with the
989
- specified parameters, False otherwise.
990
- """
991
- portfolio = self.portfolio_service.find({"market": market})
992
- position = self.position_service.find(
993
- {"portfolio": portfolio.id, "symbol": portfolio.trading_symbol}
994
- )
995
-
996
- if amount_gt is not None:
997
- return position.get_amount() > amount_gt
998
-
999
- if amount_gte is not None:
1000
- return position.get_amount() >= amount_gte
1001
-
1002
- if percentage_of_portfolio is not None:
1003
- net_size = portfolio.get_net_size()
1004
- return position.get_amount() >= net_size \
1005
- * percentage_of_portfolio / 100
1006
-
1007
- return position.get_amount() > 0
1008
-
1009
- def strategy(
1010
- self,
1011
- function=None,
1012
- time_unit: TimeUnit = TimeUnit.MINUTE,
1013
- interval=10,
1014
- market_data_sources=None,
1015
- ):
1016
- from .strategy import TradingStrategy
1017
-
1018
- if function:
1019
- strategy_object = TradingStrategy(
1020
- decorated=function,
1021
- time_unit=time_unit,
1022
- interval=interval,
1023
- market_data_sources=market_data_sources
1024
- )
1025
- self.add_strategy(strategy_object)
1026
- else:
1027
-
1028
- def wrapper(f):
1029
- self.add_strategy(
1030
- TradingStrategy(
1031
- decorated=f,
1032
- time_unit=time_unit,
1033
- interval=interval,
1034
- market_data_sources=market_data_sources,
1035
- worker_id=f.__name__
1036
- )
1037
- )
1038
- return f
1039
-
1040
- return wrapper
1041
-
1042
- def add_task(self, task):
1043
- if inspect.isclass(task):
1044
- task = task()
1045
-
1046
- assert isinstance(task, Task), \
1047
- OperationalException(
1048
- "Task object is not an instance of a Task"
1049
- )
1050
-
1051
- self._tasks.append(task)
1052
-
1053
- def add_data_sources(self, data_sources):
1054
- self._data_sources = data_sources
1055
-
1056
- @property
1057
- def tasks(self):
1058
- return self._tasks
1059
-
1060
- def get_pending_orders(
1061
- self, order_side=None, target_symbol=None, portfolio_id=None
1062
- ):
1063
- """
1064
- Function to get all pending orders of the algorithm. If the
1065
- portfolio_id parameter is specified, the function will return
1066
- all pending orders of the portfolio with the specified id.
1067
- """
1068
- query_params = {}
1069
-
1070
- if portfolio_id:
1071
- query_params["portfolio"] = portfolio_id
1072
-
1073
- if target_symbol:
1074
- query_params["target_symbol"] = target_symbol
1075
-
1076
- if order_side:
1077
- query_params["order_side"] = order_side
1078
-
1079
- return self.order_service.get_all({"status": OrderStatus.OPEN.value})
1080
-
1081
- def get_unfilled_buy_value(self):
1082
- """
1083
- Returns the total value of all unfilled buy orders.
1084
- """
1085
- pending_orders = self.get_pending_orders(
1086
- order_side=OrderSide.BUY.value
1087
- )
1088
-
1089
- return sum(
1090
- [order.get_amount() * order.get_price()
1091
- for order in pending_orders]
1092
- )
1093
-
1094
- def get_unfilled_sell_value(self):
1095
- """
1096
- Returns the total value of all unfilled buy orders.
1097
- """
1098
- pending_orders = self.get_pending_orders(
1099
- order_side=OrderSide.SELL.value
1100
- )
1101
-
1102
- return sum(
1103
- [order.get_amount() * order.get_price()
1104
- for order in pending_orders]
1105
- )