investing-algorithm-framework 1.5__py3-none-any.whl → 7.25.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. investing_algorithm_framework/__init__.py +192 -16
  2. investing_algorithm_framework/analysis/__init__.py +16 -0
  3. investing_algorithm_framework/analysis/backtest_data_ranges.py +202 -0
  4. investing_algorithm_framework/analysis/data.py +170 -0
  5. investing_algorithm_framework/analysis/markdown.py +91 -0
  6. investing_algorithm_framework/analysis/ranking.py +298 -0
  7. investing_algorithm_framework/app/__init__.py +29 -4
  8. investing_algorithm_framework/app/algorithm/__init__.py +7 -0
  9. investing_algorithm_framework/app/algorithm/algorithm.py +193 -0
  10. investing_algorithm_framework/app/algorithm/algorithm_factory.py +118 -0
  11. investing_algorithm_framework/app/app.py +2220 -379
  12. investing_algorithm_framework/app/app_hook.py +28 -0
  13. investing_algorithm_framework/app/context.py +1724 -0
  14. investing_algorithm_framework/app/eventloop.py +620 -0
  15. investing_algorithm_framework/app/reporting/__init__.py +27 -0
  16. investing_algorithm_framework/app/reporting/ascii.py +921 -0
  17. investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
  18. investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
  19. investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
  20. investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
  21. investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
  22. investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
  23. investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
  24. investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
  25. investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
  26. investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
  27. investing_algorithm_framework/app/reporting/generate.py +185 -0
  28. investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
  29. investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
  30. investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
  31. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
  32. investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
  33. investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
  34. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
  35. investing_algorithm_framework/app/stateless/action_handlers/__init__.py +6 -3
  36. investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
  37. investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +2 -1
  38. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
  39. investing_algorithm_framework/app/strategy.py +867 -60
  40. investing_algorithm_framework/app/task.py +5 -3
  41. investing_algorithm_framework/app/web/__init__.py +2 -1
  42. investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
  43. investing_algorithm_framework/app/web/controllers/orders.py +3 -2
  44. investing_algorithm_framework/app/web/controllers/positions.py +2 -2
  45. investing_algorithm_framework/app/web/create_app.py +4 -2
  46. investing_algorithm_framework/app/web/schemas/position.py +1 -0
  47. investing_algorithm_framework/cli/__init__.py +0 -0
  48. investing_algorithm_framework/cli/cli.py +231 -0
  49. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
  50. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  51. investing_algorithm_framework/cli/initialize_app.py +603 -0
  52. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  53. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  54. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  55. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  56. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  57. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  58. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  59. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  60. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  61. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  62. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  63. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  64. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  65. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  66. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  67. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  68. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  69. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  70. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  71. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  72. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  73. investing_algorithm_framework/cli/validate_backtest_checkpoints.py +197 -0
  74. investing_algorithm_framework/create_app.py +40 -7
  75. investing_algorithm_framework/dependency_container.py +100 -47
  76. investing_algorithm_framework/domain/__init__.py +97 -30
  77. investing_algorithm_framework/domain/algorithm_id.py +69 -0
  78. investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
  79. investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
  80. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
  81. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
  82. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
  83. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  84. investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
  85. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  86. investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
  87. investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
  88. investing_algorithm_framework/domain/config.py +59 -136
  89. investing_algorithm_framework/domain/constants.py +18 -37
  90. investing_algorithm_framework/domain/data_provider.py +334 -0
  91. investing_algorithm_framework/domain/data_structures.py +42 -0
  92. investing_algorithm_framework/domain/exceptions.py +51 -1
  93. investing_algorithm_framework/domain/models/__init__.py +26 -19
  94. investing_algorithm_framework/domain/models/app_mode.py +34 -0
  95. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  96. investing_algorithm_framework/domain/models/data/data_source.py +222 -0
  97. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  98. investing_algorithm_framework/domain/models/event.py +35 -0
  99. investing_algorithm_framework/domain/models/market/__init__.py +5 -0
  100. investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
  101. investing_algorithm_framework/domain/models/order/__init__.py +3 -4
  102. investing_algorithm_framework/domain/models/order/order.py +198 -65
  103. investing_algorithm_framework/domain/models/order/order_status.py +2 -2
  104. investing_algorithm_framework/domain/models/order/order_type.py +1 -3
  105. investing_algorithm_framework/domain/models/portfolio/__init__.py +6 -2
  106. investing_algorithm_framework/domain/models/portfolio/portfolio.py +98 -3
  107. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -43
  108. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
  109. investing_algorithm_framework/domain/models/position/__init__.py +2 -1
  110. investing_algorithm_framework/domain/models/position/position.py +20 -0
  111. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  112. investing_algorithm_framework/domain/models/position/position_snapshot.py +0 -2
  113. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  114. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  115. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  116. investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
  117. investing_algorithm_framework/domain/models/strategy_profile.py +19 -141
  118. investing_algorithm_framework/domain/models/time_frame.py +94 -98
  119. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  120. investing_algorithm_framework/domain/models/time_unit.py +66 -2
  121. investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
  122. investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
  123. investing_algorithm_framework/domain/models/trade/__init__.py +11 -0
  124. investing_algorithm_framework/domain/models/trade/trade.py +389 -0
  125. investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
  126. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
  127. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
  128. investing_algorithm_framework/domain/order_executor.py +112 -0
  129. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  130. investing_algorithm_framework/domain/services/__init__.py +11 -0
  131. investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
  132. investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
  133. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
  134. investing_algorithm_framework/domain/services/rounding_service.py +27 -0
  135. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  136. investing_algorithm_framework/domain/strategy.py +1 -29
  137. investing_algorithm_framework/domain/utils/__init__.py +15 -5
  138. investing_algorithm_framework/domain/utils/csv.py +22 -0
  139. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  140. investing_algorithm_framework/domain/utils/dates.py +57 -0
  141. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  142. investing_algorithm_framework/domain/utils/polars.py +53 -0
  143. investing_algorithm_framework/domain/utils/random.py +29 -0
  144. investing_algorithm_framework/download_data.py +244 -0
  145. investing_algorithm_framework/infrastructure/__init__.py +37 -11
  146. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  147. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -0
  148. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  149. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  150. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  151. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +86 -12
  152. investing_algorithm_framework/infrastructure/models/__init__.py +7 -3
  153. investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
  154. investing_algorithm_framework/infrastructure/models/order/order.py +53 -53
  155. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  156. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  157. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +1 -1
  158. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -2
  159. investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -6
  160. investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +3 -1
  161. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  162. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  163. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
  164. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
  165. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  166. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  167. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  168. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  169. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  170. investing_algorithm_framework/infrastructure/repositories/__init__.py +10 -4
  171. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  172. investing_algorithm_framework/infrastructure/repositories/order_repository.py +16 -5
  173. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
  174. investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
  175. investing_algorithm_framework/infrastructure/repositories/repository.py +84 -30
  176. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  177. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
  178. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
  179. investing_algorithm_framework/infrastructure/services/__init__.py +9 -4
  180. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  181. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -0
  182. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  183. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  184. investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +9 -0
  185. investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
  186. investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
  187. investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
  188. investing_algorithm_framework/services/__init__.py +123 -15
  189. investing_algorithm_framework/services/configuration_service.py +77 -11
  190. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  191. investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
  192. investing_algorithm_framework/services/market_credential_service.py +40 -0
  193. investing_algorithm_framework/services/metrics/__init__.py +119 -0
  194. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  195. investing_algorithm_framework/services/metrics/beta.py +0 -0
  196. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  197. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  198. investing_algorithm_framework/services/metrics/drawdown.py +218 -0
  199. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  200. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  201. investing_algorithm_framework/services/metrics/generate.py +358 -0
  202. investing_algorithm_framework/services/metrics/mean_daily_return.py +84 -0
  203. investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
  204. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  205. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  206. investing_algorithm_framework/services/metrics/returns.py +452 -0
  207. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  208. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  209. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  210. investing_algorithm_framework/services/metrics/standard_deviation.py +156 -0
  211. investing_algorithm_framework/services/metrics/trades.py +473 -0
  212. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  213. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  214. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  215. investing_algorithm_framework/services/metrics/volatility.py +118 -0
  216. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  217. investing_algorithm_framework/services/order_service/__init__.py +9 -0
  218. investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
  219. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  220. investing_algorithm_framework/services/order_service/order_service.py +826 -0
  221. investing_algorithm_framework/services/portfolios/__init__.py +16 -0
  222. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
  223. investing_algorithm_framework/services/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
  224. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  225. investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
  226. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
  227. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
  228. investing_algorithm_framework/services/positions/__init__.py +7 -0
  229. investing_algorithm_framework/services/positions/position_service.py +210 -0
  230. investing_algorithm_framework/services/repository_service.py +8 -2
  231. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  232. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +117 -0
  233. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  234. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  235. investing_algorithm_framework/services/trade_service/__init__.py +9 -0
  236. investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
  237. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  238. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  239. investing_algorithm_framework-7.25.6.dist-info/METADATA +535 -0
  240. investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
  241. {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
  242. investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
  243. investing_algorithm_framework/app/algorithm.py +0 -630
  244. investing_algorithm_framework/domain/models/backtest_profile.py +0 -414
  245. investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
  246. investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
  247. investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -105
  248. investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
  249. investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
  250. investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
  251. investing_algorithm_framework/domain/models/trade.py +0 -78
  252. investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
  253. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
  254. investing_algorithm_framework/domain/singleton.py +0 -9
  255. investing_algorithm_framework/domain/utils/backtesting.py +0 -82
  256. investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
  257. investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
  258. investing_algorithm_framework/infrastructure/services/market_backtest_service.py +0 -360
  259. investing_algorithm_framework/infrastructure/services/market_service.py +0 -410
  260. investing_algorithm_framework/infrastructure/services/performance_service.py +0 -192
  261. investing_algorithm_framework/services/backtest_service.py +0 -268
  262. investing_algorithm_framework/services/market_data_service.py +0 -77
  263. investing_algorithm_framework/services/order_backtest_service.py +0 -122
  264. investing_algorithm_framework/services/order_service.py +0 -752
  265. investing_algorithm_framework/services/portfolio_service.py +0 -164
  266. investing_algorithm_framework/services/portfolio_snapshot_service.py +0 -68
  267. investing_algorithm_framework/services/position_cost_service.py +0 -5
  268. investing_algorithm_framework/services/position_service.py +0 -63
  269. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -225
  270. investing_algorithm_framework-1.5.dist-info/AUTHORS.md +0 -8
  271. investing_algorithm_framework-1.5.dist-info/METADATA +0 -230
  272. investing_algorithm_framework-1.5.dist-info/RECORD +0 -119
  273. investing_algorithm_framework-1.5.dist-info/top_level.txt +0 -1
  274. /investing_algorithm_framework/{infrastructure/services/performance_backtest_service.py → app/reporting/tables/stop_loss_table.py} +0 -0
  275. /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
  276. {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -0
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
 
3
3
  from sqlalchemy import create_engine, StaticPool
4
+ from sqlalchemy import inspect
4
5
  from sqlalchemy.orm import DeclarativeBase, sessionmaker
5
6
 
6
7
  from investing_algorithm_framework.domain import SQLALCHEMY_DATABASE_URI, \
@@ -19,18 +20,11 @@ class SQLAlchemyAdapter:
19
20
  raise OperationalException("SQLALCHEMY_DATABASE_URI not set")
20
21
 
21
22
  global Session
22
-
23
- if app.config[SQLALCHEMY_DATABASE_URI] != "sqlite:///:memory:":
24
- engine = create_engine(
25
- app.config[SQLALCHEMY_DATABASE_URI],
26
- connect_args={'check_same_thread': False},
27
- poolclass=StaticPool
28
- )
29
- else:
30
- engine = create_engine(
31
- app.config[SQLALCHEMY_DATABASE_URI],
32
- )
33
-
23
+ engine = create_engine(
24
+ app.config[SQLALCHEMY_DATABASE_URI],
25
+ connect_args={'check_same_thread': False},
26
+ poolclass=StaticPool
27
+ )
34
28
  Session.configure(bind=engine)
35
29
 
36
30
 
@@ -45,9 +39,89 @@ def setup_sqlalchemy(app, throw_exception_if_not_set=True):
45
39
  return app
46
40
 
47
41
 
42
+
48
43
  class SQLBaseModel(DeclarativeBase):
49
44
  pass
50
45
 
51
46
 
52
47
  def create_all_tables():
53
48
  SQLBaseModel.metadata.create_all(bind=Session().bind)
49
+
50
+
51
+ from sqlalchemy import event
52
+ from sqlalchemy.orm import mapper
53
+ from datetime import timezone
54
+
55
+ def clear_db(db_uri):
56
+ """
57
+ Clear the database by dropping all tables.
58
+ This is useful for testing purposes.
59
+
60
+ Args:
61
+ db_uri (str): The database URI to connect to.
62
+
63
+ Returns:
64
+ None
65
+ """
66
+ # Drop all tables before deleting file
67
+ try:
68
+ engine = create_engine(db_uri)
69
+ inspector = inspect(engine)
70
+ if inspector.get_table_names():
71
+ logger.info("Dropping all tables in backtest database")
72
+ SQLBaseModel.metadata.drop_all(bind=engine)
73
+ except Exception as e:
74
+ logger.error(f"Error dropping tables: {e}")
75
+
76
+ # # Clear mappers (if using classical mappings)
77
+ # try:
78
+ # clear_mappers()
79
+ # except Exception:
80
+ # pass # ignore if not needed
81
+
82
+
83
+ @event.listens_for(mapper, "load")
84
+ def attach_utc_timezone_on_load(target, context):
85
+ """
86
+ For each model instance loaded from the database,
87
+ this function will check if one of the following attributes are
88
+ present: created_at, updated_at, closed_at, opened_at, triggered_at.
89
+ If so, it will check if these datetime
90
+ attributes are timezone-naive and, if so, will set them to UTC.
91
+
92
+ Its documented in the contributing guide (https://coding-kitties.github
93
+ .io/investing-algorithm-framework/Contributing%20Guide/contributing)
94
+ that each datetime attribute should be utc timezone-aware.
95
+
96
+ Args:
97
+ target: The model instance being loaded from the database.
98
+ context: The context in which the event is being handled.
99
+
100
+ Returns:
101
+ None
102
+ """
103
+ # This will apply to every model instance loaded from the DB
104
+ if hasattr(target, "created_at"):
105
+ dt = getattr(target, "created_at")
106
+ if dt and dt.tzinfo is None:
107
+ target.created_at = dt.replace(tzinfo=timezone.utc)
108
+
109
+ if hasattr(target, "updated_at"):
110
+ dt = getattr(target, "updated_at")
111
+ if dt and dt.tzinfo is None:
112
+ target.updated_at = dt.replace(tzinfo=timezone.utc)
113
+
114
+ if hasattr(target, "closed_at"):
115
+ dt = getattr(target, "closed_at")
116
+ if dt and dt.tzinfo is None:
117
+ target.closed_at = dt.replace(tzinfo=timezone.utc)
118
+
119
+ if hasattr(target, "opened_at"):
120
+ dt = getattr(target, "opened_at")
121
+ if dt and dt.tzinfo is None:
122
+ target.opened_at = dt.replace(tzinfo=timezone.utc)
123
+
124
+ if hasattr(target, "triggered_at"):
125
+ dt = getattr(target, "triggered_at")
126
+ if dt and dt.tzinfo is None:
127
+ target.triggered_at = dt.replace(tzinfo=timezone.utc)
@@ -1,12 +1,16 @@
1
- from .order import SQLOrder, SQLOrderFee
1
+ from .order import SQLOrder, SQLOrderMetadata
2
2
  from .portfolio import SQLPortfolio, SQLPortfolioSnapshot
3
3
  from .position import SQLPosition, SQLPositionSnapshot
4
+ from .trades import SQLTrade, SQLTradeStopLoss, SQLTradeTakeProfit
4
5
 
5
6
  __all__ = [
6
7
  "SQLOrder",
7
8
  "SQLPosition",
8
9
  "SQLPortfolio",
9
- "SQLOrderFee",
10
10
  "SQLPositionSnapshot",
11
- "SQLPortfolioSnapshot"
11
+ "SQLPortfolioSnapshot",
12
+ "SQLTrade",
13
+ "SQLTradeStopLoss",
14
+ "SQLTradeTakeProfit",
15
+ "SQLOrderMetadata",
12
16
  ]
@@ -1,4 +1,4 @@
1
1
  from .order import SQLOrder
2
- from .order_fee import SQLOrderFee
2
+ from .order_metadata import SQLOrderMetadata
3
3
 
4
- __all__ = ["SQLOrder", "SQLOrderFee"]
4
+ __all__ = ["SQLOrder", "SQLOrderMetadata"]
@@ -1,19 +1,28 @@
1
1
  import logging
2
- from datetime import datetime
2
+ from datetime import datetime, timezone
3
3
 
4
4
  from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Float
5
5
  from sqlalchemy.orm import relationship
6
6
 
7
7
  from investing_algorithm_framework.domain import OrderType, \
8
- OrderSide, Order, OrderStatus, OrderFee
8
+ OrderSide, Order, OrderStatus
9
9
  from investing_algorithm_framework.infrastructure.database import SQLBaseModel
10
10
  from investing_algorithm_framework.infrastructure.models.model_extension \
11
11
  import SQLAlchemyModelExtension
12
+ from investing_algorithm_framework.infrastructure.models.\
13
+ order_trade_association import order_trade_association
12
14
 
13
15
  logger = logging.getLogger("investing_algorithm_framework")
14
16
 
15
17
 
18
+ def utcnow():
19
+ return datetime.now(tz=timezone.utc)
20
+
21
+
16
22
  class SQLOrder(Order, SQLBaseModel, SQLAlchemyModelExtension):
23
+ """
24
+ SQLOrder model based on the Order domain model.
25
+ """
17
26
  __tablename__ = "orders"
18
27
  id = Column(Integer, primary_key=True, unique=True)
19
28
  external_id = Column(Integer)
@@ -21,61 +30,33 @@ class SQLOrder(Order, SQLBaseModel, SQLAlchemyModelExtension):
21
30
  trading_symbol = Column(String)
22
31
  order_side = Column(String, nullable=False, default=OrderSide.BUY.value)
23
32
  order_type = Column(String, nullable=False, default=OrderType.LIMIT.value)
33
+ trades = relationship(
34
+ 'SQLTrade', secondary=order_trade_association, back_populates='orders'
35
+ )
24
36
  price = Column(Float)
25
37
  amount = Column(Float)
26
- filled = Column(Float)
27
- remaining = Column(Float)
28
- cost = Column(Float)
29
- status = Column(String)
38
+ remaining = Column(Float, default=None)
39
+ filled = Column(Float, default=None)
40
+ cost = Column(Float, default=0)
41
+ status = Column(String, default=OrderStatus.CREATED.value)
30
42
  position_id = Column(Integer, ForeignKey('positions.id'))
31
43
  position = relationship("SQLPosition", back_populates="orders")
32
- created_at = Column(DateTime, default=datetime.utcnow)
33
- updated_at = Column(DateTime, default=datetime.utcnow)
34
- trade_closed_at = Column(DateTime, default=None)
35
- trade_closed_price = Column(Float, default=None)
36
- trade_closed_amount = Column(Float, default=None)
37
- net_gain = Column(Float, default=0)
38
- fee = relationship(
39
- "SQLOrderFee",
40
- uselist=False,
41
- back_populates="order",
42
- cascade="all, delete"
44
+ created_at = Column(DateTime(timezone=True), default=utcnow)
45
+ updated_at = Column(
46
+ DateTime(timezone=True), default=utcnow, onupdate=utcnow
47
+ )
48
+ order_fee = Column(Float, default=None)
49
+ order_fee_currency = Column(String, default=None)
50
+ order_fee_rate = Column(Float, default=None)
51
+ sell_order_metadata_id = Column(Integer, ForeignKey('orders.id'))
52
+ order_metadata = relationship(
53
+ 'SQLOrderMetadata', back_populates='order'
43
54
  )
44
55
 
45
56
  def update(self, data):
46
57
 
47
- if 'amount' in data and data['amount'] is not None:
48
- amount = data.pop('amount')
49
- self.amount = amount
50
-
51
- if 'price' in data and data['price'] is not None:
52
- price = data.pop('price')
53
- self.price = price
54
-
55
- if 'filled' in data and data['filled'] is not None:
56
- filled = data.pop('filled')
57
- self.filled = filled
58
-
59
- if 'remaining' in data and data['remaining'] is not None:
60
- remaining = data.pop('remaining')
61
- self.remaining = remaining
62
-
63
- if 'net_gain' in data and data['net_gain'] is not None:
64
- net_gain = data.pop('net_gain')
65
- self.net_gain = net_gain
66
-
67
- if 'trade_closed_price' in data and \
68
- data['trade_closed_price'] is not None:
69
- trade_closed_price = data.pop('trade_closed_price')
70
- self.trade_closed_price = trade_closed_price
71
-
72
- if 'trade_closed_amount' in data and \
73
- data['trade_closed_amount'] is not None:
74
- trade_closed_amount = data.pop('trade_closed_amount')
75
- self.trade_closed_amount = trade_closed_amount
76
-
77
58
  if "status" in data and data["status"] is not None:
78
- self.status = OrderStatus.from_value(data.pop("status")).value
59
+ data["status"] = OrderStatus.from_value(data["status"]).value
79
60
 
80
61
  super().update(data)
81
62
 
@@ -84,6 +65,8 @@ class SQLOrder(Order, SQLBaseModel, SQLAlchemyModelExtension):
84
65
  return SQLOrder(
85
66
  external_id=order.external_id,
86
67
  amount=order.get_amount(),
68
+ filled=order.get_filled(),
69
+ remaining=order.get_remaining(),
87
70
  price=order.price,
88
71
  order_type=order.get_order_type(),
89
72
  order_side=order.get(),
@@ -92,17 +75,32 @@ class SQLOrder(Order, SQLBaseModel, SQLAlchemyModelExtension):
92
75
  trading_symbol=order.get_trading_symbol(),
93
76
  created_at=order.get_created_at(),
94
77
  updated_at=order.get_updated_at(),
95
- trade_closed_at=order.get_trade_closed_at(),
96
- trade_closed_price=order.get_trade_closed_price(),
97
- trade_closed_amount=order.get_trade_closed_amount(),
98
- net_gain=order.get_net_gain(),
99
78
  )
100
79
 
101
80
  @staticmethod
102
81
  def from_ccxt_order(ccxt_order):
82
+ """
83
+ Create an Order object from a CCXT order object
84
+
85
+ Args:
86
+ ccxt_order: CCXT order object
87
+
88
+ Returns:
89
+ Order: Order object
90
+ """
103
91
  status = OrderStatus.from_value(ccxt_order["status"])
104
92
  target_symbol = ccxt_order.get("symbol").split("/")[0]
105
93
  trading_symbol = ccxt_order.get("symbol").split("/")[1]
94
+ ccxt_fee = ccxt_order.get("fee", None)
95
+ order_fee = None
96
+ order_fee_rate = None
97
+ order_fee_currency = None
98
+
99
+ if ccxt_fee is not None:
100
+ order_fee = ccxt_fee.get("cost", None)
101
+ order_fee_rate = ccxt_fee.get("rate", None)
102
+ order_fee_currency = ccxt_fee.get("currency", None)
103
+
106
104
  return Order(
107
105
  external_id=ccxt_order.get("id", None),
108
106
  target_symbol=target_symbol,
@@ -115,7 +113,9 @@ class SQLOrder(Order, SQLBaseModel, SQLAlchemyModelExtension):
115
113
  filled=ccxt_order.get("filled", None),
116
114
  remaining=ccxt_order.get("remaining", None),
117
115
  cost=ccxt_order.get("cost", None),
118
- fee=OrderFee.from_ccxt_fee(ccxt_order.get("fee", None)),
116
+ order_fee=order_fee,
117
+ order_fee_rate=order_fee_rate,
118
+ order_fee_currency=order_fee_currency,
119
119
  created_at=ccxt_order.get("datetime", None),
120
120
  )
121
121
 
@@ -0,0 +1,44 @@
1
+ import logging
2
+
3
+ from sqlalchemy import Column, Integer, ForeignKey, Float
4
+ from sqlalchemy.orm import relationship
5
+
6
+ from investing_algorithm_framework.infrastructure.database import SQLBaseModel
7
+ from investing_algorithm_framework.infrastructure.models.model_extension \
8
+ import SQLAlchemyModelExtension
9
+
10
+ logger = logging.getLogger("investing_algorithm_framework")
11
+
12
+
13
+ class SQLOrderMetadata(SQLBaseModel, SQLAlchemyModelExtension):
14
+ __tablename__ = "sql_order_metadata"
15
+ id = Column(Integer, primary_key=True, unique=True)
16
+ order_id = Column(Integer, ForeignKey('orders.id'))
17
+ order = relationship('SQLOrder', back_populates='order_metadata')
18
+ trade_id = Column(Integer)
19
+ stop_loss_id = Column(Integer)
20
+ take_profit_id = Column(Integer)
21
+ amount = Column(Float)
22
+ amount_pending = Column(Float)
23
+
24
+ def __init__(
25
+ self,
26
+ order_id,
27
+ amount,
28
+ amount_pending,
29
+ trade_id=None,
30
+ stop_loss_id=None,
31
+ take_profit_id=None,
32
+ ):
33
+ self.order_id = order_id
34
+ self.trade_id = trade_id
35
+ self.stop_loss_id = stop_loss_id
36
+ self.take_profit_id = take_profit_id
37
+ self.amount = amount
38
+ self.amount_pending = amount_pending
39
+
40
+ def __repr__(self):
41
+ return f"<SQLOrderMetadata(id={self.id}, order_id={self.order_id}, " \
42
+ f"trade_id={self.trade_id}, stop_loss_id={self.stop_loss_id}, "\
43
+ f"take_profit_id={self.take_profit_id}, amount={self.amount}, "\
44
+ f"amount_pending={self.amount_pending})>"
@@ -0,0 +1,10 @@
1
+ from sqlalchemy import Table, Column, Integer, ForeignKey
2
+ from investing_algorithm_framework.infrastructure.database import SQLBaseModel
3
+
4
+ # Association table
5
+ order_trade_association = Table(
6
+ 'order_trade', # Table name
7
+ SQLBaseModel.metadata,
8
+ Column('order_id', Integer, ForeignKey('orders.id'), primary_key=True),
9
+ Column('trade_id', Integer, ForeignKey('trades.id'), primary_key=True)
10
+ )
@@ -1,4 +1,4 @@
1
- from .portfolio import SQLPortfolio
1
+ from .sql_portfolio import SQLPortfolio
2
2
  from .portfolio_snapshot import SQLPortfolioSnapshot
3
3
 
4
4
  __all__ = ['SQLPortfolio', "SQLPortfolioSnapshot"]
@@ -10,15 +10,23 @@ from investing_algorithm_framework.infrastructure.models.model_extension \
10
10
  class SQLPortfolioSnapshot(
11
11
  PortfolioSnapshot, SQLBaseModel, SQLAlchemyModelExtension
12
12
  ):
13
+ """
14
+ SQLAlchemy model for portfolio snapshots.
15
+
16
+ Portfolio snapshots represent the state of a portfolio at a specific
17
+ point in time.
18
+ """
13
19
  __tablename__ = "portfolio_snapshots"
14
20
  id = Column(Integer, primary_key=True)
15
21
  portfolio_id = Column(String, nullable=False)
16
22
  trading_symbol = Column(String, nullable=False)
17
23
  pending_value = Column(Float, nullable=False, default=0)
18
24
  unallocated = Column(Float, nullable=False, default=0)
25
+ net_size = Column(Float, nullable=False, default=0)
19
26
  total_net_gain = Column(Float, nullable=False, default=0)
20
27
  total_revenue = Column(Float, nullable=False, default=0)
21
28
  total_cost = Column(Float, nullable=False, default=0)
29
+ total_value = Column(Float, nullable=False, default=0)
22
30
  cash_flow = Column(Float, nullable=False, default=0)
23
31
  created_at = Column(DateTime, nullable=False, default=0)
24
32
  position_snapshots = relationship(
@@ -27,5 +35,3 @@ class SQLPortfolioSnapshot(
27
35
  lazy="dynamic",
28
36
  cascade="all,delete",
29
37
  )
30
-
31
-
@@ -1,6 +1,6 @@
1
- from datetime import datetime
1
+ from datetime import datetime, timezone
2
2
 
3
- from sqlalchemy import Column, Integer, String, DateTime, Float
3
+ from sqlalchemy import Column, Integer, String, DateTime, Float, Boolean
4
4
  from sqlalchemy import UniqueConstraint
5
5
  from sqlalchemy.orm import relationship
6
6
  from sqlalchemy.orm import validates
@@ -23,6 +23,7 @@ class SQLPortfolio(Portfolio, SQLBaseModel, SQLAlchemyModelExtension):
23
23
  total_trade_volume = Column(Float, nullable=False, default=0)
24
24
  net_size = Column(Float, nullable=False, default=0)
25
25
  unallocated = Column(Float, nullable=False, default=0)
26
+ initial_balance = Column(Float, nullable=True)
26
27
  market = Column(String, nullable=False)
27
28
  positions = relationship(
28
29
  "SQLPosition",
@@ -30,8 +31,10 @@ class SQLPortfolio(Portfolio, SQLBaseModel, SQLAlchemyModelExtension):
30
31
  lazy="dynamic",
31
32
  cascade="all,delete",
32
33
  )
33
- created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
34
- updated_at = Column(DateTime, nullable=False, default=datetime.utcnow)
34
+ created_at = Column(DateTime, nullable=False)
35
+ updated_at = Column(DateTime, nullable=False)
36
+ initialized = Column(Boolean, nullable=False, default=False)
37
+
35
38
  __table_args__ = (
36
39
  UniqueConstraint(
37
40
  'trading_symbol',
@@ -53,15 +56,21 @@ class SQLPortfolio(Portfolio, SQLBaseModel, SQLAlchemyModelExtension):
53
56
  trading_symbol,
54
57
  market,
55
58
  unallocated,
59
+ initialized,
60
+ initial_balance=None,
56
61
  identifier=None,
57
62
  created_at=None,
63
+ updated_at=None,
58
64
  ):
59
65
 
60
66
  if identifier is None:
61
67
  identifier = market
62
68
 
63
69
  if created_at is None:
64
- created_at = datetime.utcnow()
70
+ created_at = datetime.now(tz=timezone.utc)
71
+
72
+ if updated_at is None:
73
+ updated_at = datetime.now(tz=timezone.utc)
65
74
 
66
75
  super().__init__(
67
76
  trading_symbol=trading_symbol,
@@ -73,6 +82,9 @@ class SQLPortfolio(Portfolio, SQLBaseModel, SQLAlchemyModelExtension):
73
82
  total_revenue=0,
74
83
  total_cost=0,
75
84
  created_at=created_at,
85
+ updated_at=updated_at,
86
+ initialized=initialized,
87
+ initial_balance=initial_balance,
76
88
  )
77
89
 
78
90
  def update(self, data):
@@ -89,7 +101,6 @@ class SQLPortfolio(Portfolio, SQLBaseModel, SQLAlchemyModelExtension):
89
101
  if "total_revenue" in data:
90
102
  self.total_revenue = data.pop("total_revenue")
91
103
 
92
-
93
104
  if "total_trade_volume" in data:
94
105
  self.total_trade_volume = data.pop("total_trade_volume")
95
106
 
@@ -15,7 +15,9 @@ class SQLPositionSnapshot(
15
15
  symbol = Column(String)
16
16
  amount = Column(Float)
17
17
  cost = Column(Float)
18
- portfolio_snapshot_id = Column(Integer, ForeignKey('portfolio_snapshots.id'))
18
+ portfolio_snapshot_id = Column(
19
+ Integer, ForeignKey('portfolio_snapshots.id')
20
+ )
19
21
  portfolio_snapshot = relationship(
20
22
  "SQLPortfolioSnapshot", back_populates="position_snapshots"
21
23
  )
@@ -0,0 +1,9 @@
1
+ from .trade import SQLTrade
2
+ from .trade_stop_loss import SQLTradeStopLoss
3
+ from .trade_take_profit import SQLTradeTakeProfit
4
+
5
+ __all__ = [
6
+ "SQLTrade",
7
+ "SQLTradeStopLoss",
8
+ "SQLTradeTakeProfit",
9
+ ]
@@ -0,0 +1,130 @@
1
+ from sqlalchemy import Column, Integer, String, DateTime, Float
2
+ from sqlalchemy.orm import relationship
3
+
4
+ from investing_algorithm_framework.domain import Trade, TradeStatus
5
+ from investing_algorithm_framework.infrastructure.database import SQLBaseModel
6
+ from investing_algorithm_framework.infrastructure.models.model_extension \
7
+ import SQLAlchemyModelExtension
8
+ from investing_algorithm_framework.infrastructure.models\
9
+ .order_trade_association import order_trade_association
10
+
11
+
12
+ class SQLTrade(Trade, SQLBaseModel, SQLAlchemyModelExtension):
13
+ """
14
+ SQL Trade model
15
+
16
+ A trade is a combination of a buy and sell order that has been opened or
17
+ closed.
18
+
19
+ A trade is considered opened when a buy order is executed and there is
20
+ no corresponding sell order. A trade is considered closed when a sell
21
+ order is executed and the amount of the sell order is equal or larger
22
+ to the amount of the buy order.
23
+
24
+ A single sell order can close multiple buy orders. Also, a single
25
+ buy order can be closed by multiple sell orders.
26
+
27
+ Attributes:
28
+ orders: str, the id of the buy order
29
+ target_symbol: str, the target symbol of the trade
30
+ trading_symbol: str, the trading symbol of the trade
31
+ closed_at: datetime, the datetime when the trade was closed
32
+ amount: float, the amount of the trade
33
+ available_amount: float, the available amount of the trade
34
+ remaining: float, the remaining amount that is not filled by the
35
+ buy order that opened the trade.
36
+ filled_amount: float, the filled amount of the trade by the buy
37
+ order that opened the trade.
38
+ net_gain: float, the net gain of the trade
39
+ last_reported_price: float, the last reported price of the trade
40
+ last_reported_price_datetime: datetime, the datetime when the last
41
+ reported price was reported
42
+ created_at: datetime, the datetime when the trade was created
43
+ updated_at: datetime, the datetime when the trade was last updated
44
+ status: str, the status of the trade
45
+ """
46
+
47
+ __tablename__ = "trades"
48
+ id = Column(Integer, primary_key=True, unique=True)
49
+ orders = relationship(
50
+ 'SQLOrder',
51
+ secondary=order_trade_association,
52
+ back_populates='trades',
53
+ lazy='joined'
54
+ )
55
+ target_symbol = Column(String)
56
+ trading_symbol = Column(String)
57
+ closed_at = Column(DateTime, default=None)
58
+ opened_at = Column(DateTime, default=None)
59
+ open_price = Column(Float, default=None)
60
+ amount = Column(Float, default=None)
61
+ available_amount = Column(Float, default=None)
62
+ filled_amount = Column(Float, default=None)
63
+ remaining = Column(Float, default=None)
64
+ net_gain = Column(Float, default=0)
65
+ cost = Column(Float, default=0)
66
+ last_reported_price = Column(Float, default=None)
67
+ last_reported_price_datetime = Column(DateTime, default=None)
68
+ high_water_mark = Column(Float, default=None)
69
+ high_water_mark_datetime = Column(DateTime, default=None)
70
+ updated_at = Column(DateTime, default=None)
71
+ status = Column(String, default=TradeStatus.CREATED.value)
72
+ # Stop losses should be actively loaded
73
+ stop_losses = relationship(
74
+ 'SQLTradeStopLoss',
75
+ back_populates='trade',
76
+ lazy='joined'
77
+ )
78
+ # Take profits should be actively loaded
79
+ take_profits = relationship(
80
+ 'SQLTradeTakeProfit',
81
+ back_populates='trade',
82
+ lazy='joined'
83
+ )
84
+
85
+ def __init__(
86
+ self,
87
+ buy_order,
88
+ target_symbol,
89
+ trading_symbol,
90
+ opened_at,
91
+ amount,
92
+ available_amount,
93
+ filled_amount,
94
+ remaining,
95
+ status=TradeStatus.CREATED.value,
96
+ closed_at=None,
97
+ updated_at=None,
98
+ net_gain=0,
99
+ cost=0,
100
+ last_reported_price=None,
101
+ last_reported_price_datetime=None,
102
+ high_water_mark=None,
103
+ high_water_mark_datetime=None,
104
+ sell_orders=[],
105
+ stop_losses=[],
106
+ take_profits=[],
107
+ ):
108
+ self.orders = [buy_order]
109
+ self.open_price = buy_order.price
110
+ self.target_symbol = target_symbol
111
+ self.trading_symbol = trading_symbol
112
+ self.closed_at = closed_at
113
+ self.amount = amount
114
+ self.available_amount = available_amount
115
+ self.filled_amount = filled_amount
116
+ self.remaining = remaining
117
+ self.net_gain = net_gain
118
+ self.cost = cost
119
+ self.last_reported_price = last_reported_price
120
+ self.last_reported_price_datetime = last_reported_price_datetime
121
+ self.high_water_mark = high_water_mark
122
+ self.high_water_mark_datetime = high_water_mark_datetime
123
+ self.opened_at = opened_at
124
+ self.updated_at = updated_at
125
+ self.status = status
126
+ self.stop_losses = stop_losses
127
+ self.take_profits = take_profits
128
+
129
+ if sell_orders is not None:
130
+ self.orders.extend(sell_orders)