investing-algorithm-framework 3.7.0__py3-none-any.whl → 7.19.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of investing-algorithm-framework might be problematic. Click here for more details.
- investing_algorithm_framework/__init__.py +168 -45
- investing_algorithm_framework/app/__init__.py +32 -1
- 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 +1933 -589
- investing_algorithm_framework/app/app_hook.py +28 -0
- investing_algorithm_framework/app/context.py +1725 -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/action_handlers/__init__.py +4 -2
- investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
- investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +1 -1
- investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
- investing_algorithm_framework/app/strategy.py +664 -84
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/app/web/__init__.py +2 -1
- investing_algorithm_framework/app/web/create_app.py +4 -2
- investing_algorithm_framework/cli/__init__.py +0 -0
- investing_algorithm_framework/cli/cli.py +226 -0
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -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 +40 -6
- investing_algorithm_framework/dependency_container.py +72 -56
- investing_algorithm_framework/domain/__init__.py +71 -47
- 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 +605 -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 +59 -91
- investing_algorithm_framework/domain/constants.py +13 -38
- investing_algorithm_framework/domain/data_provider.py +334 -0
- investing_algorithm_framework/domain/data_structures.py +3 -2
- investing_algorithm_framework/domain/exceptions.py +51 -1
- investing_algorithm_framework/domain/models/__init__.py +17 -12
- 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/market_credential.py +55 -1
- investing_algorithm_framework/domain/models/order/order.py +77 -83
- investing_algorithm_framework/domain/models/order/order_status.py +2 -2
- investing_algorithm_framework/domain/models/order/order_type.py +1 -3
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +81 -3
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +26 -3
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
- investing_algorithm_framework/domain/models/position/__init__.py +2 -1
- investing_algorithm_framework/domain/models/position/position.py +12 -0
- investing_algorithm_framework/domain/models/position/position_size.py +41 -0
- investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
- investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
- investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
- investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
- investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
- investing_algorithm_framework/domain/models/time_frame.py +37 -0
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +66 -2
- investing_algorithm_framework/domain/models/trade/__init__.py +8 -1
- investing_algorithm_framework/domain/models/trade/trade.py +295 -171
- investing_algorithm_framework/domain/models/trade/trade_status.py +9 -2
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
- investing_algorithm_framework/domain/order_executor.py +112 -0
- investing_algorithm_framework/domain/portfolio_provider.py +118 -0
- investing_algorithm_framework/domain/services/__init__.py +2 -9
- investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +0 -6
- investing_algorithm_framework/domain/services/state_handler.py +38 -0
- investing_algorithm_framework/domain/strategy.py +1 -29
- investing_algorithm_framework/domain/utils/__init__.py +12 -7
- 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 +29 -0
- investing_algorithm_framework/download_data.py +108 -0
- investing_algorithm_framework/infrastructure/__init__.py +31 -18
- 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 +6 -2
- investing_algorithm_framework/infrastructure/database/sql_alchemy.py +86 -12
- investing_algorithm_framework/infrastructure/models/__init__.py +6 -11
- investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -1
- investing_algorithm_framework/infrastructure/models/order/order.py +35 -49
- 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 +1 -1
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -0
- investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -5
- 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 +59 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -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 +8 -0
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +5 -0
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +1 -1
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +81 -27
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +4 -4
- 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 +113 -16
- investing_algorithm_framework/services/backtesting/__init__.py +0 -7
- investing_algorithm_framework/services/backtesting/backtest_service.py +566 -359
- investing_algorithm_framework/services/configuration_service.py +77 -11
- 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 +16 -1
- 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/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 +3 -1
- investing_algorithm_framework/services/order_service/order_backtest_service.py +76 -89
- investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
- investing_algorithm_framework/services/order_service/order_service.py +407 -326
- investing_algorithm_framework/services/portfolios/__init__.py +3 -1
- investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +37 -3
- investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +22 -8
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
- investing_algorithm_framework/services/portfolios/portfolio_service.py +96 -28
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +97 -28
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +116 -313
- investing_algorithm_framework/services/positions/__init__.py +7 -0
- investing_algorithm_framework/services/positions/position_service.py +210 -0
- investing_algorithm_framework/services/repository_service.py +8 -2
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +113 -0
- investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
- investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
- investing_algorithm_framework/services/trade_service/__init__.py +7 -1
- investing_algorithm_framework/services/trade_service/trade_service.py +1013 -315
- investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
- investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
- investing_algorithm_framework-7.19.15.dist-info/METADATA +537 -0
- investing_algorithm_framework-7.19.15.dist-info/RECORD +263 -0
- investing_algorithm_framework-7.19.15.dist-info/entry_points.txt +3 -0
- investing_algorithm_framework/app/algorithm.py +0 -1105
- investing_algorithm_framework/domain/graphs.py +0 -382
- investing_algorithm_framework/domain/metrics/__init__.py +0 -6
- investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -11
- investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -43
- investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
- investing_algorithm_framework/domain/models/backtesting/backtest_report.py +0 -580
- investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -243
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
- investing_algorithm_framework/domain/services/market_data_sources.py +0 -344
- investing_algorithm_framework/domain/services/market_service.py +0 -153
- investing_algorithm_framework/domain/singleton.py +0 -9
- investing_algorithm_framework/domain/utils/backtesting.py +0 -472
- investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -12
- investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -559
- investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -254
- investing_algorithm_framework/infrastructure/models/market_data_sources/us_treasury_yield.py +0 -47
- investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
- investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -455
- investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
- investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
- investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -350
- investing_algorithm_framework/services/backtesting/backtest_report_writer_service.py +0 -53
- investing_algorithm_framework/services/backtesting/graphs.py +0 -61
- investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -8
- investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -150
- investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -189
- investing_algorithm_framework/services/position_service.py +0 -31
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -264
- investing_algorithm_framework-3.7.0.dist-info/METADATA +0 -339
- investing_algorithm_framework-3.7.0.dist-info/RECORD +0 -147
- /investing_algorithm_framework/{domain → services}/metrics/price_efficiency.py +0 -0
- /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
- {investing_algorithm_framework-3.7.0.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
- {investing_algorithm_framework-3.7.0.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
|
@@ -3,8 +3,6 @@ from enum import Enum
|
|
|
3
3
|
|
|
4
4
|
class OrderType(Enum):
|
|
5
5
|
LIMIT = 'LIMIT'
|
|
6
|
-
MARKET = 'MARKET'
|
|
7
|
-
STOP_LOSS_LIMIT = "STOP_LOSS_LIMIT"
|
|
8
6
|
|
|
9
7
|
@staticmethod
|
|
10
8
|
def from_string(value: str):
|
|
@@ -15,7 +13,7 @@ class OrderType(Enum):
|
|
|
15
13
|
if value.upper() == order_type.value:
|
|
16
14
|
return order_type
|
|
17
15
|
|
|
18
|
-
raise ValueError("Could not convert value to OrderType")
|
|
16
|
+
raise ValueError(f"Could not convert value: {value} to OrderType")
|
|
19
17
|
|
|
20
18
|
@staticmethod
|
|
21
19
|
def from_value(value):
|
|
@@ -2,6 +2,37 @@ from investing_algorithm_framework.domain.models.base_model import BaseModel
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class Portfolio(BaseModel):
|
|
5
|
+
"""
|
|
6
|
+
Portfolio base class.
|
|
7
|
+
|
|
8
|
+
A portfolio is a collection of positions that are managed by an algorithm.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
* identifier: str, unique identifier of the portfolio
|
|
12
|
+
* trading_symbol: str, trading symbol of the portfolio
|
|
13
|
+
* unallocated: float, the size of the trading symbol that is not
|
|
14
|
+
allocated. For example, if the trading symbol is USDT and the unallocated
|
|
15
|
+
is 1000, it means that the portfolio has 1000 USDT that is not
|
|
16
|
+
allocated to any position.
|
|
17
|
+
* net_size: float, net size of the portfolio is the initial balance of the
|
|
18
|
+
portfolio plus the all the net gains of the trades. The
|
|
19
|
+
* realized: float, the realized gain of the portfolio is the sum of all the
|
|
20
|
+
realized gains of the trades.
|
|
21
|
+
* total_revenue: float, the total revenue of the portfolio is the sum
|
|
22
|
+
of all the orders (price * size)
|
|
23
|
+
* total_cost: float, the total cost of the portfolio is the sum of all the
|
|
24
|
+
costs of the trades (price * size (for buy orders)
|
|
25
|
+
or -price * size (for sell orders))
|
|
26
|
+
* total_net_gain: float, the total net gain of the portfolio is the sum of
|
|
27
|
+
all the net gains of the trades
|
|
28
|
+
* total_trade_volume: float, the total trade volume of the
|
|
29
|
+
portfolio is the sum of all the sizes of the trades
|
|
30
|
+
* market: str, the market of the portfolio (e.g. BITVAVO, BINANCE)
|
|
31
|
+
* created_at: datetime, the datetime when the portfolio was created
|
|
32
|
+
* updated_at: datetime, the datetime when the portfolio was last updated
|
|
33
|
+
* initialized: bool, whether the portfolio is initialized or not
|
|
34
|
+
* initial_balance: float, the initial balance of the portfolio
|
|
35
|
+
"""
|
|
5
36
|
|
|
6
37
|
def __init__(
|
|
7
38
|
self,
|
|
@@ -9,6 +40,7 @@ class Portfolio(BaseModel):
|
|
|
9
40
|
trading_symbol,
|
|
10
41
|
net_size,
|
|
11
42
|
unallocated,
|
|
43
|
+
initial_balance,
|
|
12
44
|
market,
|
|
13
45
|
realized=0,
|
|
14
46
|
total_revenue=0,
|
|
@@ -16,21 +48,25 @@ class Portfolio(BaseModel):
|
|
|
16
48
|
total_net_gain=0,
|
|
17
49
|
total_trade_volume=0,
|
|
18
50
|
created_at=None,
|
|
19
|
-
updated_at=None
|
|
51
|
+
updated_at=None,
|
|
52
|
+
initialized=False,
|
|
20
53
|
):
|
|
21
54
|
self.identifier = identifier
|
|
22
55
|
self.updated_at = None
|
|
23
56
|
self.trading_symbol = trading_symbol.upper()
|
|
24
57
|
self.net_size = net_size
|
|
25
58
|
self.unallocated = unallocated
|
|
59
|
+
self.initial_balance = initial_balance
|
|
26
60
|
self.realized = realized
|
|
27
61
|
self.total_revenue = total_revenue
|
|
28
62
|
self.total_cost = total_cost
|
|
29
63
|
self.total_net_gain = total_net_gain
|
|
30
64
|
self.total_trade_volume = total_trade_volume
|
|
31
|
-
self.market = market
|
|
65
|
+
self.market = market.upper()
|
|
32
66
|
self.created_at = created_at
|
|
33
67
|
self.updated_at = updated_at
|
|
68
|
+
self.initialized = initialized
|
|
69
|
+
self._allocated = None
|
|
34
70
|
|
|
35
71
|
def __repr__(self):
|
|
36
72
|
return self.repr(
|
|
@@ -42,6 +78,7 @@ class Portfolio(BaseModel):
|
|
|
42
78
|
total_revenue=self.total_revenue,
|
|
43
79
|
total_cost=self.total_cost,
|
|
44
80
|
market=self.market,
|
|
81
|
+
initial_balance=self.initial_balance
|
|
45
82
|
)
|
|
46
83
|
|
|
47
84
|
def get_identifier(self):
|
|
@@ -80,12 +117,53 @@ class Portfolio(BaseModel):
|
|
|
80
117
|
def get_market(self):
|
|
81
118
|
return self.market
|
|
82
119
|
|
|
120
|
+
def get_initial_balance(self):
|
|
121
|
+
return self.initial_balance
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def allocated(self):
|
|
125
|
+
|
|
126
|
+
if self._allocated is None:
|
|
127
|
+
return 0.0
|
|
128
|
+
|
|
129
|
+
return self._allocated
|
|
130
|
+
|
|
131
|
+
@allocated.setter
|
|
132
|
+
def allocated(self, value):
|
|
133
|
+
self._allocated = value
|
|
134
|
+
|
|
83
135
|
@staticmethod
|
|
84
136
|
def from_portfolio_configuration(portfolio_configuration):
|
|
137
|
+
"""
|
|
138
|
+
Function to create a portfolio from a portfolio configuration
|
|
139
|
+
|
|
140
|
+
We assume that a portfolio that is created from a configuration
|
|
141
|
+
is always un initialized.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
portfolio_configuration: PortfolioConfiguration
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Portfolio
|
|
148
|
+
"""
|
|
85
149
|
return Portfolio(
|
|
86
150
|
identifier=portfolio_configuration.identifier,
|
|
87
151
|
trading_symbol=portfolio_configuration.trading_symbol,
|
|
88
152
|
unallocated=portfolio_configuration.initial_balance,
|
|
89
153
|
net_size=portfolio_configuration.initial_balance,
|
|
90
|
-
market=portfolio_configuration.market
|
|
154
|
+
market=portfolio_configuration.market,
|
|
155
|
+
initial_balance=portfolio_configuration.initial_balance,
|
|
156
|
+
initialized=False
|
|
91
157
|
)
|
|
158
|
+
|
|
159
|
+
def to_dict(self):
|
|
160
|
+
return {
|
|
161
|
+
"trading_symbol": self.trading_symbol,
|
|
162
|
+
"market": self.market,
|
|
163
|
+
"unallocated": self.unallocated,
|
|
164
|
+
"identifier": self.identifier,
|
|
165
|
+
"created_at": self.created_at,
|
|
166
|
+
"updated_at": self.updated_at,
|
|
167
|
+
"initialized": self.initialized,
|
|
168
|
+
"initial_balance": self.initial_balance,
|
|
169
|
+
}
|
|
@@ -6,6 +6,23 @@ from investing_algorithm_framework.domain.models.base_model import BaseModel
|
|
|
6
6
|
|
|
7
7
|
|
|
8
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
|
+
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
|
+
"""
|
|
9
26
|
|
|
10
27
|
def __init__(
|
|
11
28
|
self,
|
|
@@ -16,13 +33,19 @@ class PortfolioConfiguration(BaseModel):
|
|
|
16
33
|
initial_balance=None,
|
|
17
34
|
):
|
|
18
35
|
self._market = market
|
|
36
|
+
|
|
37
|
+
if self._market is not None:
|
|
38
|
+
self._market = self._market.upper()
|
|
39
|
+
|
|
19
40
|
self._track_from = None
|
|
20
41
|
self._trading_symbol = trading_symbol.upper()
|
|
21
42
|
self._identifier = identifier
|
|
22
43
|
self._initial_balance = initial_balance
|
|
23
44
|
|
|
24
45
|
if self.identifier is None:
|
|
25
|
-
self._identifier = market.
|
|
46
|
+
self._identifier = market.upper()
|
|
47
|
+
else:
|
|
48
|
+
self._identifier = identifier.upper()
|
|
26
49
|
|
|
27
50
|
if track_from:
|
|
28
51
|
self._track_from = parse(track_from)
|
|
@@ -35,8 +58,8 @@ class PortfolioConfiguration(BaseModel):
|
|
|
35
58
|
@property
|
|
36
59
|
def market(self):
|
|
37
60
|
|
|
38
|
-
if hasattr(self._market, "
|
|
39
|
-
return self._market.
|
|
61
|
+
if hasattr(self._market, "upper"):
|
|
62
|
+
return self._market.upper()
|
|
40
63
|
|
|
41
64
|
return self._market
|
|
42
65
|
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
from datetime import timezone
|
|
2
|
+
|
|
3
|
+
from dateutil import parser
|
|
4
|
+
|
|
1
5
|
from investing_algorithm_framework.domain.models.base_model import BaseModel
|
|
2
6
|
|
|
3
7
|
|
|
@@ -5,16 +9,19 @@ class PortfolioSnapshot(BaseModel):
|
|
|
5
9
|
|
|
6
10
|
def __init__(
|
|
7
11
|
self,
|
|
8
|
-
portfolio_id,
|
|
9
|
-
trading_symbol,
|
|
10
|
-
pending_value,
|
|
11
|
-
unallocated,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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,
|
|
18
25
|
):
|
|
19
26
|
self.portfolio_id = portfolio_id
|
|
20
27
|
self.trading_symbol = trading_symbol
|
|
@@ -22,9 +29,19 @@ class PortfolioSnapshot(BaseModel):
|
|
|
22
29
|
self.unallocated = unallocated
|
|
23
30
|
self.total_net_gain = total_net_gain
|
|
24
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
|
|
25
34
|
self.total_cost = total_cost
|
|
26
35
|
self.cash_flow = cash_flow
|
|
27
|
-
self.
|
|
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)
|
|
28
45
|
|
|
29
46
|
if position_snapshots is None:
|
|
30
47
|
position_snapshots = []
|
|
@@ -67,6 +84,12 @@ class PortfolioSnapshot(BaseModel):
|
|
|
67
84
|
def set_total_revenue(self, total_revenue):
|
|
68
85
|
self.total_revenue = total_revenue
|
|
69
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
|
+
|
|
70
93
|
def get_total_cost(self):
|
|
71
94
|
return self.total_cost
|
|
72
95
|
|
|
@@ -102,10 +125,84 @@ class PortfolioSnapshot(BaseModel):
|
|
|
102
125
|
portfolio_id=self.portfolio_id,
|
|
103
126
|
created_at=self.created_at.strftime("%Y-%m-%d %H:%M:%S"),
|
|
104
127
|
trading_symbol=self.trading_symbol,
|
|
128
|
+
net_size=self.net_size,
|
|
105
129
|
unallocated=self.unallocated,
|
|
106
130
|
pending_value=self.pending_value,
|
|
107
131
|
total_net_gain=self.total_net_gain,
|
|
108
132
|
total_revenue=self.total_revenue,
|
|
109
133
|
total_cost=self.total_cost,
|
|
110
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", {})
|
|
111
208
|
)
|
|
@@ -2,6 +2,9 @@ from investing_algorithm_framework.domain.models.base_model import BaseModel
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class Position(BaseModel):
|
|
5
|
+
"""
|
|
6
|
+
This class represents a position in a portfolio.
|
|
7
|
+
"""
|
|
5
8
|
|
|
6
9
|
def __init__(
|
|
7
10
|
self,
|
|
@@ -47,6 +50,15 @@ class Position(BaseModel):
|
|
|
47
50
|
"portfolio_id": self.portfolio_id,
|
|
48
51
|
}
|
|
49
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
|
+
|
|
50
62
|
def __repr__(self):
|
|
51
63
|
return self.repr(
|
|
52
64
|
symbol=self.symbol,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from investing_algorithm_framework.domain.exceptions import \
|
|
3
|
+
OperationalException
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PositionSize:
|
|
7
|
+
"""
|
|
8
|
+
Defines how much capital to allocate to a specific symbol.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
symbol: str,
|
|
14
|
+
percentage_of_portfolio: Optional[float] = None,
|
|
15
|
+
fixed_amount: Optional[float] = None
|
|
16
|
+
):
|
|
17
|
+
self.symbol = symbol
|
|
18
|
+
self.percentage_of_portfolio = percentage_of_portfolio
|
|
19
|
+
self.fixed_amount = fixed_amount
|
|
20
|
+
|
|
21
|
+
def get_size(self, portfolio, asset_price) -> float:
|
|
22
|
+
"""
|
|
23
|
+
Calculate size in currency/units.
|
|
24
|
+
"""
|
|
25
|
+
if self.fixed_amount is not None:
|
|
26
|
+
return self.fixed_amount
|
|
27
|
+
elif self.percentage_of_portfolio is not None:
|
|
28
|
+
total_value = portfolio.get_unallocated() + portfolio.allocated
|
|
29
|
+
return total_value * (self.percentage_of_portfolio / 100)
|
|
30
|
+
else:
|
|
31
|
+
raise OperationalException(
|
|
32
|
+
"A position size object must have either a fixed amount or a "
|
|
33
|
+
"percentage of the portfolio defined."
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def __repr__(self) -> str:
|
|
37
|
+
return (
|
|
38
|
+
f"PositionSize(symbol={self.symbol}, "
|
|
39
|
+
f"percentage_of_portfolio={self.percentage_of_portfolio}, "
|
|
40
|
+
f"fixed_amount={self.fixed_amount})"
|
|
41
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
class StopLossRule:
|
|
2
|
+
"""
|
|
3
|
+
A rule that defines when to trigger a stop loss based
|
|
4
|
+
on a specified threshold such as a percentage drop in price or
|
|
5
|
+
a fixed amount.
|
|
6
|
+
|
|
7
|
+
if trade_risk_type is fixed, the stop loss price is calculated as follows:
|
|
8
|
+
You buy an asset at $100.
|
|
9
|
+
You set a 5% stop loss, meaning you will sell if
|
|
10
|
+
the price drops to $95.
|
|
11
|
+
If the price rises to $120, the stop loss is not triggered.
|
|
12
|
+
But if the price keeps falling to $95, the stop loss triggers,
|
|
13
|
+
and you exit with a $5 loss.
|
|
14
|
+
|
|
15
|
+
if trade_risk_type is trailing, the stop loss price is
|
|
16
|
+
calculated as follows:
|
|
17
|
+
You buy an asset at $100.
|
|
18
|
+
You set a 5% trailing stop loss, meaning you will sell if
|
|
19
|
+
the price drops 5% from its peak at $96
|
|
20
|
+
If the price rises to $120, the stop loss adjusts
|
|
21
|
+
to $114 (5% below $120).
|
|
22
|
+
If the price falls to $114, the position is
|
|
23
|
+
closed, securing a $14 profit.
|
|
24
|
+
But if the price keeps rising to $150, the stop
|
|
25
|
+
loss moves up to $142.50.
|
|
26
|
+
If the price drops from $150 to $142.50, the stop
|
|
27
|
+
loss triggers, and you exit with a $42.50 profit.
|
|
28
|
+
|
|
29
|
+
Attributes:
|
|
30
|
+
- percentage_threshold (float): The percentage drop in price
|
|
31
|
+
that triggers the stop loss.
|
|
32
|
+
- trailing (bool): Indicates whether the stop loss is trailing
|
|
33
|
+
or fixed.
|
|
34
|
+
- sell_percentage (float): The percentage of the position to sell
|
|
35
|
+
when the stop loss is triggered.
|
|
36
|
+
- symbol (str): The symbol of the asset the stop loss rule
|
|
37
|
+
applies to. Symbol is defined as the target symbol
|
|
38
|
+
(the asset being traded) combined with the trading symbol
|
|
39
|
+
(the asset used to trade the target symbol), e.g., 'BTC-EUR'.
|
|
40
|
+
"""
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
percentage_threshold: float,
|
|
44
|
+
sell_percentage: float,
|
|
45
|
+
symbol: str,
|
|
46
|
+
trailing: bool = False,
|
|
47
|
+
):
|
|
48
|
+
self.percentage_threshold = percentage_threshold
|
|
49
|
+
self.trailing = trailing
|
|
50
|
+
self.sell_percentage = sell_percentage
|
|
51
|
+
self.symbol = symbol
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
class TakeProfitRule:
|
|
2
|
+
"""
|
|
3
|
+
A rule that defines when to trigger a take profit based
|
|
4
|
+
on a specified threshold such as a percentage gain in price or
|
|
5
|
+
a fixed amount.
|
|
6
|
+
|
|
7
|
+
if trailing is set to true, the take profit price is
|
|
8
|
+
calculated as follows:
|
|
9
|
+
You buy an asset at $100.
|
|
10
|
+
You set a 5% take profit, meaning you will sell if the price
|
|
11
|
+
rises to $105.
|
|
12
|
+
If the price rises to $120, the take profit triggers,
|
|
13
|
+
and you exit with a $20 profit.
|
|
14
|
+
But if the price keeps falling below $105, the take profit is not
|
|
15
|
+
triggered.
|
|
16
|
+
|
|
17
|
+
if trailing is set to true, the take profit price is
|
|
18
|
+
calculated as follows:
|
|
19
|
+
You buy an asset at $100.
|
|
20
|
+
You set a 5% trailing take profit, the moment the price rises
|
|
21
|
+
5% the initial take profit mark will be set. This means you
|
|
22
|
+
will set the take_profit_price initially at none and
|
|
23
|
+
only if the price hits $105, you will set the
|
|
24
|
+
take_profit_price to $105.
|
|
25
|
+
if the price drops below $105, the take profit is triggered.
|
|
26
|
+
If the price rises to $120, the take profit adjusts to
|
|
27
|
+
$114 (5% below $120).
|
|
28
|
+
If the price falls to $114, the position is closed,
|
|
29
|
+
securing a $14 profit.
|
|
30
|
+
But if the price keeps rising to $150, the take profit
|
|
31
|
+
moves up to $142.50.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
- percentage_threshold (float): The percentage gain in price
|
|
36
|
+
that triggers the stop loss.
|
|
37
|
+
- trailing (bool): Indicates whether the take profit is trailing
|
|
38
|
+
or fixed.
|
|
39
|
+
- sell_percentage (float): The percentage of the position to sell
|
|
40
|
+
when the take profit is triggered.
|
|
41
|
+
- symbol (str): The symbol of the asset the take profit rule
|
|
42
|
+
applies to. Symbol is defined as only the target symbol.
|
|
43
|
+
So for example, 'BTC' in 'BTC-EUR' or 'META' in 'META-USD'.
|
|
44
|
+
"""
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
percentage_threshold: float,
|
|
48
|
+
sell_percentage: float,
|
|
49
|
+
symbol: str,
|
|
50
|
+
trailing: bool = False,
|
|
51
|
+
):
|
|
52
|
+
self.percentage_threshold = percentage_threshold
|
|
53
|
+
self.trailing = trailing
|
|
54
|
+
self.sell_percentage = sell_percentage
|
|
55
|
+
self.symbol = symbol
|
|
@@ -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
|