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
@@ -0,0 +1,55 @@
1
+ from sqlalchemy import Column, Integer, String, Float, ForeignKey, Boolean, \
2
+ DateTime
3
+ from sqlalchemy.orm import relationship
4
+
5
+ from investing_algorithm_framework.domain import TradeTakeProfit
6
+ from investing_algorithm_framework.infrastructure.database import SQLBaseModel
7
+ from investing_algorithm_framework.infrastructure.models.model_extension \
8
+ import SQLAlchemyModelExtension
9
+
10
+
11
+ class SQLTradeTakeProfit(
12
+ TradeTakeProfit, SQLBaseModel, SQLAlchemyModelExtension
13
+ ):
14
+ """
15
+ SQLTradeTakeProfit model
16
+
17
+ A trade take profit is a take profit strategy for a trade.
18
+
19
+ Attributes:
20
+ - trade (Trade): the trade that the take profit is for
21
+ - percentage (float): the take profit percentage
22
+ - trailing (bool): indicates whether the take profit is trailing
23
+ - sell_percentage (float) the percentage of the trade to sell
24
+ when the take profit is triggered.
25
+ - target_price (float): the target price at which to take profit.
26
+ - sell_prices (String): a serialized list of prices at which
27
+ take profits were executed.
28
+ - sell_dates (String): a serialized list of dates when
29
+ take profits were executed.
30
+ - sell_amount (float): the total amount sold due to take profits.
31
+ - sold_amount (float): the total amount that has been sold due
32
+ to take profits.
33
+ - active (bool): indicates whether the take profit is currently active.
34
+ """
35
+
36
+ __tablename__ = "trade_take_profits"
37
+ id = Column(Integer, primary_key=True, unique=True)
38
+ trade_id = Column(Integer, ForeignKey('trades.id'))
39
+ trade = relationship('SQLTrade', back_populates='take_profits')
40
+ trailing = Column(Boolean)
41
+ percentage = Column(Float)
42
+ sell_percentage = Column(Float)
43
+ open_price = Column(Float)
44
+ high_water_mark = Column(Float)
45
+ high_water_mark_date = Column(String)
46
+ sell_prices = Column(String)
47
+ take_profit_price = Column(Float)
48
+ sell_amount = Column(Float)
49
+ sell_dates = Column(String)
50
+ sold_amount = Column(Float)
51
+ active = Column(Boolean)
52
+ triggered = Column(Boolean, default=False)
53
+ triggered_at = Column(DateTime, default=None)
54
+ created_at = Column(DateTime)
55
+ updated_at = Column(DateTime, default=None)
@@ -0,0 +1,21 @@
1
+ from .ccxt_order_executor import CCXTOrderExecutor
2
+ from .backtest_oder_executor import BacktestOrderExecutor
3
+
4
+
5
+ def get_default_order_executors():
6
+ """
7
+ Function to get the default order executors.
8
+
9
+ Returns:
10
+ list: List of default order executors.
11
+ """
12
+ return [
13
+ CCXTOrderExecutor(),
14
+ ]
15
+
16
+
17
+ __all__ = [
18
+ 'CCXTOrderExecutor',
19
+ 'BacktestOrderExecutor',
20
+ 'get_default_order_executors',
21
+ ]
@@ -0,0 +1,28 @@
1
+ from investing_algorithm_framework.domain import OrderExecutor, OrderStatus, \
2
+ INDEX_DATETIME, Order
3
+
4
+
5
+ class BacktestOrderExecutor(OrderExecutor):
6
+ """
7
+ Backtest implementation of order executor. This executor is used to
8
+ simulate order execution in a backtesting environment.
9
+
10
+ !Important: This executor does not actually execute orders on any market.
11
+ It should be used only for backtesting purposes.
12
+ """
13
+
14
+ def execute_order(self, portfolio, order, market_credential) -> Order:
15
+ order.status = OrderStatus.OPEN.value
16
+ order.remaining = order.get_amount()
17
+ order.filled = 0
18
+ order.updated_at = self.config[INDEX_DATETIME]
19
+ return order
20
+
21
+ def cancel_order(self, portfolio, order, market_credential) -> Order:
22
+ order.status = OrderStatus.CANCELED.value
23
+ order.remaining = 0
24
+ order.updated_at = self.config[INDEX_DATETIME]
25
+ return order
26
+
27
+ def supports_market(self, market):
28
+ return True
@@ -0,0 +1,200 @@
1
+ from logging import getLogger
2
+
3
+ import ccxt
4
+
5
+ from investing_algorithm_framework.domain import OrderExecutor, \
6
+ OperationalException, Order, OrderStatus, OrderSide, OrderType, \
7
+ MarketCredential
8
+
9
+ logger = getLogger("investing_algorithm_framework")
10
+
11
+
12
+ class CCXTOrderExecutor(OrderExecutor):
13
+ """
14
+ CCXTOrderExecutor is a class that implements the OrderExecutor
15
+ interface for executing orders using the CCXT library.
16
+ """
17
+
18
+ def execute_order(self, portfolio, order, market_credential) -> Order:
19
+ """
20
+ Executes an order for a given portfolio on a CCXT exchange.
21
+
22
+ Args:
23
+ order: The order to be executed
24
+ portfolio: The portfolio in which the order will be executed
25
+ market_credential: The market credential to use for the order
26
+
27
+ Returns:
28
+ Order: Instance of the executed order. The order instance
29
+ should copy the id of the order that has been provided as a
30
+ """
31
+ market = portfolio.market
32
+ exchange = self.initialize_exchange(market, market_credential)
33
+ symbol = order.get_symbol()
34
+ amount = order.get_amount()
35
+ price = order.get_price()
36
+ order_type = order.get_order_type()
37
+ order_side = order.get_order_side()
38
+
39
+ try:
40
+ if OrderType.LIMIT.equals(order_type):
41
+ if OrderSide.BUY.equals(order_side):
42
+
43
+ # Check if the exchange supports the
44
+ # createLimitBuyOrder method
45
+ if not hasattr(exchange, "createLimitBuyOrder"):
46
+ raise OperationalException(
47
+ f"Exchange {market} does not support "
48
+ f"functionality createLimitBuyOrder"
49
+ )
50
+
51
+ # Create a limit buy order
52
+ external_order = exchange.createLimitBuyOrder(
53
+ symbol, amount, price,
54
+ )
55
+ else:
56
+ # Check if the exchange supports
57
+ # the createLimitSellOrder method
58
+ if not hasattr(exchange, "createLimitSellOrder"):
59
+ raise OperationalException(
60
+ f"Exchange {market} does not support "
61
+ f"functionality createLimitSellOrder"
62
+ )
63
+
64
+ # Create a limit sell order
65
+ external_order = exchange.createLimitSellOrder(
66
+ symbol, amount, price,
67
+ )
68
+ else:
69
+ raise OperationalException(
70
+ f"Order type {order_type} not supported "
71
+ f"by CCXT OrderExecutor"
72
+ )
73
+
74
+ external_order = Order.from_ccxt_order(external_order)
75
+ external_order.id = order.id
76
+ return external_order
77
+ except Exception as e:
78
+ logger.exception(e)
79
+ raise OperationalException("Could not create limit buy order")
80
+
81
+ def cancel_order(self, portfolio, order, market_credential) -> Order:
82
+ """
83
+ Cancels an order for a given portfolio on a CCXT exchange.
84
+
85
+ Args:
86
+ order: The order to be canceled
87
+ portfolio: The portfolio in which the order was executed
88
+ market_credential: The market credential to use for the order
89
+
90
+ Returns:
91
+ Order: Instance of the canceled order.
92
+ """
93
+ market = portfolio.market
94
+ exchange = self.initialize_exchange(market, market_credential)
95
+
96
+ if not exchange.has['cancelOrder']:
97
+ raise OperationalException(
98
+ f"Exchange {market} does not support "
99
+ f"functionality cancelOrder"
100
+ )
101
+
102
+ try:
103
+ exchange.cancelOrder(
104
+ order.get_external_id(),
105
+ f"{order.get_target_symbol()}/{order.get_trading_symbol()}"
106
+ )
107
+ order.status = OrderStatus.CANCELED.value
108
+ return order
109
+ except Exception as e:
110
+ logger.exception(e)
111
+ raise OperationalException("Could not cancel order")
112
+
113
+ @staticmethod
114
+ def initialize_exchange(market, market_credential):
115
+ """
116
+ Function to initialize the exchange for the market.
117
+
118
+ Args:
119
+ market (str): The market to initialize the exchange for
120
+ market_credential (MarketCredential): The market credential to use
121
+ for the exchange
122
+
123
+ Returns:
124
+
125
+ """
126
+ market = market.lower()
127
+
128
+ if not hasattr(ccxt, market):
129
+ raise OperationalException(
130
+ f"No ccxt exchange for market id {market}"
131
+ )
132
+
133
+ exchange_class = getattr(ccxt, market)
134
+
135
+ if exchange_class is None:
136
+ raise OperationalException(
137
+ f"No market service found for market id {market}"
138
+ )
139
+
140
+ # Check the credentials for the exchange
141
+ CCXTOrderExecutor.check_credentials(exchange_class, market_credential)
142
+ exchange = exchange_class({
143
+ 'apiKey': market_credential.api_key,
144
+ 'secret': market_credential.secret_key,
145
+ })
146
+ return exchange
147
+
148
+ @staticmethod
149
+ def check_credentials(
150
+ exchange_class, market_credential: MarketCredential
151
+ ):
152
+ """
153
+ Function to check if the credentials are valid for the exchange.
154
+
155
+ Args:
156
+ exchange_class: The exchange class to check the credentials for
157
+ market_credential: The market credential to use for the exchange
158
+
159
+ Raises:
160
+ OperationalException: If the credentials are not valid
161
+
162
+ Returns:
163
+ None
164
+ """
165
+ exchange = exchange_class()
166
+ credentials_info = exchange.requiredCredentials
167
+ market = market_credential.get_market()
168
+
169
+ if ('apiKey' in credentials_info
170
+ and credentials_info["apiKey"]
171
+ and market_credential.get_api_key() is None):
172
+ raise OperationalException(
173
+ f"Market credential for market {market}"
174
+ " requires an api key, either"
175
+ " as an argument or as an environment variable"
176
+ f" named as {market.upper()}_API_KEY"
177
+ )
178
+
179
+ if ('secret' in credentials_info
180
+ and credentials_info["secret"]
181
+ and market_credential.get_secret_key() is None):
182
+ raise OperationalException(
183
+ f"Market credential for market {market}"
184
+ " requires a secret key, either"
185
+ " as an argument or as an environment variable"
186
+ f" named as {market.upper()}_SECRET_KEY"
187
+ )
188
+
189
+ def supports_market(self, market):
190
+ """
191
+ Function to check if the market is supported by the portfolio
192
+ provider.
193
+
194
+ Args:
195
+ market: Market object
196
+
197
+ Returns:
198
+ bool: True if the market is supported, False otherwise
199
+ """
200
+ return hasattr(ccxt, market.lower())
@@ -0,0 +1,19 @@
1
+ from .ccxt_portfolio_provider import CCXTPortfolioProvider
2
+
3
+
4
+ def get_default_portfolio_providers():
5
+ """
6
+ Function to get the default portfolio providers.
7
+
8
+ Returns:
9
+ list: List of default portfolio providers.
10
+ """
11
+ return [
12
+ CCXTPortfolioProvider(),
13
+ ]
14
+
15
+
16
+ __all__ = [
17
+ "CCXTPortfolioProvider",
18
+ "get_default_portfolio_providers",
19
+ ]
@@ -0,0 +1,199 @@
1
+ import ccxt
2
+ from logging import getLogger
3
+ from typing import Union
4
+
5
+ from investing_algorithm_framework.domain import PortfolioProvider, \
6
+ OperationalException, Order, Position, MarketCredential
7
+
8
+
9
+ logger = getLogger("investing_algorithm_framework")
10
+
11
+
12
+ class CCXTPortfolioProvider(PortfolioProvider):
13
+ """
14
+ Implementation of Portfolio Provider for CCXT.
15
+ """
16
+
17
+ def get_order(
18
+ self, portfolio, order, market_credential
19
+ ) -> Union[Order, None]:
20
+ """
21
+ Method to check if there are any pending orders for the portfolio.
22
+ This method will retrieve the open orders from the exchange and
23
+ check if there are any pending orders for the portfolio.
24
+
25
+ !IMPORTANT: This function should return None if the order is
26
+ not found or if the order is not available on the
27
+ exchange or broker. Please do not throw an exception if the
28
+ order is not found.
29
+
30
+ Args:
31
+ portfolio: Portfolio object
32
+ order: Order object from the database
33
+ market_credential: Market credential object
34
+
35
+ Returns:
36
+ None
37
+ """
38
+ exchange = self.initialize_exchange(
39
+ portfolio.market, market_credential
40
+ )
41
+
42
+ if not exchange.has['fetchOrder']:
43
+ raise OperationalException(
44
+ f"Market service {portfolio.market} does not support "
45
+ f"functionality get_order"
46
+ )
47
+
48
+ symbol = order.get_symbol()
49
+
50
+ try:
51
+ external_order = exchange.fetchOrder(order.external_id, symbol)
52
+ external_order = Order.from_ccxt_order(external_order)
53
+ external_order.id = order.id
54
+ return external_order
55
+ except Exception as e:
56
+ logger.exception(e)
57
+ raise OperationalException("Could not retrieve order")
58
+
59
+ def get_position(
60
+ self, portfolio, symbol, market_credential
61
+ ) -> Union[Position, None]:
62
+ """
63
+ Function to get the position for a given symbol in the portfolio.
64
+ The returned position should be an object that reflects the current
65
+ state of the position on the exchange or broker.
66
+
67
+ !IMPORTANT: This function should return None if the position is
68
+ not found or if the position is not available on the
69
+ exchange or broker. Please do not throw an exception if the
70
+ position is not found.
71
+
72
+ Args:
73
+ portfolio (Portfolio): Portfolio object
74
+ symbol (str): Symbol object
75
+ market_credential (MarketCredential): MarketCredential object
76
+
77
+ Returns:
78
+ Position: Position for the given symbol in the portfolio
79
+ """
80
+
81
+ exchange = self.initialize_exchange(
82
+ portfolio.market, market_credential
83
+ )
84
+
85
+ if not exchange.has['fetchBalance']:
86
+ raise OperationalException(
87
+ f"Market service {portfolio.market} does not support "
88
+ f"functionality get_balance"
89
+ )
90
+
91
+ try:
92
+ amount = exchange.fetchBalance()["free"]
93
+
94
+ if symbol not in amount:
95
+ return None
96
+
97
+ return Position(
98
+ symbol=symbol,
99
+ amount=amount[symbol],
100
+ cost=0,
101
+ portfolio_id=portfolio.id
102
+ )
103
+ except Exception as e:
104
+ logger.exception(e)
105
+ raise OperationalException(
106
+ f"Please make sure you have "
107
+ f"registered a valid market credential "
108
+ f"object to the app: {str(e)}"
109
+ )
110
+
111
+ @staticmethod
112
+ def initialize_exchange(market, market_credential):
113
+ """
114
+ Function to initialize the exchange for the market.
115
+
116
+ Args:
117
+ market (str): The market to initialize the exchange for
118
+ market_credential (MarketCredential): The market credential to use
119
+ for the exchange
120
+
121
+ Returns:
122
+
123
+ """
124
+ market = market.lower()
125
+
126
+ if not hasattr(ccxt, market):
127
+ raise OperationalException(
128
+ f"No ccxt exchange for market id {market}"
129
+ )
130
+
131
+ exchange_class = getattr(ccxt, market)
132
+
133
+ if exchange_class is None:
134
+ raise OperationalException(
135
+ f"No market service found for market id {market}"
136
+ )
137
+
138
+ # Check the credentials for the exchange
139
+ (CCXTPortfolioProvider
140
+ .check_credentials(exchange_class, market_credential))
141
+ exchange = exchange_class({
142
+ 'apiKey': market_credential.api_key,
143
+ 'secret': market_credential.secret_key,
144
+ })
145
+ return exchange
146
+
147
+ @staticmethod
148
+ def check_credentials(
149
+ exchange_class, market_credential: MarketCredential
150
+ ):
151
+ """
152
+ Function to check if the credentials are valid for the exchange.
153
+
154
+ Args:
155
+ exchange_class: The exchange class to check the credentials for
156
+ market_credential: The market credential to use for the exchange
157
+
158
+ Raises:
159
+ OperationalException: If the credentials are not valid
160
+
161
+ Returns:
162
+ None
163
+ """
164
+ exchange = exchange_class()
165
+ credentials_info = exchange.requiredCredentials
166
+ market = market_credential.get_market()
167
+
168
+ if ('apiKey' in credentials_info
169
+ and credentials_info["apiKey"]
170
+ and market_credential.get_api_key() is None):
171
+ raise OperationalException(
172
+ f"Market credential for market {market}"
173
+ " requires an api key, either"
174
+ " as an argument or as an environment variable"
175
+ f" named as {market.upper()}_API_KEY"
176
+ )
177
+
178
+ if ('secret' in credentials_info
179
+ and credentials_info["secret"]
180
+ and market_credential.get_secret_key() is None):
181
+ raise OperationalException(
182
+ f"Market credential for market {market}"
183
+ " requires a secret key, either"
184
+ " as an argument or as an environment variable"
185
+ f" named as {market.upper()}_SECRET_KEY"
186
+ )
187
+
188
+ def supports_market(self, market):
189
+ """
190
+ Function to check if the market is supported by the portfolio
191
+ provider.
192
+
193
+ Args:
194
+ market: Market object
195
+
196
+ Returns:
197
+ bool: True if the market is supported, False otherwise
198
+ """
199
+ return hasattr(ccxt, market.lower())
@@ -1,8 +1,12 @@
1
1
  from .order_repository import SQLOrderRepository
