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,40 +1,85 @@
1
- from investing_algorithm_framework.domain import OperationalException
2
- from investing_algorithm_framework.domain import \
3
- TimeUnit, StrategyProfile, Trade
4
- from .algorithm import Algorithm
1
+ from datetime import datetime
2
+ from typing import List, Dict, Any, Union
3
+
5
4
  import pandas as pd
6
5
 
6
+ from investing_algorithm_framework.domain import OperationalException, \
7
+ Position, PositionSize, TimeUnit, StrategyProfile, Trade, \
8
+ DataSource, OrderSide, StopLossRule, TakeProfitRule, Order, \
9
+ INDEX_DATETIME
10
+ from .context import Context
11
+
7
12
 
8
13
  class TradingStrategy:
9
- time_unit: str = None
14
+ """
15
+ TradingStrategy is the base class for all trading strategies. A trading
16
+ strategy is a set of rules that defines when to buy or sell an asset.
17
+
18
+ Attributes:
19
+ time_unit: TimeUnit - the time unit of the strategy that defines
20
+ when the strategy should run e.g. HOUR, DAY, WEEK, MONTH
21
+ interval: int - the interval of the strategy that defines how often
22
+ the strategy should run within the time unit e.g. every 5 hours,
23
+ every 2 days, every 3 weeks, every 4 months
24
+ worker_id (optional): str - the id of the worker
25
+ strategy_id (optional): str - the id of the strategy
26
+ decorated (optional): function - the decorated function
27
+ data_sources (List[DataSource] optional): the list of data
28
+ sources to use for the strategy. The data sources will be used
29
+ to indentify data providers that will be called to gather data
30
+ and pass to the strategy before its run.
31
+ metadata (optional): Dict[str, Any] - a dictionary
32
+ containing metadata about the strategy. This can be used to
33
+ store additional information about the strategy, such as its
34
+ author, version, description, params etc.
35
+ """
36
+ time_unit: TimeUnit = None
10
37
  interval: int = None
11
38
  worker_id: str = None
12
39
  strategy_id: str = None
13
40
  decorated = None
14
- market_data_sources = None
41
+ data_sources: List[DataSource] = []
15
42
  traces = None
43
+ context: Context = None
44
+ metadata: Dict[str, Any] = None
45
+ position_sizes: List[PositionSize] = []
46
+ stop_loss_rules: List[StopLossRule] = []
47
+ take_profit_rules: List[TakeProfitRule] = []
48
+ symbols: List[str] = []
49
+ trading_symbol: str = None
16
50
 
17
51
  def __init__(
18
52
  self,
19
53
  strategy_id=None,
20
54
  time_unit=None,
21
55
  interval=None,
22
- market_data_sources=None,
56
+ data_sources=None,
57
+ metadata=None,
58
+ position_sizes=None,
59
+ symbols=None,
60
+ trading_symbol=None,
23
61
  worker_id=None,
24
62
  decorated=None
25
63
  ):
26
-
27
64
  if time_unit is not None:
28
65
  self.time_unit = TimeUnit.from_value(time_unit)
66
+ else:
67
+ # Check if time_unit is None
68
+ if self.time_unit is None:
69
+ raise OperationalException(
70
+ f"Time unit attribute not set for "
71
+ f"strategy instance {self.strategy_id}"
72
+ )
73
+
74
+ self.time_unit = TimeUnit.from_value(self.time_unit)
29
75
 
30
76
  if interval is not None:
31
77
  self.interval = interval
32
78
 
33
- if time_unit is not None:
34
- self.time_unit = TimeUnit.from_value(time_unit)
79
+ if data_sources is not None:
80
+ self.data_sources = data_sources
35
81
 
36
- if market_data_sources is not None:
37
- self.market_data_sources = market_data_sources
82
+ self.metadata = metadata
38
83
 
39
84
  if decorated is not None:
40
85
  self.decorated = decorated
@@ -51,11 +96,14 @@ class TradingStrategy:
51
96
  else:
52
97
  self.strategy_id = self.worker_id
