mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
Merge branch 'develop' into improve-freqai-docs
This commit is contained in:
commit
3e1e530aca
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -24,7 +24,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-18.04, ubuntu-20.04, ubuntu-22.04 ]
|
os: [ ubuntu-18.04, ubuntu-20.04, ubuntu-22.04 ]
|
||||||
python-version: ["3.8", "3.9", "3.10"]
|
python-version: ["3.8", "3.9", "3.10.6"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
@ -121,7 +121,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ macos-latest ]
|
os: [ macos-latest ]
|
||||||
python-version: ["3.8", "3.9", "3.10"]
|
python-version: ["3.8", "3.9", "3.10.6"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
@ -205,7 +205,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ windows-latest ]
|
os: [ windows-latest ]
|
||||||
python-version: ["3.8", "3.9", "3.10"]
|
python-version: ["3.8", "3.9", "3.10.6"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
|
@ -15,7 +15,7 @@ repos:
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- types-cachetools==5.2.1
|
- types-cachetools==5.2.1
|
||||||
- types-filelock==3.2.7
|
- types-filelock==3.2.7
|
||||||
- types-requests==2.28.9
|
- types-requests==2.28.10
|
||||||
- types-tabulate==0.8.11
|
- types-tabulate==0.8.11
|
||||||
- types-python-dateutil==2.8.19
|
- types-python-dateutil==2.8.19
|
||||||
# stages: [push]
|
# stages: [push]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -659,17 +659,7 @@ You should also make sure to read the [Exchanges](exchanges.md) section of the d
|
||||||
|
|
||||||
### Using proxy with Freqtrade
|
### Using proxy with Freqtrade
|
||||||
|
|
||||||
To use a proxy with freqtrade, add the kwarg `"aiohttp_trust_env"=true` to the `"ccxt_async_kwargs"` dict in the exchange section of the configuration.
|
To use a proxy with freqtrade, export your proxy settings using the variables `"HTTP_PROXY"` and `"HTTPS_PROXY"` set to the appropriate values.
|
||||||
|
|
||||||
An example for this can be found in `config_examples/config_full.example.json`
|
|
||||||
|
|
||||||
``` json
|
|
||||||
"ccxt_async_config": {
|
|
||||||
"aiohttp_trust_env": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, export your proxy settings using the variables `"HTTP_PROXY"` and `"HTTPS_PROXY"` set to the appropriate values
|
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
export HTTP_PROXY="http://addr:port"
|
export HTTP_PROXY="http://addr:port"
|
||||||
|
@ -677,6 +667,20 @@ export HTTPS_PROXY="http://addr:port"
|
||||||
freqtrade
|
freqtrade
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Proxy just exchange requests
|
||||||
|
|
||||||
|
To use a proxy just for exchange connections (skips/ignores telegram and coingecko) - you can also define the proxies as part of the ccxt configuration.
|
||||||
|
|
||||||
|
``` json
|
||||||
|
"ccxt_config": {
|
||||||
|
"aiohttp_proxy": "http://addr:port",
|
||||||
|
"proxies": {
|
||||||
|
"http": "http://addr:port",
|
||||||
|
"https": "http://addr:port"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Next step
|
## Next step
|
||||||
|
|
||||||
Now you have configured your config.json, the next step is to [start your bot](bot-usage.md).
|
Now you have configured your config.json, the next step is to [start your bot](bot-usage.md).
|
||||||
|
|
|
@ -25,8 +25,7 @@ usage: freqtrade download-data [-h] [-v] [--logfile FILE] [-V] [-c PATH]
|
||||||
[--include-inactive-pairs]
|
[--include-inactive-pairs]
|
||||||
[--timerange TIMERANGE] [--dl-trades]
|
[--timerange TIMERANGE] [--dl-trades]
|
||||||
[--exchange EXCHANGE]
|
[--exchange EXCHANGE]
|
||||||
[-t {1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} [{1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} ...]]
|
[-t TIMEFRAMES [TIMEFRAMES ...]] [--erase]
|
||||||
[--erase]
|
|
||||||
[--data-format-ohlcv {json,jsongz,hdf5}]
|
[--data-format-ohlcv {json,jsongz,hdf5}]
|
||||||
[--data-format-trades {json,jsongz,hdf5}]
|
[--data-format-trades {json,jsongz,hdf5}]
|
||||||
[--trading-mode {spot,margin,futures}]
|
[--trading-mode {spot,margin,futures}]
|
||||||
|
@ -37,7 +36,8 @@ optional arguments:
|
||||||
-p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...]
|
-p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...]
|
||||||
Limit command to these pairs. Pairs are space-
|
Limit command to these pairs. Pairs are space-
|
||||||
separated.
|
separated.
|
||||||
--pairs-file FILE File containing a list of pairs to download.
|
--pairs-file FILE File containing a list of pairs. Takes precedence over
|
||||||
|
--pairs or pairs configured in the configuration.
|
||||||
--days INT Download data for given number of days.
|
--days INT Download data for given number of days.
|
||||||
--new-pairs-days INT Download data of new pairs for given number of days.
|
--new-pairs-days INT Download data of new pairs for given number of days.
|
||||||
Default: `None`.
|
Default: `None`.
|
||||||
|
@ -50,7 +50,7 @@ optional arguments:
|
||||||
as --timeframes/-t.
|
as --timeframes/-t.
|
||||||
--exchange EXCHANGE Exchange name (default: `bittrex`). Only valid if no
|
--exchange EXCHANGE Exchange name (default: `bittrex`). Only valid if no
|
||||||
config is provided.
|
config is provided.
|
||||||
-t {1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} [{1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} ...], --timeframes {1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} [{1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} ...]
|
-t TIMEFRAMES [TIMEFRAMES ...], --timeframes TIMEFRAMES [TIMEFRAMES ...]
|
||||||
Specify which tickers to download. Space-separated
|
Specify which tickers to download. Space-separated
|
||||||
list. Default: `1m 5m`.
|
list. Default: `1m 5m`.
|
||||||
--erase Clean all existing data for the selected
|
--erase Clean all existing data for the selected
|
||||||
|
@ -61,7 +61,7 @@ optional arguments:
|
||||||
--data-format-trades {json,jsongz,hdf5}
|
--data-format-trades {json,jsongz,hdf5}
|
||||||
Storage format for downloaded trades data. (default:
|
Storage format for downloaded trades data. (default:
|
||||||
`jsongz`).
|
`jsongz`).
|
||||||
--trading-mode {spot,margin,futures}
|
--trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures}
|
||||||
Select Trading mode
|
Select Trading mode
|
||||||
--prepend Allow data prepending. (Data-appending is disabled)
|
--prepend Allow data prepending. (Data-appending is disabled)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
@ -232,7 +233,7 @@ OKX requires a passphrase for each api key, you will therefore need to add this
|
||||||
|
|
||||||
!!! Warning "Futures"
|
!!! Warning "Futures"
|
||||||
OKX Futures has the concept of "position mode" - which can be Net or long/short (hedge mode).
|
OKX Futures has the concept of "position mode" - which can be Net or long/short (hedge mode).
|
||||||
Freqtrade supports both modes - but changing the mode mid-trading is not supported and will lead to exceptions and failures to place trades.
|
Freqtrade supports both modes (we recommend to use net mode) - but changing the mode mid-trading is not supported and will lead to exceptions and failures to place trades.
|
||||||
OKX also only provides MARK candles for the past ~3 months. Backtesting futures prior to that date will therefore lead to slight deviations, as funding-fees cannot be calculated correctly without this data.
|
OKX also only provides MARK candles for the past ~3 months. Backtesting futures prior to that date will therefore lead to slight deviations, as funding-fees cannot be calculated correctly without this data.
|
||||||
|
|
||||||
## Gate.io
|
## Gate.io
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
markdown==3.3.7
|
markdown==3.3.7
|
||||||
mkdocs==1.3.1
|
mkdocs==1.3.1
|
||||||
mkdocs-material==8.4.2
|
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
|
||||||
|
|
|
@ -264,7 +264,8 @@ def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFram
|
||||||
### Exit signal rules
|
### Exit signal rules
|
||||||
|
|
||||||
Edit the method `populate_exit_trend()` into your strategy file to update your exit strategy.
|
Edit the method `populate_exit_trend()` into your strategy file to update your exit strategy.
|
||||||
Please note that the exit-signal is only used if `use_exit_signal` is set to true in the configuration.
|
The exit-signal is only used for exits if `use_exit_signal` is set to true in the configuration.
|
||||||
|
`use_exit_signal` will not influence [signal collision rules](#colliding-signals) - which will still apply and can prevent entries.
|
||||||
|
|
||||||
It's important to always return the dataframe without removing/modifying the columns `"open", "high", "low", "close", "volume"`, otherwise these fields would contain something unexpected.
|
It's important to always return the dataframe without removing/modifying the columns `"open", "high", "low", "close", "volume"`, otherwise these fields would contain something unexpected.
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,8 @@ Example configuration showing the different settings:
|
||||||
"warning": "on",
|
"warning": "on",
|
||||||
"startup": "off",
|
"startup": "off",
|
||||||
"entry": "silent",
|
"entry": "silent",
|
||||||
|
"entry_fill": "on",
|
||||||
|
"entry_cancel": "silent",
|
||||||
"exit": {
|
"exit": {
|
||||||
"roi": "silent",
|
"roi": "silent",
|
||||||
"emergency_exit": "on",
|
"emergency_exit": "on",
|
||||||
|
@ -93,9 +95,7 @@ Example configuration showing the different settings:
|
||||||
"custom_exit": "silent",
|
"custom_exit": "silent",
|
||||||
"partial_exit": "on"
|
"partial_exit": "on"
|
||||||
},
|
},
|
||||||
"entry_cancel": "silent",
|
|
||||||
"exit_cancel": "on",
|
"exit_cancel": "on",
|
||||||
"entry_fill": "off",
|
|
||||||
"exit_fill": "off",
|
"exit_fill": "off",
|
||||||
"protection_trigger": "off",
|
"protection_trigger": "off",
|
||||||
"protection_trigger_global": "on",
|
"protection_trigger_global": "on",
|
||||||
|
|
|
@ -525,12 +525,14 @@ Requires a configuration with specified `pairlists` attribute.
|
||||||
Can be used to generate static pairlists to be used during backtesting / hyperopt.
|
Can be used to generate static pairlists to be used during backtesting / hyperopt.
|
||||||
|
|
||||||
```
|
```
|
||||||
usage: freqtrade test-pairlist [-h] [-v] [-c PATH]
|
usage: freqtrade test-pairlist [-h] [--userdir PATH] [-v] [-c PATH]
|
||||||
[--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]]
|
[--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]]
|
||||||
[-1] [--print-json] [--exchange EXCHANGE]
|
[-1] [--print-json] [--exchange EXCHANGE]
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
|
--userdir PATH, --user-data-dir PATH
|
||||||
|
Path to userdata directory.
|
||||||
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
|
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
|
||||||
-c PATH, --config PATH
|
-c PATH, --config PATH
|
||||||
Specify configuration file (default:
|
Specify configuration file (default:
|
||||||
|
|
|
@ -53,8 +53,8 @@ ARGS_LIST_PAIRS = ["exchange", "print_list", "list_pairs_print_json", "print_one
|
||||||
"print_csv", "base_currencies", "quote_currencies", "list_pairs_all",
|
"print_csv", "base_currencies", "quote_currencies", "list_pairs_all",
|
||||||
"trading_mode"]
|
"trading_mode"]
|
||||||
|
|
||||||
ARGS_TEST_PAIRLIST = ["verbosity", "config", "quote_currencies", "print_one_column",
|
ARGS_TEST_PAIRLIST = ["user_data_dir", "verbosity", "config", "quote_currencies",
|
||||||
"list_pairs_print_json", "exchange"]
|
"print_one_column", "list_pairs_print_json", "exchange"]
|
||||||
|
|
||||||
ARGS_CREATE_USERDIR = ["user_data_dir", "reset"]
|
ARGS_CREATE_USERDIR = ["user_data_dir", "reset"]
|
||||||
|
|
||||||
|
@ -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',
|
||||||
),
|
),
|
||||||
|
@ -393,7 +393,8 @@ AVAILABLE_CLI_OPTIONS = {
|
||||||
# Download data
|
# Download data
|
||||||
"pairs_file": Arg(
|
"pairs_file": Arg(
|
||||||
'--pairs-file',
|
'--pairs-file',
|
||||||
help='File containing a list of pairs to download.',
|
help='File containing a list of pairs. '
|
||||||
|
'Takes precedence over --pairs or pairs configured in the configuration.',
|
||||||
metavar='FILE',
|
metavar='FILE',
|
||||||
),
|
),
|
||||||
"days": Arg(
|
"days": Arg(
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
@ -289,11 +289,12 @@ CONF_SCHEMA = {
|
||||||
'warning': {'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS},
|
'warning': {'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS},
|
||||||
'startup': {'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS},
|
'startup': {'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS},
|
||||||
'entry': {'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS},
|
'entry': {'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS},
|
||||||
'entry_cancel': {'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS},
|
'entry_fill': {
|
||||||
'entry_fill': {'type': 'string',
|
'type': 'string',
|
||||||
'enum': TELEGRAM_SETTING_OPTIONS,
|
'enum': TELEGRAM_SETTING_OPTIONS,
|
||||||
'default': 'off'
|
'default': 'off'
|
||||||
},
|
},
|
||||||
|
'entry_cancel': {'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS, },
|
||||||
'exit': {
|
'exit': {
|
||||||
'type': ['string', 'object'],
|
'type': ['string', 'object'],
|
||||||
'additionalProperties': {
|
'additionalProperties': {
|
||||||
|
@ -301,12 +302,12 @@ CONF_SCHEMA = {
|
||||||
'enum': TELEGRAM_SETTING_OPTIONS
|
'enum': TELEGRAM_SETTING_OPTIONS
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'exit_cancel': {'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS},
|
|
||||||
'exit_fill': {
|
'exit_fill': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'enum': TELEGRAM_SETTING_OPTIONS,
|
'enum': TELEGRAM_SETTING_OPTIONS,
|
||||||
'default': 'on'
|
'default': 'on'
|
||||||
},
|
},
|
||||||
|
'exit_cancel': {'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS},
|
||||||
'protection_trigger': {
|
'protection_trigger': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'enum': TELEGRAM_SETTING_OPTIONS,
|
'enum': TELEGRAM_SETTING_OPTIONS,
|
||||||
|
@ -315,14 +316,17 @@ CONF_SCHEMA = {
|
||||||
'protection_trigger_global': {
|
'protection_trigger_global': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'enum': TELEGRAM_SETTING_OPTIONS,
|
'enum': TELEGRAM_SETTING_OPTIONS,
|
||||||
|
'default': 'on'
|
||||||
},
|
},
|
||||||
'show_candle': {
|
'show_candle': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'enum': ['off', 'ohlc'],
|
'enum': ['off', 'ohlc'],
|
||||||
|
'default': 'off'
|
||||||
},
|
},
|
||||||
'strategy_msg': {
|
'strategy_msg': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'enum': TELEGRAM_SETTING_OPTIONS,
|
'enum': TELEGRAM_SETTING_OPTIONS,
|
||||||
|
'default': 'on'
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -504,6 +508,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},
|
||||||
|
@ -603,3 +608,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,
|
||||||
|
@ -292,6 +292,7 @@ def convert_ohlcv_format(
|
||||||
timeframe,
|
timeframe,
|
||||||
candle_type=candle_type
|
candle_type=candle_type
|
||||||
))
|
))
|
||||||
|
config['pairs'] = sorted(set(config['pairs']))
|
||||||
logger.info(f"Converting candle (OHLCV) data for {config['pairs']}")
|
logger.info(f"Converting candle (OHLCV) data for {config['pairs']}")
|
||||||
|
|
||||||
for timeframe in timeframes:
|
for timeframe in timeframes:
|
||||||
|
@ -302,7 +303,7 @@ def convert_ohlcv_format(
|
||||||
drop_incomplete=False,
|
drop_incomplete=False,
|
||||||
startup_candles=0,
|
startup_candles=0,
|
||||||
candle_type=candle_type)
|
candle_type=candle_type)
|
||||||
logger.info(f"Converting {len(data)} {candle_type} candles for {pair}")
|
logger.info(f"Converting {len(data)} {timeframe} {candle_type} candles for {pair}")
|
||||||
if len(data) > 0:
|
if len(data) > 0:
|
||||||
trg.ohlcv_store(
|
trg.ohlcv_store(
|
||||||
pair=pair,
|
pair=pair,
|
||||||
|
|
|
@ -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, RunMode
|
from freqtrade.enums import CandleType, RunMode
|
||||||
from freqtrade.exceptions import ExchangeError, OperationalException
|
from freqtrade.exceptions import ExchangeError, OperationalException
|
||||||
|
@ -28,7 +28,7 @@ MAX_DATAFRAME_CANDLES = 1000
|
||||||
|
|
||||||
class DataProvider:
|
class DataProvider:
|
||||||
|
|
||||||
def __init__(self, config: dict, exchange: Optional[Exchange], pairlists=None) -> None:
|
def __init__(self, config: Config, exchange: Optional[Exchange], pairlists=None) -> None:
|
||||||
self._config = config
|
self._config = config
|
||||||
self._exchange = exchange
|
self._exchange = exchange
|
||||||
self._pairlists = pairlists
|
self._pairlists = pairlists
|
||||||
|
@ -86,7 +86,7 @@ class DataProvider:
|
||||||
"""
|
"""
|
||||||
_candle_type = CandleType.from_string(
|
_candle_type = CandleType.from_string(
|
||||||
candle_type) if candle_type != '' else self._config['candle_type_def']
|
candle_type) if candle_type != '' else self._config['candle_type_def']
|
||||||
saved_pair = (pair, str(timeframe), _candle_type)
|
saved_pair: PairWithTimeframe = (pair, str(timeframe), _candle_type)
|
||||||
if saved_pair not in self.__cached_pairs_backtesting:
|
if saved_pair not in self.__cached_pairs_backtesting:
|
||||||
timerange = TimeRange.parse_timerange(None if self._config.get(
|
timerange = TimeRange.parse_timerange(None if self._config.get(
|
||||||
'timerange') is None else str(self._config.get('timerange')))
|
'timerange') is None else str(self._config.get('timerange')))
|
||||||
|
@ -196,7 +196,9 @@ class DataProvider:
|
||||||
Clear pair dataframe cache.
|
Clear pair dataframe cache.
|
||||||
"""
|
"""
|
||||||
self.__cached_pairs = {}
|
self.__cached_pairs = {}
|
||||||
self.__cached_pairs_backtesting = {}
|
# Don't reset backtesting pairs -
|
||||||
|
# otherwise they're reloaded each time during hyperopt due to with analyze_per_epoch
|
||||||
|
# self.__cached_pairs_backtesting = {}
|
||||||
self.__slice_index = 0
|
self.__slice_index = 0
|
||||||
|
|
||||||
# Exchange functions
|
# Exchange functions
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -71,6 +71,7 @@ class Okx(Exchange):
|
||||||
try:
|
try:
|
||||||
if self.trading_mode == TradingMode.FUTURES and not self._config['dry_run']:
|
if self.trading_mode == TradingMode.FUTURES and not self._config['dry_run']:
|
||||||
accounts = self._api.fetch_accounts()
|
accounts = self._api.fetch_accounts()
|
||||||
|
self._log_exchange_response('fetch_accounts', accounts)
|
||||||
if len(accounts) > 0:
|
if len(accounts) > 0:
|
||||||
self.net_only = accounts[0].get('info', {}).get('posMode') == 'net_mode'
|
self.net_only = accounts[0].get('info', {}).get('posMode') == 'net_mode'
|
||||||
except ccxt.DDoSProtection as e:
|
except ccxt.DDoSProtection as e:
|
||||||
|
|
|
@ -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
|
||||||
|
@ -355,7 +344,7 @@ class FreqaiDataDrawer:
|
||||||
for dir in model_folders:
|
for dir in model_folders:
|
||||||
result = pattern.match(str(dir.name))
|
result = pattern.match(str(dir.name))
|
||||||
if result is None:
|
if result is None:
|
||||||
break
|
continue
|
||||||
coin = result.group(1)
|
coin = result.group(1)
|
||||||
timestamp = result.group(2)
|
timestamp = result.group(2)
|
||||||
|
|
||||||
|
|
|
@ -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,12 +184,16 @@ 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]
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
(_, trained_timestamp, _) = self.dd.get_pair_dict_info(pair)
|
(_, trained_timestamp, _) = self.dd.get_pair_dict_info(pair)
|
||||||
|
|
||||||
if self.dd.pair_dict[pair]["priority"] != 1:
|
|
||||||
continue
|
|
||||||
dk = FreqaiDataKitchen(self.config, self.live, pair)
|
dk = FreqaiDataKitchen(self.config, self.live, pair)
|
||||||
dk.set_paths(pair, trained_timestamp)
|
dk.set_paths(pair, trained_timestamp)
|
||||||
(
|
(
|
||||||
|
@ -198,11 +205,18 @@ class IFreqaiModel(ABC):
|
||||||
|
|
||||||
if retrain:
|
if retrain:
|
||||||
self.train_timer('start')
|
self.train_timer('start')
|
||||||
|
try:
|
||||||
self.extract_data_and_train_model(
|
self.extract_data_and_train_model(
|
||||||
new_trained_timerange, pair, strategy, dk, data_load_timerange
|
new_trained_timerange, pair, strategy, dk, data_load_timerange
|
||||||
)
|
)
|
||||||
|
except Exception as msg:
|
||||||
|
logger.warning(f'Training {pair} raised exception {msg}, skipping.')
|
||||||
|
|
||||||
self.train_timer('stop')
|
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()
|
self.dd.save_historic_predictions_to_disk()
|
||||||
|
|
||||||
def start_backtesting(
|
def start_backtesting(
|
||||||
|
@ -557,11 +571,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 +699,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.
|
||||||
|
|
||||||
|
|
85
freqtrade/freqai/prediction_models/XGBoostClassifier.py
Normal file
85
freqtrade/freqai/prediction_models/XGBoostClassifier.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import logging
|
||||||
|
from typing import Any, Dict, Tuple
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import numpy.typing as npt
|
||||||
|
import pandas as pd
|
||||||
|
from pandas import DataFrame
|
||||||
|
from pandas.api.types import is_integer_dtype
|
||||||
|
from sklearn.preprocessing import LabelEncoder
|
||||||
|
from xgboost import XGBClassifier
|
||||||
|
|
||||||
|
from freqtrade.freqai.base_models.BaseClassifierModel import BaseClassifierModel
|
||||||
|
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class XGBoostClassifier(BaseClassifierModel):
|
||||||
|
"""
|
||||||
|
User created prediction model. The class needs to override three necessary
|
||||||
|
functions, predict(), train(), fit(). The class inherits ModelHandler which
|
||||||
|
has its own DataHandler where data is held, saved, loaded, and managed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def fit(self, data_dictionary: Dict, dk: FreqaiDataKitchen, **kwargs) -> Any:
|
||||||
|
"""
|
||||||
|
User sets up the training and test data to fit their desired model here
|
||||||
|
:params:
|
||||||
|
:data_dictionary: the dictionary constructed by DataHandler to hold
|
||||||
|
all the training and test data/labels.
|
||||||
|
"""
|
||||||
|
|
||||||
|
X = data_dictionary["train_features"].to_numpy()
|
||||||
|
y = data_dictionary["train_labels"].to_numpy()[:, 0]
|
||||||
|
|
||||||
|
le = LabelEncoder()
|
||||||
|
if not is_integer_dtype(y):
|
||||||
|
y = pd.Series(le.fit_transform(y), dtype="int64")
|
||||||
|
|
||||||
|
if self.freqai_info.get('data_split_parameters', {}).get('test_size', 0.1) == 0:
|
||||||
|
eval_set = None
|
||||||
|
else:
|
||||||
|
test_features = data_dictionary["test_features"].to_numpy()
|
||||||
|
test_labels = data_dictionary["test_labels"].to_numpy()[:, 0]
|
||||||
|
|
||||||
|
if not is_integer_dtype(test_labels):
|
||||||
|
test_labels = pd.Series(le.transform(test_labels), dtype="int64")
|
||||||
|
|
||||||
|
eval_set = [(test_features, test_labels)]
|
||||||
|
|
||||||
|
train_weights = data_dictionary["train_weights"]
|
||||||
|
|
||||||
|
init_model = self.get_init_model(dk.pair)
|
||||||
|
|
||||||
|
model = XGBClassifier(**self.model_training_parameters)
|
||||||
|
|
||||||
|
model.fit(X=X, y=y, eval_set=eval_set, sample_weight=train_weights,
|
||||||
|
xgb_model=init_model)
|
||||||
|
|
||||||
|
return model
|
||||||
|
|
||||||
|
def predict(
|
||||||
|
self, unfiltered_df: DataFrame, dk: FreqaiDataKitchen, **kwargs
|
||||||
|
) -> Tuple[DataFrame, npt.NDArray[np.int_]]:
|
||||||
|
"""
|
||||||
|
Filter the prediction features data and predict with it.
|
||||||
|
:param: unfiltered_df: Full dataframe for the current backtest period.
|
||||||
|
:return:
|
||||||
|
:pred_df: dataframe containing the predictions
|
||||||
|
:do_predict: np.array of 1s and 0s to indicate places where freqai needed to remove
|
||||||
|
data (NaNs) or felt uncertain about data (PCA and DI index)
|
||||||
|
"""
|
||||||
|
|
||||||
|
(pred_df, dk.do_predict) = super().predict(unfiltered_df, dk, **kwargs)
|
||||||
|
|
||||||
|
le = LabelEncoder()
|
||||||
|
label = dk.label_list[0]
|
||||||
|
labels_before = list(dk.data['labels_std'].keys())
|
||||||
|
labels_after = le.fit_transform(labels_before).tolist()
|
||||||
|
pred_df[label] = le.inverse_transform(pred_df[label])
|
||||||
|
pred_df = pred_df.rename(
|
||||||
|
columns={labels_after[i]: labels_before[i] for i in range(len(labels_before))})
|
||||||
|
|
||||||
|
return (pred_df, dk.do_predict)
|
|
@ -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)
|
||||||
|
|
|
@ -11,9 +11,9 @@ from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from schedule import Scheduler
|
from schedule import Scheduler
|
||||||
|
|
||||||
from freqtrade import __version__, constants
|
from freqtrade import 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
|
||||||
|
@ -44,7 +44,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()
|
||||||
|
@ -52,8 +52,6 @@ class FreqtradeBot(LoggingMixin):
|
||||||
"""
|
"""
|
||||||
self.active_pair_whitelist: List[str] = []
|
self.active_pair_whitelist: List[str] = []
|
||||||
|
|
||||||
logger.info('Starting freqtrade %s', __version__)
|
|
||||||
|
|
||||||
# Init bot state
|
# Init bot state
|
||||||
self.state = State.STOPPED
|
self.state = State.STOPPED
|
||||||
|
|
||||||
|
@ -596,7 +594,7 @@ class FreqtradeBot(LoggingMixin):
|
||||||
amount = trade.amount
|
amount = trade.amount
|
||||||
|
|
||||||
if amount == 0.0:
|
if amount == 0.0:
|
||||||
logger.info("Amount to sell is 0.0 due to exchange limits - not selling.")
|
logger.info("Amount to exit is 0.0 due to exchange limits - not exiting.")
|
||||||
return
|
return
|
||||||
|
|
||||||
remaining = (trade.amount - amount) * current_exit_rate
|
remaining = (trade.amount - amount) * current_exit_rate
|
||||||
|
@ -923,7 +921,7 @@ class FreqtradeBot(LoggingMixin):
|
||||||
'stake_amount': trade.stake_amount,
|
'stake_amount': trade.stake_amount,
|
||||||
'stake_currency': self.config['stake_currency'],
|
'stake_currency': self.config['stake_currency'],
|
||||||
'fiat_currency': self.config.get('fiat_display_currency', None),
|
'fiat_currency': self.config.get('fiat_display_currency', None),
|
||||||
'amount': order.safe_amount_after_fee if fill else order.amount,
|
'amount': order.safe_amount_after_fee if fill else (order.amount or trade.amount),
|
||||||
'open_date': trade.open_date or datetime.utcnow(),
|
'open_date': trade.open_date or datetime.utcnow(),
|
||||||
'current_rate': current_rate,
|
'current_rate': current_rate,
|
||||||
'sub_trade': sub_trade,
|
'sub_trade': sub_trade,
|
||||||
|
@ -1072,6 +1070,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}.")
|
||||||
|
@ -1145,10 +1144,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')):
|
||||||
|
@ -1186,7 +1184,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 ...")
|
||||||
|
@ -1598,14 +1597,14 @@ class FreqtradeBot(LoggingMixin):
|
||||||
# second condition is for mypy only; order will always be passed during sub trade
|
# second condition is for mypy only; order will always be passed during sub trade
|
||||||
if sub_trade and order is not None:
|
if sub_trade and order is not None:
|
||||||
amount = order.safe_filled if fill else order.amount
|
amount = order.safe_filled if fill else order.amount
|
||||||
profit_rate = order.safe_price
|
order_rate: float = order.safe_price
|
||||||
|
|
||||||
profit = trade.calc_profit(rate=profit_rate, amount=amount, open_rate=trade.open_rate)
|
profit = trade.calc_profit(rate=order_rate, amount=amount, open_rate=trade.open_rate)
|
||||||
profit_ratio = trade.calc_profit_ratio(profit_rate, amount, trade.open_rate)
|
profit_ratio = trade.calc_profit_ratio(order_rate, amount, trade.open_rate)
|
||||||
else:
|
else:
|
||||||
profit_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested
|
order_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested
|
||||||
profit = trade.calc_profit(rate=profit_rate) + (0.0 if fill else trade.realized_profit)
|
profit = trade.calc_profit(rate=order_rate) + (0.0 if fill else trade.realized_profit)
|
||||||
profit_ratio = trade.calc_profit_ratio(profit_rate)
|
profit_ratio = trade.calc_profit_ratio(order_rate)
|
||||||
amount = trade.amount
|
amount = trade.amount
|
||||||
gain = "profit" if profit_ratio > 0 else "loss"
|
gain = "profit" if profit_ratio > 0 else "loss"
|
||||||
|
|
||||||
|
@ -1618,11 +1617,12 @@ class FreqtradeBot(LoggingMixin):
|
||||||
'leverage': trade.leverage,
|
'leverage': trade.leverage,
|
||||||
'direction': 'Short' if trade.is_short else 'Long',
|
'direction': 'Short' if trade.is_short else 'Long',
|
||||||
'gain': gain,
|
'gain': gain,
|
||||||
'limit': profit_rate,
|
'limit': order_rate, # Deprecated
|
||||||
|
'order_rate': order_rate,
|
||||||
'order_type': order_type,
|
'order_type': order_type,
|
||||||
'amount': amount,
|
'amount': amount,
|
||||||
'open_rate': trade.open_rate,
|
'open_rate': trade.open_rate,
|
||||||
'close_rate': profit_rate,
|
'close_rate': order_rate,
|
||||||
'current_rate': current_rate,
|
'current_rate': current_rate,
|
||||||
'profit_amount': profit,
|
'profit_amount': profit,
|
||||||
'profit_ratio': profit_ratio,
|
'profit_ratio': profit_ratio,
|
||||||
|
|
|
@ -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
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -12,6 +12,7 @@ from typing import Any, List
|
||||||
if sys.version_info < (3, 8): # pragma: no cover
|
if sys.version_info < (3, 8): # pragma: no cover
|
||||||
sys.exit("Freqtrade requires Python version >= 3.8")
|
sys.exit("Freqtrade requires Python version >= 3.8")
|
||||||
|
|
||||||
|
from freqtrade import __version__
|
||||||
from freqtrade.commands import Arguments
|
from freqtrade.commands import Arguments
|
||||||
from freqtrade.exceptions import FreqtradeException, OperationalException
|
from freqtrade.exceptions import FreqtradeException, OperationalException
|
||||||
from freqtrade.loggers import setup_logging_pre
|
from freqtrade.loggers import setup_logging_pre
|
||||||
|
@ -34,6 +35,7 @@ def main(sysargv: List[str] = None) -> None:
|
||||||
|
|
||||||
# Call subcommand.
|
# Call subcommand.
|
||||||
if 'func' in args:
|
if 'func' in args:
|
||||||
|
logger.info(f'freqtrade {__version__}')
|
||||||
return_code = args['func'](args)
|
return_code = args['func'](args)
|
||||||
else:
|
else:
|
||||||
# No subcommand was issued.
|
# No subcommand was issued.
|
||||||
|
|
|
@ -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')
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ class Hyperopt:
|
||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
attr.value = params_dict[attr_name]
|
attr.value = params_dict[attr_name]
|
||||||
|
|
||||||
def generate_optimizer(self, raw_params: List[Any], iteration=None) -> Dict:
|
def generate_optimizer(self, raw_params: List[Any]) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Used Optimize function.
|
Used Optimize function.
|
||||||
Called once per epoch to optimize whatever is configured.
|
Called once per epoch to optimize whatever is configured.
|
||||||
|
@ -410,9 +410,11 @@ class Hyperopt:
|
||||||
model_queue_size=SKOPT_MODEL_QUEUE_SIZE,
|
model_queue_size=SKOPT_MODEL_QUEUE_SIZE,
|
||||||
)
|
)
|
||||||
|
|
||||||
def run_optimizer_parallel(self, parallel, asked, i) -> List:
|
def run_optimizer_parallel(
|
||||||
|
self, parallel: Parallel, asked: List[List]) -> List[Dict[str, Any]]:
|
||||||
|
""" Start optimizer in a parallel way """
|
||||||
return parallel(delayed(
|
return parallel(delayed(
|
||||||
wrap_non_picklable_objects(self.generate_optimizer))(v, i) for v in asked)
|
wrap_non_picklable_objects(self.generate_optimizer))(v) for v in asked)
|
||||||
|
|
||||||
def _set_random_state(self, random_state: Optional[int]) -> int:
|
def _set_random_state(self, random_state: Optional[int]) -> int:
|
||||||
return random_state or random.randint(1, 2**16 - 1)
|
return random_state or random.randint(1, 2**16 - 1)
|
||||||
|
@ -491,6 +493,53 @@ class Hyperopt:
|
||||||
else:
|
else:
|
||||||
return self.opt.ask(n_points=n_points), [False for _ in range(n_points)]
|
return self.opt.ask(n_points=n_points), [False for _ in range(n_points)]
|
||||||
|
|
||||||
|
def get_progressbar_widgets(self):
|
||||||
|
if self.print_colorized:
|
||||||
|
widgets = [
|
||||||
|
' [Epoch ', progressbar.Counter(), ' of ', str(self.total_epochs),
|
||||||
|
' (', progressbar.Percentage(), ')] ',
|
||||||
|
progressbar.Bar(marker=progressbar.AnimatedMarker(
|
||||||
|
fill='\N{FULL BLOCK}',
|
||||||
|
fill_wrap=Fore.GREEN + '{}' + Fore.RESET,
|
||||||
|
marker_wrap=Style.BRIGHT + '{}' + Style.RESET_ALL,
|
||||||
|
)),
|
||||||
|
' [', progressbar.ETA(), ', ', progressbar.Timer(), ']',
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
widgets = [
|
||||||
|
' [Epoch ', progressbar.Counter(), ' of ', str(self.total_epochs),
|
||||||
|
' (', progressbar.Percentage(), ')] ',
|
||||||
|
progressbar.Bar(marker=progressbar.AnimatedMarker(
|
||||||
|
fill='\N{FULL BLOCK}',
|
||||||
|
)),
|
||||||
|
' [', progressbar.ETA(), ', ', progressbar.Timer(), ']',
|
||||||
|
]
|
||||||
|
return widgets
|
||||||
|
|
||||||
|
def evaluate_result(self, val: Dict[str, Any], current: int, is_random: bool):
|
||||||
|
"""
|
||||||
|
Evaluate results returned from generate_optimizer
|
||||||
|
"""
|
||||||
|
val['current_epoch'] = current
|
||||||
|
val['is_initial_point'] = current <= INITIAL_POINTS
|
||||||
|
|
||||||
|
logger.debug("Optimizer epoch evaluated: %s", val)
|
||||||
|
|
||||||
|
is_best = HyperoptTools.is_best_loss(val, self.current_best_loss)
|
||||||
|
# This value is assigned here and not in the optimization method
|
||||||
|
# to keep proper order in the list of results. That's because
|
||||||
|
# evaluations can take different time. Here they are aligned in the
|
||||||
|
# order they will be shown to the user.
|
||||||
|
val['is_best'] = is_best
|
||||||
|
val['is_random'] = is_random
|
||||||
|
self.print_results(val)
|
||||||
|
|
||||||
|
if is_best:
|
||||||
|
self.current_best_loss = val['loss']
|
||||||
|
self.current_best_epoch = val
|
||||||
|
|
||||||
|
self._save_result(val)
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
self.random_state = self._set_random_state(self.config.get('hyperopt_random_state'))
|
self.random_state = self._set_random_state(self.config.get('hyperopt_random_state'))
|
||||||
logger.info(f"Using optimizer random state: {self.random_state}")
|
logger.info(f"Using optimizer random state: {self.random_state}")
|
||||||
|
@ -526,64 +575,40 @@ class Hyperopt:
|
||||||
logger.info(f'Effective number of parallel workers used: {jobs}')
|
logger.info(f'Effective number of parallel workers used: {jobs}')
|
||||||
|
|
||||||
# Define progressbar
|
# Define progressbar
|
||||||
if self.print_colorized:
|
widgets = self.get_progressbar_widgets()
|
||||||
widgets = [
|
|
||||||
' [Epoch ', progressbar.Counter(), ' of ', str(self.total_epochs),
|
|
||||||
' (', progressbar.Percentage(), ')] ',
|
|
||||||
progressbar.Bar(marker=progressbar.AnimatedMarker(
|
|
||||||
fill='\N{FULL BLOCK}',
|
|
||||||
fill_wrap=Fore.GREEN + '{}' + Fore.RESET,
|
|
||||||
marker_wrap=Style.BRIGHT + '{}' + Style.RESET_ALL,
|
|
||||||
)),
|
|
||||||
' [', progressbar.ETA(), ', ', progressbar.Timer(), ']',
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
widgets = [
|
|
||||||
' [Epoch ', progressbar.Counter(), ' of ', str(self.total_epochs),
|
|
||||||
' (', progressbar.Percentage(), ')] ',
|
|
||||||
progressbar.Bar(marker=progressbar.AnimatedMarker(
|
|
||||||
fill='\N{FULL BLOCK}',
|
|
||||||
)),
|
|
||||||
' [', progressbar.ETA(), ', ', progressbar.Timer(), ']',
|
|
||||||
]
|
|
||||||
with progressbar.ProgressBar(
|
with progressbar.ProgressBar(
|
||||||
max_value=self.total_epochs, redirect_stdout=False, redirect_stderr=False,
|
max_value=self.total_epochs, redirect_stdout=False, redirect_stderr=False,
|
||||||
widgets=widgets
|
widgets=widgets
|
||||||
) as pbar:
|
) as pbar:
|
||||||
EVALS = ceil(self.total_epochs / jobs)
|
start = 0
|
||||||
for i in range(EVALS):
|
|
||||||
|
if self.analyze_per_epoch:
|
||||||
|
# First analysis not in parallel mode when using --analyze-per-epoch.
|
||||||
|
# This allows dataprovider to load it's informative cache.
|
||||||
|
asked, is_random = self.get_asked_points(n_points=1)
|
||||||
|
f_val0 = self.generate_optimizer(asked[0])
|
||||||
|
self.opt.tell(asked, [f_val0['loss']])
|
||||||
|
self.evaluate_result(f_val0, 1, is_random[0])
|
||||||
|
pbar.update(1)
|
||||||
|
start += 1
|
||||||
|
|
||||||
|
evals = ceil((self.total_epochs - start) / jobs)
|
||||||
|
for i in range(evals):
|
||||||
# Correct the number of epochs to be processed for the last
|
# Correct the number of epochs to be processed for the last
|
||||||
# iteration (should not exceed self.total_epochs in total)
|
# iteration (should not exceed self.total_epochs in total)
|
||||||
n_rest = (i + 1) * jobs - self.total_epochs
|
n_rest = (i + 1) * jobs - (self.total_epochs - start)
|
||||||
current_jobs = jobs - n_rest if n_rest > 0 else jobs
|
current_jobs = jobs - n_rest if n_rest > 0 else jobs
|
||||||
|
|
||||||
asked, is_random = self.get_asked_points(n_points=current_jobs)
|
asked, is_random = self.get_asked_points(n_points=current_jobs)
|
||||||
f_val = self.run_optimizer_parallel(parallel, asked, i)
|
f_val = self.run_optimizer_parallel(parallel, asked)
|
||||||
self.opt.tell(asked, [v['loss'] for v in f_val])
|
self.opt.tell(asked, [v['loss'] for v in f_val])
|
||||||
|
|
||||||
# Calculate progressbar outputs
|
# Calculate progressbar outputs
|
||||||
for j, val in enumerate(f_val):
|
for j, val in enumerate(f_val):
|
||||||
# Use human-friendly indexes here (starting from 1)
|
# Use human-friendly indexes here (starting from 1)
|
||||||
current = i * jobs + j + 1
|
current = i * jobs + j + 1 + start
|
||||||
val['current_epoch'] = current
|
|
||||||
val['is_initial_point'] = current <= INITIAL_POINTS
|
|
||||||
|
|
||||||
logger.debug(f"Optimizer epoch evaluated: {val}")
|
self.evaluate_result(val, current, is_random[j])
|
||||||
|
|
||||||
is_best = HyperoptTools.is_best_loss(val, self.current_best_loss)
|
|
||||||
# This value is assigned here and not in the optimization method
|
|
||||||
# to keep proper order in the list of results. That's because
|
|
||||||
# evaluations can take different time. Here they are aligned in the
|
|
||||||
# order they will be shown to the user.
|
|
||||||
val['is_best'] = is_best
|
|
||||||
val['is_random'] = is_random[j]
|
|
||||||
self.print_results(val)
|
|
||||||
|
|
||||||
if is_best:
|
|
||||||
self.current_best_loss = val['loss']
|
|
||||||
self.current_best_epoch = val
|
|
||||||
|
|
||||||
self._save_result(val)
|
|
||||||
|
|
||||||
pbar.update(current)
|
pbar.update(current)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
@ -100,23 +100,19 @@ class RangeStabilityFilter(IPairList):
|
||||||
if cached_res is not None:
|
if cached_res is not None:
|
||||||
return cached_res
|
return cached_res
|
||||||
|
|
||||||
result = False
|
result = True
|
||||||
if daily_candles is not None and not daily_candles.empty:
|
if daily_candles is not None and not daily_candles.empty:
|
||||||
highest_high = daily_candles['high'].max()
|
highest_high = daily_candles['high'].max()
|
||||||
lowest_low = daily_candles['low'].min()
|
lowest_low = daily_candles['low'].min()
|
||||||
pct_change = ((highest_high - lowest_low) / lowest_low) if lowest_low > 0 else 0
|
pct_change = ((highest_high - lowest_low) / lowest_low) if lowest_low > 0 else 0
|
||||||
if pct_change >= self._min_rate_of_change:
|
if pct_change < self._min_rate_of_change:
|
||||||
result = True
|
|
||||||
else:
|
|
||||||
self.log_once(f"Removed {pair} from whitelist, because rate of change "
|
self.log_once(f"Removed {pair} from whitelist, because rate of change "
|
||||||
f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, "
|
f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, "
|
||||||
f"which is below the threshold of {self._min_rate_of_change}.",
|
f"which is below the threshold of {self._min_rate_of_change}.",
|
||||||
logger.info)
|
logger.info)
|
||||||
result = False
|
result = False
|
||||||
if self._max_rate_of_change:
|
if self._max_rate_of_change:
|
||||||
if pct_change <= self._max_rate_of_change:
|
if pct_change > self._max_rate_of_change:
|
||||||
result = True
|
|
||||||
else:
|
|
||||||
self.log_once(
|
self.log_once(
|
||||||
f"Removed {pair} from whitelist, because rate of change "
|
f"Removed {pair} from whitelist, because rate of change "
|
||||||
f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, "
|
f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, "
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -8,6 +8,7 @@ from fastapi import Depends, FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
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.rpc import RPC, RPCException, RPCHandler
|
from freqtrade.rpc.rpc import RPC, RPCException, RPCHandler
|
||||||
|
@ -37,10 +38,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
|
||||||
|
|
||||||
|
@ -54,7 +55,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
|
||||||
|
|
||||||
|
@ -77,6 +78,8 @@ class RPCManager:
|
||||||
mod.send_msg(msg)
|
mod.send_msg(msg)
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
logger.error(f"Message type '{msg['type']}' not implemented by handler {mod.name}.")
|
logger.error(f"Message type '{msg['type']}' not implemented by handler {mod.name}.")
|
||||||
|
except Exception:
|
||||||
|
logger.exception('Exception occurred within RPC module %s', mod.name)
|
||||||
|
|
||||||
def process_msg_queue(self, queue: deque) -> None:
|
def process_msg_queue(self, queue: deque) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -89,7 +92,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
|
||||||
|
@ -286,7 +286,7 @@ class Telegram(RPCHandler):
|
||||||
if msg['type'] in [RPCMessageType.ENTRY_FILL]:
|
if msg['type'] in [RPCMessageType.ENTRY_FILL]:
|
||||||
message += f"*Open Rate:* `{msg['open_rate']:.8f}`\n"
|
message += f"*Open Rate:* `{msg['open_rate']:.8f}`\n"
|
||||||
elif msg['type'] in [RPCMessageType.ENTRY]:
|
elif msg['type'] in [RPCMessageType.ENTRY]:
|
||||||
message += f"*Open Rate:* `{msg['limit']:.8f}`\n"\
|
message += f"*Open Rate:* `{msg['open_rate']:.8f}`\n"\
|
||||||
f"*Current Rate:* `{msg['current_rate']:.8f}`\n"
|
f"*Current Rate:* `{msg['current_rate']:.8f}`\n"
|
||||||
|
|
||||||
message += f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}"
|
message += f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}"
|
||||||
|
@ -353,8 +353,9 @@ class Telegram(RPCHandler):
|
||||||
f"*Open Rate:* `{msg['open_rate']:.8f}`\n"
|
f"*Open Rate:* `{msg['open_rate']:.8f}`\n"
|
||||||
)
|
)
|
||||||
if msg['type'] == RPCMessageType.EXIT:
|
if msg['type'] == RPCMessageType.EXIT:
|
||||||
message += (f"*Current Rate:* `{msg['current_rate']:.8f}`\n"
|
message += f"*Current Rate:* `{msg['current_rate']:.8f}`\n"
|
||||||
f"*Exit Rate:* `{msg['limit']:.8f}`")
|
if msg['order_rate']:
|
||||||
|
message += f"*Exit Rate:* `{msg['order_rate']:.8f}`"
|
||||||
|
|
||||||
elif msg['type'] == RPCMessageType.EXIT_FILL:
|
elif msg['type'] == RPCMessageType.EXIT_FILL:
|
||||||
message += f"*Exit Rate:* `{msg['close_rate']:.8f}`"
|
message += f"*Exit Rate:* `{msg['close_rate']:.8f}`"
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -118,7 +118,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] = {}
|
||||||
|
@ -613,6 +613,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).
|
||||||
|
@ -637,6 +653,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.'
|
||||||
|
|
|
@ -57,6 +57,8 @@ theme:
|
||||||
logo: "images/logo.png"
|
logo: "images/logo.png"
|
||||||
favicon: "images/logo.png"
|
favicon: "images/logo.png"
|
||||||
custom_dir: "docs/overrides"
|
custom_dir: "docs/overrides"
|
||||||
|
features:
|
||||||
|
- search.share
|
||||||
palette:
|
palette:
|
||||||
- scheme: default
|
- scheme: default
|
||||||
primary: "blue grey"
|
primary: "blue grey"
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user