2
+ from .order_metadata_repository import SQLOrderMetadataRepository
2
3
  from .portfolio_repository import SQLPortfolioRepository
3
4
  from .portfolio_snapshot_repository import SQLPortfolioSnapshotRepository
4
5
  from .position_repository import SQLPositionRepository
5
6
  from .position_snapshot_repository import SQLPositionSnapshotRepository
7
+ from .trade_repository import SQLTradeRepository
8
+ from .trade_stop_loss_repository import SQLTradeStopLossRepository
9
+ from .trade_take_profit_repository import SQLTradeTakeProfitRepository
6
10
 
7
11
  __all__ = [
8
12
  "SQLOrderRepository",
@@ -10,4 +14,8 @@ __all__ = [
10
14
  "SQLPositionSnapshotRepository",
11
15
  "SQLPortfolioRepository",
12
16
  "SQLPortfolioSnapshotRepository",
17
+ "SQLTradeRepository",
18
+ "SQLTradeTakeProfitRepository",
19
+ "SQLTradeStopLossRepository",
20
+ "SQLOrderMetadataRepository"
13
21
  ]
@@ -0,0 +1,17 @@
1
+ from investing_algorithm_framework.infrastructure.models import \
2
+ SQLOrderMetadata
3
+ from .repository import Repository
4
+
5
+
6
+ class SQLOrderMetadataRepository(Repository):
7
+ base_class = SQLOrderMetadata
8
+ DEFAULT_NOT_FOUND_MESSAGE = "The requested order metadata was not found"
9
+
10
+ def _apply_query_params(self, db, query, query_params):
11
+
12
+ if "order_id" in query_params:
13
+ query = query.filter(
14
+ SQLOrderMetadata.order_id == query_params["order_id"]
15
+ )
16
+
17
+ return query
@@ -7,8 +7,10 @@ from .repository import Repository
7
7
 
8
8
  class SQLOrderRepository(Repository):
9
9
  base_class = SQLOrder
10
+ DEFAULT_NOT_FOUND_MESSAGE = "The requested order was not found"
10
11
 
11
12
  def _apply_query_params(self, db, query, query_params):
13
+ id_query_param = self.get_query_param("id", query_params)
12
14
  external_id_query_param = self.get_query_param(
13
15
  "external_id", query_params
14
16
  )
@@ -33,6 +35,9 @@ class SQLOrderRepository(Repository):
33
35
  "order_by_created_at_asc", query_params
34
36
  )