53
98
 
54
- # Check if time_unit is None
55
- if self.time_unit is None:
56
- raise OperationalException(
57
- f"Time unit not set for strategy instance {self.strategy_id}"
58
- )
99
+ if position_sizes is not None:
100
+ self.position_sizes = position_sizes
101
+
102
+ if symbols is not None:
103
+ self.symbols = symbols
104
+
105
+ if trading_symbol is not None:
106
+ self.trading_symbol = trading_symbol
59
107
 
60
108
  # Check if interval is None
61
109
  if self.interval is None:
@@ -65,17 +113,229 @@ class TradingStrategy:
65
113
 
66
114
  # context initialization
67
115
  self._context = None
116
+ self._last_run = None
117
+ self.stop_loss_rules_lookup = {}
118
+ self.take_profit_rules_lookup = {}
119
+ self.position_sizes_lookup = {}
120
+
121
+ def generate_buy_signals(
122
+ self, data: Dict[str, Any]
123
+ ) -> Dict[str, pd.Series]:
124
+ """
125
+ Function that needs to be implemented by the user.
126
+ This function should return a pandas Series containing the buy signals.
68
127
 
69
- def run_strategy(self, algorithm, market_data):
70
- # Check pending orders before running the strategy
71
- algorithm.check_pending_orders()
128
+ Args:
129
+ data (Dict[str, Any]): All the data that matched the
130
+ data sources of the strategy.
72
131
 
73
- # Run user defined strategy
74
- self.apply_strategy(algorithm=algorithm, market_data=market_data)
132
+ Returns:
133
+ Dict[str, Series]: A dictionary where the keys are the
134
+ symbols and the values are pandas Series containing
135
+ the buy signals. The series must be a pandas Series with
136
+ a boolean value for each row in the data source, e.g.
137
+ pd.Series([True, False, False, True, ...], index=data.index)
138
+ Also the return dictionary must look like:
139
+ {
140
+ "BTC": pd.Series([...]),
141
+ "ETH": pd.Series([...]),
142
+ ...
143
+ }
144
+ where the symbols are exactly the same as defined in the
145
+ symbols attribute of the strategy.
146
+ """
147
+ raise NotImplementedError(
148
+ "generate_buy_signals method not implemented"
149
+ )
150
+
151
+ def generate_sell_signals(
152
+ self, data: Dict[str, Any]
153
+ ) -> Dict[str, pd.Series]:
154
+ """
155
+ Function that needs to be implemented by the user.
156
+ This function should return a pandas Series containing
157
+ the sell signals.
75
158
 
76
- def apply_strategy(self, algorithm, market_data):
159
+ Args:
160
+ data (Dict[str, Any]): All the data that is defined in the
161
+ data sources of the strategy. E.g. if there is a data source
162
+ defined as DataSource(identifier="bitvavo_btc_eur_1h",
163
+ symbol="BTC/EUR", time_frame="1h", data_type=DataType.OHLCV,
164
+ window_size=100, market="BITVAVO"), the data dictionary
165
+ will contain a key "bitvavo_btc_eur_1h"
166
+ with the corresponding data as a polars DataFrame.
167
+
168
+ Returns:
169
+ Dict[str, Series]: A dictionary where the keys are the
170
+ symbols and the values are pandas Series containing
171
+ the sell signals. The series must be a pandas Series with
172
+ a boolean value for each row in the data source, e.g.
173
+ pd.Series([True, False, False, True, ...], index=data.index)
174
+ Also the return dictionary must look like:
175
+ {
176
+ "BTC": pd.Series([...]),
177
+ "ETH": pd.Series([...]),
178
+ ...
179
+ }
180
+ where the symbols are exactly the same as defined in the
181
+ symbols attribute of the strategy.
182
+ """
183
+ raise NotImplementedError(
184
+ "generate_sell_signals method not implemented"
185
+ )
186
+
187
+ def run_strategy(self, context: Context, data: Dict[str, Any]):
188
+ """
189
+ Main function for running the strategy. This function will be called
190
+ by the framework when the trigger of your strategy is met.
191
+
192
+ The flow of this function is as follows:
193
+ 1. Loop through all the symbols defined in the strategy.
194
+ 2. For each symbol, check if there are any open orders.
195
+ A. If there are open orders, skip to the next symbol.
196
+ 3. If there is no open position, generate buy signals
197
+ A. Generate buy signals
198
+ B. If there is a buy signal, retrieve the position size
199
+ defined for the symbol.
200
+ C. If there is a take profit or stop loss rule defined
201
+ for the symbol, register them for the trade that
202
+ has been created as part of the order execution.
203
+ 4. If there is an open position, generate sell signals
204
+ A. Generate sell signals
205
+ B. If there is a sell signal, create a limit order to
206
+ sell the position.
207
+
208
+ During execution of this function, the context and market data
209
+ will be passed to the function. The context is an instance of
210
+ the Context class, this class has various methods to do operations
211
+ with your portfolio, orders, trades, positions and other components.
212
+
213
+ The market data is a dictionary containing all the data retrieved
214
+ from the specified data sources.
215
+
216
+ When buy or sell signals are generated, the strategy will create
217
+ limit orders to buy or sell the assets based on the generated signals.
218
+ For each symbol a corresponding position size must be defined. If
219
+ no position size is defined, an OperationalException will be raised.
220
+
221
+ Before creating new orders, the strategy will check if there are any
222
+ stop losses or take profits for symbol registered. It will
223
+ use the function get_stop_losses and get_take_profits, these functions
224
+ can be overridden by the user to provide custom stop losses and
225
+ take profits logic. The default functions will return the stop losses
226
+ and take profits that are registered for the symbol if any.
227
+
228
+ Args:
229
+ context (Context): The context of the strategy. This is an instance
230
+ of the Context class, this class has various methods to do
231
+ operations with your portfolio, orders, trades, positions and
232
+ other components.
233
+ data (Dict[str, Any]): The data for the strategy.
234
+ This is a dictionary containing all the data retrieved from the
235
+ specified data sources. The keys are either the
236
+ identifiers of the data sources or a generated key, usually
237
+ <target_symbol>_<trading_symbol>_<time_frame> e.g. BTC-EUR_1h.
238
+
239
+ Returns:
240
+ None
241
+ """
242
+ self.context = context
243
+ index_datetime = context.config[INDEX_DATETIME]
244
+ buy_signals = self.generate_buy_signals(data)
245
+ sell_signals = self.generate_sell_signals(data)
246
+
247
+ for symbol in self.symbols:
248
+
249
+ if self.has_open_orders(symbol):
250
+ continue
251
+
252
+ if not self.has_position(symbol):
253
+
254
+ if symbol not in buy_signals:
255
+ continue
256
+
257
+ signals = buy_signals[symbol]
258
+ last_row = signals.iloc[-1]
259
+
260
+ if last_row:
261
+ position_size = self.get_position_size(symbol)
262
+ full_symbol = (f"{symbol}/"
263
+ f"{self.context.get_trading_symbol()}")
264
+ price = self.context.get_latest_price(full_symbol)
265
+ amount = position_size.get_size(
266
+ self.context.get_portfolio(), price
267
+ )
268
+ order_amount = amount / price
269
+ order = self.create_limit_order(
270
+ target_symbol=symbol,
271
+ order_side=OrderSide.BUY,
272
+ amount=order_amount,
273
+ price=price,
274
+ execute=True,
275
+ validate=True,
276
+ sync=True
277
+ )
278
+
279
+ # Retrieve stop loss and take profit rules if any
280
+ stop_loss_rule = self.get_stop_loss_rule(symbol)
281
+ take_profit_rule = self.get_take_profit_rule(symbol)
282
+
283
+ if stop_loss_rule is not None:
284
+ trade = self.context.get_trade(
285
+ order_id=order.id
286
+ )
287
+ self.context.add_stop_loss(
288
+ trade=trade,
289
+ percentage=stop_loss_rule.percentage_threshold,
290
+ trailing=stop_loss_rule.trailing,
291
+ sell_percentage=stop_loss_rule.sell_percentage,
292
+ created_at=index_datetime
293
+ )
294
+
295
+ if take_profit_rule is not None:
296
+ trade = self.context.get_trade(
297
+ order_id=order.id
298
+ )
299
+ self.context.add_take_profit(
300
+ trade=trade,
301
+ percentage=take_profit_rule.percentage_threshold,
302
+ trailing=take_profit_rule.trailing,
303
+ sell_percentage=take_profit_rule.sell_percentage,
304
+ created_at=index_datetime
305
+ )
306
+ else:
307
+ # Check in the last row if there is a sell signal
308
+ if symbol not in sell_signals:
309
+ continue
310
+
311
+ signals = sell_signals[symbol]
312
+ last_row = signals.iloc[-1]
313
+
314
+ if last_row:
315
+ position = self.get_position(symbol)
316
+
317
+ if position is None:
318
+ raise OperationalException(
319
+ f"No position found for symbol {symbol} "
320
+ f"in strategy {self.strategy_id}"
321
+ )
322
+
323
+ full_symbol = (f"{symbol}/"
324
+ f"{self.context.get_trading_symbol()}")
325
+ price = self.context.get_latest_price(full_symbol)
326
+ self.create_limit_order(
327
+ target_symbol=symbol,
328
+ order_side=OrderSide.SELL,
329
+ amount=position.amount,
330
+ execute=True,
331
+ validate=True,
332
+ sync=True,
333
+ price=price
334
+ )
335
+
336
+ def apply_strategy(self, context, data):
77
337
  if self.decorated:
