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
|
@@ -1,382 +0,0 @@
|
|
|
1
|
-
import pandas as pd
|
|
2
|
-
import plotly.graph_objs as go
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def create_rsi_graph(data: pd.DataFrame):
|
|
6
|
-
"""
|
|
7
|
-
Create a graph for the RSI metric.
|
|
8
|
-
:param data: DataFrame with a 'RSI' column and a Datetime index
|
|
9
|
-
:return: Plotly graph object
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
# Check if the index is of type datetime
|
|
13
|
-
if not isinstance(data.index, pd.DatetimeIndex):
|
|
14
|
-
raise ValueError("The index of the data should be of type datetime")
|
|
15
|
-
|
|
16
|
-
# Check if the 'RSI' column exists
|
|
17
|
-
if 'RSI' not in data.columns:
|
|
18
|
-
raise ValueError("The data should have a 'RSI' column")
|
|
19
|
-
|
|
20
|
-
return go.Scatter(
|
|
21
|
-
x=data.index,
|
|
22
|
-
y=data['RSI'],
|
|
23
|
-
mode='lines',
|
|
24
|
-
line=dict(color="green", width=1),
|
|
25
|
-
name="RSI"
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def create_prices_graph(
|
|
30
|
-
data: pd.DataFrame,
|
|
31
|
-
data_key="Close",
|
|
32
|
-
graph_name="Price",
|
|
33
|
-
color="blue",
|
|
34
|
-
line_width=1
|
|
35
|
-
):
|
|
36
|
-
"""
|
|
37
|
-
Create a graph for the close prices. By default, the key is set to 'Close'.
|
|
38
|
-
|
|
39
|
-
Args:
|
|
40
|
-
data (pd.DataFrame): The data to plot
|
|
41
|
-
data_key (str): The key to use for the prices
|
|
42
|
-
graph_name (str): The name of the graph
|
|
43
|
-
color (str): The color of the graph
|
|
44
|
-
line_width (int): The width of the line
|
|
45
|
-
|
|
46
|
-
Returns:
|
|
47
|
-
go.Scatter: The Plotly graph object
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
# Check if the index is of type datetime
|
|
51
|
-
if not isinstance(data.index, pd.DatetimeIndex):
|
|
52
|
-
raise ValueError("The index of the data should be of type datetime")
|
|
53
|
-
|
|
54
|
-
# Check if the 'Close' column exists
|
|
55
|
-
if data_key not in data.columns:
|
|
56
|
-
raise ValueError("The data should have a 'Close' column")
|
|
57
|
-
|
|
58
|
-
return go.Scatter(
|
|
59
|
-
x=data.index,
|
|
60
|
-
y=data[data_key],
|
|
61
|
-
mode='lines',
|
|
62
|
-
line=dict(color=color, width=line_width),
|
|
63
|
-
name=graph_name
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def create_adx_graph(data: pd.DataFrame):
|
|
68
|
-
"""
|
|
69
|
-
Create a graph for the ADX metric.
|
|
70
|
-
:param data: DataFrame with a 'ADX' column and a Datetime index
|
|
71
|
-
:return: Plotly graph object
|
|
72
|
-
"""
|
|
73
|
-
|
|
74
|
-
# Check if the index is of type datetime
|
|
75
|
-
if not isinstance(data.index, pd.DatetimeIndex):
|
|
76
|
-
raise ValueError("The index of the data should be of type datetime")
|
|
77
|
-
|
|
78
|
-
# Check if the 'ADX' column exists
|
|
79
|
-
if 'ADX' not in data.columns:
|
|
80
|
-
raise ValueError("The data should have a 'ADX' column")
|
|
81
|
-
|
|
82
|
-
return go.Scatter(
|
|
83
|
-
x=data.index,
|
|
84
|
-
y=data['ADX'],
|
|
85
|
-
mode='lines',
|
|
86
|
-
line=dict(color="green", width=1),
|
|
87
|
-
name="ADX"
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def create_di_plus_graph(data: pd.DataFrame):
|
|
92
|
-
"""
|
|
93
|
-
Create a graph for the DI+ metric.
|
|
94
|
-
:param data: DataFrame with a '+DI' column and a Datetime index
|
|
95
|
-
:return: Plotly graph object
|
|
96
|
-
"""
|
|
97
|
-
|
|
98
|
-
# Check if the index is of type datetime
|
|
99
|
-
if not isinstance(data.index, pd.DatetimeIndex):
|
|
100
|
-
raise ValueError("The index of the data should be of type datetime")
|
|
101
|
-
|
|
102
|
-
# Check if the '+DI' column exists
|
|
103
|
-
if '+DI' not in data.columns:
|
|
104
|
-
raise ValueError("The data should have a '+DI' column")
|
|
105
|
-
|
|
106
|
-
return go.Scatter(
|
|
107
|
-
x=data.index,
|
|
108
|
-
y=data['+DI'],
|
|
109
|
-
mode='lines',
|
|
110
|
-
line=dict(color="orange", width=1),
|
|
111
|
-
name="+DI"
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def create_di_minus_graph(data: pd.DataFrame):
|
|
116
|
-
"""
|
|
117
|
-
Create a graph for the DI- metric.
|
|
118
|
-
:param data: DataFrame with a '-DI' column and a Datetime index
|
|
119
|
-
:return: Plotly graph object
|
|
120
|
-
"""
|
|
121
|
-
|
|
122
|
-
# Check if the index is of type datetime
|
|
123
|
-
if not isinstance(data.index, pd.DatetimeIndex):
|
|
124
|
-
raise ValueError("The index of the data should be of type datetime")
|
|
125
|
-
|
|
126
|
-
# Check if the '-DI' column exists
|
|
127
|
-
if '-DI' not in data.columns:
|
|
128
|
-
raise ValueError("The data should have a '-DI' column")
|
|
129
|
-
|
|
130
|
-
return go.Scatter(
|
|
131
|
-
x=data.index,
|
|
132
|
-
y=data['-DI'],
|
|
133
|
-
mode='lines',
|
|
134
|
-
line=dict(color="purple", width=1),
|
|
135
|
-
name="-DI"
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def create_di_plus_di_minus_crossover_graph(data: pd.DataFrame):
|
|
140
|
-
"""
|
|
141
|
-
Create a graph for the DI- and DI+ crossover.
|
|
142
|
-
"""
|
|
143
|
-
|
|
144
|
-
# Check if the index is of type datetime
|
|
145
|
-
if not isinstance(data.index, pd.DatetimeIndex):
|
|
146
|
-
raise ValueError("The index of the data should be of type datetime")
|
|
147
|
-
|
|
148
|
-
# Check if the '-DI' and '+DI' columns exist
|
|
149
|
-
if '-DI' not in data.columns or '+DI' not in data.columns:
|
|
150
|
-
raise ValueError("The data should have a '-DI' and '+DI' column")
|
|
151
|
-
|
|
152
|
-
# Get all crossover indexes
|
|
153
|
-
crossover_index = data[(data['+DI'] < data['-DI']) &
|
|
154
|
-
(data['+DI'].shift(1) > data['-DI'].shift(1))].index
|
|
155
|
-
|
|
156
|
-
# Use .loc to get the corresponding 'Close' values
|
|
157
|
-
crossover_close_values = data.loc[crossover_index, '+DI']
|
|
158
|
-
|
|
159
|
-
return go.Scatter(
|
|
160
|
-
x=crossover_index,
|
|
161
|
-
y=crossover_close_values,
|
|
162
|
-
mode='markers',
|
|
163
|
-
marker=dict(symbol='circle', size=10, color='blue'),
|
|
164
|
-
name='DI- DI+ Crossover'
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
def create_ema_graph(data: pd.DataFrame, key, color="blue"):
|
|
169
|
-
# Check if the index is of type datetime
|
|
170
|
-
if not isinstance(data.index, pd.DatetimeIndex):
|
|
171
|
-
raise ValueError("The index of the data should be of type datetime")
|
|
172
|
-
|
|
173
|
-
# Check if the key columns exist
|
|
174
|
-
if key not in data.columns:
|
|
175
|
-
raise ValueError(f"The data should have a {key} column")
|
|
176
|
-
|
|
177
|
-
return go.Scatter(
|
|
178
|
-
x=data.index,
|
|
179
|
-
y=data[key],
|
|
180
|
-
mode='lines',
|
|
181
|
-
line=dict(color=color, width=1),
|
|
182
|
-
name=key
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def create_crossover_graph(data: pd.DataFrame, key_one, key_two, color="blue"):
|
|
187
|
-
# Check if the index is of type datetime
|
|
188
|
-
if not isinstance(data.index, pd.DatetimeIndex):
|
|
189
|
-
raise ValueError("The index of the data should be of type datetime")
|
|
190
|
-
|
|
191
|
-
# Check if the key columns exist
|
|
192
|
-
if key_one not in data.columns or key_two not in data.columns:
|
|
193
|
-
raise ValueError(f"The data should have a {key_one} "
|
|
194
|
-
f"and {key_two} column")
|
|
195
|
-
|
|
196
|
-
# Get all crossover indexes
|
|
197
|
-
crossover_index = data[
|
|
198
|
-
(data[key_one] <= data[key_two]) &
|
|
199
|
-
(data[key_one].shift(1) >= data[key_two].shift(1))
|
|
200
|
-
].index
|
|
201
|
-
|
|
202
|
-
# Use .loc to get the corresponding 'Close' values
|
|
203
|
-
crossover_close_values = data.loc[crossover_index, key_one]
|
|
204
|
-
|
|
205
|
-
return go.Scatter(
|
|
206
|
-
x=crossover_index,
|
|
207
|
-
y=crossover_close_values,
|
|
208
|
-
mode='markers',
|
|
209
|
-
marker=dict(symbol='circle', size=10, color=color),
|
|
210
|
-
name=f'{key_one} {key_two} Crossover'
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def create_peaks_chart(data: pd.DataFrame, key="Close", order=5):
|
|
215
|
-
|
|
216
|
-
# Check if the index is of type datetime
|
|
217
|
-
if not isinstance(data.index, pd.DatetimeIndex):
|
|
218
|
-
raise ValueError("The index of the data should be of type datetime")
|
|
219
|
-
|
|
220
|
-
keys = [f'{key}_highs', f'{key}_lows']
|
|
221
|
-
|
|
222
|
-
for key_column in keys:
|
|
223
|
-
if key_column not in data.columns:
|
|
224
|
-
raise ValueError(f"The data should have a '{key_column}' column")
|
|
225
|
-
|
|
226
|
-
# Get all peak indexes
|
|
227
|
-
hh_close_index = data[data[f'{key}_highs'] == 1].index
|
|
228
|
-
lh_close_index = data[data[f'{key}_highs'] == -1].index
|
|
229
|
-
ll_close_index = data[data[f'{key}_lows'] == 1].index
|
|
230
|
-
hl_close_index = data[data[f'{key}_lows'] == -1].index
|
|
231
|
-
|
|
232
|
-
# Use .loc to get the corresponding 'Close' values if
|
|
233
|
-
# the index is in the DataFrame
|
|
234
|
-
hh_close_values = data.loc[hh_close_index, key]
|
|
235
|
-
lh_close_values = data.loc[lh_close_index, key]
|
|
236
|
-
ll_close_values = data.loc[ll_close_index, key]
|
|
237
|
-
hl_close_values = data.loc[hl_close_index, key]
|
|
238
|
-
|
|
239
|
-
# Add higher highs
|
|
240
|
-
higher_high_graph = go.Scatter(
|
|
241
|
-
x=hh_close_index,
|
|
242
|
-
# x=dates[hh_close_index - order].values,
|
|
243
|
-
y=hh_close_values,
|
|
244
|
-
mode='markers',
|
|
245
|
-
marker=dict(symbol='triangle-up', size=10, color='blue'),
|
|
246
|
-
name='Higher High Confirmation'
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
# Add lower highs
|
|
250
|
-
lower_high_graph = go.Scatter(
|
|
251
|
-
x=lh_close_index,
|
|
252
|
-
y=lh_close_values,
|
|
253
|
-
mode='markers',
|
|
254
|
-
marker=dict(symbol='triangle-down', size=10, color='red'),
|
|
255
|
-
name='Lower High Confirmation'
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
# Add lower lows
|
|
259
|
-
lower_lows_graph = go.Scatter(
|
|
260
|
-
x=ll_close_index,
|
|
261
|
-
y=ll_close_values,
|
|
262
|
-
mode='markers',
|
|
263
|
-
marker=dict(symbol='triangle-down', size=10, color='green'),
|
|
264
|
-
name='Lower Lows Confirmation'
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
# Add higher lows
|
|
268
|
-
higher_lows = go.Scatter(
|
|
269
|
-
x=hl_close_index,
|
|
270
|
-
y=hl_close_values,
|
|
271
|
-
mode='markers',
|
|
272
|
-
marker=dict(symbol='triangle-up', size=10, color='purple'),
|
|
273
|
-
name='Higher Lows Confirmation'
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
return higher_high_graph, lower_high_graph, lower_lows_graph, higher_lows
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
def create_bullish_divergence_chart(
|
|
280
|
-
data: pd.DataFrame, key_one, key_two, color='red'
|
|
281
|
-
):
|
|
282
|
-
"""
|
|
283
|
-
A bullish divergence occurs when the "<key_one>_lows" makes
|
|
284
|
-
a new low but the "<key_two>_lows" makes a higher low.
|
|
285
|
-
|
|
286
|
-
For example, if the RSI makes a new low but the close price
|
|
287
|
-
makes a higher low, then we have a bullish divergence.
|
|
288
|
-
"""
|
|
289
|
-
divergence_index = data[(data[f'{key_one}_lows'] == -1)
|
|
290
|
-
& (data[f'{key_two}_lows'] == 1)].index
|
|
291
|
-
divergence_close_values = data.loc[divergence_index, 'Close']
|
|
292
|
-
|
|
293
|
-
return go.Scatter(
|
|
294
|
-
x=divergence_index,
|
|
295
|
-
y=divergence_close_values,
|
|
296
|
-
mode='markers',
|
|
297
|
-
marker=dict(symbol='circle', size=10, color=color),
|
|
298
|
-
name='Bullish Divergence'
|
|
299
|
-
)
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
def create_bearish_divergence_chart(
|
|
303
|
-
data: pd.DataFrame, key_one, key_two, color='red'
|
|
304
|
-
):
|
|
305
|
-
"""
|
|
306
|
-
A bearish divergence occurs when the "<key_one>_highs" makes a
|
|
307
|
-
new high but the "<key_two>_highs" makes a lower high.
|
|
308
|
-
|
|
309
|
-
For example, if the RSI makes a new high but the close price makes
|
|
310
|
-
a lower high, then we have a bearish divergence.
|
|
311
|
-
"""
|
|
312
|
-
|
|
313
|
-
# Add divergence charts
|
|
314
|
-
divergence_index = data[(data[f'{key_one}_highs'] == -1)
|
|
315
|
-
& (data[f'{key_two}_highs'] == 1)].index
|
|
316
|
-
divergence_close_values = data.loc[divergence_index, 'Close']
|
|
317
|
-
|
|
318
|
-
return go.Scatter(
|
|
319
|
-
x=divergence_index,
|
|
320
|
-
y=divergence_close_values,
|
|
321
|
-
mode='markers',
|
|
322
|
-
marker=dict(symbol='circle', size=10, color=color),
|
|
323
|
-
name='Bearish Divergence'
|
|
324
|
-
)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
def create_entry_graph(data: pd.DataFrame):
|
|
328
|
-
# Iterate over each row in the DataFrame and check if there is a
|
|
329
|
-
# bullish divergence between the RSI and the close price
|
|
330
|
-
# and if there is a crossover between the DI+ and DI- for
|
|
331
|
-
# the last 12 hours (6 candles)
|
|
332
|
-
# Get all crossover indexes
|
|
333
|
-
crossover_index = data[(data['+DI'] <= data['-DI']) &
|
|
334
|
-
(data['+DI'].shift(1) >= data['-DI'].shift(1))]\
|
|
335
|
-
.index
|
|
336
|
-
data['di_crossover'] = 0
|
|
337
|
-
data.loc[crossover_index, 'di_crossover'] = 1
|
|
338
|
-
|
|
339
|
-
entry_indexes = []
|
|
340
|
-
|
|
341
|
-
for row in data.itertuples():
|
|
342
|
-
|
|
343
|
-
if row.di_crossover == 1:
|
|
344
|
-
match = False
|
|
345
|
-
# Check if there was a bullish divergence between
|
|
346
|
-
# the RSI and the close price in the last 2 days
|
|
347
|
-
rsi_window = data.loc[
|
|
348
|
-
row.Index - pd.Timedelta(days=2):row.Index,
|
|
349
|
-
'RSI_lows'
|
|
350
|
-
]
|
|
351
|
-
close_window = data.loc[
|
|
352
|
-
row.Index - pd.Timedelta(days=2):row.Index,
|
|
353
|
-
'Close_lows'
|
|
354
|
-
]
|
|
355
|
-
|
|
356
|
-
# Go over each row and check if there is a bullish
|
|
357
|
-
# divergence between the RSI and the close price
|
|
358
|
-
for rsi_row, close_row in zip(rsi_window, close_window):
|
|
359
|
-
|
|
360
|
-
if rsi_row == -1 and close_row == 1:
|
|
361
|
-
entry_indexes.append(row.Index)
|
|
362
|
-
match = True
|
|
363
|
-
break
|
|
364
|
-
|
|
365
|
-
if not match:
|
|
366
|
-
# Check if the RSI had decreased
|
|
367
|
-
rsi_window = data.loc[
|
|
368
|
-
row.Index - pd.Timedelta(days=1):row.Index, 'RSI'
|
|
369
|
-
]
|
|
370
|
-
rsi_diff = rsi_window.diff().mean()
|
|
371
|
-
|
|
372
|
-
if rsi_diff < -2:
|
|
373
|
-
entry_indexes.append(row.Index)
|
|
374
|
-
|
|
375
|
-
entry_close_values = data.loc[entry_indexes, 'Close']
|
|
376
|
-
return go.Scatter(
|
|
377
|
-
x=entry_indexes,
|
|
378
|
-
y=entry_close_values,
|
|
379
|
-
mode='markers',
|
|
380
|
-
marker=dict(symbol='circle', size=10, color='green'),
|
|
381
|
-
name='Entry Signal'
|
|
382
|
-
)
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
from .backtest_position import BacktestPosition
|
|
2
|
-
from .backtest_report import BacktestReport
|
|
3
|
-
from .backtest_reports_evaluation import BacktestReportsEvaluation
|
|
4
|
-
from .backtest_date_range import BacktestDateRange
|
|
5
|
-
|
|
6
|
-
__all__ = [
|
|
7
|
-
"BacktestReport",
|
|
8
|
-
"BacktestPosition",
|
|
9
|
-
"BacktestReportsEvaluation",
|
|
10
|
-
"BacktestDateRange"
|
|
11
|
-
]
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
|
-
from dateutil.parser import parse
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class BacktestDateRange:
|
|
6
|
-
"""
|
|
7
|
-
Represents a date range for a backtest
|
|
8
|
-
"""
|
|
9
|
-
def __init__(self, start_date, end_date=None, name=None):
|
|
10
|
-
|
|
11
|
-
if isinstance(start_date, str):
|
|
12
|
-
start_date = parse(start_date)
|
|
13
|
-
|
|
14
|
-
if end_date is not None and isinstance(end_date, str):
|
|
15
|
-
end_date = parse(end_date)
|
|
16
|
-
|
|
17
|
-
self._start_date = start_date
|
|
18
|
-
self._end_date = end_date
|
|
19
|
-
self._name = name
|
|
20
|
-
|
|
21
|
-
if end_date is None:
|
|
22
|
-
self._end_date = datetime.now()
|
|
23
|
-
|
|
24
|
-
if end_date < start_date:
|
|
25
|
-
raise ValueError(
|
|
26
|
-
"End date cannot be before start date for a backtest "
|
|
27
|
-
"date range."
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
@property
|
|
31
|
-
def start_date(self):
|
|
32
|
-
return self._start_date
|
|
33
|
-
|
|
34
|
-
@property
|
|
35
|
-
def end_date(self):
|
|
36
|
-
return self._end_date
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def name(self):
|
|
40
|
-
return self._name
|
|
41
|
-
|
|
42
|
-
def __str__(self):
|
|
43
|
-
return f"{self.name}: {self._start_date} - {self._end_date}"
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
from investing_algorithm_framework.domain.models.base_model import BaseModel
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class BacktestPosition(BaseModel):
|
|
5
|
-
|
|
6
|
-
def __init__(
|
|
7
|
-
self,
|
|
8
|
-
position,
|
|
9
|
-
trading_symbol=False,
|
|
10
|
-
amount_pending_buy=0.0,
|
|
11
|
-
amount_pending_sell=0.0,
|
|
12
|
-
total_value_portfolio=0.0
|
|
13
|
-
):
|
|
14
|
-
self._symbol = position.symbol
|
|
15
|
-
self._amount = position.amount + amount_pending_sell
|
|
16
|
-
self._cost = position.cost
|
|
17
|
-
self._price = 0.0
|
|
18
|
-
self._trading_symbol = trading_symbol
|
|
19
|
-
self._amount_pending_buy = amount_pending_buy
|
|
20
|
-
self._amount_pending_sell = amount_pending_sell
|
|
21
|
-
self._total_value_portfolio = total_value_portfolio
|
|
22
|
-
|
|
23
|
-
@property
|
|
24
|
-
def price(self):
|
|
25
|
-
return self._price
|
|
26
|
-
|
|
27
|
-
@price.setter
|
|
28
|
-
def price(self, value):
|
|
29
|
-
self._price = value
|
|
30
|
-
|
|
31
|
-
@property
|
|
32
|
-
def cost(self):
|
|
33
|
-
|
|
34
|
-
if self._trading_symbol:
|
|
35
|
-
return self.amount
|
|
36
|
-
|
|
37
|
-
return self._cost
|
|
38
|
-
|
|
39
|
-
@property
|
|
40
|
-
def value(self):
|
|
41
|
-
|
|
42
|
-
if self._trading_symbol:
|
|
43
|
-
return self.amount
|
|
44
|
-
|
|
45
|
-
return self._price * self.amount
|
|
46
|
-
|
|
47
|
-
@property
|
|
48
|
-
def growth(self):
|
|
49
|
-
|
|
50
|
-
if self._amount == 0:
|
|
51
|
-
return 0.0
|
|
52
|
-
|
|
53
|
-
if self._trading_symbol:
|
|
54
|
-
return 0.0
|
|
55
|
-
|
|
56
|
-
return self.value - self.cost
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def growth_rate(self):
|
|
60
|
-
|
|
61
|
-
if self._trading_symbol:
|
|
62
|
-
return 0.0
|
|
63
|
-
|
|
64
|
-
if self.cost == 0:
|
|
65
|
-
return 0.0
|
|
66
|
-
|
|
67
|
-
return self.growth / self.cost * 100
|
|
68
|
-
|
|
69
|
-
@property
|
|
70
|
-
def symbol(self):
|
|
71
|
-
return self._symbol
|
|
72
|
-
|
|
73
|
-
@property
|
|
74
|
-
def amount(self):
|
|
75
|
-
return self._amount
|
|
76
|
-
|
|
77
|
-
@property
|
|
78
|
-
def amount_pending_sell(self):
|
|
79
|
-
return self._amount_pending_sell
|
|
80
|
-
|
|
81
|
-
@amount_pending_sell.setter
|
|
82
|
-
def amount_pending_sell(self, value):
|
|
83
|
-
self._amount_pending_sell = value
|
|
84
|
-
|
|
85
|
-
@property
|
|
86
|
-
def amount_pending_buy(self):
|
|
87
|
-
return self._amount_pending_buy
|
|
88
|
-
|
|
89
|
-
@amount_pending_buy.setter
|
|
90
|
-
def amount_pending_buy(self, value):
|
|
91
|
-
self._amount_pending_buy = value
|
|
92
|
-
|
|
93
|
-
@property
|
|
94
|
-
def percentage_of_portfolio(self):
|
|
95
|
-
|
|
96
|
-
if self._total_value_portfolio == 0:
|
|
97
|
-
return 0.0
|
|
98
|
-
|
|
99
|
-
return (self.value / self._total_value_portfolio) * 100
|
|
100
|
-
|
|
101
|
-
def get_percentage_of_portfolio(self):
|
|
102
|
-
|
|
103
|
-
if self._total_value_portfolio == 0:
|
|
104
|
-
return 0.0
|
|
105
|
-
|
|
106
|
-
return self.value / self._total_value_portfolio * 100
|
|
107
|
-
|
|
108
|
-
def to_dict(self):
|
|
109
|
-
return {
|
|
110
|
-
"symbol": self.symbol,
|
|
111
|
-
"amount": self.amount,
|
|
112
|
-
"cost": self.cost,
|
|
113
|
-
"price": self.price,
|
|
114
|
-
"value": self.value,
|
|
115
|
-
"growth": self.growth,
|
|
116
|
-
"growth_rate": self.growth_rate,
|
|
117
|
-
"amount_pending_buy": self.amount_pending_buy,
|
|
118
|
-
"amount_pending_sell": self.amount_pending_sell,
|
|
119
|
-
"percentage_of_portfolio": self.percentage_of_portfolio
|
|
120
|
-
}
|