investing-algorithm-framework 7.19.14__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.
- investing_algorithm_framework/__init__.py +197 -0
- investing_algorithm_framework/app/__init__.py +47 -0
- investing_algorithm_framework/app/algorithm/__init__.py +7 -0
- investing_algorithm_framework/app/algorithm/algorithm.py +239 -0
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +114 -0
- investing_algorithm_framework/app/analysis/__init__.py +15 -0
- investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
- investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
- investing_algorithm_framework/app/analysis/permutation.py +116 -0
- investing_algorithm_framework/app/analysis/ranking.py +297 -0
- investing_algorithm_framework/app/app.py +2204 -0
- investing_algorithm_framework/app/app_hook.py +28 -0
- investing_algorithm_framework/app/context.py +1667 -0
- investing_algorithm_framework/app/eventloop.py +590 -0
- investing_algorithm_framework/app/reporting/__init__.py +27 -0
- investing_algorithm_framework/app/reporting/ascii.py +921 -0
- investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
- investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
- investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
- investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
- investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
- investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
- investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
- investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
- investing_algorithm_framework/app/reporting/generate.py +185 -0
- investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
- investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
- investing_algorithm_framework/app/reporting/tables/stop_loss_table.py +0 -0
- investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
- investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
- investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
- investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
- investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
- investing_algorithm_framework/app/stateless/__init__.py +35 -0
- investing_algorithm_framework/app/stateless/action_handlers/__init__.py +84 -0
- investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +8 -0
- investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +15 -0
- investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +40 -0
- investing_algorithm_framework/app/stateless/exception_handler.py +40 -0
- investing_algorithm_framework/app/strategy.py +675 -0
- investing_algorithm_framework/app/task.py +41 -0
- investing_algorithm_framework/app/web/__init__.py +5 -0
- investing_algorithm_framework/app/web/controllers/__init__.py +13 -0
- investing_algorithm_framework/app/web/controllers/orders.py +20 -0
- investing_algorithm_framework/app/web/controllers/portfolio.py +20 -0
- investing_algorithm_framework/app/web/controllers/positions.py +18 -0
- investing_algorithm_framework/app/web/create_app.py +20 -0
- investing_algorithm_framework/app/web/error_handler.py +59 -0
- investing_algorithm_framework/app/web/responses.py +20 -0
- investing_algorithm_framework/app/web/run_strategies.py +4 -0
- investing_algorithm_framework/app/web/schemas/__init__.py +12 -0
- investing_algorithm_framework/app/web/schemas/order.py +12 -0
- investing_algorithm_framework/app/web/schemas/portfolio.py +22 -0
- investing_algorithm_framework/app/web/schemas/position.py +15 -0
- investing_algorithm_framework/app/web/setup_cors.py +6 -0
- investing_algorithm_framework/cli/__init__.py +0 -0
- investing_algorithm_framework/cli/cli.py +207 -0
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +499 -0
- investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
- investing_algorithm_framework/cli/initialize_app.py +603 -0
- investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
- investing_algorithm_framework/cli/templates/app.py.template +18 -0
- investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
- investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
- investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
- investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
- investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
- investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
- investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
- investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
- investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
- investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
- investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
- investing_algorithm_framework/cli/templates/env.example.template +2 -0
- investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
- investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
- investing_algorithm_framework/cli/templates/readme.md.template +135 -0
- investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
- investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
- investing_algorithm_framework/create_app.py +54 -0
- investing_algorithm_framework/dependency_container.py +155 -0
- investing_algorithm_framework/domain/__init__.py +148 -0
- investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
- investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
- investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
- investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
- investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
- investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
- investing_algorithm_framework/domain/backtesting/backtest_run.py +435 -0
- investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
- investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
- investing_algorithm_framework/domain/config.py +111 -0
- investing_algorithm_framework/domain/constants.py +83 -0
- investing_algorithm_framework/domain/data_provider.py +334 -0
- investing_algorithm_framework/domain/data_structures.py +42 -0
- investing_algorithm_framework/domain/decimal_parsing.py +40 -0
- investing_algorithm_framework/domain/exceptions.py +112 -0
- investing_algorithm_framework/domain/models/__init__.py +43 -0
- investing_algorithm_framework/domain/models/app_mode.py +34 -0
- investing_algorithm_framework/domain/models/base_model.py +25 -0
- investing_algorithm_framework/domain/models/data/__init__.py +7 -0
- investing_algorithm_framework/domain/models/data/data_source.py +214 -0
- investing_algorithm_framework/domain/models/data/data_type.py +46 -0
- investing_algorithm_framework/domain/models/event.py +35 -0
- investing_algorithm_framework/domain/models/market/__init__.py +5 -0
- investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
- investing_algorithm_framework/domain/models/order/__init__.py +6 -0
- investing_algorithm_framework/domain/models/order/order.py +384 -0
- investing_algorithm_framework/domain/models/order/order_side.py +36 -0
- investing_algorithm_framework/domain/models/order/order_status.py +37 -0
- investing_algorithm_framework/domain/models/order/order_type.py +30 -0
- investing_algorithm_framework/domain/models/portfolio/__init__.py +9 -0
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +169 -0
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +93 -0
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
- investing_algorithm_framework/domain/models/position/__init__.py +4 -0
- investing_algorithm_framework/domain/models/position/position.py +68 -0
- investing_algorithm_framework/domain/models/position/position_snapshot.py +47 -0
- investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
- investing_algorithm_framework/domain/models/strategy_profile.py +33 -0
- investing_algorithm_framework/domain/models/time_frame.py +153 -0
- investing_algorithm_framework/domain/models/time_interval.py +124 -0
- investing_algorithm_framework/domain/models/time_unit.py +149 -0
- investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
- investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
- investing_algorithm_framework/domain/models/trade/__init__.py +13 -0
- investing_algorithm_framework/domain/models/trade/trade.py +388 -0
- investing_algorithm_framework/domain/models/trade/trade_risk_type.py +34 -0
- investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +267 -0
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +303 -0
- investing_algorithm_framework/domain/order_executor.py +112 -0
- investing_algorithm_framework/domain/portfolio_provider.py +118 -0
- investing_algorithm_framework/domain/positions/__init__.py +4 -0
- investing_algorithm_framework/domain/positions/position_size.py +41 -0
- investing_algorithm_framework/domain/services/__init__.py +11 -0
- investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
- investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
- investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
- investing_algorithm_framework/domain/services/rounding_service.py +27 -0
- investing_algorithm_framework/domain/services/state_handler.py +38 -0
- investing_algorithm_framework/domain/stateless_actions.py +7 -0
- investing_algorithm_framework/domain/strategy.py +44 -0
- investing_algorithm_framework/domain/utils/__init__.py +27 -0
- investing_algorithm_framework/domain/utils/csv.py +104 -0
- investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
- investing_algorithm_framework/domain/utils/dates.py +57 -0
- investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
- investing_algorithm_framework/domain/utils/polars.py +53 -0
- investing_algorithm_framework/domain/utils/random.py +41 -0
- investing_algorithm_framework/domain/utils/signatures.py +17 -0
- investing_algorithm_framework/domain/utils/stoppable_thread.py +26 -0
- investing_algorithm_framework/domain/utils/synchronized.py +12 -0
- investing_algorithm_framework/download_data.py +108 -0
- investing_algorithm_framework/infrastructure/__init__.py +50 -0
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1143 -0
- investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
- investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
- investing_algorithm_framework/infrastructure/database/__init__.py +10 -0
- investing_algorithm_framework/infrastructure/database/sql_alchemy.py +120 -0
- investing_algorithm_framework/infrastructure/models/__init__.py +16 -0
- investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
- investing_algorithm_framework/infrastructure/models/model_extension.py +6 -0
- investing_algorithm_framework/infrastructure/models/order/__init__.py +4 -0
- investing_algorithm_framework/infrastructure/models/order/order.py +124 -0
- investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
- investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
- investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +4 -0
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
- investing_algorithm_framework/infrastructure/models/portfolio/sql_portfolio.py +114 -0
- investing_algorithm_framework/infrastructure/models/position/__init__.py +4 -0
- investing_algorithm_framework/infrastructure/models/position/position.py +63 -0
- investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +23 -0
- investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
- investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +40 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +41 -0
- investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
- investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
- investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
- investing_algorithm_framework/infrastructure/repositories/__init__.py +21 -0
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +96 -0
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +30 -0
- investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +66 -0
- investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +299 -0
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +23 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +23 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +7 -0
- investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
- investing_algorithm_framework/infrastructure/services/aws/state_handler.py +113 -0
- investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
- investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
- investing_algorithm_framework/services/__init__.py +132 -0
- investing_algorithm_framework/services/backtesting/__init__.py +5 -0
- investing_algorithm_framework/services/backtesting/backtest_service.py +651 -0
- investing_algorithm_framework/services/configuration_service.py +96 -0
- investing_algorithm_framework/services/data_providers/__init__.py +5 -0
- investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
- investing_algorithm_framework/services/market_credential_service.py +40 -0
- investing_algorithm_framework/services/metrics/__init__.py +114 -0
- investing_algorithm_framework/services/metrics/alpha.py +0 -0
- investing_algorithm_framework/services/metrics/beta.py +0 -0
- investing_algorithm_framework/services/metrics/cagr.py +60 -0
- investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
- investing_algorithm_framework/services/metrics/drawdown.py +181 -0
- investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
- investing_algorithm_framework/services/metrics/exposure.py +210 -0
- investing_algorithm_framework/services/metrics/generate.py +358 -0
- investing_algorithm_framework/services/metrics/mean_daily_return.py +83 -0
- investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
- investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
- investing_algorithm_framework/services/metrics/recovery.py +113 -0
- investing_algorithm_framework/services/metrics/returns.py +452 -0
- investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
- investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
- investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
- investing_algorithm_framework/services/metrics/standard_deviation.py +157 -0
- investing_algorithm_framework/services/metrics/trades.py +500 -0
- investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
- investing_algorithm_framework/services/metrics/ulcer.py +0 -0
- investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
- investing_algorithm_framework/services/metrics/volatility.py +97 -0
- investing_algorithm_framework/services/metrics/win_rate.py +177 -0
- investing_algorithm_framework/services/order_service/__init__.py +9 -0
- investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
- investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
- investing_algorithm_framework/services/order_service/order_service.py +826 -0
- investing_algorithm_framework/services/portfolios/__init__.py +16 -0
- investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
- investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +75 -0
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
- investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
- investing_algorithm_framework/services/positions/__init__.py +7 -0
- investing_algorithm_framework/services/positions/position_service.py +210 -0
- investing_algorithm_framework/services/positions/position_snapshot_service.py +18 -0
- investing_algorithm_framework/services/repository_service.py +40 -0
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +132 -0
- investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +66 -0
- investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +41 -0
- investing_algorithm_framework/services/trade_service/__init__.py +3 -0
- investing_algorithm_framework/services/trade_service/trade_service.py +1083 -0
- investing_algorithm_framework-7.19.14.dist-info/LICENSE +201 -0
- investing_algorithm_framework-7.19.14.dist-info/METADATA +459 -0
- investing_algorithm_framework-7.19.14.dist-info/RECORD +260 -0
- investing_algorithm_framework-7.19.14.dist-info/WHEEL +4 -0
- investing_algorithm_framework-7.19.14.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from dateutil.parser import parse
|
|
2
|
+
|
|
3
|
+
from investing_algorithm_framework.domain.exceptions import \
|
|
4
|
+
ImproperlyConfigured
|
|
5
|
+
from investing_algorithm_framework.domain.models.base_model import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PortfolioConfiguration(BaseModel):
|
|
9
|
+
"""
|
|
10
|
+
This class represents a portfolio configuration. It is used to
|
|
11
|
+
configure the portfolio that the user wants to create.
|
|
12
|
+
|
|
13
|
+
The portfolio configuration will have the following attributes:
|
|
14
|
+
- market: The market where the portfolio will be created
|
|
15
|
+
- trading_symbol: The trading symbol of the portfolio
|
|
16
|
+
- track_from: The date from which the portfolio will be tracked
|
|
17
|
+
- identifier: The identifier of the portfolio
|
|
18
|
+
- initial_balance: The initial balance of the portfolio
|
|
19
|
+
|
|
20
|
+
For backtesting, a portfolio configuration is used to create a
|
|
21
|
+
portfolio that will be used to simulate the trading of the algorithm. if
|
|
22
|
+
the user does not provide an initial balance, the portfolio will be created
|
|
23
|
+
with a balance of according to the initial balanace of
|
|
24
|
+
the PortfolioConfiguration class.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
market,
|
|
30
|
+
trading_symbol,
|
|
31
|
+
track_from=None,
|
|
32
|
+
identifier=None,
|
|
33
|
+
initial_balance=None,
|
|
34
|
+
):
|
|
35
|
+
self._market = market
|
|
36
|
+
|
|
37
|
+
if self._market is not None:
|
|
38
|
+
self._market = self._market.upper()
|
|
39
|
+
|
|
40
|
+
self._track_from = None
|
|
41
|
+
self._trading_symbol = trading_symbol.upper()
|
|
42
|
+
self._identifier = identifier
|
|
43
|
+
self._initial_balance = initial_balance
|
|
44
|
+
|
|
45
|
+
if self.identifier is None:
|
|
46
|
+
self._identifier = market.upper()
|
|
47
|
+
else:
|
|
48
|
+
self._identifier = identifier.upper()
|
|
49
|
+
|
|
50
|
+
if track_from:
|
|
51
|
+
self._track_from = parse(track_from)
|
|
52
|
+
|
|
53
|
+
if self.trading_symbol is None:
|
|
54
|
+
raise ImproperlyConfigured(
|
|
55
|
+
"Portfolio configuration requires a trading symbol"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def market(self):
|
|
60
|
+
|
|
61
|
+
if hasattr(self._market, "upper"):
|
|
62
|
+
return self._market.upper()
|
|
63
|
+
|
|
64
|
+
return self._market
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def track_from(self):
|
|
68
|
+
return self._track_from
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def identifier(self):
|
|
72
|
+
return self._identifier
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def trading_symbol(self):
|
|
76
|
+
return self._trading_symbol
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def initial_balance(self):
|
|
80
|
+
return self._initial_balance
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def has_initial_balance(self):
|
|
84
|
+
return self._initial_balance is not None
|
|
85
|
+
|
|
86
|
+
def __repr__(self):
|
|
87
|
+
return self.repr(
|
|
88
|
+
market=self.market,
|
|
89
|
+
trading_symbol=self.trading_symbol,
|
|
90
|
+
identifier=self.identifier,
|
|
91
|
+
track_from=self.track_from,
|
|
92
|
+
initial_balance=self.initial_balance
|
|
93
|
+
)
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
from datetime import timezone
|
|
2
|
+
|
|
3
|
+
from dateutil import parser
|
|
4
|
+
|
|
5
|
+
from investing_algorithm_framework.domain.models.base_model import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PortfolioSnapshot(BaseModel):
|
|
9
|
+
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
portfolio_id=None,
|
|
13
|
+
trading_symbol=None,
|
|
14
|
+
pending_value=None,
|
|
15
|
+
unallocated=None,
|
|
16
|
+
net_size=None,
|
|
17
|
+
total_net_gain=None,
|
|
18
|
+
total_revenue=None,
|
|
19
|
+
total_cost=None,
|
|
20
|
+
total_value=None,
|
|
21
|
+
cash_flow=None,
|
|
22
|
+
created_at=None,
|
|
23
|
+
position_snapshots=None,
|
|
24
|
+
metadata=None,
|
|
25
|
+
):
|
|
26
|
+
self.portfolio_id = portfolio_id
|
|
27
|
+
self.trading_symbol = trading_symbol
|
|
28
|
+
self.pending_value = pending_value
|
|
29
|
+
self.unallocated = unallocated
|
|
30
|
+
self.total_net_gain = total_net_gain
|
|
31
|
+
self.total_revenue = total_revenue
|
|
32
|
+
self.total_value = total_value if total_value is not None else 0.0
|
|
33
|
+
self.net_size = net_size
|
|
34
|
+
self.total_cost = total_cost
|
|
35
|
+
self.cash_flow = cash_flow
|
|
36
|
+
self.metadata = metadata if metadata is not None else {}
|
|
37
|
+
|
|
38
|
+
if created_at is not None and isinstance(created_at, str):
|
|
39
|
+
self.created_at = parser.parse(created_at)
|
|
40
|
+
else:
|
|
41
|
+
self.created_at = created_at
|
|
42
|
+
|
|
43
|
+
# Make sure that created_at is a timezone aware datetime object
|
|
44
|
+
self.created_at = self.created_at.replace(tzinfo=timezone.utc)
|
|
45
|
+
|
|
46
|
+
if position_snapshots is None:
|
|
47
|
+
position_snapshots = []
|
|
48
|
+
|
|
49
|
+
self.position_snapshots = position_snapshots
|
|
50
|
+
|
|
51
|
+
def get_portfolio_id(self):
|
|
52
|
+
return self.portfolio_id
|
|
53
|
+
|
|
54
|
+
def set_portfolio_id(self, portfolio_id):
|
|
55
|
+
self.portfolio_id = portfolio_id
|
|
56
|
+
|
|
57
|
+
def get_trading_symbol(self):
|
|
58
|
+
return self.trading_symbol
|
|
59
|
+
|
|
60
|
+
def set_trading_symbol(self, trading_symbol):
|
|
61
|
+
self.trading_symbol = trading_symbol.upper()
|
|
62
|
+
|
|
63
|
+
def get_pending_value(self):
|
|
64
|
+
return self.pending_value
|
|
65
|
+
|
|
66
|
+
def set_pending_value(self, pending_value):
|
|
67
|
+
self.pending_value = pending_value
|
|
68
|
+
|
|
69
|
+
def get_unallocated(self):
|
|
70
|
+
return self.unallocated
|
|
71
|
+
|
|
72
|
+
def set_unallocated(self, unallocated):
|
|
73
|
+
self.unallocated = unallocated
|
|
74
|
+
|
|
75
|
+
def get_total_net_gain(self):
|
|
76
|
+
return self.total_net_gain
|
|
77
|
+
|
|
78
|
+
def set_total_net_gain(self, total_net_gain):
|
|
79
|
+
self.total_net_gain = total_net_gain
|
|
80
|
+
|
|
81
|
+
def get_total_revenue(self):
|
|
82
|
+
return self.total_revenue
|
|
83
|
+
|
|
84
|
+
def set_total_revenue(self, total_revenue):
|
|
85
|
+
self.total_revenue = total_revenue
|
|
86
|
+
|
|
87
|
+
def get_total_value(self):
|
|
88
|
+
return self.total_value
|
|
89
|
+
|
|
90
|
+
def set_total_value(self, total_value):
|
|
91
|
+
self.total_value = total_value
|
|
92
|
+
|
|
93
|
+
def get_total_cost(self):
|
|
94
|
+
return self.total_cost
|
|
95
|
+
|
|
96
|
+
def set_total_cost(self, total_cost):
|
|
97
|
+
self.total_cost = total_cost
|
|
98
|
+
|
|
99
|
+
def get_cash_flow(self):
|
|
100
|
+
return self.cash_flow
|
|
101
|
+
|
|
102
|
+
def set_cash_flow(self, cash_flow):
|
|
103
|
+
self.cash_flow = cash_flow
|
|
104
|
+
|
|
105
|
+
def get_created_at(self):
|
|
106
|
+
return self.created_at
|
|
107
|
+
|
|
108
|
+
def set_created_at(self, created_at):
|
|
109
|
+
self.created_at = created_at
|
|
110
|
+
|
|
111
|
+
def get_portfolio_snapshot_id(self):
|
|
112
|
+
return self.portfolio_snapshot_id
|
|
113
|
+
|
|
114
|
+
def set_position_snapshots(self, position_snapshots):
|
|
115
|
+
self.position_snapshots = position_snapshots
|
|
116
|
+
|
|
117
|
+
def get_position_snapshots(self):
|
|
118
|
+
return self.position_snapshots
|
|
119
|
+
|
|
120
|
+
def set_portfolio_snapshot_id(self, portfolio_snapshot_id):
|
|
121
|
+
self.portfolio_snapshot_id = portfolio_snapshot_id
|
|
122
|
+
|
|
123
|
+
def __repr__(self):
|
|
124
|
+
return self.repr(
|
|
125
|
+
portfolio_id=self.portfolio_id,
|
|
126
|
+
created_at=self.created_at.strftime("%Y-%m-%d %H:%M:%S"),
|
|
127
|
+
trading_symbol=self.trading_symbol,
|
|
128
|
+
net_size=self.net_size,
|
|
129
|
+
unallocated=self.unallocated,
|
|
130
|
+
pending_value=self.pending_value,
|
|
131
|
+
total_net_gain=self.total_net_gain,
|
|
132
|
+
total_revenue=self.total_revenue,
|
|
133
|
+
total_cost=self.total_cost,
|
|
134
|
+
cash_flow=self.cash_flow,
|
|
135
|
+
metadata=self.metadata,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def to_dict(self, datetime_format=None):
|
|
139
|
+
"""
|
|
140
|
+
Convert the portfolio snapshot object to a dictionary
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
datetime_format (str): The format to use for the datetime fields.
|
|
144
|
+
If None, the datetime fields will be returned as is.
|
|
145
|
+
Defaults to None.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
dict: A dictionary representation of the portfolio snapshot object.
|
|
149
|
+
"""
|
|
150
|
+
def ensure_iso(value):
|
|
151
|
+
if hasattr(value, "isoformat"):
|
|
152
|
+
if value.tzinfo is None:
|
|
153
|
+
value = value.replace(tzinfo=timezone.utc)
|
|
154
|
+
return value.isoformat()
|
|
155
|
+
return value
|
|
156
|
+
|
|
157
|
+
created_at = ensure_iso(self.created_at) if self.created_at else None
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
"metadata": self.metadata if self.metadata else {},
|
|
161
|
+
"portfolio_id": self.portfolio_id if self.portfolio_id else "",
|
|
162
|
+
"trading_symbol": self.trading_symbol
|
|
163
|
+
if self.trading_symbol else "",
|
|
164
|
+
"pending_value": self.pending_value if self.pending_value else 0.0,
|
|
165
|
+
"unallocated": self.unallocated if self.unallocated else 0.0,
|
|
166
|
+
"total_net_gain": self.total_net_gain
|
|
167
|
+
if self.total_net_gain else 0.0,
|
|
168
|
+
"total_revenue": self.total_revenue if self.total_revenue else 0.0,
|
|
169
|
+
"total_cost": self.total_cost if self.total_cost else 0.0,
|
|
170
|
+
"cash_flow": self.cash_flow if self.cash_flow else 0.0,
|
|
171
|
+
"net_size": self.net_size if self.net_size else 0.0,
|
|
172
|
+
"created_at": created_at if created_at else "",
|
|
173
|
+
"total_value": self.total_value if self.total_value else 0.0,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def from_dict(data):
|
|
178
|
+
"""
|
|
179
|
+
Create a PortfolioSnapshot object from a dictionary.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
data (dict): A dictionary containing the portfolio snapshot data.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
PortfolioSnapshot: An instance of PortfolioSnapshot.
|
|
186
|
+
"""
|
|
187
|
+
created_at_str = data.get("created_at")
|
|
188
|
+
created_at = parser.parse(created_at_str)
|
|
189
|
+
|
|
190
|
+
# Ensure created_at is timezone aware
|
|
191
|
+
created_at = created_at.replace(tzinfo=timezone.utc)
|
|
192
|
+
|
|
193
|
+
return PortfolioSnapshot(
|
|
194
|
+
net_size=data.get("net_size", 0.0),
|
|
195
|
+
created_at=created_at,
|
|
196
|
+
total_value=data.get("total_value", 0.0),
|
|
197
|
+
trading_symbol=data.get(
|
|
198
|
+
"trading_symbol", None
|
|
199
|
+
),
|
|
200
|
+
portfolio_id=data.get("portfolio_id", None),
|
|
201
|
+
pending_value=data.get("pending_value", 0.0),
|
|
202
|
+
unallocated=data.get("unallocated", 0.0),
|
|
203
|
+
total_net_gain=data.get("total_net_gain", 0.0),
|
|
204
|
+
total_revenue=data.get("total_revenue", 0.0),
|
|
205
|
+
total_cost=data.get("total_cost", 0.0),
|
|
206
|
+
cash_flow=data.get("cash_flow", 0.0),
|
|
207
|
+
metadata=data.get("metadata", {})
|
|
208
|
+
)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from investing_algorithm_framework.domain.models.base_model import BaseModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Position(BaseModel):
|
|
5
|
+
"""
|
|
6
|
+
This class represents a position in a portfolio.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
symbol=None,
|
|
12
|
+
amount=0,
|
|
13
|
+
cost=0,
|
|
14
|
+
portfolio_id=None
|
|
15
|
+
):
|
|
16
|
+
self.symbol = symbol
|
|
17
|
+
self.amount = amount
|
|
18
|
+
self.cost = cost
|
|
19
|
+
self.portfolio_id = portfolio_id
|
|
20
|
+
|
|
21
|
+
def get_symbol(self):
|
|
22
|
+
return self.symbol
|
|
23
|
+
|
|
24
|
+
def set_symbol(self, symbol):
|
|
25
|
+
self.symbol = symbol.upper()
|
|
26
|
+
|
|
27
|
+
def get_amount(self):
|
|
28
|
+
return self.amount
|
|
29
|
+
|
|
30
|
+
def get_cost(self):
|
|
31
|
+
return self.cost
|
|
32
|
+
|
|
33
|
+
def set_cost(self, cost):
|
|
34
|
+
self.cost = cost
|
|
35
|
+
|
|
36
|
+
def set_amount(self, amount):
|
|
37
|
+
self.amount = amount
|
|
38
|
+
|
|
39
|
+
def get_portfolio_id(self):
|
|
40
|
+
return self.portfolio_id
|
|
41
|
+
|
|
42
|
+
def set_portfolio_id(self, portfolio_id):
|
|
43
|
+
self.portfolio_id = portfolio_id
|
|
44
|
+
|
|
45
|
+
def to_dict(self):
|
|
46
|
+
return {
|
|
47
|
+
"symbol": self.symbol,
|
|
48
|
+
"amount": self.amount,
|
|
49
|
+
"cost": self.cost,
|
|
50
|
+
"portfolio_id": self.portfolio_id,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def from_dict(data: dict):
|
|
55
|
+
return Position(
|
|
56
|
+
symbol=data.get("symbol"),
|
|
57
|
+
amount=data.get("amount", 0),
|
|
58
|
+
cost=data.get("cost", 0),
|
|
59
|
+
portfolio_id=data.get("portfolio_id"),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def __repr__(self):
|
|
63
|
+
return self.repr(
|
|
64
|
+
symbol=self.symbol,
|
|
65
|
+
amount=self.amount,
|
|
66
|
+
cost=self.cost,
|
|
67
|
+
portfolio_id=self.portfolio_id,
|
|
68
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from investing_algorithm_framework.domain.models.base_model import BaseModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PositionSnapshot(BaseModel):
|
|
5
|
+
|
|
6
|
+
def __init__(
|
|
7
|
+
self,
|
|
8
|
+
symbol=None,
|
|
9
|
+
amount=0,
|
|
10
|
+
cost=0,
|
|
11
|
+
portfolio_snapshot_id=None
|
|
12
|
+
):
|
|
13
|
+
self.symbol = symbol
|
|
14
|
+
self.amount = amount
|
|
15
|
+
self.cost = cost
|
|
16
|
+
self.portfolio_snapshot_id = portfolio_snapshot_id
|
|
17
|
+
|
|
18
|
+
def get_symbol(self):
|
|
19
|
+
return self.symbol
|
|
20
|
+
|
|
21
|
+
def set_symbol(self, symbol):
|
|
22
|
+
self.symbol = symbol.upper()
|
|
23
|
+
|
|
24
|
+
def get_amount(self):
|
|
25
|
+
return self.amount
|
|
26
|
+
|
|
27
|
+
def get_cost(self):
|
|
28
|
+
return self.cost
|
|
29
|
+
|
|
30
|
+
def set_cost(self, cost):
|
|
31
|
+
self.cost = cost
|
|
32
|
+
|
|
33
|
+
def set_amount(self, amount):
|
|
34
|
+
self.amount = amount
|
|
35
|
+
|
|
36
|
+
def get_portfolio_snapshot_id(self):
|
|
37
|
+
return self.portfolio_snapshot_id
|
|
38
|
+
|
|
39
|
+
def set_portfolio_snapshot_id(self, portfolio_snapshot_id):
|
|
40
|
+
self.portfolio_snapshot_id = portfolio_snapshot_id
|
|
41
|
+
|
|
42
|
+
def __repr__(self):
|
|
43
|
+
return self.repr(
|
|
44
|
+
symbol=self.symbol,
|
|
45
|
+
amount=self.amount,
|
|
46
|
+
portfolio_snapshot_id=self.portfolio_snapshot_id,
|
|
47
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SnapshotInterval(Enum):
|
|
5
|
+
STRATEGY_ITERATION = "STRATEGY_ITERATION"
|
|
6
|
+
DAILY = "DAILY"
|
|
7
|
+
|
|
8
|
+
@staticmethod
|
|
9
|
+
def from_string(value: str):
|
|
10
|
+
|
|
11
|
+
if isinstance(value, str):
|
|
12
|
+
|
|
13
|
+
for entry in SnapshotInterval:
|
|
14
|
+
|
|
15
|
+
if value.upper() == entry.value:
|
|
16
|
+
return entry
|
|
17
|
+
|
|
18
|
+
raise ValueError(
|
|
19
|
+
f"Could not convert {value} to SnapshotInterval"
|
|
20
|
+
)
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def from_value(value):
|
|
25
|
+
|
|
26
|
+
if isinstance(value, str):
|
|
27
|
+
return SnapshotInterval.from_string(value)
|
|
28
|
+
|
|
29
|
+
if isinstance(value, SnapshotInterval):
|
|
30
|
+
|
|
31
|
+
for entry in SnapshotInterval:
|
|
32
|
+
|
|
33
|
+
if value == entry:
|
|
34
|
+
return entry
|
|
35
|
+
|
|
36
|
+
raise ValueError(
|
|
37
|
+
f"Could not convert {value} to SnapshotInterval"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def equals(self, other):
|
|
41
|
+
|
|
42
|
+
if isinstance(other, Enum):
|
|
43
|
+
return self.value == other.value
|
|
44
|
+
else:
|
|
45
|
+
return SnapshotInterval.from_string(other) == self
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from .time_unit import TimeUnit
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class StrategyProfile:
|
|
8
|
+
"""
|
|
9
|
+
StrategyProfile class that represents the profile of a trading strategy.
|
|
10
|
+
"""
|
|
11
|
+
strategy_id: str = None
|
|
12
|
+
interval: int = None
|
|
13
|
+
time_unit: TimeUnit = None
|
|
14
|
+
trading_time_frame: str = None
|
|
15
|
+
trading_time_frame_start_date: str = None
|
|
16
|
+
backtest_start_date_data: str = None
|
|
17
|
+
backtest_data_index_date: str = None
|
|
18
|
+
symbols: list = None
|
|
19
|
+
market: str = None
|
|
20
|
+
trading_data_type: str = None
|
|
21
|
+
trading_data_types: list = None
|
|
22
|
+
data_sources: list = None
|
|
23
|
+
|
|
24
|
+
def get_runs_per_day(self):
|
|
25
|
+
|
|
26
|
+
if self.time_unit is None:
|
|
27
|
+
return 0
|
|
28
|
+
elif TimeUnit.SECOND.equals(self.time_unit):
|
|
29
|
+
return 86400 / self.interval
|
|
30
|
+
elif TimeUnit.MINUTE.equals(self.time_unit):
|
|
31
|
+
return 1440 / self.interval
|
|
32
|
+
else:
|
|
33
|
+
return 24 / self.interval
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TimeFrame(Enum):
|
|
5
|
+
CURRENT = "CURRENT"
|
|
6
|
+
ONE_MINUTE = "1m"
|
|
7
|
+
TWO_MINUTE = "2m"
|
|
8
|
+
THREE_MINUTE = "3m"
|
|
9
|
+
FOUR_MINUTE = "4m"
|
|
10
|
+
FIVE_MINUTE = "5m"
|
|
11
|
+
TEN_MINUTE = "10m"
|
|
12
|
+
FIFTEEN_MINUTE = "15m"
|
|
13
|
+
THIRTY_MINUTE = "30m"
|
|
14
|
+
ONE_HOUR = "1h"
|
|
15
|
+
TWO_HOUR = "2h"
|
|
16
|
+
FOUR_HOUR = "4h"
|
|
17
|
+
TWELVE_HOUR = "12h"
|
|
18
|
+
ONE_DAY = "1d"
|
|
19
|
+
ONE_WEEK = "1W"
|
|
20
|
+
ONE_MONTH = "1M"
|
|
21
|
+
ONE_YEAR = "1Y"
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def from_string(value: str):
|
|
25
|
+
|
|
26
|
+
if isinstance(value, str):
|
|
27
|
+
|
|
28
|
+
for entry in TimeFrame:
|
|
29
|
+
|
|
30
|
+
# For hour timeframes compare with and without H
|
|
31
|
+
if "H" in entry.value:
|
|
32
|
+
|
|
33
|
+
if value == entry.value:
|
|
34
|
+
return entry
|
|
35
|
+
|
|
36
|
+
if value == entry.value.replace("H", "h"):
|
|
37
|
+
return entry
|
|
38
|
+
|
|
39
|
+
# For hour timeframes compare with and without H
|
|
40
|
+
if "d" in entry.value:
|
|
41
|
+
|
|
42
|
+
if value == entry.value:
|
|
43
|
+
return entry
|
|
44
|
+
|
|
45
|
+
if value == entry.value.replace("d", "D"):
|
|
46
|
+
return entry
|
|
47
|
+
|
|
48
|
+
if value == entry.value:
|
|
49
|
+
return entry
|
|
50
|
+
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"Could not convert {value} to TimeFrame"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def from_value(value):
|
|
57
|
+
|
|
58
|
+
if isinstance(value, str):
|
|
59
|
+
return TimeFrame.from_string(value)
|
|
60
|
+
|
|
61
|
+
if isinstance(value, TimeFrame):
|
|
62
|
+
|
|
63
|
+
for entry in TimeFrame:
|
|
64
|
+
|
|
65
|
+
if value == entry:
|
|
66
|
+
return entry
|
|
67
|
+
|
|
68
|
+
raise ValueError(
|
|
69
|
+
f"Could not convert {value} to TimeFrame"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def equals(self, other):
|
|
73
|
+
|
|
74
|
+
if isinstance(other, Enum):
|
|
75
|
+
return self.value == other.value
|
|
76
|
+
else:
|
|
77
|
+
return TimeFrame.from_string(other) == self
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def amount_of_minutes(self):
|
|
81
|
+
|
|
82
|
+
if self.equals(TimeFrame.ONE_MINUTE):
|
|
83
|
+
return 1
|
|
84
|
+
|
|
85
|
+
if self.equals(TimeFrame.TWO_MINUTE):
|
|
86
|
+
return 2
|
|
87
|
+
|
|
88
|
+
if self.equals(TimeFrame.THREE_MINUTE):
|
|
89
|
+
return 3
|
|
90
|
+
|
|
91
|
+
if self.equals(TimeFrame.FOUR_MINUTE):
|
|
92
|
+
return 4
|
|
93
|
+
|
|
94
|
+
if self.equals(TimeFrame.FIVE_MINUTE):
|
|
95
|
+
return 5
|
|
96
|
+
|
|
97
|
+
if self.equals(TimeFrame.TEN_MINUTE):
|
|
98
|
+
return 10
|
|
99
|
+
|
|
100
|
+
if self.equals(TimeFrame.FIFTEEN_MINUTE):
|
|
101
|
+
return 15
|
|
102
|
+
|
|
103
|
+
if self.equals(TimeFrame.THIRTY_MINUTE):
|
|
104
|
+
return 30
|
|
105
|
+
|
|
106
|
+
if self.equals(TimeFrame.ONE_HOUR):
|
|
107
|
+
return 60
|
|
108
|
+
|
|
109
|
+
if self.equals(TimeFrame.TWO_HOUR):
|
|
110
|
+
return 120
|
|
111
|
+
|
|
112
|
+
if self.equals(TimeFrame.FOUR_HOUR):
|
|
113
|
+
return 240
|
|
114
|
+
|
|
115
|
+
if self.equals(TimeFrame.TWELVE_HOUR):
|
|
116
|
+
return 720
|
|
117
|
+
|
|
118
|
+
if self.equals(TimeFrame.ONE_DAY):
|
|
119
|
+
return 1440
|
|
120
|
+
|
|
121
|
+
if self.equals(TimeFrame.ONE_WEEK):
|
|
122
|
+
return 10080
|
|
123
|
+
|
|
124
|
+
if self.equals(TimeFrame.ONE_MONTH):
|
|
125
|
+
return 40320
|
|
126
|
+
|
|
127
|
+
if self.equals(TimeFrame.ONE_YEAR):
|
|
128
|
+
return 525600
|
|
129
|
+
|
|
130
|
+
raise ValueError(
|
|
131
|
+
f"Could not determine amount of minutes for {self.value}"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Add comparison methods for ordering
|
|
135
|
+
def __lt__(self, other):
|
|
136
|
+
if isinstance(other, TimeFrame):
|
|
137
|
+
return self.amount_of_minutes < other.amount_of_minutes
|
|
138
|
+
raise TypeError(f"Cannot compare TimeFrame with {type(other)}")
|
|
139
|
+
|
|
140
|
+
def __le__(self, other):
|
|
141
|
+
if isinstance(other, TimeFrame):
|
|
142
|
+
return self.amount_of_minutes <= other.amount_of_minutes
|
|
143
|
+
raise TypeError(f"Cannot compare TimeFrame with {type(other)}")
|
|
144
|
+
|
|
145
|
+
def __gt__(self, other):
|
|
146
|
+
if isinstance(other, TimeFrame):
|
|
147
|
+
return self.amount_of_minutes > other.amount_of_minutes
|
|
148
|
+
raise TypeError(f"Cannot compare TimeFrame with {type(other)}")
|
|
149
|
+
|
|
150
|
+
def __ge__(self, other):
|
|
151
|
+
if isinstance(other, TimeFrame):
|
|
152
|
+
return self.amount_of_minutes >= other.amount_of_minutes
|
|
153
|
+
raise TypeError(f"Cannot compare TimeFrame with {type(other)}")
|