78
- self.decorated(algorithm=algorithm, market_data=market_data)
338
+ self.decorated(context=context, data=data)
79
339
  else:
80
340
  raise NotImplementedError("Apply strategy is not implemented")
81
341
 
@@ -85,54 +345,130 @@ class TradingStrategy:
85
345
  strategy_id=self.worker_id,
86
346
  interval=self.interval,
87
347
  time_unit=self.time_unit,
88
- market_data_sources=self.market_data_sources
348
+ data_sources=self.data_sources
89
349
  )
90
350
 
91
- def on_trade_closed(self, algorithm: Algorithm, trade: Trade):
351
+ def get_take_profit_rule(self, symbol: str) -> Union[TakeProfitRule, None]:
352
+ """
353
+ Get the take profit definition for a given symbol.
354
+
355
+ Args:
356
+ symbol (str): The symbol of the asset.
357
+
358
+ Returns:
359
+ Union[TakeProfitRule, None]: The take profit rule if found,
360
+ None otherwise.
361
+ """
362
+
363
+ if len(self.take_profit_rules) == 0:
364
+ return None
365
+
366
+ if self.take_profit_rules_lookup == {}:
367
+ for tp in self.take_profit_rules:
368
+ self.take_profit_rules_lookup[tp.symbol] = tp
369
+
370
+ return self.take_profit_rules_lookup.get(symbol, None)
371
+
372
+ def get_stop_loss_rule(self, symbol: str) -> Union[StopLossRule, None]:
373
+ """
374
+ Get the stop loss definition for a given symbol.
375
+
376
+ Args:
377
+ symbol (str): The symbol of the asset.
378
+
379
+ Returns:
380
+ Union[StopLossRule, None]: The stop loss rule if found,
381
+ None otherwise.
382
+ """
383
+
384
+ if len(self.stop_loss_rules) == 0:
385
+ return None
386
+
387
+ if self.stop_loss_rules_lookup == {}:
388
+ for sl in self.stop_loss_rules:
389
+ self.stop_loss_rules_lookup[sl.symbol] = sl
390
+
391
+ return self.stop_loss_rules_lookup.get(symbol, None)
392
+
393
+ def get_position_size(self, symbol: str) -> Union[PositionSize, None]:
394
+ """
395
+ Get the position size definition for a given symbol.
396
+
397
+ Args:
398
+ symbol (str): The symbol of the asset.
399
+
400
+ Returns:
401
+ Union[PositionSize, None]: The position size if found,
402
+ None otherwise.
403
+ """
404
+
405
+ if len(self.position_sizes) == 0:
406
+ raise OperationalException(
407
+ f"No position size defined for symbol "
408
+ f"{symbol} in strategy "
409
+ f"{self.strategy_id}"
410
+ )
411
+
412
+ if self.position_sizes_lookup == {}:
413
+ for ps in self.position_sizes:
414
+ self.position_sizes_lookup[ps.symbol] = ps
415
+
416
+ position_size = self.position_sizes_lookup.get(symbol, None)
417
+
418
+ if position_size is None:
419
+ raise OperationalException(
420
+ f"No position size defined for symbol "
421
+ f"{symbol} in strategy "
422
+ f"{self.strategy_id}"
423
+ )
424
+
425
+ return position_size
426
+
427
+ def on_trade_closed(self, context: Context, trade: Trade):
92
428
  pass
