investing-algorithm-framework 6.9.1__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 (192) hide show
  1. investing_algorithm_framework/__init__.py +147 -44
  2. investing_algorithm_framework/app/__init__.py +23 -6
  3. investing_algorithm_framework/app/algorithm/algorithm.py +5 -41
  4. investing_algorithm_framework/app/algorithm/algorithm_factory.py +17 -10
  5. investing_algorithm_framework/app/analysis/__init__.py +15 -0
  6. investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
  7. investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
  8. investing_algorithm_framework/app/analysis/permutation.py +116 -0
  9. investing_algorithm_framework/app/analysis/ranking.py +297 -0
  10. investing_algorithm_framework/app/app.py +1322 -707
  11. investing_algorithm_framework/app/context.py +196 -88
  12. investing_algorithm_framework/app/eventloop.py +590 -0
  13. investing_algorithm_framework/app/reporting/__init__.py +16 -5
  14. investing_algorithm_framework/app/reporting/ascii.py +57 -202
  15. investing_algorithm_framework/app/reporting/backtest_report.py +284 -170
  16. investing_algorithm_framework/app/reporting/charts/__init__.py +10 -2
  17. investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
  18. investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
  19. investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +11 -26
  20. investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
  21. investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
  22. investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +1 -1
  23. investing_algorithm_framework/app/reporting/generate.py +100 -114
  24. investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +40 -32
  25. investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +34 -27
  26. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +23 -19
  27. investing_algorithm_framework/app/reporting/tables/trades_table.py +1 -1
  28. investing_algorithm_framework/app/reporting/tables/utils.py +1 -0
  29. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +10 -16
  30. investing_algorithm_framework/app/strategy.py +315 -175
  31. investing_algorithm_framework/app/task.py +5 -3
  32. investing_algorithm_framework/cli/cli.py +30 -12
  33. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +131 -34
  34. investing_algorithm_framework/cli/initialize_app.py +20 -1
  35. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +18 -6
  36. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  37. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  38. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -2
  39. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +1 -1
  40. investing_algorithm_framework/create_app.py +3 -5
  41. investing_algorithm_framework/dependency_container.py +25 -39
  42. investing_algorithm_framework/domain/__init__.py +45 -38
  43. investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
  44. investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
  45. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
  46. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
  47. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
  48. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  49. investing_algorithm_framework/domain/backtesting/backtest_run.py +605 -0
  50. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  51. investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
  52. investing_algorithm_framework/domain/config.py +27 -0
  53. investing_algorithm_framework/domain/constants.py +6 -34
  54. investing_algorithm_framework/domain/data_provider.py +200 -56
  55. investing_algorithm_framework/domain/exceptions.py +34 -1
  56. investing_algorithm_framework/domain/models/__init__.py +10 -19
  57. investing_algorithm_framework/domain/models/base_model.py +0 -6
  58. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  59. investing_algorithm_framework/domain/models/data/data_source.py +214 -0
  60. investing_algorithm_framework/domain/models/{market_data_type.py → data/data_type.py} +7 -7
  61. investing_algorithm_framework/domain/models/market/market_credential.py +6 -0
  62. investing_algorithm_framework/domain/models/order/order.py +34 -13
  63. investing_algorithm_framework/domain/models/order/order_status.py +1 -1
  64. investing_algorithm_framework/domain/models/order/order_type.py +1 -1
  65. investing_algorithm_framework/domain/models/portfolio/portfolio.py +14 -1
  66. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +5 -1
  67. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +51 -11
  68. investing_algorithm_framework/domain/models/position/__init__.py +2 -1
  69. investing_algorithm_framework/domain/models/position/position.py +9 -0
  70. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  71. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  72. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  73. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  74. investing_algorithm_framework/domain/models/snapshot_interval.py +0 -1
  75. investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
  76. investing_algorithm_framework/domain/models/time_frame.py +7 -0
  77. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  78. investing_algorithm_framework/domain/models/time_unit.py +63 -1
  79. investing_algorithm_framework/domain/models/trade/__init__.py +0 -2
  80. investing_algorithm_framework/domain/models/trade/trade.py +56 -32
  81. investing_algorithm_framework/domain/models/trade/trade_status.py +8 -2
  82. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +106 -41
  83. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +161 -99
  84. investing_algorithm_framework/domain/order_executor.py +19 -0
  85. investing_algorithm_framework/domain/portfolio_provider.py +20 -1
  86. investing_algorithm_framework/domain/services/__init__.py +0 -13
  87. investing_algorithm_framework/domain/strategy.py +1 -29
  88. investing_algorithm_framework/domain/utils/__init__.py +5 -1
  89. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  90. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  91. investing_algorithm_framework/domain/utils/polars.py +17 -14
  92. investing_algorithm_framework/download_data.py +40 -10
  93. investing_algorithm_framework/infrastructure/__init__.py +13 -25
  94. investing_algorithm_framework/infrastructure/data_providers/__init__.py +7 -4
  95. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +811 -546
  96. investing_algorithm_framework/infrastructure/data_providers/csv.py +433 -122
  97. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  98. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  99. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +81 -0
  100. investing_algorithm_framework/infrastructure/models/__init__.py +0 -13
  101. investing_algorithm_framework/infrastructure/models/order/order.py +9 -3
  102. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +27 -8
  103. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +21 -7
  104. investing_algorithm_framework/infrastructure/order_executors/__init__.py +2 -0
  105. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  106. investing_algorithm_framework/infrastructure/repositories/repository.py +16 -2
  107. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +2 -2
  108. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +6 -0
  109. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +6 -0
  110. investing_algorithm_framework/infrastructure/services/__init__.py +0 -4
  111. investing_algorithm_framework/services/__init__.py +105 -8
  112. investing_algorithm_framework/services/backtesting/backtest_service.py +536 -476
  113. investing_algorithm_framework/services/configuration_service.py +14 -4
  114. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  115. investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
  116. investing_algorithm_framework/{app/reporting → services}/metrics/__init__.py +48 -17
  117. investing_algorithm_framework/{app/reporting → services}/metrics/drawdown.py +10 -10
  118. investing_algorithm_framework/{app/reporting → services}/metrics/equity_curve.py +2 -2
  119. investing_algorithm_framework/{app/reporting → services}/metrics/exposure.py +60 -2
  120. investing_algorithm_framework/services/metrics/generate.py +358 -0
  121. investing_algorithm_framework/{app/reporting → services}/metrics/profit_factor.py +36 -0
  122. investing_algorithm_framework/{app/reporting → services}/metrics/recovery.py +2 -2
  123. investing_algorithm_framework/{app/reporting → services}/metrics/returns.py +146 -147
  124. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  125. investing_algorithm_framework/{app/reporting/metrics/sharp_ratio.py → services/metrics/sharpe_ratio.py} +6 -10
  126. investing_algorithm_framework/{app/reporting → services}/metrics/sortino_ratio.py +3 -7
  127. investing_algorithm_framework/services/metrics/trades.py +500 -0
  128. investing_algorithm_framework/services/metrics/volatility.py +97 -0
  129. investing_algorithm_framework/{app/reporting → services}/metrics/win_rate.py +70 -3
  130. investing_algorithm_framework/services/order_service/order_backtest_service.py +21 -31
  131. investing_algorithm_framework/services/order_service/order_service.py +9 -71
  132. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +0 -2
  133. investing_algorithm_framework/services/portfolios/portfolio_service.py +3 -13
  134. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +62 -96
  135. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +0 -3
  136. investing_algorithm_framework/services/repository_service.py +5 -2
  137. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  138. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +113 -0
  139. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  140. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  141. investing_algorithm_framework/services/trade_service/__init__.py +7 -1
  142. investing_algorithm_framework/services/trade_service/trade_service.py +51 -29
  143. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  144. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  145. investing_algorithm_framework-7.19.15.dist-info/METADATA +537 -0
  146. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/RECORD +159 -148
  147. investing_algorithm_framework/app/reporting/evaluation.py +0 -243
  148. investing_algorithm_framework/app/reporting/metrics/risk_free_rate.py +0 -8
  149. investing_algorithm_framework/app/reporting/metrics/volatility.py +0 -69
  150. investing_algorithm_framework/cli/templates/requirements_azure_function.txt.template +0 -3
  151. investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -9
  152. investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -47
  153. investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
  154. investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -0
  155. investing_algorithm_framework/domain/models/backtesting/backtest_results.py +0 -440
  156. investing_algorithm_framework/domain/models/data_source.py +0 -21
  157. investing_algorithm_framework/domain/models/date_range.py +0 -64
  158. investing_algorithm_framework/domain/models/trade/trade_risk_type.py +0 -34
  159. investing_algorithm_framework/domain/models/trading_data_types.py +0 -48
  160. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
  161. investing_algorithm_framework/domain/services/market_data_sources.py +0 -543
  162. investing_algorithm_framework/domain/services/market_service.py +0 -153
  163. investing_algorithm_framework/domain/services/observable.py +0 -51
  164. investing_algorithm_framework/domain/services/observer.py +0 -19
  165. investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -16
  166. investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -746
  167. investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -270
  168. investing_algorithm_framework/infrastructure/models/market_data_sources/pandas.py +0 -312
  169. investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
  170. investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -471
  171. investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
  172. investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
  173. investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -322
  174. investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -10
  175. investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -269
  176. investing_algorithm_framework/services/market_data_source_service/data_provider_service.py +0 -350
  177. investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -377
  178. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -296
  179. investing_algorithm_framework-6.9.1.dist-info/METADATA +0 -440
  180. /investing_algorithm_framework/{app/reporting → services}/metrics/alpha.py +0 -0
  181. /investing_algorithm_framework/{app/reporting → services}/metrics/beta.py +0 -0
  182. /investing_algorithm_framework/{app/reporting → services}/metrics/cagr.py +0 -0
  183. /investing_algorithm_framework/{app/reporting → services}/metrics/calmar_ratio.py +0 -0
  184. /investing_algorithm_framework/{app/reporting → services}/metrics/mean_daily_return.py +0 -0
  185. /investing_algorithm_framework/{app/reporting → services}/metrics/price_efficiency.py +0 -0
  186. /investing_algorithm_framework/{app/reporting → services}/metrics/standard_deviation.py +0 -0
  187. /investing_algorithm_framework/{app/reporting → services}/metrics/treynor_ratio.py +0 -0
  188. /investing_algorithm_framework/{app/reporting → services}/metrics/ulcer.py +0 -0
  189. /investing_algorithm_framework/{app/reporting → services}/metrics/value_at_risk.py +0 -0
  190. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
  191. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
  192. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/entry_points.txt +0 -0
