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,559 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
import logging
|
|
3
|
-
import os
|
|
4
|
-
from datetime import timedelta
|
|
5
|
-
from dateutil.parser import parse
|
|
6
|
-
import polars
|
|
7
|
-
from dateutil import parser
|
|
8
|
-
|
|
9
|
-
from investing_algorithm_framework.domain import RESOURCE_DIRECTORY, \
|
|
10
|
-
BACKTEST_DATA_DIRECTORY_NAME, DATETIME_FORMAT_BACKTESTING, \
|
|
11
|
-
OperationalException, DATETIME_FORMAT, OHLCVMarketDataSource, \
|
|
12
|
-
BacktestMarketDataSource, OrderBookMarketDataSource, \
|
|
13
|
-
TickerMarketDataSource, TimeFrame
|
|
14
|
-
from investing_algorithm_framework.infrastructure.services import \
|
|
15
|
-
CCXTMarketService
|
|
16
|
-
|
|
17
|
-
logger = logging.getLogger(__name__)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class CCXTOHLCVBacktestMarketDataSource(
|
|
21
|
-
OHLCVMarketDataSource, BacktestMarketDataSource
|
|
22
|
-
):
|
|
23
|
-
"""
|
|
24
|
-
CCXTOHLCVBacktestMarketDataSource implementation using ccxt to download
|
|
25
|
-
all data sources.
|
|
26
|
-
|
|
27
|
-
This class will determine the start and end date of the data range by
|
|
28
|
-
taking the backtest start date (e.g. 01-01-2024) and the backtest
|
|
29
|
-
end date (e.g. 31-12-2024) in combination with the difference between
|
|
30
|
-
start and end date. The reason for this is that the data source needs
|
|
31
|
-
to have data on the first run (e.g. an algorithm starting on
|
|
32
|
-
01-01-2024 that requires 2h data for the last 17 days will
|
|
33
|
-
need to have pulled data from 15-12-2023)
|
|
34
|
-
|
|
35
|
-
To achieve this, a backtest_data_start_date attribute is used. This
|
|
36
|
-
attribute is indexed on this calculated date.
|
|
37
|
-
"""
|
|
38
|
-
backtest_data_directory = None
|
|
39
|
-
backtest_data_end_date = None
|
|
40
|
-
total_minutes_timeframe = None
|
|
41
|
-
column_names = ["Datetime", "Open", "High", "Low", "Close", "Volume"]
|
|
42
|
-
|
|
43
|
-
def __init__(
|
|
44
|
-
self,
|
|
45
|
-
identifier,
|
|
46
|
-
market,
|
|
47
|
-
symbol,
|
|
48
|
-
timeframe,
|
|
49
|
-
window_size=None,
|
|
50
|
-
):
|
|
51
|
-
super().__init__(
|
|
52
|
-
identifier=identifier,
|
|
53
|
-
market=market,
|
|
54
|
-
symbol=symbol,
|
|
55
|
-
timeframe=timeframe,
|
|
56
|
-
window_size=window_size,
|
|
57
|
-
)
|
|
58
|
-
self.data = None
|
|
59
|
-
self._start_date_data_source = None
|
|
60
|
-
self._end_date_data_source = None
|
|
61
|
-
|
|
62
|
-
def prepare_data(
|
|
63
|
-
self,
|
|
64
|
-
config,
|
|
65
|
-
backtest_start_date,
|
|
66
|
-
backtest_end_date,
|
|
67
|
-
**kwargs
|
|
68
|
-
):
|
|
69
|
-
"""
|
|
70
|
-
Prepare data implementation of ccxt based ohlcv backtest market
|
|
71
|
-
data source
|
|
72
|
-
|
|
73
|
-
This implementation will check if the data source already exists before
|
|
74
|
-
pulling all the data. This optimization will prevent downloading
|
|
75
|
-
of unnecessary resources.
|
|
76
|
-
|
|
77
|
-
When downloading the data it will use the ccxt library.
|
|
78
|
-
"""
|
|
79
|
-
# Calculating the backtest data start date
|
|
80
|
-
backtest_data_start_date = \
|
|
81
|
-
backtest_start_date - timedelta(
|
|
82
|
-
minutes=(
|
|
83
|
-
(self.window_size + 1) *
|
|
84
|
-
TimeFrame.from_value(self.timeframe).amount_of_minutes
|
|
85
|
-
)
|
|
86
|
-
)
|
|
87
|
-
self.backtest_data_start_date = backtest_data_start_date\
|
|
88
|
-
.replace(microsecond=0)
|
|
89
|
-
self.backtest_data_index_date = backtest_data_start_date\
|
|
90
|
-
.replace(microsecond=0)
|
|
91
|
-
self.backtest_data_end_date = backtest_end_date.replace(microsecond=0)
|
|
92
|
-
|
|
93
|
-
# Creating the backtest data directory and file
|
|
94
|
-
self.backtest_data_directory = os.path.join(
|
|
95
|
-
config.get(RESOURCE_DIRECTORY),
|
|
96
|
-
config.get(BACKTEST_DATA_DIRECTORY_NAME)
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
if not os.path.isdir(self.backtest_data_directory):
|
|
100
|
-
os.mkdir(self.backtest_data_directory)
|
|
101
|
-
|
|
102
|
-
file_path = self._create_file_path()
|
|
103
|
-
|
|
104
|
-
if not self._data_source_exists(file_path):
|
|
105
|
-
if not os.path.isfile(file_path):
|
|
106
|
-
try:
|
|
107
|
-
with open(file_path, 'w') as _:
|
|
108
|
-
pass
|
|
109
|
-
except Exception as e:
|
|
110
|
-
logger.error(e)
|
|
111
|
-
raise OperationalException(
|
|
112
|
-
f"Could not create backtest data file {file_path}"
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
# Get the OHLCV data from the ccxt market service
|
|
116
|
-
market_service = CCXTMarketService(
|
|
117
|
-
market_credential_service=self.market_credential_service,
|
|
118
|
-
)
|
|
119
|
-
market_service.config = config
|
|
120
|
-
ohlcv = market_service.get_ohlcv(
|
|
121
|
-
symbol=self.symbol,
|
|
122
|
-
time_frame=self.timeframe.value,
|
|
123
|
-
from_timestamp=backtest_data_start_date,
|
|
124
|
-
to_timestamp=backtest_end_date,
|
|
125
|
-
market=self.market
|
|
126
|
-
)
|
|
127
|
-
self.write_data_to_file_path(file_path, ohlcv)
|
|
128
|
-
|
|
129
|
-
self.load_data()
|
|
130
|
-
|
|
131
|
-
def load_data(self):
|
|
132
|
-
file_path = self._create_file_path()
|
|
133
|
-
self.data = polars.read_csv(file_path)
|
|
134
|
-
first_row = self.data.head(1)
|
|
135
|
-
last_row = self.data.tail(1)
|
|
136
|
-
self._start_date_data_source = parse(first_row["Datetime"][0])
|
|
137
|
-
self._end_date_data_source = parse(last_row["Datetime"][0])
|
|
138
|
-
|
|
139
|
-
def _create_file_path(self):
|
|
140
|
-
"""
|
|
141
|
-
Function to create a filename in the following format:
|
|
142
|
-
OHLCV_{symbol}_{market}_{timeframe}_{start_date}_{end_date}.csv
|
|
143
|
-
"""
|
|
144
|
-
symbol_string = self.symbol.replace("/", "-")
|
|
145
|
-
time_frame_string = self.timeframe.value.replace("_", "")
|
|
146
|
-
backtest_data_start_date = \
|
|
147
|
-
self.backtest_data_start_date.strftime(DATETIME_FORMAT_BACKTESTING)
|
|
148
|
-
backtest_data_end_date = \
|
|
149
|
-
self.backtest_data_end_date.strftime(DATETIME_FORMAT_BACKTESTING)
|
|
150
|
-
return os.path.join(
|
|
151
|
-
self.backtest_data_directory,
|
|
152
|
-
os.path.join(
|
|
153
|
-
f"OHLCV_"
|
|
154
|
-
f"{symbol_string}_"
|
|
155
|
-
f"{self.market}_"
|
|
156
|
-
f"{time_frame_string}_"
|
|
157
|
-
f"{backtest_data_start_date}_"
|
|
158
|
-
f"{backtest_data_end_date}.csv"
|
|
159
|
-
)
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
def get_data(self, **kwargs):
|
|
163
|
-
"""
|
|
164
|
-
Get data implementation of ccxt based ohlcv backtest market data
|
|
165
|
-
source. This implementation will use polars to load and filter the
|
|
166
|
-
data.
|
|
167
|
-
"""
|
|
168
|
-
start_date = kwargs.get("start_date")
|
|
169
|
-
end_date = kwargs.get("end_date")
|
|
170
|
-
backtest_index_date = kwargs.get("backtest_index_date")
|
|
171
|
-
|
|
172
|
-
if self.data is None:
|
|
173
|
-
self.load_data()
|
|
174
|
-
|
|
175
|
-
if start_date is None \
|
|
176
|
-
and end_date is None \
|
|
177
|
-
and backtest_index_date is None:
|
|
178
|
-
return self.data
|
|
179
|
-
|
|
180
|
-
if backtest_index_date is not None:
|
|
181
|
-
end_date = backtest_index_date
|
|
182
|
-
start_date = self.create_start_date(
|
|
183
|
-
end_date, self.timeframe, self.window_size
|
|
184
|
-
)
|
|
185
|
-
else:
|
|
186
|
-
if start_date is None:
|
|
187
|
-
start_date = self.create_start_date(
|
|
188
|
-
end_date, self.timeframe, self.window_size
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
if end_date is None:
|
|
192
|
-
end_date = self.create_end_date(
|
|
193
|
-
start_date, self.timeframe, self.window_size
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
if start_date < self._start_date_data_source:
|
|
197
|
-
raise OperationalException(
|
|
198
|
-
f"Start date {start_date} is before the start date "
|
|
199
|
-
f"of the data source {self._start_date_data_source}"
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
if end_date > self._end_date_data_source:
|
|
203
|
-
raise OperationalException(
|
|
204
|
-
f"End date {end_date} is after the end date "
|
|
205
|
-
f"of the data source {self._end_date_data_source}"
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
selection = self.data.filter(
|
|
209
|
-
(self.data['Datetime'] >= start_date.strftime(DATETIME_FORMAT))
|
|
210
|
-
& (self.data['Datetime'] <= end_date.strftime(DATETIME_FORMAT))
|
|
211
|
-
)
|
|
212
|
-
return selection
|
|
213
|
-
|
|
214
|
-
def to_backtest_market_data_source(self) -> BacktestMarketDataSource:
|
|
215
|
-
# Ignore this method for now
|
|
216
|
-
pass
|
|
217
|
-
|
|
218
|
-
def empty(self):
|
|
219
|
-
return False
|
|
220
|
-
|
|
221
|
-
@property
|
|
222
|
-
def file_name(self):
|
|
223
|
-
return self._create_file_path().split("/")[-1]
|
|
224
|
-
|
|
225
|
-
def write_data_to_file_path(self, data_file, data: polars.DataFrame):
|
|
226
|
-
data.write_csv(data_file)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
class CCXTTickerBacktestMarketDataSource(
|
|
230
|
-
TickerMarketDataSource, BacktestMarketDataSource
|
|
231
|
-
):
|
|
232
|
-
"""
|
|
233
|
-
CCXTTickerBacktestMarketDataSource implementation using ccxt to download
|
|
234
|
-
all data sources.
|
|
235
|
-
|
|
236
|
-
This class will determine the start and end date of the data range by
|
|
237
|
-
taking the start date of the backtest minus 1 day and the end date of the
|
|
238
|
-
backtest. The reason for this is that the data source needs
|
|
239
|
-
to have data on the first run (e.g. an algorithm starting on
|
|
240
|
-
01-01-2024 that requires ticker data will need to have pulled data from
|
|
241
|
-
01-01-2024 - amount of minutes of the provided timeframe)
|
|
242
|
-
|
|
243
|
-
To achieve this, a backtest_data_start_date attribute is used. This
|
|
244
|
-
attribute is indexed on this calculated date.
|
|
245
|
-
"""
|
|
246
|
-
backtest_data_directory = None
|
|
247
|
-
backtest_data_start_date = None
|
|
248
|
-
backtest_data_end_date = None
|
|
249
|
-
timeframe = None
|
|
250
|
-
column_names = ["Datetime", "Open", "High", "Low", "Close", "Volume"]
|
|
251
|
-
|
|
252
|
-
def __init__(
|
|
253
|
-
self,
|
|
254
|
-
identifier,
|
|
255
|
-
market,
|
|
256
|
-
symbol=None,
|
|
257
|
-
timeframe=None,
|
|
258
|
-
):
|
|
259
|
-
super().__init__(
|
|
260
|
-
identifier=identifier,
|
|
261
|
-
market=market,
|
|
262
|
-
symbol=symbol,
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
if timeframe is not None:
|
|
266
|
-
self.timeframe = timeframe
|
|
267
|
-
|
|
268
|
-
if self.timeframe is None:
|
|
269
|
-
raise OperationalException(
|
|
270
|
-
"timeframe should be set for "
|
|
271
|
-
"CCXTTickerBacktestMarketDataSource"
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
def prepare_data(
|
|
275
|
-
self,
|
|
276
|
-
config,
|
|
277
|
-
backtest_start_date,
|
|
278
|
-
backtest_end_date,
|
|
279
|
-
**kwargs
|
|
280
|
-
):
|
|
281
|
-
"""
|
|
282
|
-
Prepare data implementation of ccxt based ticker backtest market
|
|
283
|
-
data source
|
|
284
|
-
|
|
285
|
-
This implementation will check if the data source already exists before
|
|
286
|
-
pulling all the data. This optimization will prevent downloading
|
|
287
|
-
of unnecessary resources.
|
|
288
|
-
|
|
289
|
-
When downloading the data it will use the ccxt library.
|
|
290
|
-
"""
|
|
291
|
-
total_minutes = TimeFrame.from_string(self.timeframe).amount_of_minutes
|
|
292
|
-
self.backtest_data_start_date = \
|
|
293
|
-
backtest_start_date - timedelta(minutes=total_minutes)
|
|
294
|
-
self.backtest_data_end_date = backtest_end_date
|
|
295
|
-
|
|
296
|
-
# Creating the backtest data directory and file
|
|
297
|
-
self.backtest_data_directory = os.path.join(
|
|
298
|
-
config.get(RESOURCE_DIRECTORY),
|
|
299
|
-
config.get(BACKTEST_DATA_DIRECTORY_NAME)
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
if not os.path.isdir(self.backtest_data_directory):
|
|
303
|
-
os.mkdir(self.backtest_data_directory)
|
|
304
|
-
|
|
305
|
-
file_path = self._create_file_path()
|
|
306
|
-
|
|
307
|
-
if not os.path.isfile(file_path):
|
|
308
|
-
try:
|
|
309
|
-
with open(file_path, 'w') as _:
|
|
310
|
-
pass
|
|
311
|
-
except Exception as e:
|
|
312
|
-
logger.error(e)
|
|
313
|
-
raise OperationalException(
|
|
314
|
-
f"Could not create backtest data file {file_path}"
|
|
315
|
-
)
|
|
316
|
-
|
|
317
|
-
# Check if the data source already exists, if not download the data
|
|
318
|
-
if not self._data_source_exists(file_path):
|
|
319
|
-
if not os.path.isfile(file_path):
|
|
320
|
-
try:
|
|
321
|
-
with open(file_path, 'w') as _:
|
|
322
|
-
pass
|
|
323
|
-
except Exception as e:
|
|
324
|
-
logger.error(e)
|
|
325
|
-
raise OperationalException(
|
|
326
|
-
f"Could not create backtest data file {file_path}"
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
# Get the OHLCV data from the ccxt market service
|
|
330
|
-
market_service = CCXTMarketService(
|
|
331
|
-
market_credential_service=self.market_credential_service
|
|
332
|
-
)
|
|
333
|
-
market_service.config = config
|
|
334
|
-
ohlcv = market_service.get_ohlcv(
|
|
335
|
-
symbol=self.symbol,
|
|
336
|
-
time_frame=self.timeframe,
|
|
337
|
-
from_timestamp=self.backtest_data_start_date,
|
|
338
|
-
to_timestamp=backtest_end_date,
|
|
339
|
-
market=self.market
|
|
340
|
-
)
|
|
341
|
-
self.write_data_to_file_path(file_path, ohlcv)
|
|
342
|
-
|
|
343
|
-
def _create_file_path(self):
|
|
344
|
-
|
|
345
|
-
if self.symbol is None or self.market is None:
|
|
346
|
-
return None
|
|
347
|
-
|
|
348
|
-
symbol_string = self.symbol.replace("/", "-")
|
|
349
|
-
market_string = self.market.replace("/", "-")
|
|
350
|
-
backtest_data_start_date = \
|
|
351
|
-
self.backtest_data_start_date.strftime(DATETIME_FORMAT_BACKTESTING)
|
|
352
|
-
backtest_data_end_date = \
|
|
353
|
-
self.backtest_data_end_date.strftime(DATETIME_FORMAT_BACKTESTING)
|
|
354
|
-
return os.path.join(
|
|
355
|
-
self.backtest_data_directory,
|
|
356
|
-
os.path.join(
|
|
357
|
-
f"TICKER_"
|
|
358
|
-
f"{symbol_string}_"
|
|
359
|
-
f"{market_string}_"
|
|
360
|
-
f"{backtest_data_start_date}_"
|
|
361
|
-
f"{backtest_data_end_date}.csv"
|
|
362
|
-
)
|
|
363
|
-
)
|
|
364
|
-
|
|
365
|
-
def to_backtest_market_data_source(self) -> BacktestMarketDataSource:
|
|
366
|
-
# Ignore this method for now
|
|
367
|
-
pass
|
|
368
|
-
|
|
369
|
-
def empty(self):
|
|
370
|
-
return False
|
|
371
|
-
|
|
372
|
-
def get_data(self, **kwargs):
|
|
373
|
-
"""
|
|
374
|
-
Get data implementation of ccxt based ticker backtest market data
|
|
375
|
-
source
|
|
376
|
-
"""
|
|
377
|
-
if "backtest_index_date" not in kwargs:
|
|
378
|
-
raise OperationalException(
|
|
379
|
-
"backtest_index_date should be passed as a parameter "
|
|
380
|
-
"for CCXTTickerBacktestMarketDataSource"
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
file_path = self._create_file_path()
|
|
384
|
-
backtest_index_date = kwargs["backtest_index_date"]
|
|
385
|
-
|
|
386
|
-
# Filter the data based on the backtest index date and the end date
|
|
387
|
-
df = polars.read_csv(file_path)
|
|
388
|
-
filtered_df = df.filter(
|
|
389
|
-
(df['Datetime'] >= backtest_index_date.strftime(DATETIME_FORMAT))
|
|
390
|
-
)
|
|
391
|
-
|
|
392
|
-
# If nothing is found, get all dates before the index date
|
|
393
|
-
if len(filtered_df) == 0:
|
|
394
|
-
filtered_df = df.filter(
|
|
395
|
-
(df['Datetime'] <= backtest_index_date.strftime(
|
|
396
|
-
DATETIME_FORMAT))
|
|
397
|
-
)
|
|
398
|
-
first_row = filtered_df.tail(1)[0]
|
|
399
|
-
else:
|
|
400
|
-
first_row = filtered_df.head(1)[0]
|
|
401
|
-
|
|
402
|
-
first_row_datetime = parser.parse(first_row["Datetime"][0])
|
|
403
|
-
|
|
404
|
-
# Calculate the bid and ask price based on the high and low price
|
|
405
|
-
return {
|
|
406
|
-
"symbol": self.symbol,
|
|
407
|
-
"bid": float((first_row["Low"][0])
|
|
408
|
-
+ float(first_row["High"][0]))/2,
|
|
409
|
-
"ask": float((first_row["Low"][0])
|
|
410
|
-
+ float(first_row["High"][0]))/2,
|
|
411
|
-
"datetime": first_row_datetime,
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
def write_data_to_file_path(self, data_file, data: polars.DataFrame):
|
|
415
|
-
data.write_csv(data_file)
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
class CCXTOHLCVMarketDataSource(OHLCVMarketDataSource):
|
|
419
|
-
"""
|
|
420
|
-
CCXTOHLCVMarketDataSource implementation of OHLCVMarketDataSource using
|
|
421
|
-
ccxt to download all ohlcv data sources.
|
|
422
|
-
"""
|
|
423
|
-
|
|
424
|
-
def get_data(self, **kwargs):
|
|
425
|
-
"""
|
|
426
|
-
Implementation of get_data for CCXTOHLCVMarketDataSource.
|
|
427
|
-
This implementation uses the CCXTMarketService to get the OHLCV data.
|
|
428
|
-
|
|
429
|
-
In the kwargs, the start_date should be set as a datetime object.
|
|
430
|
-
|
|
431
|
-
returns a polars.DataFrame with the OHLCV data
|
|
432
|
-
"""
|
|
433
|
-
market_service = CCXTMarketService(
|
|
434
|
-
market_credential_service=self.market_credential_service,
|
|
435
|
-
)
|
|
436
|
-
|
|
437
|
-
# Add config if present
|
|
438
|
-
if self.config is not None:
|
|
439
|
-
market_service.config = self.config
|
|
440
|
-
|
|
441
|
-
if "start_date" in kwargs:
|
|
442
|
-
start_date = kwargs["start_date"]
|
|
443
|
-
|
|
444
|
-
if not isinstance(start_date, datetime.datetime):
|
|
445
|
-
raise OperationalException(
|
|
446
|
-
"start_date should be a datetime object"
|
|
447
|
-
)
|
|
448
|
-
else:
|
|
449
|
-
raise OperationalException(
|
|
450
|
-
"start_date should be set for CCXTOHLCVMarketDataSource"
|
|
451
|
-
)
|
|
452
|
-
|
|
453
|
-
if "end_date" not in kwargs:
|
|
454
|
-
end_date = self.create_end_date(
|
|
455
|
-
start_date, self.timeframe, self.window_size
|
|
456
|
-
)
|
|
457
|
-
else:
|
|
458
|
-
end_date = kwargs["end_date"]
|
|
459
|
-
|
|
460
|
-
if not isinstance(end_date, datetime.datetime):
|
|
461
|
-
raise OperationalException(
|
|
462
|
-
"end_date should be a datetime object"
|
|
463
|
-
)
|
|
464
|
-
|
|
465
|
-
if not isinstance(start_date, datetime.datetime):
|
|
466
|
-
raise OperationalException(
|
|
467
|
-
"start_date should be a datetime object"
|
|
468
|
-
)
|
|
469
|
-
|
|
470
|
-
return market_service.get_ohlcv(
|
|
471
|
-
symbol=self.symbol,
|
|
472
|
-
time_frame=self.timeframe,
|
|
473
|
-
from_timestamp=start_date,
|
|
474
|
-
to_timestamp=end_date,
|
|
475
|
-
market=self.market
|
|
476
|
-
)
|
|
477
|
-
|
|
478
|
-
def to_backtest_market_data_source(self) -> BacktestMarketDataSource:
|
|
479
|
-
return CCXTOHLCVBacktestMarketDataSource(
|
|
480
|
-
identifier=self.identifier,
|
|
481
|
-
market=self.market,
|
|
482
|
-
symbol=self.symbol,
|
|
483
|
-
timeframe=self.timeframe,
|
|
484
|
-
window_size=self.window_size
|
|
485
|
-
)
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
class CCXTOrderBookMarketDataSource(OrderBookMarketDataSource):
|
|
489
|
-
|
|
490
|
-
def get_data(self, **kwargs):
|
|
491
|
-
market_service = CCXTMarketService(
|
|
492
|
-
market_credential_service=self.market_credential_service
|
|
493
|
-
)
|
|
494
|
-
market_service.config = self.config
|
|
495
|
-
return market_service.get_order_book(
|
|
496
|
-
symbol=self.symbol, market=self.market
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
def to_backtest_market_data_source(self) -> BacktestMarketDataSource:
|
|
500
|
-
pass
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
class CCXTTickerMarketDataSource(TickerMarketDataSource):
|
|
504
|
-
|
|
505
|
-
def __init__(
|
|
506
|
-
self,
|
|
507
|
-
identifier,
|
|
508
|
-
market,
|
|
509
|
-
symbol=None,
|
|
510
|
-
backtest_timeframe=None,
|
|
511
|
-
|
|
512
|
-
):
|
|
513
|
-
super().__init__(
|
|
514
|
-
identifier=identifier,
|
|
515
|
-
market=market,
|
|
516
|
-
symbol=symbol,
|
|
517
|
-
)
|
|
518
|
-
self._backtest_timeframe = backtest_timeframe
|
|
519
|
-
|
|
520
|
-
def get_data(self, **kwargs):
|
|
521
|
-
market_service = CCXTMarketService(
|
|
522
|
-
market_credential_service=self.market_credential_service
|
|
523
|
-
)
|
|
524
|
-
market_service.config = self.config
|
|
525
|
-
|
|
526
|
-
if self.market is None:
|
|
527
|
-
|
|
528
|
-
if "market" not in kwargs:
|
|
529
|
-
raise OperationalException(
|
|
530
|
-
"Either market or market should be "
|
|
531
|
-
"passed as a parameter"
|
|
532
|
-
)
|
|
533
|
-
else:
|
|
534
|
-
market = kwargs["market"]
|
|
535
|
-
else:
|
|
536
|
-
market = self.market
|
|
537
|
-
|
|
538
|
-
market_service.market = market
|
|
539
|
-
|
|
540
|
-
if self.symbol is None:
|
|
541
|
-
|
|
542
|
-
if "symbol" not in kwargs:
|
|
543
|
-
raise OperationalException(
|
|
544
|
-
"Either symbol or symbol should be passed as a parameter"
|
|
545
|
-
)
|
|
546
|
-
else:
|
|
547
|
-
symbol = kwargs["symbol"]
|
|
548
|
-
else:
|
|
549
|
-
symbol = self.symbol
|
|
550
|
-
|
|
551
|
-
return market_service.get_ticker(symbol=symbol, market=market)
|
|
552
|
-
|
|
553
|
-
def to_backtest_market_data_source(self) -> BacktestMarketDataSource:
|
|
554
|
-
return CCXTTickerBacktestMarketDataSource(
|
|
555
|
-
identifier=self.identifier,
|
|
556
|
-
market=self.market,
|
|
557
|
-
symbol=self.symbol,
|
|
558
|
-
timeframe=self._backtest_timeframe,
|
|
559
|
-
)
|