93
429
 
94
- def on_trade_updated(self, algorithm: Algorithm, trade: Trade):
430
+ def on_trade_updated(self, context: Context, trade: Trade):
95
431
  pass
96
432
 
97
- def on_trade_created(self, algorithm: Algorithm, trade: Trade):
433
+ def on_trade_created(self, context: Context, trade: Trade):
98
434
  pass
99
435
 
100
- def on_trade_opened(self, algorithm: Algorithm, trade: Trade):
436
+ def on_trade_opened(self, context: Context, trade: Trade):
101
437
  pass
102
438
 
103
- def on_trade_stop_loss_triggered(self, algorithm: Algorithm, trade: Trade):
439
+ def on_trade_stop_loss_triggered(self, context: Context, trade: Trade):
104
440
  pass
105
441
 
106
442
  def on_trade_trailing_stop_loss_triggered(
107
- self, algorithm: Algorithm, trade: Trade
443
+ self, context: Context, trade: Trade
108
444
  ):
109
445
  pass
110
446
 
111
447
  def on_trade_take_profit_triggered(
112
- self, algorithm: Algorithm, trade: Trade
448
+ self, context: Context, trade: Trade
113
449
  ):
114
450
  pass
115
451
 
116
- def on_trade_stop_loss_updated(self, algorithm: Algorithm, trade: Trade):
452
+ def on_trade_stop_loss_updated(self, context: Context, trade: Trade):
117
453
  pass
