investing-algorithm-framework 1.3.1__py3-none-any.whl → 7.25.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- investing_algorithm_framework/__init__.py +195 -16
- investing_algorithm_framework/analysis/__init__.py +16 -0
- investing_algorithm_framework/analysis/backtest_data_ranges.py +202 -0
- investing_algorithm_framework/analysis/data.py +170 -0
- investing_algorithm_framework/analysis/markdown.py +91 -0
- investing_algorithm_framework/analysis/ranking.py +298 -0
- investing_algorithm_framework/app/__init__.py +31 -4
- investing_algorithm_framework/app/algorithm/__init__.py +7 -0
- investing_algorithm_framework/app/algorithm/algorithm.py +193 -0
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +118 -0
- investing_algorithm_framework/app/app.py +2233 -264
- investing_algorithm_framework/app/app_hook.py +28 -0
- investing_algorithm_framework/app/context.py +1724 -0
- investing_algorithm_framework/app/eventloop.py +620 -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 +6 -3
- investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
- investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +2 -1
- investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
- investing_algorithm_framework/app/stateless/exception_handler.py +1 -1
- investing_algorithm_framework/app/strategy.py +873 -52
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/app/web/__init__.py +2 -1
- investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
- investing_algorithm_framework/app/web/controllers/orders.py +4 -3
- investing_algorithm_framework/app/web/controllers/portfolio.py +1 -1
- investing_algorithm_framework/app/web/controllers/positions.py +3 -3
- investing_algorithm_framework/app/web/create_app.py +4 -2
- investing_algorithm_framework/app/web/error_handler.py +1 -1
- investing_algorithm_framework/app/web/schemas/order.py +2 -2
- investing_algorithm_framework/app/web/schemas/position.py +1 -0
- investing_algorithm_framework/cli/__init__.py +0 -0
- investing_algorithm_framework/cli/cli.py +231 -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/cli/validate_backtest_checkpoints.py +197 -0
- investing_algorithm_framework/create_app.py +43 -9
- investing_algorithm_framework/dependency_container.py +121 -33
- investing_algorithm_framework/domain/__init__.py +109 -22
- investing_algorithm_framework/domain/algorithm_id.py +69 -0
- investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
- investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
- investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
- investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
- investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
- investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
- investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
- investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
- investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
- investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
- investing_algorithm_framework/domain/config.py +60 -138
- investing_algorithm_framework/domain/constants.py +23 -34
- 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 +51 -1
- investing_algorithm_framework/domain/models/__init__.py +29 -14
- investing_algorithm_framework/domain/models/app_mode.py +34 -0
- investing_algorithm_framework/domain/models/base_model.py +3 -1
- investing_algorithm_framework/domain/models/data/__init__.py +7 -0
- investing_algorithm_framework/domain/models/data/data_source.py +222 -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 +3 -4
- investing_algorithm_framework/domain/models/order/order.py +243 -86
- 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/__init__.py +7 -2
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +134 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -37
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
- investing_algorithm_framework/domain/models/position/__init__.py +3 -2
- investing_algorithm_framework/domain/models/position/position.py +29 -0
- investing_algorithm_framework/domain/models/position/position_size.py +41 -0
- investing_algorithm_framework/domain/models/position/{position_cost.py → position_snapshot.py} +16 -8
- 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 +33 -0
- investing_algorithm_framework/domain/models/time_frame.py +94 -98
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +111 -2
- 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 +11 -0
- investing_algorithm_framework/domain/models/trade/trade.py +389 -0
- investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
- 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 +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/strategy.py +1 -29
- investing_algorithm_framework/domain/utils/__init__.py +16 -4
- investing_algorithm_framework/domain/utils/csv.py +22 -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 +29 -0
- investing_algorithm_framework/download_data.py +244 -0
- investing_algorithm_framework/infrastructure/__init__.py +39 -11
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -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 +87 -13
- investing_algorithm_framework/infrastructure/models/__init__.py +13 -4
- investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
- investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
- investing_algorithm_framework/infrastructure/models/order/order.py +73 -73
- 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 +3 -2
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
- investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +57 -3
- investing_algorithm_framework/infrastructure/models/position/__init__.py +2 -2
- investing_algorithm_framework/infrastructure/models/position/position.py +16 -11
- 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 +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 +13 -5
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +32 -19
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
- investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +47 -4
- investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +85 -31
- 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 +9 -2
- investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
- investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -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/infrastructure/services/backtesting/__init__.py +9 -0
- investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
- investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
- investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
- investing_algorithm_framework/services/__init__.py +127 -10
- investing_algorithm_framework/services/configuration_service.py +95 -0
- investing_algorithm_framework/services/data_providers/__init__.py +5 -0
- investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
- investing_algorithm_framework/services/market_credential_service.py +40 -0
- investing_algorithm_framework/services/metrics/__init__.py +119 -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 +218 -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 +84 -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 +156 -0
- investing_algorithm_framework/services/metrics/trades.py +473 -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 +118 -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/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
- 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 +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 +117 -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 +9 -0
- investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
- 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.25.6.dist-info/METADATA +535 -0
- investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
- {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
- investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
- investing_algorithm_framework/app/algorithm.py +0 -410
- investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
- investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
- investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -76
- investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
- investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
- investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -205
- investing_algorithm_framework/domain/singleton.py +0 -9
- investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
- investing_algorithm_framework/infrastructure/models/position/position_cost.py +0 -32
- investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
- investing_algorithm_framework/infrastructure/repositories/position_cost_repository.py +0 -16
- investing_algorithm_framework/infrastructure/services/market_service.py +0 -422
- investing_algorithm_framework/services/market_data_service.py +0 -75
- investing_algorithm_framework/services/order_service.py +0 -464
- investing_algorithm_framework/services/portfolio_service.py +0 -105
- investing_algorithm_framework/services/position_cost_service.py +0 -5
- investing_algorithm_framework/services/position_service.py +0 -50
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -219
- investing_algorithm_framework/setup_logging.py +0 -40
- investing_algorithm_framework-1.3.1.dist-info/AUTHORS.md +0 -8
- investing_algorithm_framework-1.3.1.dist-info/METADATA +0 -172
- investing_algorithm_framework-1.3.1.dist-info/RECORD +0 -103
- investing_algorithm_framework-1.3.1.dist-info/top_level.txt +0 -1
- {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import Union, List
|
|
2
|
+
|
|
3
|
+
from investing_algorithm_framework.domain import MarketCredential
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MarketCredentialService:
|
|
7
|
+
"""
|
|
8
|
+
Service to manage market credentials.
|
|
9
|
+
|
|
10
|
+
This service is responsible for adding, retrieving, and
|
|
11
|
+
initializing market credentials.
|
|
12
|
+
"""
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self._market_credentials = {}
|
|
15
|
+
|
|
16
|
+
def add(self, market_data_credential: MarketCredential):
|
|
17
|
+
self._market_credentials[market_data_credential.market.upper()] \
|
|
18
|
+
= market_data_credential
|
|
19
|
+
|
|
20
|
+
def add_all(self, market_data_credentials: List[MarketCredential]):
|
|
21
|
+
for market_data_credential in market_data_credentials:
|
|
22
|
+
self.add(market_data_credential)
|
|
23
|
+
|
|
24
|
+
def get(self, market) -> Union[MarketCredential, None]:
|
|
25
|
+
|
|
26
|
+
if market.upper() not in self._market_credentials:
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
return self._market_credentials[market.upper()]
|
|
30
|
+
|
|
31
|
+
def get_all(self) -> List[MarketCredential]:
|
|
32
|
+
return list(self._market_credentials.values())
|
|
33
|
+
|
|
34
|
+
def initialize(self):
|
|
35
|
+
"""
|
|
36
|
+
Initialize all market credentials.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
for market_credential in self.get_all():
|
|
40
|
+
market_credential.initialize()
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from .volatility import get_annual_volatility
|
|
2
|
+
from .sortino_ratio import get_sortino_ratio
|
|
3
|
+
from .drawdown import get_drawdown_series, get_max_drawdown
|
|
4
|
+
from .equity_curve import get_equity_curve
|
|
5
|
+
from .price_efficiency import get_price_efficiency_ratio
|
|
6
|
+
from .profit_factor import get_profit_factor, \
|
|
7
|
+
get_cumulative_profit_factor_series, get_rolling_profit_factor_series
|
|
8
|
+
from .sharpe_ratio import get_sharpe_ratio, get_rolling_sharpe_ratio
|
|
9
|
+
from .price_efficiency import get_price_efficiency_ratio
|
|
10
|
+
from .equity_curve import get_equity_curve
|
|
11
|
+
from .drawdown import get_drawdown_series, get_max_drawdown, \
|
|
12
|
+
get_max_drawdown_absolute, get_max_drawdown_duration, \
|
|
13
|
+
get_max_daily_drawdown
|
|
14
|
+
from .cagr import get_cagr
|
|
15
|
+
from .standard_deviation import get_standard_deviation_downside_returns, \
|
|
16
|
+
get_standard_deviation_returns
|
|
17
|
+
from .returns import get_yearly_returns, get_monthly_returns, \
|
|
18
|
+
get_best_year, get_best_month, get_worst_month, get_total_return, \
|
|
19
|
+
get_average_yearly_return, get_average_monthly_return, \
|
|
20
|
+
get_percentage_winning_months, get_average_monthly_return_losing_months, \
|
|
21
|
+
get_average_monthly_return_winning_months, get_total_growth, \
|
|
22
|
+
get_percentage_winning_years, get_worst_year, get_cumulative_return, \
|
|
23
|
+
get_total_loss, get_cumulative_return_series
|
|
24
|
+
from .exposure import get_average_trade_duration, \
|
|
25
|
+
get_trade_frequency, get_trades_per_day, get_trades_per_year, \
|
|
26
|
+
get_cumulative_exposure, get_exposure_ratio
|
|
27
|
+
from .win_rate import get_win_rate, get_win_loss_ratio, get_current_win_rate, \
|
|
28
|
+
get_current_win_loss_ratio
|
|
29
|
+
from .calmar_ratio import get_calmar_ratio
|
|
30
|
+
from .generate import create_backtest_metrics, \
|
|
31
|
+
create_backtest_metrics_for_backtest
|
|
32
|
+
from .risk_free_rate import get_risk_free_rate_us
|
|
33
|
+
from .trades import get_negative_trades, get_positive_trades, \
|
|
34
|
+
get_number_of_trades, get_number_of_closed_trades, \
|
|
35
|
+
get_average_trade_size, get_average_trade_return, get_best_trade, \
|
|
36
|
+
get_worst_trade, get_average_trade_gain, get_median_trade_return, \
|
|
37
|
+
get_average_trade_loss, get_current_average_trade_loss, \
|
|
38
|
+
get_current_average_trade_duration, get_current_average_trade_gain, \
|
|
39
|
+
get_current_average_trade_return, get_number_of_open_trades, \
|
|
40
|
+
get_average_trade_duration
|
|
41
|
+
from .mean_daily_return import get_mean_daily_return, get_mean_yearly_return
|
|
42
|
+
from .standard_deviation import get_daily_returns_std
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
"get_mean_daily_return",
|
|
46
|
+
"get_daily_returns_std",
|
|
47
|
+
"get_annual_volatility",
|
|
48
|
+
"get_sortino_ratio",
|
|
49
|
+
"get_drawdown_series",
|
|
50
|
+
"get_max_drawdown",
|
|
51
|
+
"get_equity_curve",
|
|
52
|
+
"get_price_efficiency_ratio",
|
|
53
|
+
"get_sharpe_ratio",
|
|
54
|
+
"get_profit_factor",
|
|
55
|
+
"get_cumulative_profit_factor_series",
|
|
56
|
+
"get_rolling_profit_factor_series",
|
|
57
|
+
"get_sharpe_ratio",
|
|
58
|
+
"get_cagr",
|
|
59
|
+
"get_standard_deviation_returns",
|
|
60
|
+
"get_standard_deviation_downside_returns",
|
|
61
|
+
"get_max_drawdown_absolute",
|
|
62
|
+
"get_total_return",
|
|
63
|
+
"get_total_loss",
|
|
64
|
+
"get_total_growth",
|
|
65
|
+
"get_cumulative_exposure",
|
|
66
|
+
"get_exposure_ratio",
|
|
67
|
+
"get_win_rate",
|
|
68
|
+
"get_win_loss_ratio",
|
|
69
|
+
"get_calmar_ratio",
|
|
70
|
+
"get_trade_frequency",
|
|
71
|
+
"get_yearly_returns",
|
|
72
|
+
"get_monthly_returns",
|
|
73
|
+
"get_best_year",
|
|
74
|
+
"get_best_month",
|
|
75
|
+
"get_worst_year",
|
|
76
|
+
"get_worst_month",
|
|
77
|
+
"get_best_trade",
|
|
78
|
+
"get_worst_trade",
|
|
79
|
+
"get_average_yearly_return",
|
|
80
|
+
"get_average_monthly_return",
|
|
81
|
+
"get_percentage_winning_months",
|
|
82
|
+
"get_average_trade_duration",
|
|
83
|
+
"get_trade_frequency",
|
|
84
|
+
"get_win_rate",
|
|
85
|
+
"get_win_loss_ratio",
|
|
86
|
+
"get_calmar_ratio",
|
|
87
|
+
"get_max_drawdown_absolute",
|
|
88
|
+
"get_max_drawdown_duration",
|
|
89
|
+
"get_max_daily_drawdown",
|
|
90
|
+
"get_trades_per_day",
|
|
91
|
+
"get_trades_per_year",
|
|
92
|
+
"get_average_monthly_return_losing_months",
|
|
93
|
+
"get_average_monthly_return_winning_months",
|
|
94
|
+
"get_percentage_winning_years",
|
|
95
|
+
"get_rolling_sharpe_ratio",
|
|
96
|
+
"create_backtest_metrics",
|
|
97
|
+
"get_risk_free_rate_us",
|
|
98
|
+
"get_median_trade_return",
|
|
99
|
+
"get_average_trade_gain",
|
|
100
|
+
"get_average_trade_loss",
|
|
101
|
+
"get_average_trade_size",
|
|
102
|
+
"get_average_trade_return",
|
|
103
|
+
"get_number_of_trades",
|
|
104
|
+
"get_number_of_closed_trades",
|
|
105
|
+
"get_negative_trades",
|
|
106
|
+
"get_positive_trades",
|
|
107
|
+
"get_cumulative_return",
|
|
108
|
+
"get_cumulative_return_series",
|
|
109
|
+
"get_current_win_rate",
|
|
110
|
+
"get_current_win_loss_ratio",
|
|
111
|
+
"get_current_average_trade_loss",
|
|
112
|
+
"get_current_average_trade_duration",
|
|
113
|
+
"get_current_average_trade_gain",
|
|
114
|
+
"get_current_average_trade_return",
|
|
115
|
+
"get_number_of_open_trades",
|
|
116
|
+
"get_average_trade_duration",
|
|
117
|
+
"create_backtest_metrics_for_backtest",
|
|
118
|
+
"get_mean_yearly_return"
|
|
119
|
+
]
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The Compound Annual Growth Rate (CAGR) normalizes returns over a one-year
|
|
3
|
+
period, allowing you to compare performance across different timeframes.
|
|
4
|
+
It assumes the investment grows at a steady rate and compounds over time.
|
|
5
|
+
|
|
6
|
+
This formula is suitable whether your data spans:
|
|
7
|
+
|
|
8
|
+
* Less than a year (e.g. 30 days)
|
|
9
|
+
* Exactly a year (365 days)
|
|
10
|
+
* More than a year (e.g. 500 days)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import pandas as pd
|
|
14
|
+
from typing import List
|
|
15
|
+
from investing_algorithm_framework.domain import PortfolioSnapshot
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_cagr(snapshots: List[PortfolioSnapshot]) -> float:
|
|
19
|
+
"""
|
|
20
|
+
Calculate the Compound Annual Growth Rate (CAGR) of a backtest report.
|
|
21
|
+
CAGR is a useful metric to evaluate the performance of an investment
|
|
22
|
+
over a period of time, normalizing the return to a one-year basis.
|
|
23
|
+
|
|
24
|
+
The formula for CAGR is:
|
|
25
|
+
CAGR = (End Value / Start Value) ^ (1 / Number of Years) - 1
|
|
26
|
+
|
|
27
|
+
Where:
|
|
28
|
+
- End Value is the final value of the investment
|
|
29
|
+
- Start Value is the initial value of the investment
|
|
30
|
+
- Number of Years is the total time period in years
|
|
31
|
+
This function assumes that the snapshots in the report are ordered by
|
|
32
|
+
creation date and that the net size represents the value of the investment.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
snapshots (list[Snapshot]): A list of snapshots
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Float: The CAGR as a decimal. Returns 0.0 if not enough
|
|
39
|
+
data is available.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
if len(snapshots) < 2:
|
|
43
|
+
return 0.0 # Not enough data
|
|
44
|
+
|
|
45
|
+
# Convert snapshots to DataFrame
|
|
46
|
+
data = [(s.total_value, s.created_at) for s in snapshots]
|
|
47
|
+
df = pd.DataFrame(data, columns=["total_value", "created_at"])
|
|
48
|
+
df['created_at'] = pd.to_datetime(df['created_at'])
|
|
49
|
+
df = df.sort_values('created_at')
|
|
50
|
+
start_value = df.iloc[0]['total_value']
|
|
51
|
+
end_value = df.iloc[-1]['total_value']
|
|
52
|
+
start_date = df.iloc[0]['created_at']
|
|
53
|
+
end_date = df.iloc[-1]['created_at']
|
|
54
|
+
num_days = (end_date - start_date).days
|
|
55
|
+
|
|
56
|
+
if num_days == 0 or start_value == 0:
|
|
57
|
+
return 0.0
|
|
58
|
+
|
|
59
|
+
# Apply CAGR formula
|
|
60
|
+
return (end_value / start_value) ** (365 / num_days) - 1
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
| **Calmar Ratio** | **Interpretation** |
|
|
3
|
+
| ---------------- | ----------------------------------------------------------- |
|
|
4
|
+
| **> 3.0** | **Excellent** – strong return vs. drawdown |
|
|
5
|
+
| **2.0 – 3.0** | **Very Good** – solid risk-adjusted performance |
|
|
6
|
+
| **1.0 – 2.0** | **Acceptable** – decent, especially for volatile strategies |
|
|
7
|
+
| **< 1.0** | **Poor** – high drawdowns relative to return |
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import List
|
|
11
|
+
from investing_algorithm_framework.domain import PortfolioSnapshot
|
|
12
|
+
from .cagr import get_cagr
|
|
13
|
+
from .drawdown import get_max_drawdown
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_calmar_ratio(snapshots: List[PortfolioSnapshot]):
|
|
17
|
+
"""
|
|
18
|
+
Calculate the Calmar Ratio, which is the ratio of the annualized
|
|
19
|
+
return to the maximum drawdown.
|
|
20
|
+
|
|
21
|
+
Formula:
|
|
22
|
+
Calmar Ratio = CAGR / |Maximum Drawdown|
|
|
23
|
+
|
|
24
|
+
The Calmar Ratio is a measure of risk-adjusted return,
|
|
25
|
+
where a higher ratio indicates a more favorable risk-return profile.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
|
|
29
|
+
from the backtest report.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
float: The Calmar Ratio.
|
|
33
|
+
"""
|
|
34
|
+
cagr = get_cagr(snapshots)
|
|
35
|
+
max_drawdown = get_max_drawdown(snapshots)
|
|
36
|
+
|
|
37
|
+
if max_drawdown == 0 or max_drawdown is None:
|
|
38
|
+
return 0.0
|
|
39
|
+
|
|
40
|
+
return cagr / max_drawdown
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
""""
|
|
2
|
+
Max Drawdown (MDD) — a key risk metric that shows the worst
|
|
3
|
+
peak-to-trough decline of a portfolio:
|
|
4
|
+
|
|
5
|
+
| **Max Drawdown (%)** | **Interpretation** |
|
|
6
|
+
|-----------------------|----------------------------------------------------------------------|
|
|
7
|
+
| **0% to -5%** | 🟢 Excellent — Very low risk, typical for conservative strategies |
|
|
8
|
+
| **-5% to -10%** | ✅ Good — Moderate volatility, acceptable for balanced portfolios |
|
|
9
|
+
| **-10% to -20%** | ⚠️ Elevated Risk — Common in growth or actively managed strategies |
|
|
10
|
+
| **-20% to -40%** | 🔻 High Risk — Significant drawdown, typical of aggressive strategies |
|
|
11
|
+
| **> -40%** | 🚨 Very High Risk — Risk of capital loss or strategy failure |
|
|
12
|
+
"""
|
|
13
|
+
from typing import List, Tuple
|
|
14
|
+
import pandas as pd
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from investing_algorithm_framework.domain import PortfolioSnapshot, Trade
|
|
17
|
+
from .equity_curve import get_equity_curve
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_drawdown_series(snapshots: List[PortfolioSnapshot]) -> List[Tuple[float, datetime]]:
|
|
21
|
+
"""
|
|
22
|
+
Calculate the drawdown series of a backtest report.
|
|
23
|
+
|
|
24
|
+
The drawdown is calculated as the percentage difference
|
|
25
|
+
between the current equity value and the maximum equity value
|
|
26
|
+
observed up to that point in time.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
List[Tuple[datetime, float]]: A list of tuples with datetime
|
|
33
|
+
and drawdown percentage. The drawdown is expressed as a
|
|
34
|
+
negative percentage, where 0% means no drawdown and -100%
|
|
35
|
+
means the portfolio has lost all its value.
|
|
36
|
+
"""
|
|
37
|
+
equity_curve = get_equity_curve(snapshots)
|
|
38
|
+
|
|
39
|
+
drawdown_series = []
|
|
40
|
+
max_value = None
|
|
41
|
+
|
|
42
|
+
for value, timestamp in equity_curve:
|
|
43
|
+
# Skip zero or negative values to avoid division by zero
|
|
44
|
+
if value <= 0:
|
|
45
|
+
drawdown_series.append((0.0, timestamp))
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
if max_value is None or max_value <= 0:
|
|
49
|
+
max_value = value
|
|
50
|
+
max_value = max(max_value, value)
|
|
51
|
+
drawdown = (value - max_value) / max_value # This will be <= 0
|
|
52
|
+
drawdown_series.append((drawdown, timestamp))
|
|
53
|
+
|
|
54
|
+
return drawdown_series
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_max_drawdown(snapshots: List[PortfolioSnapshot]) -> float:
|
|
58
|
+
"""
|
|
59
|
+
Calculate the maximum drawdown of the portfolio as a percentage from the peak.
|
|
60
|
+
|
|
61
|
+
Max Drawdown is the maximum observed loss from a peak to a
|
|
62
|
+
trough before a new peak is achieved.
|
|
63
|
+
|
|
64
|
+
It is expressed here as a negative percentage.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
float: The maximum drawdown as a negative percentage (e.g., -12.5 for a 12.5% drawdown).
|
|
71
|
+
"""
|
|
72
|
+
equity_curve = get_equity_curve(snapshots)
|
|
73
|
+
|
|
74
|
+
if not equity_curve:
|
|
75
|
+
return 0.0
|
|
76
|
+
|
|
77
|
+
peak = equity_curve[0][0]
|
|
78
|
+
|
|
79
|
+
# Handle zero or negative starting value
|
|
80
|
+
if peak <= 0:
|
|
81
|
+
# Find first positive value as the peak
|
|
82
|
+
for equity, _ in equity_curve:
|
|
83
|
+
if equity > 0:
|
|
84
|
+
peak = equity
|
|
85
|
+
break
|
|
86
|
+
else:
|
|
87
|
+
# No positive values found
|
|
88
|
+
return 0.0
|
|
89
|
+
|
|
90
|
+
max_drawdown_pct = 0.0
|
|
91
|
+
|
|
92
|
+
for equity, _ in equity_curve:
|
|
93
|
+
# Skip non-positive values
|
|
94
|
+
if equity <= 0:
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
if equity > peak:
|
|
98
|
+
peak = equity
|
|
99
|
+
|
|
100
|
+
# Avoid division by zero (shouldn't happen now but extra safety)
|
|
101
|
+
if peak <= 0:
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
drawdown_pct = (equity - peak) / peak # Will be 0 or negative
|
|
105
|
+
max_drawdown_pct = min(max_drawdown_pct, drawdown_pct)
|
|
106
|
+
|
|
107
|
+
return abs(max_drawdown_pct)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def get_max_daily_drawdown(snapshots: List[PortfolioSnapshot]) -> float:
|
|
111
|
+
"""
|
|
112
|
+
Calculate the maximum daily drawdown of the portfolio as a percentage from the peak.
|
|
113
|
+
|
|
114
|
+
This is the largest drop in equity (in percentage) from a peak to a trough
|
|
115
|
+
during the backtest period, calculated on a daily basis.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
float: The maximum daily drawdown as a negative percentage (e.g., -5.0 for a 5% drawdown).
|
|
122
|
+
"""
|
|
123
|
+
# Create DataFrame from snapshots
|
|
124
|
+
data = [(s.created_at, s.total_value) for s in snapshots]
|
|
125
|
+
df = pd.DataFrame(data, columns=["created_at", "total_value"])
|
|
126
|
+
df['created_at'] = pd.to_datetime(df['created_at'])
|
|
127
|
+
df = df.sort_values('created_at').drop_duplicates('created_at')\
|
|
128
|
+
.set_index('created_at')
|
|
129
|
+
|
|
130
|
+
# Resample to daily frequency using last value of the day
|
|
131
|
+
daily_df = df.resample('1D').last().dropna()
|
|
132
|
+
|
|
133
|
+
if daily_df.empty:
|
|
134
|
+
return 0.0
|
|
135
|
+
|
|
136
|
+
# Filter out non-positive values
|
|
137
|
+
positive_values = daily_df[daily_df['total_value'] > 0]['total_value']
|
|
138
|
+
|
|
139
|
+
if positive_values.empty:
|
|
140
|
+
return 0.0
|
|
141
|
+
|
|
142
|
+
peak = positive_values.iloc[0]
|
|
143
|
+
max_daily_drawdown_pct = 0.0
|
|
144
|
+
|
|
145
|
+
for equity in positive_values:
|
|
146
|
+
if equity > peak:
|
|
147
|
+
peak = equity
|
|
148
|
+
|
|
149
|
+
# Avoid division by zero (shouldn't happen but extra safety)
|
|
150
|
+
if peak <= 0:
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
drawdown_pct = (equity - peak) / peak
|
|
154
|
+
max_daily_drawdown_pct = min(max_daily_drawdown_pct, drawdown_pct)
|
|
155
|
+
|
|
156
|
+
return abs(max_daily_drawdown_pct) # Return as positive percentage
|
|
157
|
+
|
|
158
|
+
def get_max_drawdown_duration(snapshots: List[PortfolioSnapshot]) -> int:
|
|
159
|
+
"""
|
|
160
|
+
Calculate the maximum duration of drawdown in days.
|
|
161
|
+
|
|
162
|
+
This is the longest period where the portfolio equity was below its peak.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
int: The maximum drawdown duration in days.
|
|
169
|
+
"""
|
|
170
|
+
equity_curve = get_equity_curve(snapshots)
|
|
171
|
+
if not equity_curve:
|
|
172
|
+
return 0
|
|
173
|
+
|
|
174
|
+
peak = equity_curve[0][0]
|
|
175
|
+
max_duration = 0
|
|
176
|
+
current_duration = 0
|
|
177
|
+
|
|
178
|
+
for equity, _ in equity_curve:
|
|
179
|
+
if equity < peak:
|
|
180
|
+
current_duration += 1
|
|
181
|
+
else:
|
|
182
|
+
max_duration = max(max_duration, current_duration)
|
|
183
|
+
current_duration = 0
|
|
184
|
+
peak = equity # Reset peak to current equity
|
|
185
|
+
|
|
186
|
+
max_duration = max(max_duration, current_duration) # Final check
|
|
187
|
+
|
|
188
|
+
return max_duration
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def get_max_drawdown_absolute(snapshots: List[PortfolioSnapshot]) -> float:
|
|
192
|
+
"""
|
|
193
|
+
Calculate the maximum absolute drawdown of the portfolio.
|
|
194
|
+
|
|
195
|
+
This is the largest drop in equity (in currency units) from a peak to a trough
|
|
196
|
+
during the backtest period.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
float: The maximum absolute drawdown as a positive number (e.g., €10,000).
|
|
203
|
+
"""
|
|
204
|
+
equity_curve = get_equity_curve(snapshots)
|
|
205
|
+
if not equity_curve:
|
|
206
|
+
return 0.0
|
|
207
|
+
|
|
208
|
+
peak = equity_curve[0][0]
|
|
209
|
+
max_drawdown = 0.0
|
|
210
|
+
|
|
211
|
+
for equity, _ in equity_curve:
|
|
212
|
+
if equity > peak:
|
|
213
|
+
peak = equity
|
|
214
|
+
|
|
215
|
+
drawdown = peak - equity # Drop from peak
|
|
216
|
+
max_drawdown = max(max_drawdown, drawdown)
|
|
217
|
+
|
|
218
|
+
return abs(max_drawdown) # Return as positive number (e.g., €10,000)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import List
|
|
3
|
+
from investing_algorithm_framework.domain import PortfolioSnapshot
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_equity_curve(
|
|
7
|
+
snapshots: List[PortfolioSnapshot]
|
|
8
|
+
) -> list[tuple[float, datetime]]:
|
|
9
|
+
"""
|
|
10
|
+
Calculate the total size of the portfolio at each snapshot timestamp.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
snapshots (List[PortfolioSnapshot]): List of portfolio snapshots.
|
|
14
|
+
Returns:
|
|
15
|
+
list[tuple[datetime, float]]: A list of tuples with
|
|
16
|
+
timestamps and total sizes.
|
|
17
|
+
"""
|
|
18
|
+
series = []
|
|
19
|
+
for snapshot in snapshots:
|
|
20
|
+
timestamp = snapshot.created_at
|
|
21
|
+
total_size = snapshot.total_value
|
|
22
|
+
series.append((total_size, timestamp))
|
|
23
|
+
|
|
24
|
+
return series
|