35
37
 
38
+ if id_query_param:
39
+ query = query.filter_by(id=id_query_param)
40
+
36
41
  if portfolio_query_param is not None:
37
42
  portfolio = db.query(SQLPortfolio).filter_by(
38
43
  id=portfolio_query_param
@@ -20,7 +20,7 @@ class SQLPortfolioRepository(Repository):
20
20
  query = query.filter_by(market=market_query_param.upper())
21
21
 
22
22
  if identifier_query_param:
23
- query = query.filter_by(identifier=identifier_query_param.lower())
23
+ query = query.filter_by(identifier=identifier_query_param.upper())
24
24
 
25
25
  if position_query_param:
26
26
  position = db.query(SQLPosition)\
@@ -9,6 +9,7 @@ class SQLPositionRepository(Repository):
9
9
  DEFAULT_NOT_FOUND_MESSAGE = "Position not found"
10
10
 
11
11
  def _apply_query_params(self, db, query, query_params):
12
+ id_query_param = self.get_query_param("id", query_params)
12
13
  amount_query_param = self.get_query_param("amount", query_params)
13
14
  symbol_query_param = self.get_query_param("symbol", query_params)
14
15
  portfolio_query_param = self.get_query_param("portfolio", query_params)
@@ -20,6 +21,10 @@ class SQLPositionRepository(Repository):
20
21
  amount_lte_query_param = self.get_query_param(
21
22
  "amount_lte", query_params
22
23
  )
24
+ order_id_query_param = self.get_query_param("order_id", query_params)
25
+
26
+ if id_query_param:
27
+ query = query.filter_by(id=id_query_param)
23
28
 
24
29
  if amount_query_param:
25
30
  query = query.filter(
@@ -51,5 +56,11 @@ class SQLPositionRepository(Repository):
51
56
  query = query.filter(
52
57
  cast(SQLPosition.amount, Numeric) <= amount_lte_query_param
53
58
  )
59
+ # Filter by order_id, orders is a one-to-many relationship
60
+ # with 3 position
61
+ if order_id_query_param:
62
+ query = query.filter(
63
+ SQLPosition.orders.any(id=order_id_query_param)
64
+ )
54
65
 
55
66
  return query