118
454
 
119
455
  def on_trade_trailing_stop_loss_updated(
120
- self, algorithm: Algorithm, trade: Trade
456
+ self, context: Context, trade: Trade
121
457
  ):
122
458
  pass
123
459
 
124
- def on_trade_take_profit_updated(self, algorithm: Algorithm, trade: Trade):
460
+ def on_trade_take_profit_updated(self, context: Context, trade: Trade):
125
461
  pass
126
462
 
127
- def on_trade_stop_loss_created(self, algorithm: Algorithm, trade: Trade):
463
+ def on_trade_stop_loss_created(self, context: Context, trade: Trade):
128
464
  pass
129
465
 
130
466
  def on_trade_trailing_stop_loss_created(
131
- self, algorithm: Algorithm, trade: Trade
467
+ self, context: Context, trade: Trade
132
468
  ):
133
469
  pass
134
470
 
135
- def on_trade_take_profit_created(self, algorithm: Algorithm, trade: Trade):
471
+ def on_trade_take_profit_created(self, context: Context, trade: Trade):
136
472
  pass
137
473
 
138
474
  @property
@@ -143,71 +479,315 @@ class TradingStrategy:
143
479
 
144
480
  return self.worker_id
145
481
 
146
- @property
147
- def context(self):
148
- return self._context
482
+ def has_open_orders(
483
+ self, target_symbol=None, identifier=None, market=None
484
+ ) -> bool:
485
+ """
486
+ Check if there are open orders for a given symbol
149
487
 
150
- @context.setter
151
- def context(self, context):
152
- self._context = context
488
+ Args:
489
+ target_symbol (str): The symbol of the asset e.g BTC if the
490
+ asset is BTC/USDT
491
+ identifier (str): The identifier of the portfolio
492
+ market (str): The market of the asset
153
493
 
154
- def add_trace(
494
+ Returns:
495
+ bool: True if there are open orders, False otherwise
496
+ """
497
+ return self.context.has_open_orders(
498
+ target_symbol=target_symbol, identifier=identifier, market=market
499
+ )
500
+
501
+ def create_limit_order(
155
502
  self,
156
- symbol: str,
157
- data,
158
- drop_duplicates=True
159
- ) -> None:
503
+ target_symbol,
504
+ price,
505
+ order_side,
506
+ amount=None,
507
+ amount_trading_symbol=None,
508
+ percentage=None,
509
+ percentage_of_portfolio=None,
510
+ percentage_of_position=None,
511
+ precision=None,
512
+ market=None,
513
+ execute=True,
514
+ validate=True,
515
+ sync=True
516
+ ) -> Order:
517
+ """
518
+ Function to create a limit order. This function will create
519
+ a limit order and execute it if the execute parameter is set to True.
520
+ If the validate parameter is set to True, the order will be validated
521
+
522
+ Args:
523
+ target_symbol: The symbol of the asset to trade
524
+ price: The price of the asset
525
+ order_side: The side of the order
526
+ amount (optional): The amount of the asset to trade
527
+ amount_trading_symbol (optional): The amount of the trading
528
+ symbol to trade
529
+ percentage (optional): The percentage of the portfolio to
530
+ allocate to the order
531
+ percentage_of_portfolio (optional): The percentage of
532
+ the portfolio to allocate to the order
533
+ percentage_of_position (optional): The percentage of
534
+ the position to allocate to the order.
535
+ (Only supported for SELL orders)
536
+ precision (optional): The precision of the amount
537
+ market (optional): The market to trade the asset
538
+ execute (optional): Default True. If set to True, the order
539
+ will be executed
540
+ validate (optional): Default True. If set to True, the order
541
+ will be validated
542
+ sync (optional): Default True. If set to True, the created
543
+ order will be synced with the portfolio of the context
544
+
545
+ Returns:
546
+ Order: Instance of the order created
547
+ """
548
+ return self.context.create_limit_order(
549
+ target_symbol=target_symbol,
550
+ price=price,
551
+ order_side=order_side,
552
+ amount=amount,
553
+ amount_trading_symbol=amount_trading_symbol,
554
+ percentage=percentage,
555
+ percentage_of_portfolio=percentage_of_portfolio,
556
+ percentage_of_position=percentage_of_position,
557
+ precision=precision,
558
+ market=market,
559
+ execute=execute,
560
+ validate=validate,
561
+ sync=sync
562
+ )
563
+
564
+ def close_position(
565
+ self, symbol, market=None, identifier=None, precision=None
566
+ ) -> Order:
160
567
  """
161
- Add data to the straces object for a given symbol
568
+ Function to close a position. This function will close a position
569
+ by creating a market order to sell the position. If the precision
570
+ parameter is specified, the amount of the order will be rounded
571
+ down to the specified precision.
162
572
 
163
573
  Args:
164
- symbol (str): The symbol
165
- data (pd.DataFrame): The data to add to the tracing
166
- drop_duplicates (bool): Drop duplicates
574
+ symbol: The symbol of the asset
575
+ market: The market of the asset
576
+ identifier: The identifier of the portfolio
577
+ precision: The precision of the amount
167
578
 
168
579
  Returns:
169
580
  None
170
581
  """
