mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 02:12:01 +00:00
Merge branch 'develop' into pr/wizrds/7303
This commit is contained in:
commit
914eccecec
|
@ -77,7 +77,8 @@
|
||||||
"indicator_periods_candles": [
|
"indicator_periods_candles": [
|
||||||
10,
|
10,
|
||||||
20
|
20
|
||||||
]
|
],
|
||||||
|
"plot_feature_importance": false
|
||||||
},
|
},
|
||||||
"data_split_parameters": {
|
"data_split_parameters": {
|
||||||
"test_size": 0.33,
|
"test_size": 0.33,
|
||||||
|
@ -93,4 +94,4 @@
|
||||||
"internals": {
|
"internals": {
|
||||||
"process_throttle_secs": 5
|
"process_throttle_secs": 5
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,6 +17,7 @@ from typing import Any, Dict
|
||||||
|
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.optimize.hyperopt import IHyperOptLoss
|
from freqtrade.optimize.hyperopt import IHyperOptLoss
|
||||||
|
|
||||||
TARGET_TRADES = 600
|
TARGET_TRADES = 600
|
||||||
|
@ -31,7 +32,7 @@ class SuperDuperHyperOptLoss(IHyperOptLoss):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def hyperopt_loss_function(results: DataFrame, trade_count: int,
|
def hyperopt_loss_function(results: DataFrame, trade_count: int,
|
||||||
min_date: datetime, max_date: datetime,
|
min_date: datetime, max_date: datetime,
|
||||||
config: Dict, processed: Dict[str, DataFrame],
|
config: Config, processed: Dict[str, DataFrame],
|
||||||
backtest_stats: Dict[str, Any],
|
backtest_stats: Dict[str, Any],
|
||||||
*args, **kwargs) -> float:
|
*args, **kwargs) -> float:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -57,7 +57,8 @@ This configuration enables kraken, as well as rate-limiting to avoid bans from t
|
||||||
Binance supports [time_in_force](configuration.md#understand-order_time_in_force).
|
Binance supports [time_in_force](configuration.md#understand-order_time_in_force).
|
||||||
|
|
||||||
!!! Tip "Stoploss on Exchange"
|
!!! Tip "Stoploss on Exchange"
|
||||||
Binance supports `stoploss_on_exchange` and uses `stop-loss-limit` orders. It provides great advantages, so we recommend to benefit from it by enabling stoploss on exchange..
|
Binance supports `stoploss_on_exchange` and uses `stop-loss-limit` orders. It provides great advantages, so we recommend to benefit from it by enabling stoploss on exchange.
|
||||||
|
On futures, Binance supports both `stop-limit` as well as `stop-market` orders. You can use either `"limit"` or `"market"` in the `order_types.stoploss` configuration setting to decide which type to use.
|
||||||
|
|
||||||
### Binance Blacklist
|
### Binance Blacklist
|
||||||
|
|
||||||
|
|
|
@ -109,11 +109,12 @@ Mandatory parameters are marked as **Required**, which means that they are requi
|
||||||
| `indicator_max_period_candles` | **No longer used**. User must use the strategy set `startup_candle_count` which defines the maximum *period* used in `populate_any_indicators()` for indicator creation (timeframe independent). FreqAI uses this information in combination with the maximum timeframe to calculate how many data points it should download so that the first data point does not have a NaN <br> **Datatype:** positive integer.
|
| `indicator_max_period_candles` | **No longer used**. User must use the strategy set `startup_candle_count` which defines the maximum *period* used in `populate_any_indicators()` for indicator creation (timeframe independent). FreqAI uses this information in combination with the maximum timeframe to calculate how many data points it should download so that the first data point does not have a NaN <br> **Datatype:** positive integer.
|
||||||
| `indicator_periods_candles` | Calculate indicators for `indicator_periods_candles` time periods and add them to the feature set. <br> **Datatype:** List of positive integers.
|
| `indicator_periods_candles` | Calculate indicators for `indicator_periods_candles` time periods and add them to the feature set. <br> **Datatype:** List of positive integers.
|
||||||
| `stratify_training_data` | This value is used to indicate the grouping of the data. For example, 2 would set every 2nd data point into a separate dataset to be pulled from during training/testing. See details about how it works [here](#stratifying-the-data-for-training-and-testing-the-model) <br> **Datatype:** Positive integer.
|
| `stratify_training_data` | This value is used to indicate the grouping of the data. For example, 2 would set every 2nd data point into a separate dataset to be pulled from during training/testing. See details about how it works [here](#stratifying-the-data-for-training-and-testing-the-model) <br> **Datatype:** Positive integer.
|
||||||
| `principal_component_analysis` | Automatically reduce the dimensionality of the data set using Principal Component Analysis. See details about how it works [here](#reducing-data-dimensionality-with-principal-component-analysis) <br> **Datatype:** Boolean.
|
| `principal_component_analysis` | Automatically reduce the dimensionality of the data set using Principal Component Analysis. See details about how it works [here](#reducing-data-dimensionality-with-principal-component-analysis)
|
||||||
|
| `plot_feature_importance` | Create an interactive feature importance plot for each model.<br> **Datatype:** Boolean.<br> **Datatype:** Boolean, defaults to `False`
|
||||||
| `DI_threshold` | Activates the Dissimilarity Index for outlier detection when > 0. See details about how it works [here](#removing-outliers-with-the-dissimilarity-index). <br> **Datatype:** Positive float (typically < 1).
|
| `DI_threshold` | Activates the Dissimilarity Index for outlier detection when > 0. See details about how it works [here](#removing-outliers-with-the-dissimilarity-index). <br> **Datatype:** Positive float (typically < 1).
|
||||||
| `use_SVM_to_remove_outliers` | Train a support vector machine to detect and remove outliers from the training data set, as well as from incoming data points. See details about how it works [here](#removing-outliers-using-a-support-vector-machine-svm). <br> **Datatype:** Boolean.
|
| `use_SVM_to_remove_outliers` | Train a support vector machine to detect and remove outliers from the training data set, as well as from incoming data points. See details about how it works [here](#removing-outliers-using-a-support-vector-machine-svm). <br> **Datatype:** Boolean.
|
||||||
| `svm_params` | All parameters available in Sklearn's `SGDOneClassSVM()`. See details about some select parameters [here](#removing-outliers-using-a-support-vector-machine-svm). <br> **Datatype:** Dictionary.
|
| `svm_params` | All parameters available in Sklearn's `SGDOneClassSVM()`. See details about some select parameters [here](#removing-outliers-using-a-support-vector-machine-svm). <br> **Datatype:** Dictionary.
|
||||||
| `use_DBSCAN_to_remove_outliers` | Cluster data using DBSCAN to identify and remove outliers from training and prediction data. See details about how it works [here](#removing-outliers-with-dbscan). <br> **Datatype:** Boolean.
|
| `use_DBSCAN_to_remove_outliers` | Cluster data using DBSCAN to identify and remove outliers from training and prediction data. See details about how it works [here](#removing-outliers-with-dbscan). <br> **Datatype:** Boolean.
|
||||||
| `inlier_metric_window` | If set, FreqAI will add the `inlier_metric` to the training feature set and set the lookback to be the `inlier_metric_window`. Details of how the `inlier_metric` is computed can be found [here](#using-the-inliermetric) <br> **Datatype:** int. Default: 0
|
| `inlier_metric_window` | If set, FreqAI will add the `inlier_metric` to the training feature set and set the lookback to be the `inlier_metric_window`. Details of how the `inlier_metric` is computed can be found [here](#using-the-inliermetric) <br> **Datatype:** int. Default: 0
|
||||||
| `noise_standard_deviation` | If > 0, FreqAI adds noise to the training features. FreqAI generates random deviates from a gaussian distribution with a standard deviation of `noise_standard_deviation` and adds them to all data points. Value should be kept relative to the normalized space between -1 and 1). In other words, since data is always normalized between -1 and 1 in FreqAI, the user can expect a `noise_standard_deviation: 0.05` to see 32% of data randomly increased/decreased by more than 2.5% (i.e. the percent of data falling within the first standard deviation). Good for preventing overfitting. <br> **Datatype:** int. Default: 0
|
| `noise_standard_deviation` | If > 0, FreqAI adds noise to the training features. FreqAI generates random deviates from a gaussian distribution with a standard deviation of `noise_standard_deviation` and adds them to all data points. Value should be kept relative to the normalized space between -1 and 1). In other words, since data is always normalized between -1 and 1 in FreqAI, the user can expect a `noise_standard_deviation: 0.05` to see 32% of data randomly increased/decreased by more than 2.5% (i.e. the percent of data falling within the first standard deviation). Good for preventing overfitting. <br> **Datatype:** int. Default: 0
|
||||||
| `outlier_protection_percentage` | If more than `outlier_protection_percentage` % of points are detected as outliers by the SVM or DBSCAN, FreqAI will log a warning message and ignore outlier detection while keeping the original dataset intact. If the outlier protection is triggered, no predictions will be made based on the training data. <br> **Datatype:** Float. Default: `30`
|
| `outlier_protection_percentage` | If more than `outlier_protection_percentage` % of points are detected as outliers by the SVM or DBSCAN, FreqAI will log a warning message and ignore outlier detection while keeping the original dataset intact. If the outlier protection is triggered, no predictions will be made based on the training data. <br> **Datatype:** Float. Default: `30`
|
||||||
|
@ -190,19 +191,6 @@ The FreqAI strategy requires the user to include the following lines of code in
|
||||||
# passed to any single indicator)
|
# passed to any single indicator)
|
||||||
startup_candle_count: int = 20
|
startup_candle_count: int = 20
|
||||||
|
|
||||||
def informative_pairs(self):
|
|
||||||
whitelist_pairs = self.dp.current_whitelist()
|
|
||||||
corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
|
|
||||||
informative_pairs = []
|
|
||||||
for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]:
|
|
||||||
for pair in whitelist_pairs:
|
|
||||||
informative_pairs.append((pair, tf))
|
|
||||||
for pair in corr_pairs:
|
|
||||||
if pair in whitelist_pairs:
|
|
||||||
continue # avoid duplication
|
|
||||||
informative_pairs.append((pair, tf))
|
|
||||||
return informative_pairs
|
|
||||||
|
|
||||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
|
|
||||||
# the model will return all labels created by user in `populate_any_indicators`
|
# the model will return all labels created by user in `populate_any_indicators`
|
||||||
|
@ -523,7 +511,7 @@ The FreqAI backtesting module can be executed with the following command:
|
||||||
freqtrade backtesting --strategy FreqaiExampleStrategy --strategy-path freqtrade/templates --config config_examples/config_freqai.example.json --freqaimodel LightGBMRegressor --timerange 20210501-20210701
|
freqtrade backtesting --strategy FreqaiExampleStrategy --strategy-path freqtrade/templates --config config_examples/config_freqai.example.json --freqaimodel LightGBMRegressor --timerange 20210501-20210701
|
||||||
```
|
```
|
||||||
|
|
||||||
Backtesting mode requires the user to have the data [pre-downloaded](#downloading-data-for-backtesting) (unlike in dry/live mode where FreqAI automatically downloads the necessary data). The user should be careful to consider that the time range of the downloaded data is more than the backtesting time range. This is because FreqAI needs data prior to the desired backtesting time range in order to train a model to be ready to make predictions on the first candle of the user-set backtesting time range. More details on how to calculate the data to download can be found [here](#deciding-the-sliding-training-window-and-backtesting-duration).
|
Backtesting mode requires the user to have the data [pre-downloaded](#downloading-data-for-backtesting) (unlike in dry/live mode where FreqAI automatically downloads the necessary data). The user should be careful to consider that the time range of the downloaded data is more than the backtesting time range. This is because FreqAI needs data prior to the desired backtesting time range in order to train a model to be ready to make predictions on the first candle of the user-set backtesting time range. More details on how to calculate the data to download can be found [here](#deciding-the-sliding-training-window-and-backtesting-duration).
|
||||||
|
|
||||||
If this command has never been executed with the existing config file, it will train a new model
|
If this command has never been executed with the existing config file, it will train a new model
|
||||||
for each pair, for each backtesting window within the expanded `--timerange`.
|
for each pair, for each backtesting window within the expanded `--timerange`.
|
||||||
|
@ -551,7 +539,7 @@ Users need to have the data pre-downloaded in the same fashion as if they were d
|
||||||
- It's not possible to hyperopt indicators in `populate_any_indicators()` function. This means that the user cannot optimize model parameters using hyperopt. Apart from this exception, it is possible to optimize all other [spaces](hyperopt.md#running-hyperopt-with-smaller-search-space).
|
- It's not possible to hyperopt indicators in `populate_any_indicators()` function. This means that the user cannot optimize model parameters using hyperopt. Apart from this exception, it is possible to optimize all other [spaces](hyperopt.md#running-hyperopt-with-smaller-search-space).
|
||||||
- The [Backtesting](#backtesting) instructions also apply to Hyperopt.
|
- The [Backtesting](#backtesting) instructions also apply to Hyperopt.
|
||||||
|
|
||||||
The best method for combining hyperopt and FreqAI is to focus on hyperopting entry/exit thresholds/criteria. Users need to focus on hyperopting parameters that are not used in their FreqAI features. For example, users should not try to hyperopt rolling window lengths in their feature creation, or any of their FreqAI config which changes predictions. In order to efficiently hyperopt the FreqAI strategy, FreqAI stores predictions as dataframes and reuses them. Hence the requirement to hyperopt entry/exit thresholds/criteria only.
|
The best method for combining hyperopt and FreqAI is to focus on hyperopting entry/exit thresholds/criteria. Users need to focus on hyperopting parameters that are not used in their FreqAI features. For example, users should not try to hyperopt rolling window lengths in their feature creation, or any of their FreqAI config which changes predictions. In order to efficiently hyperopt the FreqAI strategy, FreqAI stores predictions as dataframes and reuses them. Hence the requirement to hyperopt entry/exit thresholds/criteria only.
|
||||||
|
|
||||||
A good example of a hyperoptable parameter in FreqAI is a value for `DI_values` beyond which we consider outliers and below which we consider inliers:
|
A good example of a hyperoptable parameter in FreqAI is a value for `DI_values` beyond which we consider outliers and below which we consider inliers:
|
||||||
|
|
||||||
|
@ -576,7 +564,7 @@ FreqAI will train have trained 8 separate models at the end of `--timerange` (be
|
||||||
Although fractional `backtest_period_days` is allowed, the user should be aware that the `--timerange` is divided by this value to determine the number of models that FreqAI will need to train in order to backtest the full range. For example, if the user wants to set a `--timerange` of 10 days, and asks for a `backtest_period_days` of 0.1, FreqAI will need to train 100 models per pair to complete the full backtest. Because of this, a true backtest of FreqAI adaptive training would take a *very* long time. The best way to fully test a model is to run it dry and let it constantly train. In this case, backtesting would take the exact same amount of time as a dry run.
|
Although fractional `backtest_period_days` is allowed, the user should be aware that the `--timerange` is divided by this value to determine the number of models that FreqAI will need to train in order to backtest the full range. For example, if the user wants to set a `--timerange` of 10 days, and asks for a `backtest_period_days` of 0.1, FreqAI will need to train 100 models per pair to complete the full backtest. Because of this, a true backtest of FreqAI adaptive training would take a *very* long time. The best way to fully test a model is to run it dry and let it constantly train. In this case, backtesting would take the exact same amount of time as a dry run.
|
||||||
|
|
||||||
### Downloading data for backtesting
|
### Downloading data for backtesting
|
||||||
Live/dry instances will download the data automatically for the user, but users who wish to use backtesting functionality still need to download the necessary data using `download-data` (details [here](data-download.md#data-downloading)). FreqAI users need to pay careful attention to understanding how much *additional* data needs to be downloaded to ensure that they have a sufficient amount of training data *before* the start of their backtesting timerange. The amount of additional data can be roughly estimated by moving the start date of the timerange backwards by `train_period_days` and the `startup_candle_count` ([details](#setting-the-startupcandlecount)) from the beginning of the desired backtesting timerange.
|
Live/dry instances will download the data automatically for the user, but users who wish to use backtesting functionality still need to download the necessary data using `download-data` (details [here](data-download.md#data-downloading)). FreqAI users need to pay careful attention to understanding how much *additional* data needs to be downloaded to ensure that they have a sufficient amount of training data *before* the start of their backtesting timerange. The amount of additional data can be roughly estimated by moving the start date of the timerange backwards by `train_period_days` and the `startup_candle_count` ([details](#setting-the-startupcandlecount)) from the beginning of the desired backtesting timerange.
|
||||||
|
|
||||||
As an example, if we wish to backtest the `--timerange` above of `20210501-20210701`, and we use the example config which sets `train_period_days` to 15. The startup candle count is 40 on a maximum `include_timeframes` of 1h. We would need 20210501 - 15 days - 40 * 1h / 24 hours = 20210414 (16.7 days earlier than the start of the desired training timerange).
|
As an example, if we wish to backtest the `--timerange` above of `20210501-20210701`, and we use the example config which sets `train_period_days` to 15. The startup candle count is 40 on a maximum `include_timeframes` of 1h. We would need 20210501 - 15 days - 40 * 1h / 24 hours = 20210414 (16.7 days earlier than the start of the desired training timerange).
|
||||||
|
|
||||||
|
@ -675,13 +663,13 @@ The test data is used to evaluate the performance of the model after training. I
|
||||||
|
|
||||||
### Using the `inlier_metric`
|
### Using the `inlier_metric`
|
||||||
|
|
||||||
The `inlier_metric` is a metric aimed at quantifying how different a prediction data point is from the most recent historic data points.
|
The `inlier_metric` is a metric aimed at quantifying how different a prediction data point is from the most recent historic data points.
|
||||||
|
|
||||||
User can set `inlier_metric_window` to set the look back window. FreqAI will compute the distance between the present prediction point and each of the previous data points (total of `inlier_metric_window` points).
|
User can set `inlier_metric_window` to set the look back window. FreqAI will compute the distance between the present prediction point and each of the previous data points (total of `inlier_metric_window` points).
|
||||||
|
|
||||||
This function goes one step further - during training, it computes the `inlier_metric` for all training data points and builds weibull distributions for each each lookback point. The cumulative distribution function for the weibull distribution is used to produce a quantile for each of the data points. The quantiles for each lookback point are averaged to create the `inlier_metric`.
|
This function goes one step further - during training, it computes the `inlier_metric` for all training data points and builds weibull distributions for each each lookback point. The cumulative distribution function for the weibull distribution is used to produce a quantile for each of the data points. The quantiles for each lookback point are averaged to create the `inlier_metric`.
|
||||||
|
|
||||||
FreqAI adds this `inlier_metric` score to the training features! In other words, your model is trained to recognize how this temporal inlier metric is related to the user set labels.
|
FreqAI adds this `inlier_metric` score to the training features! In other words, your model is trained to recognize how this temporal inlier metric is related to the user set labels.
|
||||||
|
|
||||||
This function does **not** remove outliers from the data set.
|
This function does **not** remove outliers from the data set.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
markdown==3.3.7
|
markdown==3.3.7
|
||||||
mkdocs==1.3.1
|
mkdocs==1.3.1
|
||||||
mkdocs-material==8.4.3
|
mkdocs-material==8.5.2
|
||||||
mdx_truly_sane_lists==1.3
|
mdx_truly_sane_lists==1.3
|
||||||
pymdown-extensions==9.5
|
pymdown-extensions==9.5
|
||||||
jinja2==3.1.2
|
jinja2==3.1.2
|
||||||
|
|
|
@ -62,9 +62,9 @@ ARGS_BUILD_CONFIG = ["config"]
|
||||||
|
|
||||||
ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"]
|
ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"]
|
||||||
|
|
||||||
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"]
|
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase", "exchange"]
|
||||||
|
|
||||||
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes", "exchange", "trading_mode",
|
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes", "trading_mode",
|
||||||
"candle_types"]
|
"candle_types"]
|
||||||
|
|
||||||
ARGS_CONVERT_TRADES = ["pairs", "timeframes", "exchange", "dataformat_ohlcv", "dataformat_trades"]
|
ARGS_CONVERT_TRADES = ["pairs", "timeframes", "exchange", "dataformat_ohlcv", "dataformat_trades"]
|
||||||
|
|
|
@ -69,7 +69,7 @@ AVAILABLE_CLI_OPTIONS = {
|
||||||
metavar='PATH',
|
metavar='PATH',
|
||||||
),
|
),
|
||||||
"datadir": Arg(
|
"datadir": Arg(
|
||||||
'-d', '--datadir',
|
'-d', '--datadir', '--data-dir',
|
||||||
help='Path to directory with historical backtesting data.',
|
help='Path to directory with historical backtesting data.',
|
||||||
metavar='PATH',
|
metavar='PATH',
|
||||||
),
|
),
|
||||||
|
|
|
@ -36,24 +36,24 @@ def deploy_new_strategy(strategy_name: str, strategy_path: Path, subtemplate: st
|
||||||
"""
|
"""
|
||||||
fallback = 'full'
|
fallback = 'full'
|
||||||
indicators = render_template_with_fallback(
|
indicators = render_template_with_fallback(
|
||||||
templatefile=f"subtemplates/indicators_{subtemplate}.j2",
|
templatefile=f"strategy_subtemplates/indicators_{subtemplate}.j2",
|
||||||
templatefallbackfile=f"subtemplates/indicators_{fallback}.j2",
|
templatefallbackfile=f"strategy_subtemplates/indicators_{fallback}.j2",
|
||||||
)
|
)
|
||||||
buy_trend = render_template_with_fallback(
|
buy_trend = render_template_with_fallback(
|
||||||
templatefile=f"subtemplates/buy_trend_{subtemplate}.j2",
|
templatefile=f"strategy_subtemplates/buy_trend_{subtemplate}.j2",
|
||||||
templatefallbackfile=f"subtemplates/buy_trend_{fallback}.j2",
|
templatefallbackfile=f"strategy_subtemplates/buy_trend_{fallback}.j2",
|
||||||
)
|
)
|
||||||
sell_trend = render_template_with_fallback(
|
sell_trend = render_template_with_fallback(
|
||||||
templatefile=f"subtemplates/sell_trend_{subtemplate}.j2",
|
templatefile=f"strategy_subtemplates/sell_trend_{subtemplate}.j2",
|
||||||
templatefallbackfile=f"subtemplates/sell_trend_{fallback}.j2",
|
templatefallbackfile=f"strategy_subtemplates/sell_trend_{fallback}.j2",
|
||||||
)
|
)
|
||||||
plot_config = render_template_with_fallback(
|
plot_config = render_template_with_fallback(
|
||||||
templatefile=f"subtemplates/plot_config_{subtemplate}.j2",
|
templatefile=f"strategy_subtemplates/plot_config_{subtemplate}.j2",
|
||||||
templatefallbackfile=f"subtemplates/plot_config_{fallback}.j2",
|
templatefallbackfile=f"strategy_subtemplates/plot_config_{fallback}.j2",
|
||||||
)
|
)
|
||||||
additional_methods = render_template_with_fallback(
|
additional_methods = render_template_with_fallback(
|
||||||
templatefile=f"subtemplates/strategy_methods_{subtemplate}.j2",
|
templatefile=f"strategy_subtemplates/strategy_methods_{subtemplate}.j2",
|
||||||
templatefallbackfile="subtemplates/strategy_methods_empty.j2",
|
templatefallbackfile="strategy_subtemplates/strategy_methods_empty.j2",
|
||||||
)
|
)
|
||||||
|
|
||||||
strategy_text = render_template(templatefile='base_strategy.py.j2',
|
strategy_text = render_template(templatefile='base_strategy.py.j2',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.enums import RunMode
|
from freqtrade.enums import RunMode
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import (available_exchanges, is_exchange_known_ccxt,
|
from freqtrade.exchange import (available_exchanges, is_exchange_known_ccxt,
|
||||||
|
@ -10,7 +10,7 @@ from freqtrade.exchange import (available_exchanges, is_exchange_known_ccxt,
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool:
|
def check_exchange(config: Config, check_for_bad: bool = True) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if the exchange name in the config file is supported by Freqtrade
|
Check if the exchange name in the config file is supported by Freqtrade
|
||||||
:param check_for_bad: if True, check the exchange against the list of known 'bad'
|
:param check_for_bad: if True, check the exchange against the list of known 'bad'
|
||||||
|
|
|
@ -13,6 +13,7 @@ from freqtrade.configuration.deprecated_settings import process_temporary_deprec
|
||||||
from freqtrade.configuration.directory_operations import create_datadir, create_userdata_dir
|
from freqtrade.configuration.directory_operations import create_datadir, create_userdata_dir
|
||||||
from freqtrade.configuration.environment_vars import enironment_vars_to_dict
|
from freqtrade.configuration.environment_vars import enironment_vars_to_dict
|
||||||
from freqtrade.configuration.load_config import load_file, load_from_files
|
from freqtrade.configuration.load_config import load_file, load_from_files
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.enums import NON_UTIL_MODES, TRADING_MODES, CandleType, RunMode, TradingMode
|
from freqtrade.enums import NON_UTIL_MODES, TRADING_MODES, CandleType, RunMode, TradingMode
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.loggers import setup_logging
|
from freqtrade.loggers import setup_logging
|
||||||
|
@ -30,10 +31,10 @@ class Configuration:
|
||||||
|
|
||||||
def __init__(self, args: Dict[str, Any], runmode: RunMode = None) -> None:
|
def __init__(self, args: Dict[str, Any], runmode: RunMode = None) -> None:
|
||||||
self.args = args
|
self.args = args
|
||||||
self.config: Optional[Dict[str, Any]] = None
|
self.config: Optional[Config] = None
|
||||||
self.runmode = runmode
|
self.runmode = runmode
|
||||||
|
|
||||||
def get_config(self) -> Dict[str, Any]:
|
def get_config(self) -> Config:
|
||||||
"""
|
"""
|
||||||
Return the config. Use this method to get the bot config
|
Return the config. Use this method to get the bot config
|
||||||
:return: Dict: Bot config
|
:return: Dict: Bot config
|
||||||
|
@ -65,7 +66,7 @@ class Configuration:
|
||||||
:return: Configuration dictionary
|
:return: Configuration dictionary
|
||||||
"""
|
"""
|
||||||
# Load all configs
|
# Load all configs
|
||||||
config: Dict[str, Any] = load_from_files(self.args.get("config", []))
|
config: Config = load_from_files(self.args.get("config", []))
|
||||||
|
|
||||||
# Load environment variables
|
# Load environment variables
|
||||||
env_data = enironment_vars_to_dict()
|
env_data = enironment_vars_to_dict()
|
||||||
|
@ -108,7 +109,7 @@ class Configuration:
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def _process_logging_options(self, config: Dict[str, Any]) -> None:
|
def _process_logging_options(self, config: Config) -> None:
|
||||||
"""
|
"""
|
||||||
Extract information for sys.argv and load logging configuration:
|
Extract information for sys.argv and load logging configuration:
|
||||||
the -v/--verbose, --logfile options
|
the -v/--verbose, --logfile options
|
||||||
|
@ -121,7 +122,7 @@ class Configuration:
|
||||||
|
|
||||||
setup_logging(config)
|
setup_logging(config)
|
||||||
|
|
||||||
def _process_trading_options(self, config: Dict[str, Any]) -> None:
|
def _process_trading_options(self, config: Config) -> None:
|
||||||
if config['runmode'] not in TRADING_MODES:
|
if config['runmode'] not in TRADING_MODES:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -137,7 +138,7 @@ class Configuration:
|
||||||
|
|
||||||
logger.info(f'Using DB: "{parse_db_uri_for_logging(config["db_url"])}"')
|
logger.info(f'Using DB: "{parse_db_uri_for_logging(config["db_url"])}"')
|
||||||
|
|
||||||
def _process_common_options(self, config: Dict[str, Any]) -> None:
|
def _process_common_options(self, config: Config) -> None:
|
||||||
|
|
||||||
# Set strategy if not specified in config and or if it's non default
|
# Set strategy if not specified in config and or if it's non default
|
||||||
if self.args.get('strategy') or not config.get('strategy'):
|
if self.args.get('strategy') or not config.get('strategy'):
|
||||||
|
@ -161,7 +162,7 @@ class Configuration:
|
||||||
if 'sd_notify' in self.args and self.args['sd_notify']:
|
if 'sd_notify' in self.args and self.args['sd_notify']:
|
||||||
config['internals'].update({'sd_notify': True})
|
config['internals'].update({'sd_notify': True})
|
||||||
|
|
||||||
def _process_datadir_options(self, config: Dict[str, Any]) -> None:
|
def _process_datadir_options(self, config: Config) -> None:
|
||||||
"""
|
"""
|
||||||
Extract information for sys.argv and load directory configurations
|
Extract information for sys.argv and load directory configurations
|
||||||
--user-data, --datadir
|
--user-data, --datadir
|
||||||
|
@ -195,7 +196,7 @@ class Configuration:
|
||||||
config['exportfilename'] = (config['user_data_dir']
|
config['exportfilename'] = (config['user_data_dir']
|
||||||
/ 'backtest_results')
|
/ 'backtest_results')
|
||||||
|
|
||||||
def _process_optimize_options(self, config: Dict[str, Any]) -> None:
|
def _process_optimize_options(self, config: Config) -> None:
|
||||||
|
|
||||||
# This will override the strategy configuration
|
# This will override the strategy configuration
|
||||||
self._args_to_config(config, argname='timeframe',
|
self._args_to_config(config, argname='timeframe',
|
||||||
|
@ -380,7 +381,7 @@ class Configuration:
|
||||||
self._args_to_config(config, argname="hyperopt_ignore_missing_space",
|
self._args_to_config(config, argname="hyperopt_ignore_missing_space",
|
||||||
logstring="Paramter --ignore-missing-space detected: {}")
|
logstring="Paramter --ignore-missing-space detected: {}")
|
||||||
|
|
||||||
def _process_plot_options(self, config: Dict[str, Any]) -> None:
|
def _process_plot_options(self, config: Config) -> None:
|
||||||
|
|
||||||
self._args_to_config(config, argname='pairs',
|
self._args_to_config(config, argname='pairs',
|
||||||
logstring='Using pairs {}')
|
logstring='Using pairs {}')
|
||||||
|
@ -432,7 +433,7 @@ class Configuration:
|
||||||
self._args_to_config(config, argname='show_timerange',
|
self._args_to_config(config, argname='show_timerange',
|
||||||
logstring='Detected --show-timerange')
|
logstring='Detected --show-timerange')
|
||||||
|
|
||||||
def _process_data_options(self, config: Dict[str, Any]) -> None:
|
def _process_data_options(self, config: Config) -> None:
|
||||||
self._args_to_config(config, argname='new_pairs_days',
|
self._args_to_config(config, argname='new_pairs_days',
|
||||||
logstring='Detected --new-pairs-days: {}')
|
logstring='Detected --new-pairs-days: {}')
|
||||||
self._args_to_config(config, argname='trading_mode',
|
self._args_to_config(config, argname='trading_mode',
|
||||||
|
@ -443,7 +444,7 @@ class Configuration:
|
||||||
self._args_to_config(config, argname='candle_types',
|
self._args_to_config(config, argname='candle_types',
|
||||||
logstring='Detected --candle-types: {}')
|
logstring='Detected --candle-types: {}')
|
||||||
|
|
||||||
def _process_analyze_options(self, config: Dict[str, Any]) -> None:
|
def _process_analyze_options(self, config: Config) -> None:
|
||||||
self._args_to_config(config, argname='analysis_groups',
|
self._args_to_config(config, argname='analysis_groups',
|
||||||
logstring='Analysis reason groups: {}')
|
logstring='Analysis reason groups: {}')
|
||||||
|
|
||||||
|
@ -456,7 +457,7 @@ class Configuration:
|
||||||
self._args_to_config(config, argname='indicator_list',
|
self._args_to_config(config, argname='indicator_list',
|
||||||
logstring='Analysis indicator list: {}')
|
logstring='Analysis indicator list: {}')
|
||||||
|
|
||||||
def _process_runmode(self, config: Dict[str, Any]) -> None:
|
def _process_runmode(self, config: Config) -> None:
|
||||||
|
|
||||||
self._args_to_config(config, argname='dry_run',
|
self._args_to_config(config, argname='dry_run',
|
||||||
logstring='Parameter --dry-run detected, '
|
logstring='Parameter --dry-run detected, '
|
||||||
|
@ -469,7 +470,7 @@ class Configuration:
|
||||||
|
|
||||||
config.update({'runmode': self.runmode})
|
config.update({'runmode': self.runmode})
|
||||||
|
|
||||||
def _process_freqai_options(self, config: Dict[str, Any]) -> None:
|
def _process_freqai_options(self, config: Config) -> None:
|
||||||
|
|
||||||
self._args_to_config(config, argname='freqaimodel',
|
self._args_to_config(config, argname='freqaimodel',
|
||||||
logstring='Using freqaimodel class name: {}')
|
logstring='Using freqaimodel class name: {}')
|
||||||
|
@ -479,7 +480,7 @@ class Configuration:
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _args_to_config(self, config: Dict[str, Any], argname: str,
|
def _args_to_config(self, config: Config, argname: str,
|
||||||
logstring: str, logfun: Optional[Callable] = None,
|
logstring: str, logfun: Optional[Callable] = None,
|
||||||
deprecated_msg: Optional[str] = None) -> None:
|
deprecated_msg: Optional[str] = None) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -502,7 +503,7 @@ class Configuration:
|
||||||
if deprecated_msg:
|
if deprecated_msg:
|
||||||
warnings.warn(f"DEPRECATED: {deprecated_msg}", DeprecationWarning)
|
warnings.warn(f"DEPRECATED: {deprecated_msg}", DeprecationWarning)
|
||||||
|
|
||||||
def _resolve_pairs_list(self, config: Dict[str, Any]) -> None:
|
def _resolve_pairs_list(self, config: Config) -> None:
|
||||||
"""
|
"""
|
||||||
Helper for download script.
|
Helper for download script.
|
||||||
Takes first found:
|
Takes first found:
|
||||||
|
|
|
@ -3,15 +3,16 @@ Functions to handle deprecated settings
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict, Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def check_conflicting_settings(config: Dict[str, Any],
|
def check_conflicting_settings(config: Config,
|
||||||
section_old: Optional[str], name_old: str,
|
section_old: Optional[str], name_old: str,
|
||||||
section_new: Optional[str], name_new: str) -> None:
|
section_new: Optional[str], name_new: str) -> None:
|
||||||
section_new_config = config.get(section_new, {}) if section_new else config
|
section_new_config = config.get(section_new, {}) if section_new else config
|
||||||
|
@ -28,7 +29,7 @@ def check_conflicting_settings(config: Dict[str, Any],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def process_removed_setting(config: Dict[str, Any],
|
def process_removed_setting(config: Config,
|
||||||
section1: str, name1: str,
|
section1: str, name1: str,
|
||||||
section2: Optional[str], name2: str) -> None:
|
section2: Optional[str], name2: str) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -47,7 +48,7 @@ def process_removed_setting(config: Dict[str, Any],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def process_deprecated_setting(config: Dict[str, Any],
|
def process_deprecated_setting(config: Config,
|
||||||
section_old: Optional[str], name_old: str,
|
section_old: Optional[str], name_old: str,
|
||||||
section_new: Optional[str], name_new: str
|
section_new: Optional[str], name_new: str
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -69,7 +70,7 @@ def process_deprecated_setting(config: Dict[str, Any],
|
||||||
del section_old_config[name_old]
|
del section_old_config[name_old]
|
||||||
|
|
||||||
|
|
||||||
def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None:
|
def process_temporary_deprecated_settings(config: Config) -> None:
|
||||||
|
|
||||||
# Kept for future deprecated / moved settings
|
# Kept for future deprecated / moved settings
|
||||||
# check_conflicting_settings(config, 'ask_strategy', 'use_sell_signal',
|
# check_conflicting_settings(config, 'ask_strategy', 'use_sell_signal',
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import logging
|
import logging
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Optional
|
from typing import Optional
|
||||||
|
|
||||||
from freqtrade.constants import USER_DATA_FILES
|
from freqtrade.constants import USER_DATA_FILES, Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def create_datadir(config: Dict[str, Any], datadir: Optional[str] = None) -> Path:
|
def create_datadir(config: Config, datadir: Optional[str] = None) -> Path:
|
||||||
|
|
||||||
folder = Path(datadir) if datadir else Path(f"{config['user_data_dir']}/data")
|
folder = Path(datadir) if datadir else Path(f"{config['user_data_dir']}/data")
|
||||||
if not datadir:
|
if not datadir:
|
||||||
|
|
|
@ -10,7 +10,7 @@ from typing import Any, Dict, List
|
||||||
|
|
||||||
import rapidjson
|
import rapidjson
|
||||||
|
|
||||||
from freqtrade.constants import MINIMAL_CONFIG
|
from freqtrade.constants import MINIMAL_CONFIG, Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.misc import deep_merge_dicts
|
from freqtrade.misc import deep_merge_dicts
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ def load_from_files(files: List[str], base_path: Path = None, level: int = 0) ->
|
||||||
Recursively load configuration files if specified.
|
Recursively load configuration files if specified.
|
||||||
Sub-files are assumed to be relative to the initial config.
|
Sub-files are assumed to be relative to the initial config.
|
||||||
"""
|
"""
|
||||||
config: Dict[str, Any] = {}
|
config: Config = {}
|
||||||
if level > 5:
|
if level > 5:
|
||||||
raise OperationalException("Config loop detected.")
|
raise OperationalException("Config loop detected.")
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"""
|
"""
|
||||||
bot constants
|
bot constants
|
||||||
"""
|
"""
|
||||||
from typing import List, Literal, Tuple
|
from typing import Any, Dict, List, Literal, Tuple
|
||||||
|
|
||||||
from freqtrade.enums import CandleType
|
from freqtrade.enums import CandleType
|
||||||
|
|
||||||
|
@ -547,6 +547,7 @@ CONF_SCHEMA = {
|
||||||
"weight_factor": {"type": "number", "default": 0},
|
"weight_factor": {"type": "number", "default": 0},
|
||||||
"principal_component_analysis": {"type": "boolean", "default": False},
|
"principal_component_analysis": {"type": "boolean", "default": False},
|
||||||
"use_SVM_to_remove_outliers": {"type": "boolean", "default": False},
|
"use_SVM_to_remove_outliers": {"type": "boolean", "default": False},
|
||||||
|
"plot_feature_importance": {"type": "boolean", "default": False},
|
||||||
"svm_params": {"type": "object",
|
"svm_params": {"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"shuffle": {"type": "boolean", "default": False},
|
"shuffle": {"type": "boolean", "default": False},
|
||||||
|
@ -646,3 +647,5 @@ LongShort = Literal['long', 'short']
|
||||||
EntryExit = Literal['entry', 'exit']
|
EntryExit = Literal['entry', 'exit']
|
||||||
BuySell = Literal['buy', 'sell']
|
BuySell = Literal['buy', 'sell']
|
||||||
MakerTaker = Literal['maker', 'taker']
|
MakerTaker = Literal['maker', 'taker']
|
||||||
|
|
||||||
|
Config = Dict[str, Any]
|
||||||
|
|
|
@ -5,12 +5,12 @@ import itertools
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from typing import Any, Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from pandas import DataFrame, to_datetime
|
from pandas import DataFrame, to_datetime
|
||||||
|
|
||||||
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS, TradeList
|
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS, Config, TradeList
|
||||||
from freqtrade.enums import CandleType
|
from freqtrade.enums import CandleType
|
||||||
|
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ def trades_to_ohlcv(trades: TradeList, timeframe: str) -> DataFrame:
|
||||||
return df_new.loc[:, DEFAULT_DATAFRAME_COLUMNS]
|
return df_new.loc[:, DEFAULT_DATAFRAME_COLUMNS]
|
||||||
|
|
||||||
|
|
||||||
def convert_trades_format(config: Dict[str, Any], convert_from: str, convert_to: str, erase: bool):
|
def convert_trades_format(config: Config, convert_from: str, convert_to: str, erase: bool):
|
||||||
"""
|
"""
|
||||||
Convert trades from one format to another format.
|
Convert trades from one format to another format.
|
||||||
:param config: Config dictionary
|
:param config: Config dictionary
|
||||||
|
@ -263,7 +263,7 @@ def convert_trades_format(config: Dict[str, Any], convert_from: str, convert_to:
|
||||||
|
|
||||||
|
|
||||||
def convert_ohlcv_format(
|
def convert_ohlcv_format(
|
||||||
config: Dict[str, Any],
|
config: Config,
|
||||||
convert_from: str,
|
convert_from: str,
|
||||||
convert_to: str,
|
convert_to: str,
|
||||||
erase: bool,
|
erase: bool,
|
||||||
|
|
|
@ -12,7 +12,7 @@ from typing import Any, Dict, List, Optional, Tuple
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
from freqtrade.constants import ListPairsWithTimeframes, PairWithTimeframe
|
from freqtrade.constants import Config, ListPairsWithTimeframes, PairWithTimeframe
|
||||||
from freqtrade.data.history import load_pair_history
|
from freqtrade.data.history import load_pair_history
|
||||||
from freqtrade.enums import CandleType, RPCMessageType, RunMode
|
from freqtrade.enums import CandleType, RPCMessageType, RunMode
|
||||||
from freqtrade.exceptions import ExchangeError, OperationalException
|
from freqtrade.exceptions import ExchangeError, OperationalException
|
||||||
|
@ -31,7 +31,7 @@ class DataProvider:
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
config: dict,
|
config: Config,
|
||||||
exchange: Optional[Exchange],
|
exchange: Optional[Exchange],
|
||||||
pairlists=None,
|
pairlists=None,
|
||||||
rpc: Optional[RPCManager] = None
|
rpc: Optional[RPCManager] = None
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
import re
|
from typing import Optional
|
||||||
from pathlib import Path
|
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
@ -20,26 +18,6 @@ class HDF5DataHandler(IDataHandler):
|
||||||
|
|
||||||
_columns = DEFAULT_DATAFRAME_COLUMNS
|
_columns = DEFAULT_DATAFRAME_COLUMNS
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def ohlcv_get_pairs(cls, datadir: Path, timeframe: str, candle_type: CandleType) -> List[str]:
|
|
||||||
"""
|
|
||||||
Returns a list of all pairs with ohlcv data available in this datadir
|
|
||||||
for the specified timeframe
|
|
||||||
:param datadir: Directory to search for ohlcv files
|
|
||||||
:param timeframe: Timeframe to search pairs for
|
|
||||||
:param candle_type: Any of the enum CandleType (must match trading mode!)
|
|
||||||
:return: List of Pairs
|
|
||||||
"""
|
|
||||||
candle = ""
|
|
||||||
if candle_type != CandleType.SPOT:
|
|
||||||
datadir = datadir.joinpath('futures')
|
|
||||||
candle = f"-{candle_type}"
|
|
||||||
|
|
||||||
_tmp = [re.search(r'^(\S+)(?=\-' + timeframe + candle + '.h5)', p.name)
|
|
||||||
for p in datadir.glob(f"*{timeframe}{candle}.h5")]
|
|
||||||
# Check if regex found something and only return these results
|
|
||||||
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
|
|
||||||
|
|
||||||
def ohlcv_store(
|
def ohlcv_store(
|
||||||
self, pair: str, timeframe: str, data: pd.DataFrame, candle_type: CandleType) -> None:
|
self, pair: str, timeframe: str, data: pd.DataFrame, candle_type: CandleType) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -121,18 +99,6 @@ class HDF5DataHandler(IDataHandler):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def trades_get_pairs(cls, datadir: Path) -> List[str]:
|
|
||||||
"""
|
|
||||||
Returns a list of all pairs for which trade data is available in this
|
|
||||||
:param datadir: Directory to search for ohlcv files
|
|
||||||
:return: List of Pairs
|
|
||||||
"""
|
|
||||||
_tmp = [re.search(r'^(\S+)(?=\-trades.h5)', p.name)
|
|
||||||
for p in datadir.glob("*trades.h5")]
|
|
||||||
# Check if regex found something and only return these results to avoid exceptions.
|
|
||||||
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
|
|
||||||
|
|
||||||
def trades_store(self, pair: str, data: TradeList) -> None:
|
def trades_store(self, pair: str, data: TradeList) -> None:
|
||||||
"""
|
"""
|
||||||
Store trades data (list of Dicts) to file
|
Store trades data (list of Dicts) to file
|
||||||
|
|
|
@ -26,7 +26,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class IDataHandler(ABC):
|
class IDataHandler(ABC):
|
||||||
|
|
||||||
_OHLCV_REGEX = r'^([a-zA-Z_-]+)\-(\d+[a-zA-Z]{1,2})\-?([a-zA-Z_]*)?(?=\.)'
|
_OHLCV_REGEX = r'^([a-zA-Z_\d-]+)\-(\d+[a-zA-Z]{1,2})\-?([a-zA-Z_]*)?(?=\.)'
|
||||||
|
|
||||||
def __init__(self, datadir: Path) -> None:
|
def __init__(self, datadir: Path) -> None:
|
||||||
self._datadir = datadir
|
self._datadir = datadir
|
||||||
|
@ -61,7 +61,6 @@ class IDataHandler(ABC):
|
||||||
) for match in _tmp if match and len(match.groups()) > 1]
|
) for match in _tmp if match and len(match.groups()) > 1]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
|
||||||
def ohlcv_get_pairs(cls, datadir: Path, timeframe: str, candle_type: CandleType) -> List[str]:
|
def ohlcv_get_pairs(cls, datadir: Path, timeframe: str, candle_type: CandleType) -> List[str]:
|
||||||
"""
|
"""
|
||||||
Returns a list of all pairs with ohlcv data available in this datadir
|
Returns a list of all pairs with ohlcv data available in this datadir
|
||||||
|
@ -71,6 +70,15 @@ class IDataHandler(ABC):
|
||||||
:param candle_type: Any of the enum CandleType (must match trading mode!)
|
:param candle_type: Any of the enum CandleType (must match trading mode!)
|
||||||
:return: List of Pairs
|
:return: List of Pairs
|
||||||
"""
|
"""
|
||||||
|
candle = ""
|
||||||
|
if candle_type != CandleType.SPOT:
|
||||||
|
datadir = datadir.joinpath('futures')
|
||||||
|
candle = f"-{candle_type}"
|
||||||
|
ext = cls._get_file_extension()
|
||||||
|
_tmp = [re.search(r'^(\S+)(?=\-' + timeframe + candle + f'.{ext})', p.name)
|
||||||
|
for p in datadir.glob(f"*{timeframe}{candle}.{ext}")]
|
||||||
|
# Check if regex found something and only return these results
|
||||||
|
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def ohlcv_store(
|
def ohlcv_store(
|
||||||
|
@ -144,13 +152,17 @@ class IDataHandler(ABC):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
|
||||||
def trades_get_pairs(cls, datadir: Path) -> List[str]:
|
def trades_get_pairs(cls, datadir: Path) -> List[str]:
|
||||||
"""
|
"""
|
||||||
Returns a list of all pairs for which trade data is available in this
|
Returns a list of all pairs for which trade data is available in this
|
||||||
:param datadir: Directory to search for ohlcv files
|
:param datadir: Directory to search for ohlcv files
|
||||||
:return: List of Pairs
|
:return: List of Pairs
|
||||||
"""
|
"""
|
||||||
|
_ext = cls._get_file_extension()
|
||||||
|
_tmp = [re.search(r'^(\S+)(?=\-trades.' + _ext + ')', p.name)
|
||||||
|
for p in datadir.glob(f"*trades.{_ext}")]
|
||||||
|
# Check if regex found something and only return these results to avoid exceptions.
|
||||||
|
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def trades_store(self, pair: str, data: TradeList) -> None:
|
def trades_store(self, pair: str, data: TradeList) -> None:
|
||||||
|
@ -255,7 +267,7 @@ class IDataHandler(ABC):
|
||||||
Rebuild pair name from filename
|
Rebuild pair name from filename
|
||||||
Assumes a asset name of max. 7 length to also support BTC-PERP and BTC-PERP:USD names.
|
Assumes a asset name of max. 7 length to also support BTC-PERP and BTC-PERP:USD names.
|
||||||
"""
|
"""
|
||||||
res = re.sub(r'^(([A-Za-z]{1,10})|^([A-Za-z\-]{1,6}))(_)', r'\g<1>/', pair, 1)
|
res = re.sub(r'^(([A-Za-z\d]{1,10})|^([A-Za-z\-]{1,6}))(_)', r'\g<1>/', pair, 1)
|
||||||
res = re.sub('_', ':', res, 1)
|
res = re.sub('_', ':', res, 1)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
import re
|
from typing import Optional
|
||||||
from pathlib import Path
|
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from pandas import DataFrame, read_json, to_datetime
|
from pandas import DataFrame, read_json, to_datetime
|
||||||
|
@ -23,26 +21,6 @@ class JsonDataHandler(IDataHandler):
|
||||||
_use_zip = False
|
_use_zip = False
|
||||||
_columns = DEFAULT_DATAFRAME_COLUMNS
|
_columns = DEFAULT_DATAFRAME_COLUMNS
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def ohlcv_get_pairs(cls, datadir: Path, timeframe: str, candle_type: CandleType) -> List[str]:
|
|
||||||
"""
|
|
||||||
Returns a list of all pairs with ohlcv data available in this datadir
|
|
||||||
for the specified timeframe
|
|
||||||
:param datadir: Directory to search for ohlcv files
|
|
||||||
:param timeframe: Timeframe to search pairs for
|
|
||||||
:param candle_type: Any of the enum CandleType (must match trading mode!)
|
|
||||||
:return: List of Pairs
|
|
||||||
"""
|
|
||||||
candle = ""
|
|
||||||
if candle_type != CandleType.SPOT:
|
|
||||||
datadir = datadir.joinpath('futures')
|
|
||||||
candle = f"-{candle_type}"
|
|
||||||
|
|
||||||
_tmp = [re.search(r'^(\S+)(?=\-' + timeframe + candle + '.json)', p.name)
|
|
||||||
for p in datadir.glob(f"*{timeframe}{candle}.{cls._get_file_extension()}")]
|
|
||||||
# Check if regex found something and only return these results
|
|
||||||
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
|
|
||||||
|
|
||||||
def ohlcv_store(
|
def ohlcv_store(
|
||||||
self, pair: str, timeframe: str, data: DataFrame, candle_type: CandleType) -> None:
|
self, pair: str, timeframe: str, data: DataFrame, candle_type: CandleType) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -119,18 +97,6 @@ class JsonDataHandler(IDataHandler):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def trades_get_pairs(cls, datadir: Path) -> List[str]:
|
|
||||||
"""
|
|
||||||
Returns a list of all pairs for which trade data is available in this
|
|
||||||
:param datadir: Directory to search for ohlcv files
|
|
||||||
:return: List of Pairs
|
|
||||||
"""
|
|
||||||
_tmp = [re.search(r'^(\S+)(?=\-trades.json)', p.name)
|
|
||||||
for p in datadir.glob(f"*trades.{cls._get_file_extension()}")]
|
|
||||||
# Check if regex found something and only return these results to avoid exceptions.
|
|
||||||
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
|
|
||||||
|
|
||||||
def trades_store(self, pair: str, data: TradeList) -> None:
|
def trades_store(self, pair: str, data: TradeList) -> None:
|
||||||
"""
|
"""
|
||||||
Store trades data (list of Dicts) to file
|
Store trades data (list of Dicts) to file
|
||||||
|
|
|
@ -11,7 +11,7 @@ import utils_find_1st as utf1st
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, UNLIMITED_STAKE_AMOUNT
|
from freqtrade.constants import DATETIME_PRINT_FORMAT, UNLIMITED_STAKE_AMOUNT, Config
|
||||||
from freqtrade.data.history import get_timerange, load_data, refresh_data
|
from freqtrade.data.history import get_timerange, load_data, refresh_data
|
||||||
from freqtrade.enums import CandleType, ExitType, RunMode
|
from freqtrade.enums import CandleType, ExitType, RunMode
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
|
@ -42,10 +42,9 @@ class Edge:
|
||||||
Author: https://github.com/mishaker
|
Author: https://github.com/mishaker
|
||||||
"""
|
"""
|
||||||
|
|
||||||
config: Dict = {}
|
|
||||||
_cached_pairs: Dict[str, Any] = {} # Keeps a list of pairs
|
_cached_pairs: Dict[str, Any] = {} # Keeps a list of pairs
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any], exchange, strategy) -> None:
|
def __init__(self, config: Config, exchange, strategy) -> None:
|
||||||
|
|
||||||
self.config = config
|
self.config = config
|
||||||
self.exchange = exchange
|
self.exchange = exchange
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
""" Binance exchange subclass """
|
""" Binance exchange subclass """
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -12,7 +11,7 @@ from freqtrade.enums import CandleType, MarginMode, TradingMode
|
||||||
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
|
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.exchange.common import retrier
|
from freqtrade.exchange.common import retrier
|
||||||
from freqtrade.misc import deep_merge_dicts
|
from freqtrade.misc import deep_merge_dicts, json_load
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -31,7 +30,7 @@ class Binance(Exchange):
|
||||||
"ccxt_futures_name": "future"
|
"ccxt_futures_name": "future"
|
||||||
}
|
}
|
||||||
_ft_has_futures: Dict = {
|
_ft_has_futures: Dict = {
|
||||||
"stoploss_order_types": {"limit": "stop"},
|
"stoploss_order_types": {"limit": "limit", "market": "market"},
|
||||||
"tickers_have_price": False,
|
"tickers_have_price": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,13 +47,12 @@ class Binance(Exchange):
|
||||||
Returns True if adjustment is necessary.
|
Returns True if adjustment is necessary.
|
||||||
:param side: "buy" or "sell"
|
:param side: "buy" or "sell"
|
||||||
"""
|
"""
|
||||||
|
order_types = ('stop_loss_limit', 'stop', 'stop_market')
|
||||||
ordertype = 'stop' if self.trading_mode == TradingMode.FUTURES else 'stop_loss_limit'
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
order.get('stopPrice', None) is None
|
order.get('stopPrice', None) is None
|
||||||
or (
|
or (
|
||||||
order['type'] == ordertype
|
order['type'] in order_types
|
||||||
and (
|
and (
|
||||||
(side == "sell" and stop_loss > float(order['stopPrice'])) or
|
(side == "sell" and stop_loss > float(order['stopPrice'])) or
|
||||||
(side == "buy" and stop_loss < float(order['stopPrice']))
|
(side == "buy" and stop_loss < float(order['stopPrice']))
|
||||||
|
@ -201,7 +199,7 @@ class Binance(Exchange):
|
||||||
Path(__file__).parent / 'binance_leverage_tiers.json'
|
Path(__file__).parent / 'binance_leverage_tiers.json'
|
||||||
)
|
)
|
||||||
with open(leverage_tiers_path) as json_file:
|
with open(leverage_tiers_path) as json_file:
|
||||||
return json.load(json_file)
|
return json_load(json_file)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
return self._api.fetch_leverage_tiers()
|
return self._api.fetch_leverage_tiers()
|
||||||
|
|
|
@ -81,6 +81,104 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"1000LUNC/USDT": [
|
||||||
|
{
|
||||||
|
"tier": 1.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 0.0,
|
||||||
|
"maxNotional": 5000.0,
|
||||||
|
"maintenanceMarginRate": 0.01,
|
||||||
|
"maxLeverage": 25.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "1",
|
||||||
|
"initialLeverage": "25",
|
||||||
|
"notionalCap": "5000",
|
||||||
|
"notionalFloor": "0",
|
||||||
|
"maintMarginRatio": "0.01",
|
||||||
|
"cum": "0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 2.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 5000.0,
|
||||||
|
"maxNotional": 25000.0,
|
||||||
|
"maintenanceMarginRate": 0.025,
|
||||||
|
"maxLeverage": 20.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "2",
|
||||||
|
"initialLeverage": "20",
|
||||||
|
"notionalCap": "25000",
|
||||||
|
"notionalFloor": "5000",
|
||||||
|
"maintMarginRatio": "0.025",
|
||||||
|
"cum": "75.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 3.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 25000.0,
|
||||||
|
"maxNotional": 100000.0,
|
||||||
|
"maintenanceMarginRate": 0.05,
|
||||||
|
"maxLeverage": 10.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "3",
|
||||||
|
"initialLeverage": "10",
|
||||||
|
"notionalCap": "100000",
|
||||||
|
"notionalFloor": "25000",
|
||||||
|
"maintMarginRatio": "0.05",
|
||||||
|
"cum": "700.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 4.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 100000.0,
|
||||||
|
"maxNotional": 250000.0,
|
||||||
|
"maintenanceMarginRate": 0.1,
|
||||||
|
"maxLeverage": 5.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "4",
|
||||||
|
"initialLeverage": "5",
|
||||||
|
"notionalCap": "250000",
|
||||||
|
"notionalFloor": "100000",
|
||||||
|
"maintMarginRatio": "0.1",
|
||||||
|
"cum": "5700.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 5.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 250000.0,
|
||||||
|
"maxNotional": 1000000.0,
|
||||||
|
"maintenanceMarginRate": 0.125,
|
||||||
|
"maxLeverage": 2.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "5",
|
||||||
|
"initialLeverage": "2",
|
||||||
|
"notionalCap": "1000000",
|
||||||
|
"notionalFloor": "250000",
|
||||||
|
"maintMarginRatio": "0.125",
|
||||||
|
"cum": "11950.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 6.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 1000000.0,
|
||||||
|
"maxNotional": 5000000.0,
|
||||||
|
"maintenanceMarginRate": 0.5,
|
||||||
|
"maxLeverage": 1.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "6",
|
||||||
|
"initialLeverage": "1",
|
||||||
|
"notionalCap": "5000000",
|
||||||
|
"notionalFloor": "1000000",
|
||||||
|
"maintMarginRatio": "0.5",
|
||||||
|
"cum": "386950.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"1000SHIB/BUSD": [
|
"1000SHIB/BUSD": [
|
||||||
{
|
{
|
||||||
"tier": 1.0,
|
"tier": 1.0,
|
||||||
|
@ -1109,6 +1207,88 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"AMB/BUSD": [
|
||||||
|
{
|
||||||
|
"tier": 1.0,
|
||||||
|
"currency": "BUSD",
|
||||||
|
"minNotional": 0.0,
|
||||||
|
"maxNotional": 25000.0,
|
||||||
|
"maintenanceMarginRate": 0.025,
|
||||||
|
"maxLeverage": 20.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "1",
|
||||||
|
"initialLeverage": "20",
|
||||||
|
"notionalCap": "25000",
|
||||||
|
"notionalFloor": "0",
|
||||||
|
"maintMarginRatio": "0.025",
|
||||||
|
"cum": "0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 2.0,
|
||||||
|
"currency": "BUSD",
|
||||||
|
"minNotional": 25000.0,
|
||||||
|
"maxNotional": 100000.0,
|
||||||
|
"maintenanceMarginRate": 0.05,
|
||||||
|
"maxLeverage": 10.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "2",
|
||||||
|
"initialLeverage": "10",
|
||||||
|
"notionalCap": "100000",
|
||||||
|
"notionalFloor": "25000",
|
||||||
|
"maintMarginRatio": "0.05",
|
||||||
|
"cum": "625.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 3.0,
|
||||||
|
"currency": "BUSD",
|
||||||
|
"minNotional": 100000.0,
|
||||||
|
"maxNotional": 250000.0,
|
||||||
|
"maintenanceMarginRate": 0.1,
|
||||||
|
"maxLeverage": 5.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "3",
|
||||||
|
"initialLeverage": "5",
|
||||||
|
"notionalCap": "250000",
|
||||||
|
"notionalFloor": "100000",
|
||||||
|
"maintMarginRatio": "0.1",
|
||||||
|
"cum": "5625.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 4.0,
|
||||||
|
"currency": "BUSD",
|
||||||
|
"minNotional": 250000.0,
|
||||||
|
"maxNotional": 1000000.0,
|
||||||
|
"maintenanceMarginRate": 0.125,
|
||||||
|
"maxLeverage": 2.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "4",
|
||||||
|
"initialLeverage": "2",
|
||||||
|
"notionalCap": "1000000",
|
||||||
|
"notionalFloor": "250000",
|
||||||
|
"maintMarginRatio": "0.125",
|
||||||
|
"cum": "11875.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 5.0,
|
||||||
|
"currency": "BUSD",
|
||||||
|
"minNotional": 1000000.0,
|
||||||
|
"maxNotional": 5000000.0,
|
||||||
|
"maintenanceMarginRate": 0.5,
|
||||||
|
"maxLeverage": 1.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "5",
|
||||||
|
"initialLeverage": "1",
|
||||||
|
"notionalCap": "5000000",
|
||||||
|
"notionalFloor": "1000000",
|
||||||
|
"maintMarginRatio": "0.5",
|
||||||
|
"cum": "386875.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"ANC/BUSD": [
|
"ANC/BUSD": [
|
||||||
{
|
{
|
||||||
"tier": 1.0,
|
"tier": 1.0,
|
||||||
|
@ -3300,13 +3480,13 @@
|
||||||
"tier": 6.0,
|
"tier": 6.0,
|
||||||
"currency": "USDT",
|
"currency": "USDT",
|
||||||
"minNotional": 1000000.0,
|
"minNotional": 1000000.0,
|
||||||
"maxNotional": 30000000.0,
|
"maxNotional": 5000000.0,
|
||||||
"maintenanceMarginRate": 0.5,
|
"maintenanceMarginRate": 0.5,
|
||||||
"maxLeverage": 1.0,
|
"maxLeverage": 1.0,
|
||||||
"info": {
|
"info": {
|
||||||
"bracket": "6",
|
"bracket": "6",
|
||||||
"initialLeverage": "1",
|
"initialLeverage": "1",
|
||||||
"notionalCap": "30000000",
|
"notionalCap": "5000000",
|
||||||
"notionalFloor": "1000000",
|
"notionalFloor": "1000000",
|
||||||
"maintMarginRatio": "0.5",
|
"maintMarginRatio": "0.5",
|
||||||
"cum": "386950.0"
|
"cum": "386950.0"
|
||||||
|
@ -4880,13 +5060,13 @@
|
||||||
"tier": 6.0,
|
"tier": 6.0,
|
||||||
"currency": "USDT",
|
"currency": "USDT",
|
||||||
"minNotional": 1000000.0,
|
"minNotional": 1000000.0,
|
||||||
"maxNotional": 30000000.0,
|
"maxNotional": 5000000.0,
|
||||||
"maintenanceMarginRate": 0.5,
|
"maintenanceMarginRate": 0.5,
|
||||||
"maxLeverage": 1.0,
|
"maxLeverage": 1.0,
|
||||||
"info": {
|
"info": {
|
||||||
"bracket": "6",
|
"bracket": "6",
|
||||||
"initialLeverage": "1",
|
"initialLeverage": "1",
|
||||||
"notionalCap": "30000000",
|
"notionalCap": "5000000",
|
||||||
"notionalFloor": "1000000",
|
"notionalFloor": "1000000",
|
||||||
"maintMarginRatio": "0.5",
|
"maintMarginRatio": "0.5",
|
||||||
"cum": "386940.0"
|
"cum": "386940.0"
|
||||||
|
@ -8333,6 +8513,104 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"FOOTBALL/USDT": [
|
||||||
|
{
|
||||||
|
"tier": 1.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 0.0,
|
||||||
|
"maxNotional": 5000.0,
|
||||||
|
"maintenanceMarginRate": 0.01,
|
||||||
|
"maxLeverage": 25.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "1",
|
||||||
|
"initialLeverage": "25",
|
||||||
|
"notionalCap": "5000",
|
||||||
|
"notionalFloor": "0",
|
||||||
|
"maintMarginRatio": "0.01",
|
||||||
|
"cum": "0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 2.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 5000.0,
|
||||||
|
"maxNotional": 25000.0,
|
||||||
|
"maintenanceMarginRate": 0.025,
|
||||||
|
"maxLeverage": 20.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "2",
|
||||||
|
"initialLeverage": "20",
|
||||||
|
"notionalCap": "25000",
|
||||||
|
"notionalFloor": "5000",
|
||||||
|
"maintMarginRatio": "0.025",
|
||||||
|
"cum": "75.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 3.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 25000.0,
|
||||||
|
"maxNotional": 100000.0,
|
||||||
|
"maintenanceMarginRate": 0.05,
|
||||||
|
"maxLeverage": 10.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "3",
|
||||||
|
"initialLeverage": "10",
|
||||||
|
"notionalCap": "100000",
|
||||||
|
"notionalFloor": "25000",
|
||||||
|
"maintMarginRatio": "0.05",
|
||||||
|
"cum": "700.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 4.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 100000.0,
|
||||||
|
"maxNotional": 250000.0,
|
||||||
|
"maintenanceMarginRate": 0.1,
|
||||||
|
"maxLeverage": 5.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "4",
|
||||||
|
"initialLeverage": "5",
|
||||||
|
"notionalCap": "250000",
|
||||||
|
"notionalFloor": "100000",
|
||||||
|
"maintMarginRatio": "0.1",
|
||||||
|
"cum": "5700.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 5.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 250000.0,
|
||||||
|
"maxNotional": 1000000.0,
|
||||||
|
"maintenanceMarginRate": 0.125,
|
||||||
|
"maxLeverage": 2.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "5",
|
||||||
|
"initialLeverage": "2",
|
||||||
|
"notionalCap": "1000000",
|
||||||
|
"notionalFloor": "250000",
|
||||||
|
"maintMarginRatio": "0.125",
|
||||||
|
"cum": "11950.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 6.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 1000000.0,
|
||||||
|
"maxNotional": 5000000.0,
|
||||||
|
"maintenanceMarginRate": 0.5,
|
||||||
|
"maxLeverage": 1.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "6",
|
||||||
|
"initialLeverage": "1",
|
||||||
|
"notionalCap": "5000000",
|
||||||
|
"notionalFloor": "1000000",
|
||||||
|
"maintMarginRatio": "0.5",
|
||||||
|
"cum": "386950.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"FTM/BUSD": [
|
"FTM/BUSD": [
|
||||||
{
|
{
|
||||||
"tier": 1.0,
|
"tier": 1.0,
|
||||||
|
@ -12123,6 +12401,104 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"LUNA2/USDT": [
|
||||||
|
{
|
||||||
|
"tier": 1.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 0.0,
|
||||||
|
"maxNotional": 5000.0,
|
||||||
|
"maintenanceMarginRate": 0.015,
|
||||||
|
"maxLeverage": 25.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "1",
|
||||||
|
"initialLeverage": "25",
|
||||||
|
"notionalCap": "5000",
|
||||||
|
"notionalFloor": "0",
|
||||||
|
"maintMarginRatio": "0.015",
|
||||||
|
"cum": "0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 2.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 5000.0,
|
||||||
|
"maxNotional": 25000.0,
|
||||||
|
"maintenanceMarginRate": 0.025,
|
||||||
|
"maxLeverage": 20.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "2",
|
||||||
|
"initialLeverage": "20",
|
||||||
|
"notionalCap": "25000",
|
||||||
|
"notionalFloor": "5000",
|
||||||
|
"maintMarginRatio": "0.025",
|
||||||
|
"cum": "50.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 3.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 25000.0,
|
||||||
|
"maxNotional": 100000.0,
|
||||||
|
"maintenanceMarginRate": 0.05,
|
||||||
|
"maxLeverage": 10.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "3",
|
||||||
|
"initialLeverage": "10",
|
||||||
|
"notionalCap": "100000",
|
||||||
|
"notionalFloor": "25000",
|
||||||
|
"maintMarginRatio": "0.05",
|
||||||
|
"cum": "675.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 4.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 100000.0,
|
||||||
|
"maxNotional": 250000.0,
|
||||||
|
"maintenanceMarginRate": 0.1,
|
||||||
|
"maxLeverage": 5.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "4",
|
||||||
|
"initialLeverage": "5",
|
||||||
|
"notionalCap": "250000",
|
||||||
|
"notionalFloor": "100000",
|
||||||
|
"maintMarginRatio": "0.1",
|
||||||
|
"cum": "5675.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 5.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 250000.0,
|
||||||
|
"maxNotional": 1000000.0,
|
||||||
|
"maintenanceMarginRate": 0.125,
|
||||||
|
"maxLeverage": 2.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "5",
|
||||||
|
"initialLeverage": "2",
|
||||||
|
"notionalCap": "1000000",
|
||||||
|
"notionalFloor": "250000",
|
||||||
|
"maintMarginRatio": "0.125",
|
||||||
|
"cum": "11925.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 6.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 1000000.0,
|
||||||
|
"maxNotional": 5000000.0,
|
||||||
|
"maintenanceMarginRate": 0.5,
|
||||||
|
"maxLeverage": 1.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "6",
|
||||||
|
"initialLeverage": "1",
|
||||||
|
"notionalCap": "5000000",
|
||||||
|
"notionalFloor": "1000000",
|
||||||
|
"maintMarginRatio": "0.5",
|
||||||
|
"cum": "386925.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"MANA/USDT": [
|
"MANA/USDT": [
|
||||||
{
|
{
|
||||||
"tier": 1.0,
|
"tier": 1.0,
|
||||||
|
@ -13028,10 +13404,10 @@
|
||||||
"minNotional": 0.0,
|
"minNotional": 0.0,
|
||||||
"maxNotional": 5000.0,
|
"maxNotional": 5000.0,
|
||||||
"maintenanceMarginRate": 0.01,
|
"maintenanceMarginRate": 0.01,
|
||||||
"maxLeverage": 50.0,
|
"maxLeverage": 25.0,
|
||||||
"info": {
|
"info": {
|
||||||
"bracket": "1",
|
"bracket": "1",
|
||||||
"initialLeverage": "50",
|
"initialLeverage": "25",
|
||||||
"notionalCap": "5000",
|
"notionalCap": "5000",
|
||||||
"notionalFloor": "0",
|
"notionalFloor": "0",
|
||||||
"maintMarginRatio": "0.01",
|
"maintMarginRatio": "0.01",
|
||||||
|
@ -13805,6 +14181,88 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"PHB/BUSD": [
|
||||||
|
{
|
||||||
|
"tier": 1.0,
|
||||||
|
"currency": "BUSD",
|
||||||
|
"minNotional": 0.0,
|
||||||
|
"maxNotional": 25000.0,
|
||||||
|
"maintenanceMarginRate": 0.025,
|
||||||
|
"maxLeverage": 20.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "1",
|
||||||
|
"initialLeverage": "20",
|
||||||
|
"notionalCap": "25000",
|
||||||
|
"notionalFloor": "0",
|
||||||
|
"maintMarginRatio": "0.025",
|
||||||
|
"cum": "0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 2.0,
|
||||||
|
"currency": "BUSD",
|
||||||
|
"minNotional": 25000.0,
|
||||||
|
"maxNotional": 100000.0,
|
||||||
|
"maintenanceMarginRate": 0.05,
|
||||||
|
"maxLeverage": 10.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "2",
|
||||||
|
"initialLeverage": "10",
|
||||||
|
"notionalCap": "100000",
|
||||||
|
"notionalFloor": "25000",
|
||||||
|
"maintMarginRatio": "0.05",
|
||||||
|
"cum": "625.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 3.0,
|
||||||
|
"currency": "BUSD",
|
||||||
|
"minNotional": 100000.0,
|
||||||
|
"maxNotional": 250000.0,
|
||||||
|
"maintenanceMarginRate": 0.1,
|
||||||
|
"maxLeverage": 5.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "3",
|
||||||
|
"initialLeverage": "5",
|
||||||
|
"notionalCap": "250000",
|
||||||
|
"notionalFloor": "100000",
|
||||||
|
"maintMarginRatio": "0.1",
|
||||||
|
"cum": "5625.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 4.0,
|
||||||
|
"currency": "BUSD",
|
||||||
|
"minNotional": 250000.0,
|
||||||
|
"maxNotional": 1000000.0,
|
||||||
|
"maintenanceMarginRate": 0.125,
|
||||||
|
"maxLeverage": 2.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "4",
|
||||||
|
"initialLeverage": "2",
|
||||||
|
"notionalCap": "1000000",
|
||||||
|
"notionalFloor": "250000",
|
||||||
|
"maintMarginRatio": "0.125",
|
||||||
|
"cum": "11875.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 5.0,
|
||||||
|
"currency": "BUSD",
|
||||||
|
"minNotional": 1000000.0,
|
||||||
|
"maxNotional": 5000000.0,
|
||||||
|
"maintenanceMarginRate": 0.5,
|
||||||
|
"maxLeverage": 1.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "5",
|
||||||
|
"initialLeverage": "1",
|
||||||
|
"notionalCap": "5000000",
|
||||||
|
"notionalFloor": "1000000",
|
||||||
|
"maintMarginRatio": "0.5",
|
||||||
|
"cum": "386875.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"QTUM/USDT": [
|
"QTUM/USDT": [
|
||||||
{
|
{
|
||||||
"tier": 1.0,
|
"tier": 1.0,
|
||||||
|
@ -14008,10 +14466,10 @@
|
||||||
"minNotional": 0.0,
|
"minNotional": 0.0,
|
||||||
"maxNotional": 5000.0,
|
"maxNotional": 5000.0,
|
||||||
"maintenanceMarginRate": 0.01,
|
"maintenanceMarginRate": 0.01,
|
||||||
"maxLeverage": 50.0,
|
"maxLeverage": 25.0,
|
||||||
"info": {
|
"info": {
|
||||||
"bracket": "1",
|
"bracket": "1",
|
||||||
"initialLeverage": "50",
|
"initialLeverage": "25",
|
||||||
"notionalCap": "5000",
|
"notionalCap": "5000",
|
||||||
"notionalFloor": "0",
|
"notionalFloor": "0",
|
||||||
"maintMarginRatio": "0.01",
|
"maintMarginRatio": "0.01",
|
||||||
|
@ -14478,13 +14936,13 @@
|
||||||
"tier": 6.0,
|
"tier": 6.0,
|
||||||
"currency": "USDT",
|
"currency": "USDT",
|
||||||
"minNotional": 1000000.0,
|
"minNotional": 1000000.0,
|
||||||
"maxNotional": 30000000.0,
|
"maxNotional": 5000000.0,
|
||||||
"maintenanceMarginRate": 0.5,
|
"maintenanceMarginRate": 0.5,
|
||||||
"maxLeverage": 1.0,
|
"maxLeverage": 1.0,
|
||||||
"info": {
|
"info": {
|
||||||
"bracket": "6",
|
"bracket": "6",
|
||||||
"initialLeverage": "1",
|
"initialLeverage": "1",
|
||||||
"notionalCap": "30000000",
|
"notionalCap": "5000000",
|
||||||
"notionalFloor": "1000000",
|
"notionalFloor": "1000000",
|
||||||
"maintMarginRatio": "0.5",
|
"maintMarginRatio": "0.5",
|
||||||
"cum": "386950.0"
|
"cum": "386950.0"
|
||||||
|
@ -14576,13 +15034,13 @@
|
||||||
"tier": 6.0,
|
"tier": 6.0,
|
||||||
"currency": "USDT",
|
"currency": "USDT",
|
||||||
"minNotional": 1000000.0,
|
"minNotional": 1000000.0,
|
||||||
"maxNotional": 30000000.0,
|
"maxNotional": 5000000.0,
|
||||||
"maintenanceMarginRate": 0.5,
|
"maintenanceMarginRate": 0.5,
|
||||||
"maxLeverage": 1.0,
|
"maxLeverage": 1.0,
|
||||||
"info": {
|
"info": {
|
||||||
"bracket": "6",
|
"bracket": "6",
|
||||||
"initialLeverage": "1",
|
"initialLeverage": "1",
|
||||||
"notionalCap": "30000000",
|
"notionalCap": "5000000",
|
||||||
"notionalFloor": "1000000",
|
"notionalFloor": "1000000",
|
||||||
"maintMarginRatio": "0.5",
|
"maintMarginRatio": "0.5",
|
||||||
"cum": "386950.0"
|
"cum": "386950.0"
|
||||||
|
@ -15487,6 +15945,104 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"SPELL/USDT": [
|
||||||
|
{
|
||||||
|
"tier": 1.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 0.0,
|
||||||
|
"maxNotional": 5000.0,
|
||||||
|
"maintenanceMarginRate": 0.01,
|
||||||
|
"maxLeverage": 25.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "1",
|
||||||
|
"initialLeverage": "25",
|
||||||
|
"notionalCap": "5000",
|
||||||
|
"notionalFloor": "0",
|
||||||
|
"maintMarginRatio": "0.01",
|
||||||
|
"cum": "0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 2.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 5000.0,
|
||||||
|
"maxNotional": 25000.0,
|
||||||
|
"maintenanceMarginRate": 0.025,
|
||||||
|
"maxLeverage": 20.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "2",
|
||||||
|
"initialLeverage": "20",
|
||||||
|
"notionalCap": "25000",
|
||||||
|
"notionalFloor": "5000",
|
||||||
|
"maintMarginRatio": "0.025",
|
||||||
|
"cum": "75.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 3.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 25000.0,
|
||||||
|
"maxNotional": 100000.0,
|
||||||
|
"maintenanceMarginRate": 0.05,
|
||||||
|
"maxLeverage": 10.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "3",
|
||||||
|
"initialLeverage": "10",
|
||||||
|
"notionalCap": "100000",
|
||||||
|
"notionalFloor": "25000",
|
||||||
|
"maintMarginRatio": "0.05",
|
||||||
|
"cum": "700.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 4.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 100000.0,
|
||||||
|
"maxNotional": 250000.0,
|
||||||
|
"maintenanceMarginRate": 0.1,
|
||||||
|
"maxLeverage": 5.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "4",
|
||||||
|
"initialLeverage": "5",
|
||||||
|
"notionalCap": "250000",
|
||||||
|
"notionalFloor": "100000",
|
||||||
|
"maintMarginRatio": "0.1",
|
||||||
|
"cum": "5700.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 5.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 250000.0,
|
||||||
|
"maxNotional": 1000000.0,
|
||||||
|
"maintenanceMarginRate": 0.125,
|
||||||
|
"maxLeverage": 2.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "5",
|
||||||
|
"initialLeverage": "2",
|
||||||
|
"notionalCap": "1000000",
|
||||||
|
"notionalFloor": "250000",
|
||||||
|
"maintMarginRatio": "0.125",
|
||||||
|
"cum": "11950.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 6.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 1000000.0,
|
||||||
|
"maxNotional": 5000000.0,
|
||||||
|
"maintenanceMarginRate": 0.5,
|
||||||
|
"maxLeverage": 1.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "6",
|
||||||
|
"initialLeverage": "1",
|
||||||
|
"notionalCap": "5000000",
|
||||||
|
"notionalFloor": "1000000",
|
||||||
|
"maintMarginRatio": "0.5",
|
||||||
|
"cum": "386950.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"SRM/USDT": [
|
"SRM/USDT": [
|
||||||
{
|
{
|
||||||
"tier": 1.0,
|
"tier": 1.0,
|
||||||
|
@ -15585,6 +16141,104 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"STG/USDT": [
|
||||||
|
{
|
||||||
|
"tier": 1.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 0.0,
|
||||||
|
"maxNotional": 5000.0,
|
||||||
|
"maintenanceMarginRate": 0.01,
|
||||||
|
"maxLeverage": 25.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "1",
|
||||||
|
"initialLeverage": "25",
|
||||||
|
"notionalCap": "5000",
|
||||||
|
"notionalFloor": "0",
|
||||||
|
"maintMarginRatio": "0.01",
|
||||||
|
"cum": "0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 2.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 5000.0,
|
||||||
|
"maxNotional": 25000.0,
|
||||||
|
"maintenanceMarginRate": 0.025,
|
||||||
|
"maxLeverage": 20.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "2",
|
||||||
|
"initialLeverage": "20",
|
||||||
|
"notionalCap": "25000",
|
||||||
|
"notionalFloor": "5000",
|
||||||
|
"maintMarginRatio": "0.025",
|
||||||
|
"cum": "75.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 3.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 25000.0,
|
||||||
|
"maxNotional": 100000.0,
|
||||||
|
"maintenanceMarginRate": 0.05,
|
||||||
|
"maxLeverage": 10.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "3",
|
||||||
|
"initialLeverage": "10",
|
||||||
|
"notionalCap": "100000",
|
||||||
|
"notionalFloor": "25000",
|
||||||
|
"maintMarginRatio": "0.05",
|
||||||
|
"cum": "700.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 4.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 100000.0,
|
||||||
|
"maxNotional": 250000.0,
|
||||||
|
"maintenanceMarginRate": 0.1,
|
||||||
|
"maxLeverage": 5.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "4",
|
||||||
|
"initialLeverage": "5",
|
||||||
|
"notionalCap": "250000",
|
||||||
|
"notionalFloor": "100000",
|
||||||
|
"maintMarginRatio": "0.1",
|
||||||
|
"cum": "5700.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 5.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 250000.0,
|
||||||
|
"maxNotional": 1000000.0,
|
||||||
|
"maintenanceMarginRate": 0.125,
|
||||||
|
"maxLeverage": 2.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "5",
|
||||||
|
"initialLeverage": "2",
|
||||||
|
"notionalCap": "1000000",
|
||||||
|
"notionalFloor": "250000",
|
||||||
|
"maintMarginRatio": "0.125",
|
||||||
|
"cum": "11950.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 6.0,
|
||||||
|
"currency": "USDT",
|
||||||
|
"minNotional": 1000000.0,
|
||||||
|
"maxNotional": 5000000.0,
|
||||||
|
"maintenanceMarginRate": 0.5,
|
||||||
|
"maxLeverage": 1.0,
|
||||||
|
"info": {
|
||||||
|
"bracket": "6",
|
||||||
|
"initialLeverage": "1",
|
||||||
|
"notionalCap": "5000000",
|
||||||
|
"notionalFloor": "1000000",
|
||||||
|
"maintMarginRatio": "0.5",
|
||||||
|
"cum": "386950.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"STMX/USDT": [
|
"STMX/USDT": [
|
||||||
{
|
{
|
||||||
"tier": 1.0,
|
"tier": 1.0,
|
||||||
|
@ -16176,13 +16830,13 @@
|
||||||
"tier": 5.0,
|
"tier": 5.0,
|
||||||
"currency": "BUSD",
|
"currency": "BUSD",
|
||||||
"minNotional": 1000000.0,
|
"minNotional": 1000000.0,
|
||||||
"maxNotional": 30000000.0,
|
"maxNotional": 5000000.0,
|
||||||
"maintenanceMarginRate": 0.5,
|
"maintenanceMarginRate": 0.5,
|
||||||
"maxLeverage": 1.0,
|
"maxLeverage": 1.0,
|
||||||
"info": {
|
"info": {
|
||||||
"bracket": "5",
|
"bracket": "5",
|
||||||
"initialLeverage": "1",
|
"initialLeverage": "1",
|
||||||
"notionalCap": "30000000",
|
"notionalCap": "5000000",
|
||||||
"notionalFloor": "1000000",
|
"notionalFloor": "1000000",
|
||||||
"maintMarginRatio": "0.5",
|
"maintMarginRatio": "0.5",
|
||||||
"cum": "386875.0"
|
"cum": "386875.0"
|
||||||
|
@ -16470,13 +17124,13 @@
|
||||||
"tier": 6.0,
|
"tier": 6.0,
|
||||||
"currency": "USDT",
|
"currency": "USDT",
|
||||||
"minNotional": 1000000.0,
|
"minNotional": 1000000.0,
|
||||||
"maxNotional": 30000000.0,
|
"maxNotional": 5000000.0,
|
||||||
"maintenanceMarginRate": 0.5,
|
"maintenanceMarginRate": 0.5,
|
||||||
"maxLeverage": 1.0,
|
"maxLeverage": 1.0,
|
||||||
"info": {
|
"info": {
|
||||||
"bracket": "6",
|
"bracket": "6",
|
||||||
"initialLeverage": "1",
|
"initialLeverage": "1",
|
||||||
"notionalCap": "30000000",
|
"notionalCap": "5000000",
|
||||||
"notionalFloor": "1000000",
|
"notionalFloor": "1000000",
|
||||||
"maintMarginRatio": "0.5",
|
"maintMarginRatio": "0.5",
|
||||||
"cum": "386950.0"
|
"cum": "386950.0"
|
||||||
|
|
|
@ -21,7 +21,8 @@ from dateutil import parser
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES, BuySell,
|
from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES, BuySell,
|
||||||
EntryExit, ListPairsWithTimeframes, MakerTaker, PairWithTimeframe)
|
Config, EntryExit, ListPairsWithTimeframes, MakerTaker,
|
||||||
|
PairWithTimeframe)
|
||||||
from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list
|
from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list
|
||||||
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, TradingMode
|
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, TradingMode
|
||||||
from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError,
|
from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError,
|
||||||
|
@ -91,7 +92,7 @@ class Exchange:
|
||||||
# TradingMode.SPOT always supported and not required in this list
|
# TradingMode.SPOT always supported and not required in this list
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any], validate: bool = True,
|
def __init__(self, config: Config, validate: bool = True,
|
||||||
load_leverage_tiers: bool = False) -> None:
|
load_leverage_tiers: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
Initializes this module with the given config,
|
Initializes this module with the given config,
|
||||||
|
@ -108,7 +109,7 @@ class Exchange:
|
||||||
self._loop_lock = Lock()
|
self._loop_lock = Lock()
|
||||||
self.loop = asyncio.new_event_loop()
|
self.loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(self.loop)
|
asyncio.set_event_loop(self.loop)
|
||||||
self._config: Dict = {}
|
self._config: Config = {}
|
||||||
|
|
||||||
self._config.update(config)
|
self._config.update(config)
|
||||||
|
|
||||||
|
@ -2304,7 +2305,7 @@ class Exchange:
|
||||||
updated = tiers.get('updated')
|
updated = tiers.get('updated')
|
||||||
if updated:
|
if updated:
|
||||||
updated_dt = parser.parse(updated)
|
updated_dt = parser.parse(updated)
|
||||||
if updated_dt < datetime.now(timezone.utc) - timedelta(days=1):
|
if updated_dt < datetime.now(timezone.utc) - timedelta(weeks=4):
|
||||||
logger.info("Cached leverage tiers are outdated. Will update.")
|
logger.info("Cached leverage tiers are outdated. Will update.")
|
||||||
return None
|
return None
|
||||||
return tiers['data']
|
return tiers['data']
|
||||||
|
|
|
@ -16,6 +16,7 @@ from numpy.typing import NDArray
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.data.history import load_pair_history
|
from freqtrade.data.history import load_pair_history
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||||
|
@ -27,9 +28,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class pair_info(TypedDict):
|
class pair_info(TypedDict):
|
||||||
model_filename: str
|
model_filename: str
|
||||||
first: bool
|
|
||||||
trained_timestamp: int
|
trained_timestamp: int
|
||||||
priority: int
|
|
||||||
data_path: str
|
data_path: str
|
||||||
extras: dict
|
extras: dict
|
||||||
|
|
||||||
|
@ -58,7 +57,7 @@ class FreqaiDataDrawer:
|
||||||
Juha Nykänen @suikula, Wagner Costa @wagnercosta, Johan Vlugt @Jooopieeert
|
Juha Nykänen @suikula, Wagner Costa @wagnercosta, Johan Vlugt @Jooopieeert
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, full_path: Path, config: dict, follow_mode: bool = False):
|
def __init__(self, full_path: Path, config: Config, follow_mode: bool = False):
|
||||||
|
|
||||||
self.config = config
|
self.config = config
|
||||||
self.freqai_info = config.get("freqai", {})
|
self.freqai_info = config.get("freqai", {})
|
||||||
|
@ -91,7 +90,7 @@ class FreqaiDataDrawer:
|
||||||
self.old_DBSCAN_eps: Dict[str, float] = {}
|
self.old_DBSCAN_eps: Dict[str, float] = {}
|
||||||
self.empty_pair_dict: pair_info = {
|
self.empty_pair_dict: pair_info = {
|
||||||
"model_filename": "", "trained_timestamp": 0,
|
"model_filename": "", "trained_timestamp": 0,
|
||||||
"priority": 1, "first": True, "data_path": "", "extras": {}}
|
"data_path": "", "extras": {}}
|
||||||
|
|
||||||
def load_drawer_from_disk(self):
|
def load_drawer_from_disk(self):
|
||||||
"""
|
"""
|
||||||
|
@ -216,7 +215,6 @@ class FreqaiDataDrawer:
|
||||||
self.pair_dict[pair] = self.empty_pair_dict.copy()
|
self.pair_dict[pair] = self.empty_pair_dict.copy()
|
||||||
model_filename = ""
|
model_filename = ""
|
||||||
trained_timestamp = 0
|
trained_timestamp = 0
|
||||||
self.pair_dict[pair]["priority"] = len(self.pair_dict)
|
|
||||||
|
|
||||||
if not data_path_set and self.follow_mode:
|
if not data_path_set and self.follow_mode:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
|
@ -236,18 +234,9 @@ class FreqaiDataDrawer:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.pair_dict[metadata["pair"]] = self.empty_pair_dict.copy()
|
self.pair_dict[metadata["pair"]] = self.empty_pair_dict.copy()
|
||||||
self.pair_dict[metadata["pair"]]["priority"] = len(self.pair_dict)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def pair_to_end_of_training_queue(self, pair: str) -> None:
|
|
||||||
# march all pairs up in the queue
|
|
||||||
with self.pair_dict_lock:
|
|
||||||
for p in self.pair_dict:
|
|
||||||
self.pair_dict[p]["priority"] -= 1
|
|
||||||
# send pair to end of queue
|
|
||||||
self.pair_dict[pair]["priority"] = len(self.pair_dict)
|
|
||||||
|
|
||||||
def set_initial_return_values(self, pair: str, pred_df: DataFrame) -> None:
|
def set_initial_return_values(self, pair: str, pred_df: DataFrame) -> None:
|
||||||
"""
|
"""
|
||||||
Set the initial return values to the historical predictions dataframe. This avoids needing
|
Set the initial return values to the historical predictions dataframe. This avoids needing
|
||||||
|
|
|
@ -18,6 +18,7 @@ from sklearn.model_selection import train_test_split
|
||||||
from sklearn.neighbors import NearestNeighbors
|
from sklearn.neighbors import NearestNeighbors
|
||||||
|
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import timeframe_to_seconds
|
from freqtrade.exchange import timeframe_to_seconds
|
||||||
from freqtrade.strategy.interface import IStrategy
|
from freqtrade.strategy.interface import IStrategy
|
||||||
|
@ -57,7 +58,7 @@ class FreqaiDataKitchen:
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
config: Dict[str, Any],
|
config: Config,
|
||||||
live: bool = False,
|
live: bool = False,
|
||||||
pair: str = "",
|
pair: str = "",
|
||||||
):
|
):
|
||||||
|
|
|
@ -3,6 +3,7 @@ import shutil
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from collections import deque
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
@ -14,12 +15,13 @@ from numpy.typing import NDArray
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT
|
from freqtrade.constants import DATETIME_PRINT_FORMAT, Config
|
||||||
from freqtrade.enums import RunMode
|
from freqtrade.enums import RunMode
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import timeframe_to_seconds
|
from freqtrade.exchange import timeframe_to_seconds
|
||||||
from freqtrade.freqai.data_drawer import FreqaiDataDrawer
|
from freqtrade.freqai.data_drawer import FreqaiDataDrawer
|
||||||
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||||
|
from freqtrade.freqai.utils import plot_feature_importance
|
||||||
from freqtrade.strategy.interface import IStrategy
|
from freqtrade.strategy.interface import IStrategy
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,7 +52,7 @@ class IFreqaiModel(ABC):
|
||||||
Juha Nykänen @suikula, Wagner Costa @wagnercosta, Johan Vlugt @Jooopieeert
|
Juha Nykänen @suikula, Wagner Costa @wagnercosta, Johan Vlugt @Jooopieeert
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any]) -> None:
|
def __init__(self, config: Config) -> None:
|
||||||
|
|
||||||
self.config = config
|
self.config = config
|
||||||
self.assert_config(self.config)
|
self.assert_config(self.config)
|
||||||
|
@ -80,6 +82,7 @@ class IFreqaiModel(ABC):
|
||||||
self.pair_it = 0
|
self.pair_it = 0
|
||||||
self.pair_it_train = 0
|
self.pair_it_train = 0
|
||||||
self.total_pairs = len(self.config.get("exchange", {}).get("pair_whitelist"))
|
self.total_pairs = len(self.config.get("exchange", {}).get("pair_whitelist"))
|
||||||
|
self.train_queue = self._set_train_queue()
|
||||||
self.last_trade_database_summary: DataFrame = {}
|
self.last_trade_database_summary: DataFrame = {}
|
||||||
self.current_trade_database_summary: DataFrame = {}
|
self.current_trade_database_summary: DataFrame = {}
|
||||||
self.analysis_lock = Lock()
|
self.analysis_lock = Lock()
|
||||||
|
@ -99,7 +102,7 @@ class IFreqaiModel(ABC):
|
||||||
"""
|
"""
|
||||||
return ({})
|
return ({})
|
||||||
|
|
||||||
def assert_config(self, config: Dict[str, Any]) -> None:
|
def assert_config(self, config: Config) -> None:
|
||||||
|
|
||||||
if not config.get("freqai", {}):
|
if not config.get("freqai", {}):
|
||||||
raise OperationalException("No freqai parameters found in configuration file.")
|
raise OperationalException("No freqai parameters found in configuration file.")
|
||||||
|
@ -181,29 +184,36 @@ class IFreqaiModel(ABC):
|
||||||
"""
|
"""
|
||||||
while not self._stop_event.is_set():
|
while not self._stop_event.is_set():
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
for pair in self.config.get("exchange", {}).get("pair_whitelist"):
|
pair = self.train_queue[0]
|
||||||
|
|
||||||
(_, trained_timestamp, _) = self.dd.get_pair_dict_info(pair)
|
# ensure pair is avaialble in dp
|
||||||
|
if pair not in strategy.dp.current_whitelist():
|
||||||
|
self.train_queue.popleft()
|
||||||
|
logger.warning(f'{pair} not in current whitelist, removing from train queue.')
|
||||||
|
continue
|
||||||
|
|
||||||
if self.dd.pair_dict[pair]["priority"] != 1:
|
(_, trained_timestamp, _) = self.dd.get_pair_dict_info(pair)
|
||||||
continue
|
|
||||||
dk = FreqaiDataKitchen(self.config, self.live, pair)
|
|
||||||
dk.set_paths(pair, trained_timestamp)
|
|
||||||
(
|
|
||||||
retrain,
|
|
||||||
new_trained_timerange,
|
|
||||||
data_load_timerange,
|
|
||||||
) = dk.check_if_new_training_required(trained_timestamp)
|
|
||||||
dk.set_paths(pair, new_trained_timerange.stopts)
|
|
||||||
|
|
||||||
if retrain:
|
dk = FreqaiDataKitchen(self.config, self.live, pair)
|
||||||
self.train_timer('start')
|
dk.set_paths(pair, trained_timestamp)
|
||||||
self.extract_data_and_train_model(
|
(
|
||||||
new_trained_timerange, pair, strategy, dk, data_load_timerange
|
retrain,
|
||||||
)
|
new_trained_timerange,
|
||||||
self.train_timer('stop')
|
data_load_timerange,
|
||||||
|
) = dk.check_if_new_training_required(trained_timestamp)
|
||||||
|
dk.set_paths(pair, new_trained_timerange.stopts)
|
||||||
|
|
||||||
self.dd.save_historic_predictions_to_disk()
|
if retrain:
|
||||||
|
self.train_timer('start')
|
||||||
|
self.extract_data_and_train_model(
|
||||||
|
new_trained_timerange, pair, strategy, dk, data_load_timerange
|
||||||
|
)
|
||||||
|
self.train_timer('stop')
|
||||||
|
|
||||||
|
# only rotate the queue after the first has been trained.
|
||||||
|
self.train_queue.rotate(-1)
|
||||||
|
|
||||||
|
self.dd.save_historic_predictions_to_disk()
|
||||||
|
|
||||||
def start_backtesting(
|
def start_backtesting(
|
||||||
self, dataframe: DataFrame, metadata: dict, dk: FreqaiDataKitchen
|
self, dataframe: DataFrame, metadata: dict, dk: FreqaiDataKitchen
|
||||||
|
@ -557,11 +567,11 @@ class IFreqaiModel(ABC):
|
||||||
|
|
||||||
self.dd.pair_dict[pair]["trained_timestamp"] = new_trained_timerange.stopts
|
self.dd.pair_dict[pair]["trained_timestamp"] = new_trained_timerange.stopts
|
||||||
dk.set_new_model_names(pair, new_trained_timerange)
|
dk.set_new_model_names(pair, new_trained_timerange)
|
||||||
self.dd.pair_dict[pair]["first"] = False
|
|
||||||
if self.dd.pair_dict[pair]["priority"] == 1 and self.scanning:
|
|
||||||
self.dd.pair_to_end_of_training_queue(pair)
|
|
||||||
self.dd.save_data(model, pair, dk)
|
self.dd.save_data(model, pair, dk)
|
||||||
|
|
||||||
|
if self.freqai_info["feature_parameters"].get("plot_feature_importance", False):
|
||||||
|
plot_feature_importance(model, pair, dk)
|
||||||
|
|
||||||
if self.freqai_info.get("purge_old_models", False):
|
if self.freqai_info.get("purge_old_models", False):
|
||||||
self.dd.purge_old_models()
|
self.dd.purge_old_models()
|
||||||
|
|
||||||
|
@ -685,6 +695,32 @@ class IFreqaiModel(ABC):
|
||||||
|
|
||||||
return init_model
|
return init_model
|
||||||
|
|
||||||
|
def _set_train_queue(self):
|
||||||
|
"""
|
||||||
|
Sets train queue from existing train timestamps if they exist
|
||||||
|
otherwise it sets the train queue based on the provided whitelist.
|
||||||
|
"""
|
||||||
|
current_pairlist = self.config.get("exchange", {}).get("pair_whitelist")
|
||||||
|
if not self.dd.pair_dict:
|
||||||
|
logger.info('Set fresh train queue from whitelist. '
|
||||||
|
f'Queue: {current_pairlist}')
|
||||||
|
return deque(current_pairlist)
|
||||||
|
|
||||||
|
best_queue = deque()
|
||||||
|
|
||||||
|
pair_dict_sorted = sorted(self.dd.pair_dict.items(),
|
||||||
|
key=lambda k: k[1]['trained_timestamp'])
|
||||||
|
for pair in pair_dict_sorted:
|
||||||
|
if pair[0] in current_pairlist:
|
||||||
|
best_queue.append(pair[0])
|
||||||
|
for pair in current_pairlist:
|
||||||
|
if pair not in best_queue:
|
||||||
|
best_queue.appendleft(pair)
|
||||||
|
|
||||||
|
logger.info('Set existing queue from trained timestamps. '
|
||||||
|
f'Best approximation queue: {best_queue}')
|
||||||
|
return best_queue
|
||||||
|
|
||||||
# Following methods which are overridden by user made prediction models.
|
# Following methods which are overridden by user made prediction models.
|
||||||
# See freqai/prediction_models/CatboostPredictionModel.py for an example.
|
# See freqai/prediction_models/CatboostPredictionModel.py for an example.
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,25 @@
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.data.history.history_utils import refresh_backtest_ohlcv_data
|
from freqtrade.data.history.history_utils import refresh_backtest_ohlcv_data
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import timeframe_to_seconds
|
from freqtrade.exchange import timeframe_to_seconds
|
||||||
from freqtrade.exchange.exchange import market_is_active
|
from freqtrade.exchange.exchange import market_is_active
|
||||||
|
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||||
from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist
|
from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def download_all_data_for_training(dp: DataProvider, config: dict) -> None:
|
def download_all_data_for_training(dp: DataProvider, config: Config) -> None:
|
||||||
"""
|
"""
|
||||||
Called only once upon start of bot to download the necessary data for
|
Called only once upon start of bot to download the necessary data for
|
||||||
populating indicators and training the model.
|
populating indicators and training the model.
|
||||||
|
@ -47,9 +53,7 @@ def download_all_data_for_training(dp: DataProvider, config: dict) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_required_data_timerange(
|
def get_required_data_timerange(config: Config) -> TimeRange:
|
||||||
config: dict
|
|
||||||
) -> TimeRange:
|
|
||||||
"""
|
"""
|
||||||
Used to compute the required data download time range
|
Used to compute the required data download time range
|
||||||
for auto data-download in FreqAI
|
for auto data-download in FreqAI
|
||||||
|
@ -86,7 +90,7 @@ def get_required_data_timerange(
|
||||||
|
|
||||||
|
|
||||||
# Keep below for when we wish to download heterogeneously lengthed data for FreqAI.
|
# Keep below for when we wish to download heterogeneously lengthed data for FreqAI.
|
||||||
# def download_all_data_for_training(dp: DataProvider, config: dict) -> None:
|
# def download_all_data_for_training(dp: DataProvider, config: Config) -> None:
|
||||||
# """
|
# """
|
||||||
# Called only once upon start of bot to download the necessary data for
|
# Called only once upon start of bot to download the necessary data for
|
||||||
# populating indicators and training a FreqAI model.
|
# populating indicators and training a FreqAI model.
|
||||||
|
@ -132,3 +136,58 @@ def get_required_data_timerange(
|
||||||
# trading_mode=config.get("trading_mode", "spot"),
|
# trading_mode=config.get("trading_mode", "spot"),
|
||||||
# prepend=config.get("prepend_data", False),
|
# prepend=config.get("prepend_data", False),
|
||||||
# )
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
def plot_feature_importance(model: Any, pair: str, dk: FreqaiDataKitchen,
|
||||||
|
count_max: int = 25) -> None:
|
||||||
|
"""
|
||||||
|
Plot Best and worst features by importance for a single sub-train.
|
||||||
|
:param model: Any = A model which was `fit` using a common library
|
||||||
|
such as catboost or lightgbm
|
||||||
|
:param pair: str = pair e.g. BTC/USD
|
||||||
|
:param dk: FreqaiDataKitchen = non-persistent data container for current coin/loop
|
||||||
|
:param count_max: int = the amount of features to be loaded per column
|
||||||
|
"""
|
||||||
|
from freqtrade.plot.plotting import go, make_subplots, store_plot_file
|
||||||
|
|
||||||
|
# Extract feature importance from model
|
||||||
|
models = {}
|
||||||
|
if 'FreqaiMultiOutputRegressor' in str(model.__class__):
|
||||||
|
for estimator, label in zip(model.estimators_, dk.label_list):
|
||||||
|
models[label] = estimator
|
||||||
|
else:
|
||||||
|
models[dk.label_list[0]] = model
|
||||||
|
|
||||||
|
for label in models:
|
||||||
|
mdl = models[label]
|
||||||
|
if "catboost.core" in str(mdl.__class__):
|
||||||
|
feature_importance = mdl.get_feature_importance()
|
||||||
|
elif "lightgbm.sklearn" or "xgb" in str(mdl.__class__):
|
||||||
|
feature_importance = mdl.feature_importances_
|
||||||
|
else:
|
||||||
|
logger.info('Model type not support for generating feature importances.')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Data preparation
|
||||||
|
fi_df = pd.DataFrame({
|
||||||
|
"feature_names": np.array(dk.training_features_list),
|
||||||
|
"feature_importance": np.array(feature_importance)
|
||||||
|
})
|
||||||
|
fi_df_top = fi_df.nlargest(count_max, "feature_importance")[::-1]
|
||||||
|
fi_df_worst = fi_df.nsmallest(count_max, "feature_importance")[::-1]
|
||||||
|
|
||||||
|
# Plotting
|
||||||
|
def add_feature_trace(fig, fi_df, col):
|
||||||
|
return fig.add_trace(
|
||||||
|
go.Bar(
|
||||||
|
x=fi_df["feature_importance"],
|
||||||
|
y=fi_df["feature_names"],
|
||||||
|
orientation='h', showlegend=False
|
||||||
|
), row=1, col=col
|
||||||
|
)
|
||||||
|
fig = make_subplots(rows=1, cols=2, horizontal_spacing=0.5)
|
||||||
|
fig = add_feature_trace(fig, fi_df_top, 1)
|
||||||
|
fig = add_feature_trace(fig, fi_df_worst, 2)
|
||||||
|
fig.update_layout(title_text=f"Best and worst features by importance {pair}")
|
||||||
|
label = label.replace('&', '').replace('%', '') # escape two FreqAI specific characters
|
||||||
|
store_plot_file(fig, f"{dk.model_filename}-{label}.html", dk.data_path)
|
||||||
|
|
|
@ -13,7 +13,7 @@ from schedule import Scheduler
|
||||||
|
|
||||||
from freqtrade import __version__, constants
|
from freqtrade import __version__, constants
|
||||||
from freqtrade.configuration import validate_config_consistency
|
from freqtrade.configuration import validate_config_consistency
|
||||||
from freqtrade.constants import BuySell, LongShort
|
from freqtrade.constants import BuySell, Config, LongShort
|
||||||
from freqtrade.data.converter import order_book_to_dataframe
|
from freqtrade.data.converter import order_book_to_dataframe
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.edge import Edge
|
from freqtrade.edge import Edge
|
||||||
|
@ -45,7 +45,7 @@ class FreqtradeBot(LoggingMixin):
|
||||||
This is from here the bot start its logic.
|
This is from here the bot start its logic.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any]) -> None:
|
def __init__(self, config: Config) -> None:
|
||||||
"""
|
"""
|
||||||
Init all variables and objects the bot needs to work
|
Init all variables and objects the bot needs to work
|
||||||
:param config: configuration dict, you can use Configuration.get_config()
|
:param config: configuration dict, you can use Configuration.get_config()
|
||||||
|
@ -1085,6 +1085,7 @@ class FreqtradeBot(LoggingMixin):
|
||||||
order_obj = Order.parse_from_ccxt_object(stoploss_order, trade.pair, 'stoploss')
|
order_obj = Order.parse_from_ccxt_object(stoploss_order, trade.pair, 'stoploss')
|
||||||
trade.orders.append(order_obj)
|
trade.orders.append(order_obj)
|
||||||
trade.stoploss_order_id = str(stoploss_order['id'])
|
trade.stoploss_order_id = str(stoploss_order['id'])
|
||||||
|
trade.stoploss_last_update = datetime.now(timezone.utc)
|
||||||
return True
|
return True
|
||||||
except InsufficientFundsError as e:
|
except InsufficientFundsError as e:
|
||||||
logger.warning(f"Unable to place stoploss order {e}.")
|
logger.warning(f"Unable to place stoploss order {e}.")
|
||||||
|
@ -1158,10 +1159,9 @@ class FreqtradeBot(LoggingMixin):
|
||||||
if self.create_stoploss_order(trade=trade, stop_price=stop_price):
|
if self.create_stoploss_order(trade=trade, stop_price=stop_price):
|
||||||
# The above will return False if the placement failed and the trade was force-sold.
|
# The above will return False if the placement failed and the trade was force-sold.
|
||||||
# in which case the trade will be closed - which we must check below.
|
# in which case the trade will be closed - which we must check below.
|
||||||
trade.stoploss_last_update = datetime.utcnow()
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# If stoploss order is canceled for some reason we add it
|
# If stoploss order is canceled for some reason we add it again
|
||||||
if (trade.is_open
|
if (trade.is_open
|
||||||
and stoploss_order
|
and stoploss_order
|
||||||
and stoploss_order['status'] in ('canceled', 'cancelled')):
|
and stoploss_order['status'] in ('canceled', 'cancelled')):
|
||||||
|
@ -1199,7 +1199,8 @@ class FreqtradeBot(LoggingMixin):
|
||||||
if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side):
|
if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side):
|
||||||
# we check if the update is necessary
|
# we check if the update is necessary
|
||||||
update_beat = self.strategy.order_types.get('stoploss_on_exchange_interval', 60)
|
update_beat = self.strategy.order_types.get('stoploss_on_exchange_interval', 60)
|
||||||
if (datetime.utcnow() - trade.stoploss_last_update).total_seconds() >= update_beat:
|
upd_req = datetime.now(timezone.utc) - timedelta(seconds=update_beat)
|
||||||
|
if trade.stoploss_last_update_utc and upd_req >= trade.stoploss_last_update_utc:
|
||||||
# cancelling the current stoploss on exchange first
|
# cancelling the current stoploss on exchange first
|
||||||
logger.info(f"Cancelling current stoploss on exchange for pair {trade.pair} "
|
logger.info(f"Cancelling current stoploss on exchange for pair {trade.pair} "
|
||||||
f"(orderid:{order['id']}) in order to add another one ...")
|
f"(orderid:{order['id']}) in order to add another one ...")
|
||||||
|
|
|
@ -2,8 +2,8 @@ import logging
|
||||||
import sys
|
import sys
|
||||||
from logging import Formatter
|
from logging import Formatter
|
||||||
from logging.handlers import BufferingHandler, RotatingFileHandler, SysLogHandler
|
from logging.handlers import BufferingHandler, RotatingFileHandler, SysLogHandler
|
||||||
from typing import Any, Dict
|
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ def setup_logging_pre() -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(config: Dict[str, Any]) -> None:
|
def setup_logging(config: Config) -> None:
|
||||||
"""
|
"""
|
||||||
Process -v/--verbose, --logfile options
|
Process -v/--verbose, --logfile options
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -15,7 +15,7 @@ from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade import constants
|
from freqtrade import constants
|
||||||
from freqtrade.configuration import TimeRange, validate_config_consistency
|
from freqtrade.configuration import TimeRange, validate_config_consistency
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, LongShort
|
from freqtrade.constants import DATETIME_PRINT_FORMAT, Config, LongShort
|
||||||
from freqtrade.data import history
|
from freqtrade.data import history
|
||||||
from freqtrade.data.btanalysis import find_existing_backtest_stats, trade_list_to_dataframe
|
from freqtrade.data.btanalysis import find_existing_backtest_stats, trade_list_to_dataframe
|
||||||
from freqtrade.data.converter import trim_dataframe, trim_dataframes
|
from freqtrade.data.converter import trim_dataframe, trim_dataframes
|
||||||
|
@ -70,7 +70,7 @@ class Backtesting:
|
||||||
backtesting.start()
|
backtesting.start()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any]) -> None:
|
def __init__(self, config: Config) -> None:
|
||||||
|
|
||||||
LoggingMixin.show_output = False
|
LoggingMixin.show_output = False
|
||||||
self.config = config
|
self.config = config
|
||||||
|
@ -812,14 +812,6 @@ class Backtesting:
|
||||||
return trade
|
return trade
|
||||||
time_in_force = self.strategy.order_time_in_force['entry']
|
time_in_force = self.strategy.order_time_in_force['entry']
|
||||||
|
|
||||||
if not pos_adjust:
|
|
||||||
# Confirm trade entry:
|
|
||||||
if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)(
|
|
||||||
pair=pair, order_type=order_type, amount=stake_amount, rate=propose_rate,
|
|
||||||
time_in_force=time_in_force, current_time=current_time,
|
|
||||||
entry_tag=entry_tag, side=direction):
|
|
||||||
return trade
|
|
||||||
|
|
||||||
if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount):
|
if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount):
|
||||||
self.order_id_counter += 1
|
self.order_id_counter += 1
|
||||||
base_currency = self.exchange.get_pair_base_currency(pair)
|
base_currency = self.exchange.get_pair_base_currency(pair)
|
||||||
|
@ -834,6 +826,15 @@ class Backtesting:
|
||||||
# Backcalculate actual stake amount.
|
# Backcalculate actual stake amount.
|
||||||
stake_amount = amount * propose_rate / leverage
|
stake_amount = amount * propose_rate / leverage
|
||||||
|
|
||||||
|
if not pos_adjust:
|
||||||
|
# Confirm trade entry:
|
||||||
|
if not strategy_safe_wrapper(
|
||||||
|
self.strategy.confirm_trade_entry, default_retval=True)(
|
||||||
|
pair=pair, order_type=order_type, amount=amount, rate=propose_rate,
|
||||||
|
time_in_force=time_in_force, current_time=current_time,
|
||||||
|
entry_tag=entry_tag, side=direction):
|
||||||
|
return trade
|
||||||
|
|
||||||
is_short = (direction == 'short')
|
is_short = (direction == 'short')
|
||||||
# Necessary for Margin trading. Disabled until support is enabled.
|
# Necessary for Margin trading. Disabled until support is enabled.
|
||||||
# interest_rate = self.exchange.get_interest_rate()
|
# interest_rate = self.exchange.get_interest_rate()
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
This module contains the edge backtesting interface
|
This module contains the edge backtesting interface
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
|
||||||
|
|
||||||
from freqtrade import constants
|
from freqtrade import constants
|
||||||
from freqtrade.configuration import TimeRange, validate_config_consistency
|
from freqtrade.configuration import TimeRange, validate_config_consistency
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.edge import Edge
|
from freqtrade.edge import Edge
|
||||||
from freqtrade.optimize.optimize_reports import generate_edge_table
|
from freqtrade.optimize.optimize_reports import generate_edge_table
|
||||||
|
@ -26,7 +26,7 @@ class EdgeCli:
|
||||||
edge.start()
|
edge.start()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any]) -> None:
|
def __init__(self, config: Config) -> None:
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
# Ensure using dry-run
|
# Ensure using dry-run
|
||||||
|
|
|
@ -21,7 +21,7 @@ from joblib import Parallel, cpu_count, delayed, dump, load, wrap_non_picklable_
|
||||||
from joblib.externals import cloudpickle
|
from joblib.externals import cloudpickle
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN
|
from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN, Config
|
||||||
from freqtrade.data.converter import trim_dataframes
|
from freqtrade.data.converter import trim_dataframes
|
||||||
from freqtrade.data.history import get_timerange
|
from freqtrade.data.history import get_timerange
|
||||||
from freqtrade.enums import HyperoptState
|
from freqtrade.enums import HyperoptState
|
||||||
|
@ -66,7 +66,7 @@ class Hyperopt:
|
||||||
hyperopt.start()
|
hyperopt.start()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any]) -> None:
|
def __init__(self, config: Config) -> None:
|
||||||
self.buy_space: List[Dimension] = []
|
self.buy_space: List[Dimension] = []
|
||||||
self.sell_space: List[Dimension] = []
|
self.sell_space: List[Dimension] = []
|
||||||
self.protection_space: List[Dimension] = []
|
self.protection_space: List[Dimension] = []
|
||||||
|
@ -132,7 +132,7 @@ class Hyperopt:
|
||||||
self.print_json = self.config.get('print_json', False)
|
self.print_json = self.config.get('print_json', False)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_lock_filename(config: Dict[str, Any]) -> str:
|
def get_lock_filename(config: Config) -> str:
|
||||||
|
|
||||||
return str(config['user_data_dir'] / 'hyperopt.lock')
|
return str(config['user_data_dir'] / 'hyperopt.lock')
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ from typing import Dict, List, Union
|
||||||
from sklearn.base import RegressorMixin
|
from sklearn.base import RegressorMixin
|
||||||
from skopt.space import Categorical, Dimension, Integer
|
from skopt.space import Categorical, Dimension, Integer
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exchange import timeframe_to_minutes
|
from freqtrade.exchange import timeframe_to_minutes
|
||||||
from freqtrade.misc import round_dict
|
from freqtrade.misc import round_dict
|
||||||
from freqtrade.optimize.space import SKDecimal
|
from freqtrade.optimize.space import SKDecimal
|
||||||
|
@ -32,7 +33,7 @@ class IHyperOpt(ABC):
|
||||||
timeframe: str
|
timeframe: str
|
||||||
strategy: IStrategy
|
strategy: IStrategy
|
||||||
|
|
||||||
def __init__(self, config: dict) -> None:
|
def __init__(self, config: Config) -> None:
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
# Assign timeframe to be used in hyperopt
|
# Assign timeframe to be used in hyperopt
|
||||||
|
|
|
@ -10,6 +10,7 @@ from typing import Any, Dict
|
||||||
|
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.data.metrics import calculate_max_drawdown
|
from freqtrade.data.metrics import calculate_max_drawdown
|
||||||
from freqtrade.optimize.hyperopt import IHyperOptLoss
|
from freqtrade.optimize.hyperopt import IHyperOptLoss
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ class CalmarHyperOptLoss(IHyperOptLoss):
|
||||||
trade_count: int,
|
trade_count: int,
|
||||||
min_date: datetime,
|
min_date: datetime,
|
||||||
max_date: datetime,
|
max_date: datetime,
|
||||||
config: Dict,
|
config: Config,
|
||||||
processed: Dict[str, DataFrame],
|
processed: Dict[str, DataFrame],
|
||||||
backtest_stats: Dict[str, Any],
|
backtest_stats: Dict[str, Any],
|
||||||
*args,
|
*args,
|
||||||
|
|
|
@ -4,10 +4,9 @@ MaxDrawDownRelativeHyperOptLoss
|
||||||
This module defines the alternative HyperOptLoss class which can be used for
|
This module defines the alternative HyperOptLoss class which can be used for
|
||||||
Hyperoptimization.
|
Hyperoptimization.
|
||||||
"""
|
"""
|
||||||
from typing import Dict
|
|
||||||
|
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.data.metrics import calculate_underwater
|
from freqtrade.data.metrics import calculate_underwater
|
||||||
from freqtrade.optimize.hyperopt import IHyperOptLoss
|
from freqtrade.optimize.hyperopt import IHyperOptLoss
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ class MaxDrawDownRelativeHyperOptLoss(IHyperOptLoss):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def hyperopt_loss_function(results: DataFrame, config: Dict,
|
def hyperopt_loss_function(results: DataFrame, config: Config,
|
||||||
*args, **kwargs) -> float:
|
*args, **kwargs) -> float:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -9,6 +9,8 @@ from typing import Any, Dict
|
||||||
|
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
|
|
||||||
|
|
||||||
class IHyperOptLoss(ABC):
|
class IHyperOptLoss(ABC):
|
||||||
"""
|
"""
|
||||||
|
@ -21,7 +23,7 @@ class IHyperOptLoss(ABC):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def hyperopt_loss_function(*, results: DataFrame, trade_count: int,
|
def hyperopt_loss_function(*, results: DataFrame, trade_count: int,
|
||||||
min_date: datetime, max_date: datetime,
|
min_date: datetime, max_date: datetime,
|
||||||
config: Dict, processed: Dict[str, DataFrame],
|
config: Config, processed: Dict[str, DataFrame],
|
||||||
backtest_stats: Dict[str, Any],
|
backtest_stats: Dict[str, Any],
|
||||||
**kwargs) -> float:
|
**kwargs) -> float:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -12,7 +12,7 @@ import tabulate
|
||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
from pandas import isna, json_normalize
|
from pandas import isna, json_normalize
|
||||||
|
|
||||||
from freqtrade.constants import FTHYPT_FILEVERSION, USERPATH_STRATEGIES
|
from freqtrade.constants import FTHYPT_FILEVERSION, USERPATH_STRATEGIES, Config
|
||||||
from freqtrade.enums import HyperoptState
|
from freqtrade.enums import HyperoptState
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.misc import deep_merge_dicts, round_coin_value, round_dict, safe_value_fallback2
|
from freqtrade.misc import deep_merge_dicts, round_coin_value, round_dict, safe_value_fallback2
|
||||||
|
@ -45,7 +45,7 @@ class HyperoptStateContainer():
|
||||||
class HyperoptTools():
|
class HyperoptTools():
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_strategy_filename(config: Dict, strategy_name: str) -> Optional[Path]:
|
def get_strategy_filename(config: Config, strategy_name: str) -> Optional[Path]:
|
||||||
"""
|
"""
|
||||||
Get Strategy-location (filename) from strategy_name
|
Get Strategy-location (filename) from strategy_name
|
||||||
"""
|
"""
|
||||||
|
@ -81,7 +81,7 @@ class HyperoptTools():
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def try_export_params(config: Dict[str, Any], strategy_name: str, params: Dict):
|
def try_export_params(config: Config, strategy_name: str, params: Dict):
|
||||||
if params.get(FTHYPT_FILEVERSION, 1) >= 2 and not config.get('disableparamexport', False):
|
if params.get(FTHYPT_FILEVERSION, 1) >= 2 and not config.get('disableparamexport', False):
|
||||||
# Export parameters ...
|
# Export parameters ...
|
||||||
fn = HyperoptTools.get_strategy_filename(config, strategy_name)
|
fn = HyperoptTools.get_strategy_filename(config, strategy_name)
|
||||||
|
@ -91,7 +91,7 @@ class HyperoptTools():
|
||||||
logger.warning("Strategy not found, not exporting parameter file.")
|
logger.warning("Strategy not found, not exporting parameter file.")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def has_space(config: Dict[str, Any], space: str) -> bool:
|
def has_space(config: Config, space: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Tell if the space value is contained in the configuration
|
Tell if the space value is contained in the configuration
|
||||||
"""
|
"""
|
||||||
|
@ -131,7 +131,7 @@ class HyperoptTools():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_filtered_results(results_file: Path, config: Dict[str, Any]) -> Tuple[List, int]:
|
def load_filtered_results(results_file: Path, config: Config) -> Tuple[List, int]:
|
||||||
filteroptions = {
|
filteroptions = {
|
||||||
'only_best': config.get('hyperopt_list_best', False),
|
'only_best': config.get('hyperopt_list_best', False),
|
||||||
'only_profitable': config.get('hyperopt_list_profitable', False),
|
'only_profitable': config.get('hyperopt_list_profitable', False),
|
||||||
|
@ -346,7 +346,7 @@ class HyperoptTools():
|
||||||
return trials
|
return trials
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_result_table(config: dict, results: list, total_epochs: int, highlight_best: bool,
|
def get_result_table(config: Config, results: list, total_epochs: int, highlight_best: bool,
|
||||||
print_colorized: bool, remove_header: int) -> str:
|
print_colorized: bool, remove_header: int) -> str:
|
||||||
"""
|
"""
|
||||||
Log result table
|
Log result table
|
||||||
|
@ -444,7 +444,7 @@ class HyperoptTools():
|
||||||
return table
|
return table
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def export_csv_file(config: dict, results: list, csv_file: str) -> None:
|
def export_csv_file(config: Config, results: list, csv_file: str) -> None:
|
||||||
"""
|
"""
|
||||||
Log result to csv-file
|
Log result to csv-file
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -7,7 +7,8 @@ from typing import Any, Dict, List, Union
|
||||||
from pandas import DataFrame, to_datetime
|
from pandas import DataFrame, to_datetime
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN, UNLIMITED_STAKE_AMOUNT
|
from freqtrade.constants import (DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN, UNLIMITED_STAKE_AMOUNT,
|
||||||
|
Config)
|
||||||
from freqtrade.data.metrics import (calculate_cagr, calculate_csum, calculate_market_change,
|
from freqtrade.data.metrics import (calculate_cagr, calculate_csum, calculate_market_change,
|
||||||
calculate_max_drawdown)
|
calculate_max_drawdown)
|
||||||
from freqtrade.misc import decimals_per_coin, file_dump_joblib, file_dump_json, round_coin_value
|
from freqtrade.misc import decimals_per_coin, file_dump_joblib, file_dump_json, round_coin_value
|
||||||
|
@ -898,7 +899,7 @@ def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency:
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
def show_backtest_results(config: Dict, backtest_stats: Dict):
|
def show_backtest_results(config: Config, backtest_stats: Dict):
|
||||||
stake_currency = config['stake_currency']
|
stake_currency = config['stake_currency']
|
||||||
|
|
||||||
for strategy, results in backtest_stats['strategy'].items():
|
for strategy, results in backtest_stats['strategy'].items():
|
||||||
|
@ -918,7 +919,7 @@ def show_backtest_results(config: Dict, backtest_stats: Dict):
|
||||||
print('\nFor more details, please look at the detail tables above')
|
print('\nFor more details, please look at the detail tables above')
|
||||||
|
|
||||||
|
|
||||||
def show_sorted_pairlist(config: Dict, backtest_stats: Dict):
|
def show_sorted_pairlist(config: Config, backtest_stats: Dict):
|
||||||
if config.get('backtest_show_pair_list', False):
|
if config.get('backtest_show_pair_list', False):
|
||||||
for strategy, results in backtest_stats['strategy'].items():
|
for strategy, results in backtest_stats['strategy'].items():
|
||||||
print(f"Pairs for Strategy {strategy}: \n[")
|
print(f"Pairs for Strategy {strategy}: \n[")
|
||||||
|
|
|
@ -83,7 +83,7 @@ class Order(_DECL_BASE):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def safe_price(self) -> float:
|
def safe_price(self) -> float:
|
||||||
return self.average or self.price
|
return self.average or self.price or self.stop_price
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def safe_filled(self) -> float:
|
def safe_filled(self) -> float:
|
||||||
|
@ -376,6 +376,12 @@ class LocalTrade():
|
||||||
def open_date_utc(self):
|
def open_date_utc(self):
|
||||||
return self.open_date.replace(tzinfo=timezone.utc)
|
return self.open_date.replace(tzinfo=timezone.utc)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stoploss_last_update_utc(self):
|
||||||
|
if self.stoploss_last_update:
|
||||||
|
return self.stoploss_last_update.replace(tzinfo=timezone.utc)
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def close_date_utc(self):
|
def close_date_utc(self):
|
||||||
return self.close_date.replace(tzinfo=timezone.utc)
|
return self.close_date.replace(tzinfo=timezone.utc)
|
||||||
|
@ -560,7 +566,6 @@ class LocalTrade():
|
||||||
self.stop_loss = stop_loss_norm
|
self.stop_loss = stop_loss_norm
|
||||||
|
|
||||||
self.stop_loss_pct = -1 * abs(percent)
|
self.stop_loss_pct = -1 * abs(percent)
|
||||||
self.stoploss_last_update = datetime.utcnow()
|
|
||||||
|
|
||||||
def adjust_stop_loss(self, current_price: float, stoploss: float,
|
def adjust_stop_loss(self, current_price: float, stoploss: float,
|
||||||
initial: bool = False, refresh: bool = False) -> None:
|
initial: bool = False, refresh: bool = False) -> None:
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.data.btanalysis import (analyze_trade_parallelism, extract_trades_of_period,
|
from freqtrade.data.btanalysis import (analyze_trade_parallelism, extract_trades_of_period,
|
||||||
load_trades)
|
load_trades)
|
||||||
from freqtrade.data.converter import trim_dataframe
|
from freqtrade.data.converter import trim_dataframe
|
||||||
|
@ -618,7 +619,7 @@ def store_plot_file(fig, filename: str, directory: Path, auto_open: bool = False
|
||||||
logger.info(f"Stored plot as {_filename}")
|
logger.info(f"Stored plot as {_filename}")
|
||||||
|
|
||||||
|
|
||||||
def load_and_plot_trades(config: Dict[str, Any]):
|
def load_and_plot_trades(config: Config):
|
||||||
"""
|
"""
|
||||||
From configuration provided
|
From configuration provided
|
||||||
- Initializes plot-script
|
- Initializes plot-script
|
||||||
|
@ -666,7 +667,7 @@ def load_and_plot_trades(config: Dict[str, Any]):
|
||||||
logger.info('End of plotting process. %s plots generated', pair_counter)
|
logger.info('End of plotting process. %s plots generated', pair_counter)
|
||||||
|
|
||||||
|
|
||||||
def plot_profit(config: Dict[str, Any]) -> None:
|
def plot_profit(config: Config) -> None:
|
||||||
"""
|
"""
|
||||||
Plots the total profit for all pairs.
|
Plots the total profit for all pairs.
|
||||||
Note, the profit calculation isn't realistic.
|
Note, the profit calculation isn't realistic.
|
||||||
|
|
|
@ -8,7 +8,7 @@ from typing import Any, Dict, List, Optional
|
||||||
import arrow
|
import arrow
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.constants import ListPairsWithTimeframes
|
from freqtrade.constants import Config, ListPairsWithTimeframes
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.misc import plural
|
from freqtrade.misc import plural
|
||||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||||
|
@ -21,7 +21,7 @@ logger = logging.getLogger(__name__)
|
||||||
class AgeFilter(IPairList):
|
class AgeFilter(IPairList):
|
||||||
|
|
||||||
def __init__(self, exchange, pairlistmanager,
|
def __init__(self, exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Config, pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from abc import ABC, abstractmethod, abstractproperty
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import Exchange, market_is_active
|
from freqtrade.exchange import Exchange, market_is_active
|
||||||
from freqtrade.mixins import LoggingMixin
|
from freqtrade.mixins import LoggingMixin
|
||||||
|
@ -17,7 +18,7 @@ logger = logging.getLogger(__name__)
|
||||||
class IPairList(LoggingMixin, ABC):
|
class IPairList(LoggingMixin, ABC):
|
||||||
|
|
||||||
def __init__(self, exchange: Exchange, pairlistmanager,
|
def __init__(self, exchange: Exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Config, pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
"""
|
"""
|
||||||
:param exchange: Exchange instance
|
:param exchange: Exchange instance
|
||||||
|
|
|
@ -4,6 +4,7 @@ Offset pair list filter
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ logger = logging.getLogger(__name__)
|
||||||
class OffsetFilter(IPairList):
|
class OffsetFilter(IPairList):
|
||||||
|
|
||||||
def __init__(self, exchange, pairlistmanager,
|
def __init__(self, exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Config, pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from typing import Any, Dict, List
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ logger = logging.getLogger(__name__)
|
||||||
class PerformanceFilter(IPairList):
|
class PerformanceFilter(IPairList):
|
||||||
|
|
||||||
def __init__(self, exchange, pairlistmanager,
|
def __init__(self, exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Config, pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ Precision pair list filter
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ logger = logging.getLogger(__name__)
|
||||||
class PrecisionFilter(IPairList):
|
class PrecisionFilter(IPairList):
|
||||||
|
|
||||||
def __init__(self, exchange, pairlistmanager,
|
def __init__(self, exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Config, pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ Price pair list filter
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ logger = logging.getLogger(__name__)
|
||||||
class PriceFilter(IPairList):
|
class PriceFilter(IPairList):
|
||||||
|
|
||||||
def __init__(self, exchange, pairlistmanager,
|
def __init__(self, exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Config, pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import logging
|
||||||
import random
|
import random
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.enums import RunMode
|
from freqtrade.enums import RunMode
|
||||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ logger = logging.getLogger(__name__)
|
||||||
class ShuffleFilter(IPairList):
|
class ShuffleFilter(IPairList):
|
||||||
|
|
||||||
def __init__(self, exchange, pairlistmanager,
|
def __init__(self, exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Config, pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ Spread pair list filter
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ logger = logging.getLogger(__name__)
|
||||||
class SpreadFilter(IPairList):
|
class SpreadFilter(IPairList):
|
||||||
|
|
||||||
def __init__(self, exchange, pairlistmanager,
|
def __init__(self, exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Config, pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import logging
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ logger = logging.getLogger(__name__)
|
||||||
class StaticPairList(IPairList):
|
class StaticPairList(IPairList):
|
||||||
|
|
||||||
def __init__(self, exchange, pairlistmanager,
|
def __init__(self, exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Config, pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import numpy as np
|
||||||
from cachetools import TTLCache
|
from cachetools import TTLCache
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.constants import ListPairsWithTimeframes
|
from freqtrade.constants import Config, ListPairsWithTimeframes
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.misc import plural
|
from freqtrade.misc import plural
|
||||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||||
|
@ -26,7 +26,7 @@ class VolatilityFilter(IPairList):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, exchange, pairlistmanager,
|
def __init__(self, exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Config, pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ from typing import Any, Dict, List
|
||||||
|
|
||||||
from cachetools import TTLCache
|
from cachetools import TTLCache
|
||||||
|
|
||||||
from freqtrade.constants import ListPairsWithTimeframes
|
from freqtrade.constants import Config, ListPairsWithTimeframes
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_prev_date
|
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_prev_date
|
||||||
from freqtrade.misc import format_ms_time
|
from freqtrade.misc import format_ms_time
|
||||||
|
@ -25,7 +25,7 @@ SORT_VALUES = ['quoteVolume']
|
||||||
class VolumePairList(IPairList):
|
class VolumePairList(IPairList):
|
||||||
|
|
||||||
def __init__(self, exchange, pairlistmanager,
|
def __init__(self, exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Config, pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import re
|
import re
|
||||||
from typing import Any, Dict, List
|
from typing import List
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
|
|
||||||
|
|
||||||
def expand_pairlist(wildcardpl: List[str], available_pairs: List[str],
|
def expand_pairlist(wildcardpl: List[str], available_pairs: List[str],
|
||||||
|
@ -42,7 +44,7 @@ def expand_pairlist(wildcardpl: List[str], available_pairs: List[str],
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def dynamic_expand_pairlist(config: Dict[str, Any], markets: List[str]) -> List[str]:
|
def dynamic_expand_pairlist(config: Config, markets: List[str]) -> List[str]:
|
||||||
expanded_pairs = expand_pairlist(config['pairs'], markets)
|
expanded_pairs = expand_pairlist(config['pairs'], markets)
|
||||||
if config.get('freqai', {}).get('enabled', False):
|
if config.get('freqai', {}).get('enabled', False):
|
||||||
corr_pairlist = config['freqai']['feature_parameters']['include_corr_pairlist']
|
corr_pairlist = config['freqai']['feature_parameters']['include_corr_pairlist']
|
||||||
|
|
|
@ -9,7 +9,7 @@ import arrow
|
||||||
from cachetools import TTLCache
|
from cachetools import TTLCache
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.constants import ListPairsWithTimeframes
|
from freqtrade.constants import Config, ListPairsWithTimeframes
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.misc import plural
|
from freqtrade.misc import plural
|
||||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||||
|
@ -21,7 +21,7 @@ logger = logging.getLogger(__name__)
|
||||||
class RangeStabilityFilter(IPairList):
|
class RangeStabilityFilter(IPairList):
|
||||||
|
|
||||||
def __init__(self, exchange, pairlistmanager,
|
def __init__(self, exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Config, pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ from typing import Dict, List
|
||||||
|
|
||||||
from cachetools import TTLCache, cached
|
from cachetools import TTLCache, cached
|
||||||
|
|
||||||
from freqtrade.constants import ListPairsWithTimeframes
|
from freqtrade.constants import Config, ListPairsWithTimeframes
|
||||||
from freqtrade.enums import CandleType
|
from freqtrade.enums import CandleType
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.mixins import LoggingMixin
|
from freqtrade.mixins import LoggingMixin
|
||||||
|
@ -21,7 +21,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class PairListManager(LoggingMixin):
|
class PairListManager(LoggingMixin):
|
||||||
|
|
||||||
def __init__(self, exchange, config: dict) -> None:
|
def __init__(self, exchange, config: Config) -> None:
|
||||||
self._exchange = exchange
|
self._exchange = exchange
|
||||||
self._config = config
|
self._config = config
|
||||||
self._whitelist = self._config['exchange'].get('pair_whitelist')
|
self._whitelist = self._config['exchange'].get('pair_whitelist')
|
||||||
|
|
|
@ -5,7 +5,7 @@ import logging
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from freqtrade.constants import LongShort
|
from freqtrade.constants import Config, LongShort
|
||||||
from freqtrade.persistence import PairLocks
|
from freqtrade.persistence import PairLocks
|
||||||
from freqtrade.persistence.models import PairLock
|
from freqtrade.persistence.models import PairLock
|
||||||
from freqtrade.plugins.protections import IProtection
|
from freqtrade.plugins.protections import IProtection
|
||||||
|
@ -17,7 +17,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class ProtectionManager():
|
class ProtectionManager():
|
||||||
|
|
||||||
def __init__(self, config: Dict, protections: List) -> None:
|
def __init__(self, config: Config, protections: List) -> None:
|
||||||
self._config = config
|
self._config = config
|
||||||
|
|
||||||
self._protection_handlers: List[IProtection] = []
|
self._protection_handlers: List[IProtection] = []
|
||||||
|
|
|
@ -5,7 +5,7 @@ from dataclasses import dataclass
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from freqtrade.constants import LongShort
|
from freqtrade.constants import Config, LongShort
|
||||||
from freqtrade.exchange import timeframe_to_minutes
|
from freqtrade.exchange import timeframe_to_minutes
|
||||||
from freqtrade.misc import plural
|
from freqtrade.misc import plural
|
||||||
from freqtrade.mixins import LoggingMixin
|
from freqtrade.mixins import LoggingMixin
|
||||||
|
@ -30,7 +30,7 @@ class IProtection(LoggingMixin, ABC):
|
||||||
# Can stop trading for one pair
|
# Can stop trading for one pair
|
||||||
has_local_stop: bool = False
|
has_local_stop: bool = False
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any], protection_config: Dict[str, Any]) -> None:
|
def __init__(self, config: Config, protection_config: Dict[str, Any]) -> None:
|
||||||
self._config = config
|
self._config = config
|
||||||
self._protection_config = protection_config
|
self._protection_config = protection_config
|
||||||
self._stop_duration_candles: Optional[int] = None
|
self._stop_duration_candles: Optional[int] = None
|
||||||
|
|
|
@ -3,7 +3,7 @@ import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
from freqtrade.constants import LongShort
|
from freqtrade.constants import Config, LongShort
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.plugins.protections import IProtection, ProtectionReturn
|
from freqtrade.plugins.protections import IProtection, ProtectionReturn
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ class LowProfitPairs(IProtection):
|
||||||
has_global_stop: bool = False
|
has_global_stop: bool = False
|
||||||
has_local_stop: bool = True
|
has_local_stop: bool = True
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any], protection_config: Dict[str, Any]) -> None:
|
def __init__(self, config: Config, protection_config: Dict[str, Any]) -> None:
|
||||||
super().__init__(config, protection_config)
|
super().__init__(config, protection_config)
|
||||||
|
|
||||||
self._trade_limit = protection_config.get('trade_limit', 1)
|
self._trade_limit = protection_config.get('trade_limit', 1)
|
||||||
|
|
|
@ -5,7 +5,7 @@ from typing import Any, Dict, Optional
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
from freqtrade.constants import LongShort
|
from freqtrade.constants import Config, LongShort
|
||||||
from freqtrade.data.metrics import calculate_max_drawdown
|
from freqtrade.data.metrics import calculate_max_drawdown
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.plugins.protections import IProtection, ProtectionReturn
|
from freqtrade.plugins.protections import IProtection, ProtectionReturn
|
||||||
|
@ -19,7 +19,7 @@ class MaxDrawdown(IProtection):
|
||||||
has_global_stop: bool = True
|
has_global_stop: bool = True
|
||||||
has_local_stop: bool = False
|
has_local_stop: bool = False
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any], protection_config: Dict[str, Any]) -> None:
|
def __init__(self, config: Config, protection_config: Dict[str, Any]) -> None:
|
||||||
super().__init__(config, protection_config)
|
super().__init__(config, protection_config)
|
||||||
|
|
||||||
self._trade_limit = protection_config.get('trade_limit', 1)
|
self._trade_limit = protection_config.get('trade_limit', 1)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
from freqtrade.constants import LongShort
|
from freqtrade.constants import Config, LongShort
|
||||||
from freqtrade.enums import ExitType
|
from freqtrade.enums import ExitType
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.plugins.protections import IProtection, ProtectionReturn
|
from freqtrade.plugins.protections import IProtection, ProtectionReturn
|
||||||
|
@ -17,7 +17,7 @@ class StoplossGuard(IProtection):
|
||||||
has_global_stop: bool = True
|
has_global_stop: bool = True
|
||||||
has_local_stop: bool = True
|
has_local_stop: bool = True
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any], protection_config: Dict[str, Any]) -> None:
|
def __init__(self, config: Config, protection_config: Dict[str, Any]) -> None:
|
||||||
super().__init__(config, protection_config)
|
super().__init__(config, protection_config)
|
||||||
|
|
||||||
self._trade_limit = protection_config.get('trade_limit', 10)
|
self._trade_limit = protection_config.get('trade_limit', 10)
|
||||||
|
|
|
@ -4,6 +4,7 @@ This module loads custom exchanges
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import freqtrade.exchange as exchanges
|
import freqtrade.exchange as exchanges
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exchange import MAP_EXCHANGE_CHILDCLASS, Exchange
|
from freqtrade.exchange import MAP_EXCHANGE_CHILDCLASS, Exchange
|
||||||
from freqtrade.resolvers import IResolver
|
from freqtrade.resolvers import IResolver
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ class ExchangeResolver(IResolver):
|
||||||
object_type = Exchange
|
object_type = Exchange
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_exchange(exchange_name: str, config: dict, validate: bool = True,
|
def load_exchange(exchange_name: str, config: Config, validate: bool = True,
|
||||||
load_leverage_tiers: bool = False) -> Exchange:
|
load_leverage_tiers: bool = False) -> Exchange:
|
||||||
"""
|
"""
|
||||||
Load the custom class from config parameter
|
Load the custom class from config parameter
|
||||||
|
|
|
@ -5,9 +5,8 @@ This module load a custom model for freqai
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict
|
|
||||||
|
|
||||||
from freqtrade.constants import USERPATH_FREQAIMODELS
|
from freqtrade.constants import USERPATH_FREQAIMODELS, Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.freqai.freqai_interface import IFreqaiModel
|
from freqtrade.freqai.freqai_interface import IFreqaiModel
|
||||||
from freqtrade.resolvers import IResolver
|
from freqtrade.resolvers import IResolver
|
||||||
|
@ -29,7 +28,7 @@ class FreqaiModelResolver(IResolver):
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_freqaimodel(config: Dict) -> IFreqaiModel:
|
def load_freqaimodel(config: Config) -> IFreqaiModel:
|
||||||
"""
|
"""
|
||||||
Load the custom class from config parameter
|
Load the custom class from config parameter
|
||||||
:param config: configuration dictionary
|
:param config: configuration dictionary
|
||||||
|
|
|
@ -5,9 +5,8 @@ This module load custom hyperopt
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict
|
|
||||||
|
|
||||||
from freqtrade.constants import HYPEROPT_LOSS_BUILTIN, USERPATH_HYPEROPTS
|
from freqtrade.constants import HYPEROPT_LOSS_BUILTIN, USERPATH_HYPEROPTS, Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
|
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
|
||||||
from freqtrade.resolvers import IResolver
|
from freqtrade.resolvers import IResolver
|
||||||
|
@ -26,7 +25,7 @@ class HyperOptLossResolver(IResolver):
|
||||||
initial_search_path = Path(__file__).parent.parent.joinpath('optimize/hyperopt_loss').resolve()
|
initial_search_path = Path(__file__).parent.parent.joinpath('optimize/hyperopt_loss').resolve()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_hyperoptloss(config: Dict) -> IHyperOptLoss:
|
def load_hyperoptloss(config: Config) -> IHyperOptLoss:
|
||||||
"""
|
"""
|
||||||
Load the custom class from config parameter
|
Load the custom class from config parameter
|
||||||
:param config: configuration dictionary
|
:param config: configuration dictionary
|
||||||
|
|
|
@ -10,6 +10,7 @@ import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Iterator, List, Optional, Tuple, Type, Union
|
from typing import Any, Dict, Iterator, List, Optional, Tuple, Type, Union
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,7 +44,7 @@ class IResolver:
|
||||||
initial_search_path: Optional[Path]
|
initial_search_path: Optional[Path]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def build_search_paths(cls, config: Dict[str, Any], user_subdir: Optional[str] = None,
|
def build_search_paths(cls, config: Config, user_subdir: Optional[str] = None,
|
||||||
extra_dirs: List[str] = []) -> List[Path]:
|
extra_dirs: List[str] = []) -> List[Path]:
|
||||||
|
|
||||||
abs_paths: List[Path] = []
|
abs_paths: List[Path] = []
|
||||||
|
@ -153,7 +154,7 @@ class IResolver:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_object(cls, object_name: str, config: dict, *, kwargs: dict,
|
def load_object(cls, object_name: str, config: Config, *, kwargs: dict,
|
||||||
extra_dir: Optional[str] = None) -> Any:
|
extra_dir: Optional[str] = None) -> Any:
|
||||||
"""
|
"""
|
||||||
Search and loads the specified object as configured in hte child class.
|
Search and loads the specified object as configured in hte child class.
|
||||||
|
|
|
@ -6,6 +6,7 @@ This module load custom pairlists
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||||
from freqtrade.resolvers import IResolver
|
from freqtrade.resolvers import IResolver
|
||||||
|
|
||||||
|
@ -24,7 +25,7 @@ class PairListResolver(IResolver):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_pairlist(pairlist_name: str, exchange, pairlistmanager,
|
def load_pairlist(pairlist_name: str, exchange, pairlistmanager,
|
||||||
config: dict, pairlistconfig: dict, pairlist_pos: int) -> IPairList:
|
config: Config, pairlistconfig: dict, pairlist_pos: int) -> IPairList:
|
||||||
"""
|
"""
|
||||||
Load the pairlist with pairlist_name
|
Load the pairlist with pairlist_name
|
||||||
:param pairlist_name: Classname of the pairlist
|
:param pairlist_name: Classname of the pairlist
|
||||||
|
|
|
@ -5,6 +5,7 @@ import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.plugins.protections import IProtection
|
from freqtrade.plugins.protections import IProtection
|
||||||
from freqtrade.resolvers import IResolver
|
from freqtrade.resolvers import IResolver
|
||||||
|
|
||||||
|
@ -22,7 +23,8 @@ class ProtectionResolver(IResolver):
|
||||||
initial_search_path = Path(__file__).parent.parent.joinpath('plugins/protections').resolve()
|
initial_search_path = Path(__file__).parent.parent.joinpath('plugins/protections').resolve()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_protection(protection_name: str, config: Dict, protection_config: Dict) -> IProtection:
|
def load_protection(protection_name: str, config: Config,
|
||||||
|
protection_config: Dict) -> IProtection:
|
||||||
"""
|
"""
|
||||||
Load the protection with protection_name
|
Load the protection with protection_name
|
||||||
:param protection_name: Classname of the pairlist
|
:param protection_name: Classname of the pairlist
|
||||||
|
|
|
@ -9,10 +9,10 @@ from base64 import urlsafe_b64decode
|
||||||
from inspect import getfullargspec
|
from inspect import getfullargspec
|
||||||
from os import walk
|
from os import walk
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
from freqtrade.configuration.config_validation import validate_migrated_strategy_settings
|
from freqtrade.configuration.config_validation import validate_migrated_strategy_settings
|
||||||
from freqtrade.constants import REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES, USERPATH_STRATEGIES
|
from freqtrade.constants import REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES, USERPATH_STRATEGIES, Config
|
||||||
from freqtrade.enums import TradingMode
|
from freqtrade.enums import TradingMode
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.resolvers import IResolver
|
from freqtrade.resolvers import IResolver
|
||||||
|
@ -32,7 +32,7 @@ class StrategyResolver(IResolver):
|
||||||
initial_search_path = None
|
initial_search_path = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_strategy(config: Dict[str, Any] = None) -> IStrategy:
|
def load_strategy(config: Config = None) -> IStrategy:
|
||||||
"""
|
"""
|
||||||
Load the custom class from config parameter
|
Load the custom class from config parameter
|
||||||
:param config: configuration dictionary or None
|
:param config: configuration dictionary or None
|
||||||
|
@ -91,8 +91,7 @@ class StrategyResolver(IResolver):
|
||||||
return strategy
|
return strategy
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _override_attribute_helper(strategy, config: Dict[str, Any],
|
def _override_attribute_helper(strategy, config: Config, attribute: str, default: Any):
|
||||||
attribute: str, default: Any):
|
|
||||||
"""
|
"""
|
||||||
Override attributes in the strategy.
|
Override attributes in the strategy.
|
||||||
Prevalence:
|
Prevalence:
|
||||||
|
@ -215,7 +214,7 @@ class StrategyResolver(IResolver):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _load_strategy(strategy_name: str,
|
def _load_strategy(strategy_name: str,
|
||||||
config: dict, extra_dir: Optional[str] = None) -> IStrategy:
|
config: Config, extra_dir: Optional[str] = None) -> IStrategy:
|
||||||
"""
|
"""
|
||||||
Search and loads the specified strategy.
|
Search and loads the specified strategy.
|
||||||
:param strategy_name: name of the module to import
|
:param strategy_name: name of the module to import
|
||||||
|
|
|
@ -12,6 +12,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||||
from janus import Queue as ThreadedQueue
|
from janus import Queue as ThreadedQueue
|
||||||
from starlette.responses import JSONResponse
|
from starlette.responses import JSONResponse
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.rpc.api_server.uvicorn_threaded import UvicornServer
|
from freqtrade.rpc.api_server.uvicorn_threaded import UvicornServer
|
||||||
from freqtrade.rpc.api_server.ws import ChannelManager
|
from freqtrade.rpc.api_server.ws import ChannelManager
|
||||||
|
@ -42,10 +43,10 @@ class ApiServer(RPCHandler):
|
||||||
_bt = None
|
_bt = None
|
||||||
_bt_data = None
|
_bt_data = None
|
||||||
_bt_timerange = None
|
_bt_timerange = None
|
||||||
_bt_last_config: Dict[str, Any] = {}
|
_bt_last_config: Config = {}
|
||||||
_has_rpc: bool = False
|
_has_rpc: bool = False
|
||||||
_bgtask_running: bool = False
|
_bgtask_running: bool = False
|
||||||
_config: Dict[str, Any] = {}
|
_config: Config = {}
|
||||||
# Exchange - only available in webserver mode.
|
# Exchange - only available in webserver mode.
|
||||||
_exchange = None
|
_exchange = None
|
||||||
# websocket message queue stuff
|
# websocket message queue stuff
|
||||||
|
@ -63,7 +64,7 @@ class ApiServer(RPCHandler):
|
||||||
ApiServer.__initialized = False
|
ApiServer.__initialized = False
|
||||||
return ApiServer.__instance
|
return ApiServer.__instance
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any], standalone: bool = False) -> None:
|
def __init__(self, config: Config, standalone: bool = False) -> None:
|
||||||
ApiServer._config = config
|
ApiServer._config = config
|
||||||
if self.__initialized and (standalone or self._standalone):
|
if self.__initialized and (standalone or self._standalone):
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.enums import RPCMessageType
|
from freqtrade.enums import RPCMessageType
|
||||||
from freqtrade.rpc import RPC
|
from freqtrade.rpc import RPC
|
||||||
from freqtrade.rpc.webhook import Webhook
|
from freqtrade.rpc.webhook import Webhook
|
||||||
|
@ -10,7 +10,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Discord(Webhook):
|
class Discord(Webhook):
|
||||||
def __init__(self, rpc: 'RPC', config: Dict[str, Any]):
|
def __init__(self, rpc: 'RPC', config: Config):
|
||||||
# super().__init__(rpc, config)
|
# super().__init__(rpc, config)
|
||||||
self.rpc = rpc
|
self.rpc = rpc
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
|
@ -16,7 +16,7 @@ from pandas import DataFrame, NaT
|
||||||
|
|
||||||
from freqtrade import __version__
|
from freqtrade import __version__
|
||||||
from freqtrade.configuration.timerange import TimeRange
|
from freqtrade.configuration.timerange import TimeRange
|
||||||
from freqtrade.constants import CANCEL_REASON, DATETIME_PRINT_FORMAT
|
from freqtrade.constants import CANCEL_REASON, DATETIME_PRINT_FORMAT, Config
|
||||||
from freqtrade.data.history import load_data
|
from freqtrade.data.history import load_data
|
||||||
from freqtrade.data.metrics import calculate_max_drawdown
|
from freqtrade.data.metrics import calculate_max_drawdown
|
||||||
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, SignalDirection, State,
|
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, SignalDirection, State,
|
||||||
|
@ -58,7 +58,7 @@ class RPCException(Exception):
|
||||||
|
|
||||||
class RPCHandler:
|
class RPCHandler:
|
||||||
|
|
||||||
def __init__(self, rpc: 'RPC', config: Dict[str, Any]) -> None:
|
def __init__(self, rpc: 'RPC', config: Config) -> None:
|
||||||
"""
|
"""
|
||||||
Initializes RPCHandlers
|
Initializes RPCHandlers
|
||||||
:param rpc: instance of RPC Helper class
|
:param rpc: instance of RPC Helper class
|
||||||
|
@ -66,7 +66,7 @@ class RPCHandler:
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self._rpc = rpc
|
self._rpc = rpc
|
||||||
self._config: Dict[str, Any] = config
|
self._config: Config = config
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
@ -96,7 +96,7 @@ class RPC:
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self._freqtrade = freqtrade
|
self._freqtrade = freqtrade
|
||||||
self._config: Dict[str, Any] = freqtrade.config
|
self._config: Config = freqtrade.config
|
||||||
if self._config.get('fiat_display_currency'):
|
if self._config.get('fiat_display_currency'):
|
||||||
self._fiat_converter = CryptoToFiatConverter()
|
self._fiat_converter = CryptoToFiatConverter()
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import logging
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.enums import RPCMessageType
|
from freqtrade.enums import RPCMessageType
|
||||||
from freqtrade.rpc import RPC, RPCHandler
|
from freqtrade.rpc import RPC, RPCHandler
|
||||||
|
|
||||||
|
@ -90,7 +91,7 @@ class RPCManager:
|
||||||
'msg': msg,
|
'msg': msg,
|
||||||
})
|
})
|
||||||
|
|
||||||
def startup_messages(self, config: Dict[str, Any], pairlist, protections) -> None:
|
def startup_messages(self, config: Config, pairlist, protections) -> None:
|
||||||
if config['dry_run']:
|
if config['dry_run']:
|
||||||
self.send_msg({
|
self.send_msg({
|
||||||
'type': RPCMessageType.WARNING,
|
'type': RPCMessageType.WARNING,
|
||||||
|
|
|
@ -24,7 +24,7 @@ from telegram.ext import CallbackContext, CallbackQueryHandler, CommandHandler,
|
||||||
from telegram.utils.helpers import escape_markdown
|
from telegram.utils.helpers import escape_markdown
|
||||||
|
|
||||||
from freqtrade.__init__ import __version__
|
from freqtrade.__init__ import __version__
|
||||||
from freqtrade.constants import DUST_PER_COIN
|
from freqtrade.constants import DUST_PER_COIN, Config
|
||||||
from freqtrade.enums import RPCMessageType, SignalDirection, TradingMode
|
from freqtrade.enums import RPCMessageType, SignalDirection, TradingMode
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.misc import chunks, plural, round_coin_value
|
from freqtrade.misc import chunks, plural, round_coin_value
|
||||||
|
@ -88,7 +88,7 @@ def authorized_only(command_handler: Callable[..., None]) -> Callable[..., Any]:
|
||||||
class Telegram(RPCHandler):
|
class Telegram(RPCHandler):
|
||||||
""" This class handles all telegram communication """
|
""" This class handles all telegram communication """
|
||||||
|
|
||||||
def __init__(self, rpc: RPC, config: Dict[str, Any]) -> None:
|
def __init__(self, rpc: RPC, config: Config) -> None:
|
||||||
"""
|
"""
|
||||||
Init the Telegram call, and init the super class RPCHandler
|
Init the Telegram call, and init the super class RPCHandler
|
||||||
:param rpc: instance of RPC Helper class
|
:param rpc: instance of RPC Helper class
|
||||||
|
|
|
@ -7,6 +7,7 @@ from typing import Any, Dict
|
||||||
|
|
||||||
from requests import RequestException, post
|
from requests import RequestException, post
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.enums import RPCMessageType
|
from freqtrade.enums import RPCMessageType
|
||||||
from freqtrade.rpc import RPC, RPCHandler
|
from freqtrade.rpc import RPC, RPCHandler
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ logger.debug('Included module rpc.webhook ...')
|
||||||
class Webhook(RPCHandler):
|
class Webhook(RPCHandler):
|
||||||
""" This class handles all webhook communication """
|
""" This class handles all webhook communication """
|
||||||
|
|
||||||
def __init__(self, rpc: RPC, config: Dict[str, Any]) -> None:
|
def __init__(self, rpc: RPC, config: Config) -> None:
|
||||||
"""
|
"""
|
||||||
Init the Webhook class, and init the super class RPCHandler
|
Init the Webhook class, and init the super class RPCHandler
|
||||||
:param rpc: instance of RPC Helper class
|
:param rpc: instance of RPC Helper class
|
||||||
|
|
|
@ -6,6 +6,7 @@ import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Iterator, List, Tuple, Type, Union
|
from typing import Any, Dict, Iterator, List, Tuple, Type, Union
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.misc import deep_merge_dicts, json_load
|
from freqtrade.misc import deep_merge_dicts, json_load
|
||||||
from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
||||||
|
@ -21,7 +22,7 @@ class HyperStrategyMixin:
|
||||||
strategy logic.
|
strategy logic.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any], *args, **kwargs):
|
def __init__(self, config: Config, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize hyperoptable strategy mixin.
|
Initialize hyperoptable strategy mixin.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -10,7 +10,7 @@ from typing import Dict, List, Optional, Tuple, Union
|
||||||
import arrow
|
import arrow
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.constants import ListPairsWithTimeframes
|
from freqtrade.constants import Config, ListPairsWithTimeframes
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, RunMode, SignalDirection,
|
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, RunMode, SignalDirection,
|
||||||
SignalTagType, SignalType, TradingMode)
|
SignalTagType, SignalType, TradingMode)
|
||||||
|
@ -119,7 +119,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||||
# Definition of plot_config. See plotting documentation for more details.
|
# Definition of plot_config. See plotting documentation for more details.
|
||||||
plot_config: Dict = {}
|
plot_config: Dict = {}
|
||||||
|
|
||||||
def __init__(self, config: dict) -> None:
|
def __init__(self, config: Config) -> None:
|
||||||
self.config = config
|
self.config = config
|
||||||
# Dict to determine if analysis is necessary
|
# Dict to determine if analysis is necessary
|
||||||
self._last_candle_seen_per_pair: Dict[str, datetime] = {}
|
self._last_candle_seen_per_pair: Dict[str, datetime] = {}
|
||||||
|
@ -614,6 +614,22 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||||
# END - Intended to be overridden by strategy
|
# END - Intended to be overridden by strategy
|
||||||
###
|
###
|
||||||
|
|
||||||
|
def __informative_pairs_freqai(self) -> ListPairsWithTimeframes:
|
||||||
|
"""
|
||||||
|
Create informative-pairs needed for FreqAI
|
||||||
|
"""
|
||||||
|
if self.config.get('freqai', {}).get('enabled', False):
|
||||||
|
whitelist_pairs = self.dp.current_whitelist()
|
||||||
|
candle_type = self.config.get('candle_type_def', CandleType.SPOT)
|
||||||
|
corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
|
||||||
|
informative_pairs = []
|
||||||
|
for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]:
|
||||||
|
for pair in set(whitelist_pairs + corr_pairs):
|
||||||
|
informative_pairs.append((pair, tf, candle_type))
|
||||||
|
return informative_pairs
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
def gather_informative_pairs(self) -> ListPairsWithTimeframes:
|
def gather_informative_pairs(self) -> ListPairsWithTimeframes:
|
||||||
"""
|
"""
|
||||||
Internal method which gathers all informative pairs (user or automatically defined).
|
Internal method which gathers all informative pairs (user or automatically defined).
|
||||||
|
@ -638,6 +654,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||||
else:
|
else:
|
||||||
for pair in self.dp.current_whitelist():
|
for pair in self.dp.current_whitelist():
|
||||||
informative_pairs.append((pair, inf_data.timeframe, candle_type))
|
informative_pairs.append((pair, inf_data.timeframe, candle_type))
|
||||||
|
informative_pairs.extend(self.__informative_pairs_freqai())
|
||||||
return list(set(informative_pairs))
|
return list(set(informative_pairs))
|
||||||
|
|
||||||
def get_strategy_name(self) -> str:
|
def get_strategy_name(self) -> str:
|
||||||
|
|
|
@ -45,20 +45,7 @@ class FreqaiExampleStrategy(IStrategy):
|
||||||
std_dev_multiplier_buy = CategoricalParameter(
|
std_dev_multiplier_buy = CategoricalParameter(
|
||||||
[0.75, 1, 1.25, 1.5, 1.75], default=1.25, space="buy", optimize=True)
|
[0.75, 1, 1.25, 1.5, 1.75], default=1.25, space="buy", optimize=True)
|
||||||
std_dev_multiplier_sell = CategoricalParameter(
|
std_dev_multiplier_sell = CategoricalParameter(
|
||||||
[0.1, 0.25, 0.4], space="sell", default=0.2, optimize=True)
|
[0.75, 1, 1.25, 1.5, 1.75], space="sell", default=1.25, optimize=True)
|
||||||
|
|
||||||
def informative_pairs(self):
|
|
||||||
whitelist_pairs = self.dp.current_whitelist()
|
|
||||||
corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
|
|
||||||
informative_pairs = []
|
|
||||||
for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]:
|
|
||||||
for pair in whitelist_pairs:
|
|
||||||
informative_pairs.append((pair, tf))
|
|
||||||
for pair in corr_pairs:
|
|
||||||
if pair in whitelist_pairs:
|
|
||||||
continue # avoid duplication
|
|
||||||
informative_pairs.append((pair, tf))
|
|
||||||
return informative_pairs
|
|
||||||
|
|
||||||
def populate_any_indicators(
|
def populate_any_indicators(
|
||||||
self, pair, df, tf, informative=None, set_generalized_indicators=False
|
self, pair, df, tf, informative=None, set_generalized_indicators=False
|
||||||
|
@ -183,25 +170,31 @@ class FreqaiExampleStrategy(IStrategy):
|
||||||
|
|
||||||
dataframe = self.freqai.start(dataframe, metadata, self)
|
dataframe = self.freqai.start(dataframe, metadata, self)
|
||||||
for val in self.std_dev_multiplier_buy.range:
|
for val in self.std_dev_multiplier_buy.range:
|
||||||
dataframe[f'target_roi_{val}'] = dataframe["&-s_close_mean"] + \
|
dataframe[f'target_roi_{val}'] = (
|
||||||
dataframe["&-s_close_std"] * val
|
dataframe["&-s_close_mean"] + dataframe["&-s_close_std"] * val
|
||||||
|
)
|
||||||
for val in self.std_dev_multiplier_sell.range:
|
for val in self.std_dev_multiplier_sell.range:
|
||||||
dataframe[f'sell_roi_{val}'] = dataframe["&-s_close_mean"] - \
|
dataframe[f'sell_roi_{val}'] = (
|
||||||
dataframe["&-s_close_std"] * val
|
dataframe["&-s_close_mean"] - dataframe["&-s_close_std"] * val
|
||||||
|
)
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
||||||
|
|
||||||
enter_long_conditions = [df["do_predict"] == 1, df["&-s_close"]
|
enter_long_conditions = [
|
||||||
> df[f"target_roi_{self.std_dev_multiplier_buy.value}"]]
|
df["do_predict"] == 1,
|
||||||
|
df["&-s_close"] > df[f"target_roi_{self.std_dev_multiplier_buy.value}"],
|
||||||
|
]
|
||||||
|
|
||||||
if enter_long_conditions:
|
if enter_long_conditions:
|
||||||
df.loc[
|
df.loc[
|
||||||
reduce(lambda x, y: x & y, enter_long_conditions), ["enter_long", "enter_tag"]
|
reduce(lambda x, y: x & y, enter_long_conditions), ["enter_long", "enter_tag"]
|
||||||
] = (1, "long")
|
] = (1, "long")
|
||||||
|
|
||||||
enter_short_conditions = [df["do_predict"] == 1, df["&-s_close"]
|
enter_short_conditions = [
|
||||||
< df[f"sell_roi_{self.std_dev_multiplier_sell.value}"]]
|
df["do_predict"] == 1,
|
||||||
|
df["&-s_close"] < df[f"sell_roi_{self.std_dev_multiplier_sell.value}"],
|
||||||
|
]
|
||||||
|
|
||||||
if enter_short_conditions:
|
if enter_short_conditions:
|
||||||
df.loc[
|
df.loc[
|
||||||
|
@ -211,13 +204,17 @@ class FreqaiExampleStrategy(IStrategy):
|
||||||
return df
|
return df
|
||||||
|
|
||||||
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
||||||
exit_long_conditions = [df["do_predict"] == 1, df["&-s_close"] <
|
exit_long_conditions = [
|
||||||
df[f"sell_roi_{self.std_dev_multiplier_sell.value}"] * 0.25]
|
df["do_predict"] == 1,
|
||||||
|
df["&-s_close"] < df[f"sell_roi_{self.std_dev_multiplier_sell.value}"] * 0.25,
|
||||||
|
]
|
||||||
if exit_long_conditions:
|
if exit_long_conditions:
|
||||||
df.loc[reduce(lambda x, y: x & y, exit_long_conditions), "exit_long"] = 1
|
df.loc[reduce(lambda x, y: x & y, exit_long_conditions), "exit_long"] = 1
|
||||||
|
|
||||||
exit_short_conditions = [df["do_predict"] == 1, df["&-s_close"] >
|
exit_short_conditions = [
|
||||||
df[f"target_roi_{self.std_dev_multiplier_buy.value}"] * 0.25]
|
df["do_predict"] == 1,
|
||||||
|
df["&-s_close"] > df[f"target_roi_{self.std_dev_multiplier_buy.value}"] * 0.25,
|
||||||
|
]
|
||||||
if exit_short_conditions:
|
if exit_short_conditions:
|
||||||
df.loc[reduce(lambda x, y: x & y, exit_short_conditions), "exit_short"] = 1
|
df.loc[reduce(lambda x, y: x & y, exit_short_conditions), "exit_short"] = 1
|
||||||
|
|
||||||
|
|
|
@ -95,20 +95,6 @@ class FreqaiExampleHybridStrategy(IStrategy):
|
||||||
short_rsi = IntParameter(low=51, high=100, default=70, space='sell', optimize=True, load=True)
|
short_rsi = IntParameter(low=51, high=100, default=70, space='sell', optimize=True, load=True)
|
||||||
exit_short_rsi = IntParameter(low=1, high=50, default=30, space='buy', optimize=True, load=True)
|
exit_short_rsi = IntParameter(low=1, high=50, default=30, space='buy', optimize=True, load=True)
|
||||||
|
|
||||||
# FreqAI required function, leave as is or add additional informatives to existing structure.
|
|
||||||
def informative_pairs(self):
|
|
||||||
whitelist_pairs = self.dp.current_whitelist()
|
|
||||||
corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
|
|
||||||
informative_pairs = []
|
|
||||||
for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]:
|
|
||||||
for pair in whitelist_pairs:
|
|
||||||
informative_pairs.append((pair, tf))
|
|
||||||
for pair in corr_pairs:
|
|
||||||
if pair in whitelist_pairs:
|
|
||||||
continue # avoid duplication
|
|
||||||
informative_pairs.append((pair, tf))
|
|
||||||
return informative_pairs
|
|
||||||
|
|
||||||
# FreqAI required function, user can add or remove indicators, but general structure
|
# FreqAI required function, user can add or remove indicators, but general structure
|
||||||
# must stay the same.
|
# must stay the same.
|
||||||
def populate_any_indicators(
|
def populate_any_indicators(
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||||
# flake8: noqa: F401
|
# flake8: noqa: F401
|
||||||
|
# isort: skip_file
|
||||||
# --- Do not remove these libs ---
|
# --- Do not remove these libs ---
|
||||||
import numpy as np # noqa
|
import numpy as np
|
||||||
import pandas as pd # noqa
|
import pandas as pd
|
||||||
from pandas import DataFrame # noqa
|
from pandas import DataFrame
|
||||||
from datetime import datetime # noqa
|
from datetime import datetime
|
||||||
from typing import Optional, Union # noqa
|
from typing import Optional, Union
|
||||||
|
|
||||||
from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter,
|
from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter,
|
||||||
IStrategy, IntParameter)
|
IntParameter, IStrategy, merge_informative_pair)
|
||||||
|
|
||||||
# --------------------------------
|
# --------------------------------
|
||||||
# Add your lib to import here
|
# Add your lib to import here
|
||||||
import talib.abstract as ta
|
import talib.abstract as ta
|
||||||
import pandas_ta as pta
|
import pandas_ta as pta
|
||||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
from technical import qtpylib
|
||||||
|
|
||||||
|
|
||||||
class {{ strategy }}(IStrategy):
|
class {{ strategy }}(IStrategy):
|
||||||
|
|
|
@ -4,6 +4,7 @@ from typing import Dict
|
||||||
|
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.optimize.hyperopt import IHyperOptLoss
|
from freqtrade.optimize.hyperopt import IHyperOptLoss
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ class SampleHyperOptLoss(IHyperOptLoss):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def hyperopt_loss_function(results: DataFrame, trade_count: int,
|
def hyperopt_loss_function(results: DataFrame, trade_count: int,
|
||||||
min_date: datetime, max_date: datetime,
|
min_date: datetime, max_date: datetime,
|
||||||
config: Dict, processed: Dict[str, DataFrame],
|
config: Config, processed: Dict[str, DataFrame],
|
||||||
*args, **kwargs) -> float:
|
*args, **kwargs) -> float:
|
||||||
"""
|
"""
|
||||||
Objective function, returns smaller number for better results
|
Objective function, returns smaller number for better results
|
||||||
|
|
|
@ -7,7 +7,7 @@ from typing import Dict, NamedTuple, Optional
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
|
|
||||||
from freqtrade.constants import UNLIMITED_STAKE_AMOUNT
|
from freqtrade.constants import UNLIMITED_STAKE_AMOUNT, Config
|
||||||
from freqtrade.enums import RunMode, TradingMode
|
from freqtrade.enums import RunMode, TradingMode
|
||||||
from freqtrade.exceptions import DependencyException
|
from freqtrade.exceptions import DependencyException
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
|
@ -35,7 +35,7 @@ class PositionWallet(NamedTuple):
|
||||||
|
|
||||||
class Wallets:
|
class Wallets:
|
||||||
|
|
||||||
def __init__(self, config: dict, exchange: Exchange, log: bool = True) -> None:
|
def __init__(self, config: Config, exchange: Exchange, log: bool = True) -> None:
|
||||||
self._config = config
|
self._config = config
|
||||||
self._log = log
|
self._log = log
|
||||||
self._exchange = exchange
|
self._exchange = exchange
|
||||||
|
|
|
@ -9,8 +9,9 @@ from typing import Any, Callable, Dict, Optional
|
||||||
|
|
||||||
import sdnotify
|
import sdnotify
|
||||||
|
|
||||||
from freqtrade import __version__, constants
|
from freqtrade import __version__
|
||||||
from freqtrade.configuration import Configuration
|
from freqtrade.configuration import Configuration
|
||||||
|
from freqtrade.constants import PROCESS_THROTTLE_SECS, RETRY_TIMEOUT, Config
|
||||||
from freqtrade.enums import State
|
from freqtrade.enums import State
|
||||||
from freqtrade.exceptions import OperationalException, TemporaryError
|
from freqtrade.exceptions import OperationalException, TemporaryError
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
|
@ -24,7 +25,7 @@ class Worker:
|
||||||
Freqtradebot worker class
|
Freqtradebot worker class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, args: Dict[str, Any], config: Dict[str, Any] = None) -> None:
|
def __init__(self, args: Dict[str, Any], config: Config = None) -> None:
|
||||||
"""
|
"""
|
||||||
Init all variables and objects the bot needs to work
|
Init all variables and objects the bot needs to work
|
||||||
"""
|
"""
|
||||||
|
@ -53,7 +54,7 @@ class Worker:
|
||||||
|
|
||||||
internals_config = self._config.get('internals', {})
|
internals_config = self._config.get('internals', {})
|
||||||
self._throttle_secs = internals_config.get('process_throttle_secs',
|
self._throttle_secs = internals_config.get('process_throttle_secs',
|
||||||
constants.PROCESS_THROTTLE_SECS)
|
PROCESS_THROTTLE_SECS)
|
||||||
self._heartbeat_interval = internals_config.get('heartbeat_interval', 60)
|
self._heartbeat_interval = internals_config.get('heartbeat_interval', 60)
|
||||||
|
|
||||||
self._sd_notify = sdnotify.SystemdNotifier() if \
|
self._sd_notify = sdnotify.SystemdNotifier() if \
|
||||||
|
@ -151,8 +152,8 @@ class Worker:
|
||||||
try:
|
try:
|
||||||
self.freqtrade.process()
|
self.freqtrade.process()
|
||||||
except TemporaryError as error:
|
except TemporaryError as error:
|
||||||
logger.warning(f"Error: {error}, retrying in {constants.RETRY_TIMEOUT} seconds...")
|
logger.warning(f"Error: {error}, retrying in {RETRY_TIMEOUT} seconds...")
|
||||||
time.sleep(constants.RETRY_TIMEOUT)
|
time.sleep(RETRY_TIMEOUT)
|
||||||
except OperationalException:
|
except OperationalException:
|
||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
hint = 'Issue `/start` if you think it is safe to restart.'
|
hint = 'Issue `/start` if you think it is safe to restart.'
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
# Required for freqai
|
# Required for freqai
|
||||||
scikit-learn==1.1.2
|
scikit-learn==1.1.2
|
||||||
joblib==1.1.0
|
joblib==1.2.0
|
||||||
catboost==1.0.6; platform_machine != 'aarch64'
|
catboost==1.0.6; platform_machine != 'aarch64'
|
||||||
lightgbm==3.3.2
|
lightgbm==3.3.2
|
||||||
xgboost==1.6.2
|
xgboost==1.6.2
|
||||||
|
|
|
@ -2,7 +2,7 @@ numpy==1.23.3
|
||||||
pandas==1.4.4
|
pandas==1.4.4
|
||||||
pandas-ta==0.3.14b
|
pandas-ta==0.3.14b
|
||||||
|
|
||||||
ccxt==1.93.35
|
ccxt==1.93.66
|
||||||
# Pin cryptography for now due to rust build errors with piwheels
|
# Pin cryptography for now due to rust build errors with piwheels
|
||||||
cryptography==38.0.1
|
cryptography==38.0.1
|
||||||
aiohttp==3.8.1
|
aiohttp==3.8.1
|
||||||
|
@ -20,7 +20,7 @@ pycoingecko==3.0.0
|
||||||
jinja2==3.1.2
|
jinja2==3.1.2
|
||||||
tables==3.7.0
|
tables==3.7.0
|
||||||
blosc==1.10.6
|
blosc==1.10.6
|
||||||
joblib==1.1.0
|
joblib==1.2.0
|
||||||
|
|
||||||
# find first, C search in arrays
|
# find first, C search in arrays
|
||||||
py_find_1st==1.1.5
|
py_find_1st==1.1.5
|
||||||
|
@ -34,9 +34,9 @@ orjson==3.8.0
|
||||||
sdnotify==0.3.2
|
sdnotify==0.3.2
|
||||||
|
|
||||||
# API Server
|
# API Server
|
||||||
fastapi==0.83.0
|
fastapi==0.85.0
|
||||||
uvicorn==0.18.3
|
uvicorn==0.18.3
|
||||||
pyjwt==2.4.0
|
pyjwt==2.5.0
|
||||||
aiofiles==22.1.0
|
aiofiles==22.1.0
|
||||||
psutil==5.9.2
|
psutil==5.9.2
|
||||||
|
|
||||||
|
|
|
@ -2287,7 +2287,7 @@ def tickers():
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def result(testdatadir):
|
def dataframe_1m(testdatadir):
|
||||||
with (testdatadir / 'UNITTEST_BTC-1m.json').open('r') as data_file:
|
with (testdatadir / 'UNITTEST_BTC-1m.json').open('r') as data_file:
|
||||||
return ohlcv_to_dataframe(json.load(data_file), '1m', pair="UNITTEST/BTC",
|
return ohlcv_to_dataframe(json.load(data_file), '1m', pair="UNITTEST/BTC",
|
||||||
fill_missing=True)
|
fill_missing=True)
|
||||||
|
|
|
@ -18,8 +18,8 @@ from tests.conftest import log_has, log_has_re
|
||||||
from tests.data.test_history import _clean_test_file
|
from tests.data.test_history import _clean_test_file
|
||||||
|
|
||||||
|
|
||||||
def test_dataframe_correct_columns(result):
|
def test_dataframe_correct_columns(dataframe_1m):
|
||||||
assert result.columns.tolist() == ['date', 'open', 'high', 'low', 'close', 'volume']
|
assert dataframe_1m.columns.tolist() == ['date', 'open', 'high', 'low', 'close', 'volume']
|
||||||
|
|
||||||
|
|
||||||
def test_ohlcv_to_dataframe(ohlcv_history_list, caplog):
|
def test_ohlcv_to_dataframe(ohlcv_history_list, caplog):
|
||||||
|
|
|
@ -23,7 +23,7 @@ from tests.exchange.test_exchange import ccxt_exceptionhandlers
|
||||||
def test_stoploss_order_binance(default_conf, mocker, limitratio, expected, side, trademode):
|
def test_stoploss_order_binance(default_conf, mocker, limitratio, expected, side, trademode):
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
|
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
|
||||||
order_type = 'stop_loss_limit' if trademode == TradingMode.SPOT else 'stop'
|
order_type = 'stop_loss_limit' if trademode == TradingMode.SPOT else 'limit'
|
||||||
|
|
||||||
api_mock.create_order = MagicMock(return_value={
|
api_mock.create_order = MagicMock(return_value={
|
||||||
'id': order_id,
|
'id': order_id,
|
||||||
|
@ -45,12 +45,15 @@ def test_stoploss_order_binance(default_conf, mocker, limitratio, expected, side
|
||||||
amount=1,
|
amount=1,
|
||||||
stop_price=190,
|
stop_price=190,
|
||||||
side=side,
|
side=side,
|
||||||
order_types={'stoploss_on_exchange_limit_ratio': 1.05},
|
order_types={'stoploss': 'limit', 'stoploss_on_exchange_limit_ratio': 1.05},
|
||||||
leverage=1.0
|
leverage=1.0
|
||||||
)
|
)
|
||||||
|
|
||||||
api_mock.create_order.reset_mock()
|
api_mock.create_order.reset_mock()
|
||||||
order_types = {} if limitratio is None else {'stoploss_on_exchange_limit_ratio': limitratio}
|
order_types = {'stoploss': 'limit'}
|
||||||
|
if limitratio is not None:
|
||||||
|
order_types.update({'stoploss_on_exchange_limit_ratio': limitratio})
|
||||||
|
|
||||||
order = exchange.stoploss(
|
order = exchange.stoploss(
|
||||||
pair='ETH/BTC',
|
pair='ETH/BTC',
|
||||||
amount=1,
|
amount=1,
|
||||||
|
|
|
@ -472,7 +472,7 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets, tmpdir, caplog,
|
||||||
|
|
||||||
api_mock.fetch_market_leverage_tiers.call_count == 0
|
api_mock.fetch_market_leverage_tiers.call_count == 0
|
||||||
# 2 day passes ...
|
# 2 day passes ...
|
||||||
time_machine.move_to(datetime.now() + timedelta(days=2))
|
time_machine.move_to(datetime.now() + timedelta(weeks=5))
|
||||||
exchange.load_leverage_tiers()
|
exchange.load_leverage_tiers()
|
||||||
|
|
||||||
assert log_has(logmsg, caplog)
|
assert log_has(logmsg, caplog)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import pytest
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||||
|
from freqtrade.plugins.pairlistmanager import PairListManager
|
||||||
from tests.conftest import get_patched_exchange, log_has_re
|
from tests.conftest import get_patched_exchange, log_has_re
|
||||||
from tests.freqai.conftest import get_patched_freqai_strategy
|
from tests.freqai.conftest import get_patched_freqai_strategy
|
||||||
|
|
||||||
|
@ -315,3 +316,62 @@ def test_principal_component_analysis(mocker, freqai_conf):
|
||||||
assert Path(freqai.dk.data_path / f"{freqai.dk.model_filename}_pca_object.pkl")
|
assert Path(freqai.dk.data_path / f"{freqai.dk.model_filename}_pca_object.pkl")
|
||||||
|
|
||||||
shutil.rmtree(Path(freqai.dk.full_path))
|
shutil.rmtree(Path(freqai.dk.full_path))
|
||||||
|
|
||||||
|
|
||||||
|
def test_plot_feature_importance(mocker, freqai_conf):
|
||||||
|
|
||||||
|
from freqtrade.freqai.utils import plot_feature_importance
|
||||||
|
|
||||||
|
freqai_conf.update({"timerange": "20180110-20180130"})
|
||||||
|
freqai_conf.get("freqai", {}).get("feature_parameters", {}).update(
|
||||||
|
{"princpial_component_analysis": "true"})
|
||||||
|
|
||||||
|
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||||
|
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||||
|
strategy.dp = DataProvider(freqai_conf, exchange)
|
||||||
|
strategy.freqai_info = freqai_conf.get("freqai", {})
|
||||||
|
freqai = strategy.freqai
|
||||||
|
freqai.live = True
|
||||||
|
freqai.dk = FreqaiDataKitchen(freqai_conf)
|
||||||
|
timerange = TimeRange.parse_timerange("20180110-20180130")
|
||||||
|
freqai.dd.load_all_pair_histories(timerange, freqai.dk)
|
||||||
|
|
||||||
|
freqai.dd.pair_dict = MagicMock()
|
||||||
|
|
||||||
|
data_load_timerange = TimeRange.parse_timerange("20180110-20180130")
|
||||||
|
new_timerange = TimeRange.parse_timerange("20180120-20180130")
|
||||||
|
|
||||||
|
freqai.extract_data_and_train_model(
|
||||||
|
new_timerange, "ADA/BTC", strategy, freqai.dk, data_load_timerange)
|
||||||
|
|
||||||
|
model = freqai.dd.load_data("ADA/BTC", freqai.dk)
|
||||||
|
|
||||||
|
plot_feature_importance(model, "ADA/BTC", freqai.dk)
|
||||||
|
|
||||||
|
assert Path(freqai.dk.data_path / f"{freqai.dk.model_filename}.html")
|
||||||
|
|
||||||
|
shutil.rmtree(Path(freqai.dk.full_path))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('timeframes,corr_pairs', [
|
||||||
|
(['5m'], ['ADA/BTC', 'DASH/BTC']),
|
||||||
|
(['5m'], ['ADA/BTC', 'DASH/BTC', 'ETH/USDT']),
|
||||||
|
(['5m', '15m'], ['ADA/BTC', 'DASH/BTC', 'ETH/USDT']),
|
||||||
|
])
|
||||||
|
def test_freqai_informative_pairs(mocker, freqai_conf, timeframes, corr_pairs):
|
||||||
|
freqai_conf['freqai']['feature_parameters'].update({
|
||||||
|
'include_timeframes': timeframes,
|
||||||
|
'include_corr_pairlist': corr_pairs,
|
||||||
|
|
||||||
|
})
|
||||||
|
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||||
|
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||||
|
pairlists = PairListManager(exchange, freqai_conf)
|
||||||
|
strategy.dp = DataProvider(freqai_conf, exchange, pairlists)
|
||||||
|
pairlist = strategy.dp.current_whitelist()
|
||||||
|
|
||||||
|
pairs_a = strategy.informative_pairs()
|
||||||
|
assert len(pairs_a) == 0
|
||||||
|
pairs_b = strategy.gather_informative_pairs()
|
||||||
|
# we expect unique pairs * timeframes
|
||||||
|
assert len(pairs_b) == len(set(pairlist + corr_pairs)) * len(timeframes)
|
||||||
|
|
|
@ -43,19 +43,6 @@ class freqai_test_multimodel_strat(IStrategy):
|
||||||
)
|
)
|
||||||
max_roi_time_long = IntParameter(0, 800, default=400, space="sell", optimize=False, load=True)
|
max_roi_time_long = IntParameter(0, 800, default=400, space="sell", optimize=False, load=True)
|
||||||
|
|
||||||
def informative_pairs(self):
|
|
||||||
whitelist_pairs = self.dp.current_whitelist()
|
|
||||||
corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
|
|
||||||
informative_pairs = []
|
|
||||||
for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]:
|
|
||||||
for pair in whitelist_pairs:
|
|
||||||
informative_pairs.append((pair, tf))
|
|
||||||
for pair in corr_pairs:
|
|
||||||
if pair in whitelist_pairs:
|
|
||||||
continue # avoid duplication
|
|
||||||
informative_pairs.append((pair, tf))
|
|
||||||
return informative_pairs
|
|
||||||
|
|
||||||
def populate_any_indicators(
|
def populate_any_indicators(
|
||||||
self, pair, df, tf, informative=None, set_generalized_indicators=False
|
self, pair, df, tf, informative=None, set_generalized_indicators=False
|
||||||
):
|
):
|
||||||
|
|
|
@ -43,19 +43,6 @@ class freqai_test_strat(IStrategy):
|
||||||
)
|
)
|
||||||
max_roi_time_long = IntParameter(0, 800, default=400, space="sell", optimize=False, load=True)
|
max_roi_time_long = IntParameter(0, 800, default=400, space="sell", optimize=False, load=True)
|
||||||
|
|
||||||
def informative_pairs(self):
|
|
||||||
whitelist_pairs = self.dp.current_whitelist()
|
|
||||||
corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
|
|
||||||
informative_pairs = []
|
|
||||||
for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]:
|
|
||||||
for pair in whitelist_pairs:
|
|
||||||
informative_pairs.append((pair, tf))
|
|
||||||
for pair in corr_pairs:
|
|
||||||
if pair in whitelist_pairs:
|
|
||||||
continue # avoid duplication
|
|
||||||
informative_pairs.append((pair, tf))
|
|
||||||
return informative_pairs
|
|
||||||
|
|
||||||
def populate_any_indicators(
|
def populate_any_indicators(
|
||||||
self, pair, df, tf, informative=None, set_generalized_indicators=False
|
self, pair, df, tf, informative=None, set_generalized_indicators=False
|
||||||
):
|
):
|
||||||
|
|
|
@ -21,14 +21,14 @@ def test_strategy_test_v3_structure():
|
||||||
(True, 'short'),
|
(True, 'short'),
|
||||||
(False, 'long'),
|
(False, 'long'),
|
||||||
])
|
])
|
||||||
def test_strategy_test_v3(result, fee, is_short, side):
|
def test_strategy_test_v3(dataframe_1m, fee, is_short, side):
|
||||||
strategy = StrategyTestV3({})
|
strategy = StrategyTestV3({})
|
||||||
|
|
||||||
metadata = {'pair': 'ETH/BTC'}
|
metadata = {'pair': 'ETH/BTC'}
|
||||||
assert type(strategy.minimal_roi) is dict
|
assert type(strategy.minimal_roi) is dict
|
||||||
assert type(strategy.stoploss) is float
|
assert type(strategy.stoploss) is float
|
||||||
assert type(strategy.timeframe) is str
|
assert type(strategy.timeframe) is str
|
||||||
indicators = strategy.populate_indicators(result, metadata)
|
indicators = strategy.populate_indicators(dataframe_1m, metadata)
|
||||||
assert type(indicators) is DataFrame
|
assert type(indicators) is DataFrame
|
||||||
assert type(strategy.populate_buy_trend(indicators, metadata)) is DataFrame
|
assert type(strategy.populate_buy_trend(indicators, metadata)) is DataFrame
|
||||||
assert type(strategy.populate_sell_trend(indicators, metadata)) is DataFrame
|
assert type(strategy.populate_sell_trend(indicators, metadata)) is DataFrame
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user