@@ -1,440 +0,0 @@
1
- from dataclasses import dataclass, field
2
- from datetime import datetime, timezone
3
- from logging import getLogger
4
- from typing import List, Optional
5
-
6
- from investing_algorithm_framework.domain.constants import DATETIME_FORMAT
7
- from investing_algorithm_framework.domain.models \
8
- .backtesting.backtest_date_range import BacktestDateRange
9
- from investing_algorithm_framework.domain.models.base_model import BaseModel
10
- from investing_algorithm_framework.domain.models.order import Order
11
- from investing_algorithm_framework.domain.models.order import OrderSide, \
12
- OrderStatus
13
- from investing_algorithm_framework.domain.models.portfolio \
14
- .portfolio_snapshot import PortfolioSnapshot
15
- from investing_algorithm_framework.domain.models.position import Position
16
- from investing_algorithm_framework.domain.models.trade import Trade, \
17
- TradeStatus
18
-
19
- logger = getLogger(__name__)
20
-
21
-
22
- @dataclass
23
- class BacktestResult(BaseModel):
24
- """
25
- Class that represents a backtest result. The backtest result
26
- contains information about the trades, positions, portfolio and
27
- trades of backtest.
28
-
29
- Attributes:
30
- backtest_date_range (BacktestDateRange): The date range of
31
- the backtest.
32
- trading_symbol (str): The trading symbol of the backtest.
33
- name (str): The name of the backtest.
34
- initial_unallocated (float): The initial unallocated amount
35
- of the backtest.
36
- symbols (List[str]): The symbols of the backtest.
37
- number_of_runs (int): The number of strategy runs of the backtest.
38
- portfolio_snapshots (List[PortfolioSnapshot]): The portfolio
39
- snapshots of the backtest.
40
- trades (List[Trade]): All trades of the backtest.
41
- orders (List[Order]): All orders of the backtest.
42
- positions (List[Position]): All positions of the backtest.
43
- created_at (Optional[datetime]): The date and time when the
44
- backtest was created.
45
- backtest_start_date (Optional[datetime]): The start date of the
46
- backtest.
47
- backtest_end_date (Optional[datetime]): The end date of the
48
- backtest.
49
- number_of_days (int): The number of days of the backtest.
50
- number_of_trades (int): The number of trades of the backtest.
51
- number_of_trades_closed (int): The number of closed trades
52
- of the backtest.
53
- number_of_trades_open (int): The number of open trades
54
- of the backtest.
55
- percentage_positive_trades (float): The percentage of positive
56
- trades of the backtest.
57
- percentage_negative_trades (float): The percentage of negative
58
- trades of the backtest.
59
- total_net_gain_percentage (float): The total net gain percentage
60
- of the backtest.
61
- growth (float): The growth of the backtest.
62
- growth_percentage (float): The growth percentage of the backtest.
63
- total_net_gain (float): The total net gain of the backtest.
64
- total_cost (float): The total cost of the backtest.
65
- total_value (float): The total value of the backtest.
66
- average_trade_duration (float): The average trade duration
67
- of the backtest in hours.
68
- average_trade_size (float): The average trade size of the
69
- backtest in the trading symbol.
70
- """
71
- backtest_date_range: BacktestDateRange
72
- trading_symbol: str
73
- name: str
74
- initial_unallocated: float
75
- number_of_runs: int
76
- portfolio_snapshots: List[PortfolioSnapshot]
77
- trades: List[Trade]
78
- orders: List[Order]
79
- positions: List[Position]
80
- created_at: Optional[datetime] = field(default=None)
81
- backtest_start_date: Optional[datetime] = field(default=None)
82
- backtest_end_date: Optional[datetime] = field(default=None)
83
- symbols: List[str] = field(default_factory=list)
84
- number_of_days: int = 0
85
- number_of_trades: int = 0
86
- number_of_trades_closed: int = 0
87
- number_of_trades_open: int = 0
88
- number_of_orders: int = 0
89
- number_of_positions: int = 0
90
- percentage_positive_trades: float = 0.0
91
- percentage_negative_trades: float = 0.0
92
- total_net_gain_percentage: float = 0.0
93
- growth: float = 0.0
94
- growth_percentage: float = 0.0
95
- total_net_gain: float = 0.0
96
- total_cost: float = 0.0
97
- total_value: float = 0.0
98
- average_trade_duration: float = 0.0
99
- average_trade_size: float = 0.0
100
-
101
- def __post_init__(self):
102
- if self.created_at is None:
103
- self.created_at = datetime.now(tz=timezone.utc)
104
-
105
- self.number_of_days = (
106
- self.backtest_date_range.end_date
107
- - self.backtest_date_range.start_date
108
- ).days
109
- self.backtest_start_date = self.backtest_date_range.start_date
110
- self.backtest_end_date = self.backtest_date_range.end_date
111
- self.growth = self.portfolio_snapshots[-1].total_value \
112
- - self.portfolio_snapshots[0].total_value
113
- self.growth_percentage = self.growth / self.initial_unallocated * 100.0
114
- self.number_of_orders = \
115
- len(self.orders) if self.orders is not None else 0
116
- self.number_of_positions = \
117
- len(self.positions) if self.positions is not None else 0
118
- last_portfolio_snapshot = \
119
- self.portfolio_snapshots[-1] if self.portfolio_snapshots else None
120
- self.total_value = last_portfolio_snapshot.total_value \
121
- if last_portfolio_snapshot is not None else 0.0
122
-
123
- number_of_negative_trades = 0.0
124
- number_of_positive_trades = 0.0
125
- number_of_trades_closed = 0
126
- number_of_trades_open = 0
127
- total_duration = 0
128
- total_trade_size = 0.0
129
-
130
- if self.trades is not None:
131
- for trade in self.trades:
132
- self.total_net_gain += trade.net_gain
133
- self.total_cost += trade.cost
134
- total_duration += \
135
- ((trade.closed_at - trade.opened_at).total_seconds() /
136
- 3600) if trade.closed_at else 0
137
- total_trade_size += trade.size
138
- if trade.status == TradeStatus.CLOSED.value:
139
- number_of_trades_closed += 1
140
-
141
- if trade.status == TradeStatus.OPEN.value:
142
- number_of_trades_open += 1
143
-
144
- if trade.net_gain > 0:
145
- number_of_positive_trades += 1
146
- elif trade.net_gain < 0:
147
- number_of_negative_trades += 1
148
-
149
- self.total_net_gain_percentage = \
150
- (self.total_net_gain / self.initial_unallocated) * 100.0 \
151
- if self.initial_unallocated > 0 else 0.0
152
- self.percentage_positive_trades = \
153
- (number_of_positive_trades / len(self.trades)) * 100.0 \
154
- if len(self.trades) > 0 else 0.0
155
- self.percentage_negative_trades = \
156
- (number_of_negative_trades / len(self.trades)) * 100.0 \
157
- if len(self.trades) > 0 else 0.0
158
- self.number_of_trades_closed = number_of_trades_closed
159
- self.number_of_trades_open = number_of_trades_open
160
- self.number_of_trades = len(self.trades)
161
- self.average_trade_duration = \
162
- total_duration / self.number_of_trades \
163
- if self.number_of_trades > 0 else 0.0
164
- self.average_trade_size = \
165
- total_trade_size / self.number_of_trades \
166
- if self.number_of_trades > 0 else 0.0
167
-
168
- # Determine all the symbols that are being traded in the backtest
169
- if self.symbols is None:
170
- self.symbols = []
171
-
172
- for order in self.orders:
173
- if order.target_symbol not in self.symbols:
174
- self.symbols.append(order.target_symbol)
175
-
176
- def to_dict(self):
177
- """
178
- Convert the backtest report to a dictionary. So it can be
179
- saved to a file.
180
-
181
- Returns:
182
- dict: The backtest report as a dictionary
183
- """
184
-
185
- return {
186
- "name": self.name,
187
- "backtest_date_range_identifier": self.backtest_date_range.name,
188
- "backtest_start_date": self.backtest_date_range.start_date
189
- .strftime(DATETIME_FORMAT),
190
- "backtest_end_date": self.backtest_date_range.end_date
191
- .strftime(DATETIME_FORMAT),
192
- "number_of_runs": self.number_of_runs,
193
- "symbols": self.symbols,
194
- "number_of_days": self.number_of_days,
195
- "number_of_orders": self.number_of_orders,
196
- "number_of_positions": self.number_of_positions,
197
- "percentage_positive_trades": self.percentage_positive_trades,
198
- "percentage_negative_trades": self.percentage_negative_trades,
199
- "number_of_trades_closed": self.number_of_trades_closed,
200
- "number_of_trades_open": self.number_of_trades_open,
201
- "total_cost": self.total_cost,
202
- "growth_percentage": self.growth_percentage,
203
- "growth": self.growth,
204
- "initial_unallocated": self.initial_unallocated,
205
- "trading_symbol": self.trading_symbol,
206
- "total_net_gain_percentage": self.total_net_gain_percentage,
207
- "total_net_gain": self.total_net_gain,
208
- "total_value": self.total_value,
209
- "average_trade_duration": self.average_trade_duration,
210
- "average_trade_size": self.average_trade_size,
211
- # "positions": [position.to_dict() for position in self.positions],
212
- "trades": [
213
- trade.to_dict(datetime_format=DATETIME_FORMAT)
214
- for trade in self.trades
215
- ],
216
- "orders": [
217
- order.to_dict(datetime_format=DATETIME_FORMAT)
218
- for order in self.orders
219
- ],
220
- "portfolio_snapshots": [
221
- snapshot.to_dict(datetime_format=DATETIME_FORMAT)
222
- for snapshot in self.portfolio_snapshots
223
- ],
224
- "created_at": self.created_at.strftime(DATETIME_FORMAT),
225
- }
226
-
227
- @staticmethod
228
- def from_dict(data):
229
- """
230
- Factory method to create a backtest report from a dictionary.
231
- """
232
-
233
- backtest_date_range = BacktestDateRange(
234
- start_date=datetime.strptime(
235
- data["backtest_start_date"], DATETIME_FORMAT),
236
- end_date=datetime.strptime(
237
- data["backtest_end_date"], DATETIME_FORMAT)
238
- )
239
- portfolio_snapshots_data = data.get("portfolio_snapshots", None)
240
-
241
- if portfolio_snapshots_data is None:
242
- portfolio_snapshots = []
243
- else:
244
- portfolio_snapshots = [
245
- PortfolioSnapshot.from_dict(snapshot)
246
- for snapshot in portfolio_snapshots_data
247
- ]
248
-
249
- positions_data = data.get("positions", None)
250
-
251
- if positions_data is not None:
252
- positions = [
253
- Position.from_dict(position) for position in positions_data
254
- ]
255
- else:
256
- positions = []
257
-
258
- trades_data = data.get("trades", None)
259
-
260
- if trades_data is not None:
261
- trades = [Trade.from_dict(trade) for trade in trades_data]
262
- else:
263
- trades = []
264
-
265
- orders_data = data.get("orders", None)
266
-
267
- if orders_data is not None:
268
- orders = [Order.from_dict(order) for order in orders_data]
269
- else:
270
- orders = []
271
-
272
- report = BacktestResult(
273
- name=data["name"],
274
- number_of_runs=data["number_of_runs"],
275
- backtest_date_range=backtest_date_range,
276
- symbols=data["symbols"],
277
- number_of_orders=data["number_of_orders"],
278
- number_of_positions=data["number_of_positions"],
279
- percentage_positive_trades=data["percentage_positive_trades"],
280
- percentage_negative_trades=data["percentage_negative_trades"],
281
- number_of_trades_closed=data["number_of_trades_closed"],
282
- number_of_trades_open=data["number_of_trades_open"],
283
- total_cost=float(data["total_cost"]),
284
- growth_percentage=float(data["growth_percentage"]),
285
- growth=float(data["growth"]),
286
- initial_unallocated=float(data["initial_unallocated"]),
287
- trading_symbol=data["trading_symbol"],
288
- total_net_gain_percentage=float(data["total_net_gain_percentage"]),
289
- total_net_gain=float(data["total_net_gain"]),
290
- total_value=float(data["total_value"]),
291
- average_trade_duration=data["average_trade_duration"],
292
- average_trade_size=float(data["average_trade_size"]),
293
- created_at=datetime.strptime(
294
- data["created_at"], DATETIME_FORMAT
295
- ),
296
- backtest_start_date=datetime.strptime(
297
- data["backtest_start_date"], DATETIME_FORMAT
298
- ),
299
- backtest_end_date=datetime.strptime(
300
- data["backtest_end_date"], DATETIME_FORMAT
301
- ),
302
- number_of_days=data["number_of_days"],
303
- portfolio_snapshots=portfolio_snapshots,
304
- # position_snapshots=position_snapshots,
305
- trades=trades,
306
- orders=orders,
307
- positions=positions,
308
- )
309
-
310
- return report
311
-
312
- def get_orders(
313
- self,
314
- target_symbol=None,
315
- order_side=None,
316
- order_status=None,
317
- created_at_lt=None,
318
- ) -> List[Order]:
319
- """
320
- Get the orders of a backtest report
321
-
322
- Args:
323
- target_symbol (str): The target_symbol
324
- order_side (str): The order side
325
- status (str): The order status
326
- created_at_lt (datetime): The created_at date to filter the orders
327
-
328
- Returns:
329
- list: The orders of the backtest report
330
- """
331
- selection = self.orders
332
-
333
- if created_at_lt is not None:
334
- selection = [
335
- order for order in selection
336
- if order.created_at < created_at_lt
337
- ]
338
-
339
- if target_symbol is not None:
340
- selection = [
341
- order for order in selection
342
- if order.target_symbol == target_symbol
343
- ]
344
-
345
- if order_side is not None:
346
- order_side = OrderSide.from_value(order_side)
347
- selection = [
348
- order for order in selection
349
- if order.order_side == order_side.value
350
- ]
351
-
352
- if order_status is not None:
353
- status = OrderStatus.from_value(order_status)
354
- selection = [
355
- order for order in selection
356
- if order.status == status.value
357
- ]
358
-
359
- return selection
360
-
361
- def get_trades(self, target_symbol=None, trade_status=None) -> List[Trade]:
362
- """
363
- Get the trades of a backtest report
364
-
365
- Args:
366
- target_symbol (str): The target_symbol
367
- trade_status: The trade_status
368
-
369
- Returns:
370
- list: The trades of the backtest report
371
- """
372
- selection = self.trades
373
-
374
- if target_symbol is not None:
375
- selection = [
376
- trade for trade in selection
377
- if trade.target_symbol == target_symbol
378
- ]
379
-
380
- if trade_status is not None:
381
- trade_status = TradeStatus.from_value(trade_status)
382
- selection = [
383
- trade for trade in selection
384
- if trade.status == trade_status.value
385
- ]
386
-
387
- return selection
388
-
389
- def get_positions(self, symbol=None) -> List[Position]:
390
- """
391
- Get the positions of the backtest report
392
-
393
- Args:
394
- symbol (str): The symbol
395
-
396
- Returns:
397
- list: The positions of the backtest report
398
- """
399
-
400
- # Get the last portfolio snapshot
401
- last_portfolio_snapshot = \
402
- self.portfolio_snapshots[-1] if self.portfolio_snapshots else None
403
- selection = []
404
-
405
- if last_portfolio_snapshot is not None:
406
- positions = last_portfolio_snapshot.position_snapshots
407
-
408
- if symbol is not None:
409
- selection = [
410
- position for position in positions
411
- if position.symbol == symbol
412
- ]
413
-
414
- return selection
415
-
416
- return selection
417
-
418
- def get_portfolio_snapshots(
419
- self,
420
- created_at_lt: Optional[datetime] = None
421
- ) -> List[PortfolioSnapshot]:
422
- """
423
- Get the portfolio snapshots of the backtest report
424
-
425
- Args:
426
- created_at_lt (datetime): The created_at date to filter
427
- the snapshots
428
-
429
- Returns:
430
- list: The portfolio snapshots of the backtest report
431
- """
432
- selection = self.portfolio_snapshots
433
-
434
- if created_at_lt is not None:
435
- selection = [
436
- snapshot for snapshot in selection
437
- if snapshot.created_at < created_at_lt
438
- ]
439
-
440
- return selection
@@ -1,21 +0,0 @@
1
- class DataSource:
2
- """
3
- Base class for data sources.
4
- """
5
-
6
- def __init__(
7
- self,
8
- data_type: str,
9
- symbol: str,
10
- market: str,
11
- time_frame: str,
12
- window_size: int,
13
- key: str, name: str
14
- ):
15
- self.name = name
16
- self.key = key
17
- self.data_type = data_type
18
- self.symbol = symbol
19
- self.market = market
20
- self.time_frame = time_frame
21
- self.window_size = window_size
@@ -1,64 +0,0 @@
1
- from datetime import datetime
2
- from typing import Union
3
- from .backtesting.backtest_date_range import BacktestDateRange
4
-
5
-
6
- class DateRange:
7
- """
8
- DateRange class. This class is used to define a date range and the name of
9
- the range. Also, it can be used to store trading metadata such as
10
- classification of the trend (Up or Down).
11
- """
12
-
13
- def __init__(
14
- self,
15
- start_date: datetime,
16
- end_date: datetime,
17
- name: str,
18
- up_trend: bool = False,
19
- down_trend: bool = False
20
- ):
21
- self.start_date = start_date
22
- self.end_date = end_date
23
- self.name = name
24
- self._up_trend = up_trend
25
- self._down_trend = down_trend
26
-
27
- @property
28
- def up_trend(self) -> Union[bool, None]:
29
-
30
- if self._up_trend and not self._down_trend:
31
- return True
32
- else:
33
- return None
34
-
35
- @up_trend.setter
36
- def up_trend(self, value: bool):
37
- self._up_trend = value
38
-
39
- @property
40
- def down_trend(self) -> Union[bool, None]:
41
-
42
- if self._down_trend and not self._up_trend:
43
- return True
44
- else:
45
- return None
46
-
47
- @down_trend.setter
48
- def down_trend(self, value: bool):
49
- self._down_trend = value
50
-
51
- def __str__(self):
52
- return f"DateRange({self.start_date}, {self.end_date}, {self.name})"
53
-
54
- def __repr__(self):
55
- return f"DateRange(Name: {self.name} " + \
56
- f"Start date: {self.start_date} " + \
57
- f"End date: {self.end_date})"
58
-
59
- def to_backtest_date_range(self):
60
- return BacktestDateRange(
61
- start_date=self.start_date,
62
- end_date=self.end_date,
63
- name=self.name
64
- )
@@ -1,34 +0,0 @@
1
- from enum import Enum
2
-
3
-
4
- class TradeRiskType(Enum):
5
- FIXED = "FIXED"
6
- TRAILING = "TRAILING"
7
-
8
- @staticmethod
9
- def from_string(value: str):
10
-
11
- if isinstance(value, str):
12
- for status in TradeRiskType:
13
-
14
- if value.upper() == status.value:
15
- return status
16
-
17
- raise ValueError("Could not convert value to TradeRiskType")
18
-
19
- @staticmethod
20
- def from_value(value):
21
-
22
- if isinstance(value, TradeRiskType):
23
- for risk_type in TradeRiskType:
24
-
25
- if value == risk_type:
26
- return risk_type
27
-
28
- elif isinstance(value, str):
29
- return TradeRiskType.from_string(value)
30
-
31
- raise ValueError("Could not convert value to TradeRiskType")
32
-
33
- def equals(self, other):
34
- return TradeRiskType.from_value(other) == self
@@ -1,48 +0,0 @@
1
- from enum import Enum
2
-
3
-
4
- class TradingDataType(Enum):
5
- TICKER = 'TICKER'
6
- ORDER_BOOK = 'ORDER_BOOK'
7
- OHLCV = "OHLCV"
8
- CUSTOM = "CUSTOM"
9
-
10
- @staticmethod
11
- def from_value(value):
12
-
13
- if isinstance(value, TradingDataType):
14
- for trading_data_type in TradingDataType:
15
-
16
- if value == trading_data_type:
17
- return trading_data_type
18
-
19
- elif isinstance(value, str):
20
- return TradingDataType.from_string(value)
21
-
22
- raise ValueError(
23
- "Could not convert value to trading data type"
24
- )
25
-
26
- @staticmethod
27
- def from_string(value: str):
28
-
29
- if isinstance(value, str):
30
- for order_type in TradingDataType:
31
-
32
- if value.upper() == order_type.value:
33
- return order_type
34
-
35
- raise ValueError(
36
- "Could not convert value to trading data type"
37
- )
38
-
39
- def equals(self, other):
40
-
41
- if other is None:
42
- return False
43
-
44
- if isinstance(other, Enum):
45
- return self.value == other.value
46
-
47
- else:
48
- return TradingDataType.from_string(other) == self