582
+ return self.context.close_position(
583
+ symbol=symbol,
584
+ market=market,
585
+ identifier=identifier,
586
+ precision=precision
587
+ )
171
588
 
172
- # Check if data is a DataFrame
173
- if not isinstance(data, pd.DataFrame):
174
- raise ValueError(
175
- "Currently only pandas DataFrames are "
176
- "supported as tracing data objects."
177
- )
589
+ def get_positions(
590
+ self,
591
+ market=None,
592
+ identifier=None,
593
+ amount_gt=None,
594
+ amount_gte=None,
595
+ amount_lt=None,
596
+ amount_lte=None
597
+ ) -> List[Position]:
598
+ """
599
+ Function to get all positions. This function will return all
600
+ positions that match the specified query parameters. If the
601
+ market parameter is specified, the positions of the specified
602
+ market will be returned. If the identifier parameter is
603
+ specified, the positions of the specified portfolio will be
604
+ returned. If the amount_gt parameter is specified, the positions
605
+ with an amount greater than the specified amount will be returned.
606
+ If the amount_gte parameter is specified, the positions with an
607
+ amount greater than or equal to the specified amount will be
608
+ returned. If the amount_lt parameter is specified, the positions
609
+ with an amount less than the specified amount will be returned.
610
+ If the amount_lte parameter is specified, the positions with an
611
+ amount less than or equal to the specified amount will be returned.
612
+
613
+ Args:
614
+ market: The market of the portfolio where the positions are
615
+ identifier: The identifier of the portfolio
616
+ amount_gt: The amount of the asset must be greater than this
617
+ amount_gte: The amount of the asset must be greater than or
618
+ equal to this
619
+ amount_lt: The amount of the asset must be less than this
620
+ amount_lte: The amount of the asset must be less than or equal
621
+ to this
622
+
623
+ Returns:
624
+ List[Position]: A list of positions that match the query parameters
625
+ """
626
+ return self.context.get_positions(
627
+ market=market,
628
+ identifier=identifier,
629
+ amount_gt=amount_gt,
630
+ amount_gte=amount_gte,
631
+ amount_lt=amount_lt,
632
+ amount_lte=amount_lte
633
+ )
178
634
 
179
- data: pd.DataFrame = data
635
+ def get_trades(self, market=None) -> List[Trade]:
636
+ """
637
+ Function to get all trades. This function will return all trades
638
+ that match the specified query parameters. If the market parameter
639
+ is specified, the trades with the specified market will be returned.
180
640
 
181
- # Check if index is a datetime object
182
- if not isinstance(data.index, pd.DatetimeIndex):
183
- raise ValueError("Dataframe Index must be a datetime object.")
641
+ Args:
642
+ market: The market of the asset
184
643
 
185
- if self.traces is None:
186
- self.traces = {}
644
+ Returns:
645
+ List[Trade]: A list of trades that match the query parameters
646
+ """
647
+ return self.context.get_trades(market)
187
648
 
188
- # Check if the key is already in the context dictionary
189
- if symbol in self.traces:
190
- # If the key is already in the context dictionary,
191
- # append the new data to the existing data
192
- combined = pd.concat([self.traces[symbol], data])
193
- else:
194
- # If the key is not in the context dictionary,
195
- # add the new data to the context dictionary
196
- combined = data
649
+ def get_closed_trades(self) -> List[Trade]:
650
+ """
651
+ Function to get all closed trades. This function will return all
652
+ closed trades of the context.
653
+
654
+ Returns:
655
+ List[Trade]: A list of closed trades
656
+ """
657
+ return self.context.get_closed_trades()
658
+
659
+ def get_open_trades(self, target_symbol=None, market=None) -> List[Trade]:
660
+ """
661
+ Function to get all open trades. This function will return all
662
+ open trades that match the specified query parameters. If the
663
+ target_symbol parameter is specified, the open trades with the
664
+ specified target symbol will be returned. If the market parameter
665
+ is specified, the open trades with the specified market will be
666
+ returned.
667
+
668
+ Args:
669
+ target_symbol: The symbol of the asset
670
+ market: The market of the asset
671
+
672
+ Returns:
673
+ List[Trade]: A list of open trades that match the query parameters
674
+ """
675
+ return self.context.get_open_trades(target_symbol, market)
676
+
677
+ def close_trade(self, trade, market=None, precision=None) -> None:
678
+ """
679
+ Function to close a trade. This function will close a trade by
680
+ creating a market order to sell the position. If the precision
681
+ parameter is specified, the amount of the order will be rounded
682
+ down to the specified precision.
683
+
684
+ Args:
685
+ trade: Trade - The trade to close
686
+ market: str - The market of the trade
687
+ precision: float - The precision of the amount
688
+
689
+ Returns:
690
+ None
691
+ """
692
+ self.context.close_trade(
693
+ trade=trade, market=market, precision=precision
694
+ )
695
+
696
+ def get_number_of_positions(self):
697
+ """
698
+ Returns the number of positions that have a positive amount.
699
+
700
+ Returns:
701
+ int: The number of positions
702
+ """
703
+ return self.context.get_number_of_positions()
704
+
705
+ def get_position(
706
+ self, symbol, market=None, identifier=None
707
+ ) -> Position:
708
+ """
709
+ Function to get a position. This function will return the
710
+ position that matches the specified query parameters. If the
711
+ market parameter is specified, the position of the specified
712
+ market will be returned. If the identifier parameter is
713
+ specified, the position of the specified portfolio will be
714
+ returned.
715
+
716
+ Args:
717
+ symbol: The symbol of the asset that represents the position
718
+ market: The market of the portfolio where the position is located
719
+ identifier: The identifier of the portfolio
720
+
721
+ Returns:
722
+ Position: The position that matches the query parameters
723
+ """
724
+ return self.context.get_position(
725
+ symbol=symbol,
726
+ market=market,
727
+ identifier=identifier
728
+ )
729
+
730
+ def has_position(
731
+ self,
732
+ symbol,
733
+ market=None,
734
+ identifier=None,
735
+ amount_gt=0,
736
+ amount_gte=None,
737
+ amount_lt=None,
738
+ amount_lte=None
739
+ ):
740
+ """
741
+ Function to check if a position exists. This function will return
742
+ True if a position exists, False otherwise. This function will check
743
+ if the amount > 0 condition by default.
744
+
745
+ Args:
746
+ param symbol: The symbol of the asset
747
+ param market: The market of the asset
748
+ param identifier: The identifier of the portfolio
749
+ param amount_gt: The amount of the asset must be greater than this
750
+ param amount_gte: The amount of the asset must be greater than
751
+ or equal to this
752
+ param amount_lt: The amount of the asset must be less than this
753
+ param amount_lte: The amount of the asset must be less than
754
+ or equal to this
197
755
 
198
- if drop_duplicates:
199
- # Drop duplicates and sort the data by the index
200
- combined = combined[~combined.index.duplicated(keep='first')]
756
+ Returns:
757
+ Boolean: True if a position exists, False otherwise
758
+ """
759
+ return self.context.has_position(
760
+ symbol=symbol,
761
+ market=market,
762
+ identifier=identifier,
763
+ amount_gt=amount_gt,
764
+ amount_gte=amount_gte,
765
+ amount_lt=amount_lt,
766
+ amount_lte=amount_lte
767
+ )
768
+
769
+ def has_balance(self, symbol, amount, market=None):
770
+ """
771
+ Function to check if the portfolio has enough balance to
772
+ create an order. This function will return True if the
773
+ portfolio has enough balance to create an order, False
774
+ otherwise.
201
775
 
202
- # Set the datetime column as the index
203
- combined.set_index(pd.DatetimeIndex(combined.index), inplace=True)
204
- self.traces[symbol] = combined
776
+ Args:
777
+ symbol: The symbol of the asset
778
+ amount: The amount of the asset
779
+ market: The market of the asset
780
+
781
+ Returns:
782
+ Boolean: True if the portfolio has enough balance
783
+ """
784
+ return self.context.has_balance(symbol, amount, market)
205
785
 
206
- def get_traces(self) -> dict:
786
+ def last_run(self) -> datetime:
207
787
  """
208
- Get the traces object
788
+ Function to get the last run of the strategy
209
789
 
210
790
  Returns:
211
- dict: The traces object
791
+ DateTime: The last run of the strategy
212
792
  """
213
- return self.traces
793
+ return self.context.last_run()