Merge branch 'develop' into main_refactoring
This commit is contained in:
commit
d5254dff7b
@ -33,15 +33,15 @@ jobs:
|
|||||||
name: pytest
|
name: pytest
|
||||||
- script:
|
- script:
|
||||||
- cp config.json.example config.json
|
- cp config.json.example config.json
|
||||||
- python freqtrade/main.py --datadir freqtrade/tests/testdata backtesting
|
- python freqtrade --datadir freqtrade/tests/testdata backtesting
|
||||||
name: backtest
|
name: backtest
|
||||||
- script:
|
- script:
|
||||||
- cp config.json.example config.json
|
- cp config.json.example config.json
|
||||||
- python freqtrade/main.py --datadir freqtrade/tests/testdata hyperopt -e 5
|
- python freqtrade --datadir freqtrade/tests/testdata hyperopt -e 5
|
||||||
name: hyperopt
|
name: hyperopt
|
||||||
- script: flake8 freqtrade
|
- script: flake8 freqtrade scripts
|
||||||
name: flake8
|
name: flake8
|
||||||
- script: mypy freqtrade
|
- script: mypy freqtrade scripts
|
||||||
name: mypy
|
name: mypy
|
||||||
|
|
||||||
- stage: docker
|
- stage: docker
|
||||||
|
@ -24,37 +24,37 @@ The backtesting is very easy with freqtrade.
|
|||||||
#### With 5 min tickers (Per default)
|
#### With 5 min tickers (Per default)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py backtesting
|
python3 freqtrade backtesting
|
||||||
```
|
```
|
||||||
|
|
||||||
#### With 1 min tickers
|
#### With 1 min tickers
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py backtesting --ticker-interval 1m
|
python3 freqtrade backtesting --ticker-interval 1m
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Update cached pairs with the latest data
|
#### Update cached pairs with the latest data
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py backtesting --refresh-pairs-cached
|
python3 freqtrade backtesting --refresh-pairs-cached
|
||||||
```
|
```
|
||||||
|
|
||||||
#### With live data (do not alter your testdata files)
|
#### With live data (do not alter your testdata files)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py backtesting --live
|
python3 freqtrade backtesting --live
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Using a different on-disk ticker-data source
|
#### Using a different on-disk ticker-data source
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py backtesting --datadir freqtrade/tests/testdata-20180101
|
python3 freqtrade backtesting --datadir freqtrade/tests/testdata-20180101
|
||||||
```
|
```
|
||||||
|
|
||||||
#### With a (custom) strategy file
|
#### With a (custom) strategy file
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py -s TestStrategy backtesting
|
python3 freqtrade -s TestStrategy backtesting
|
||||||
```
|
```
|
||||||
|
|
||||||
Where `-s TestStrategy` refers to the class name within the strategy file `test_strategy.py` found in the `freqtrade/user_data/strategies` directory
|
Where `-s TestStrategy` refers to the class name within the strategy file `test_strategy.py` found in the `freqtrade/user_data/strategies` directory
|
||||||
@ -62,7 +62,7 @@ Where `-s TestStrategy` refers to the class name within the strategy file `test_
|
|||||||
#### Exporting trades to file
|
#### Exporting trades to file
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py backtesting --export trades
|
python3 freqtrade backtesting --export trades
|
||||||
```
|
```
|
||||||
|
|
||||||
The exported trades can be used for [further analysis](#further-backtest-result-analysis), or can be used by the plotting script `plot_dataframe.py` in the scripts folder.
|
The exported trades can be used for [further analysis](#further-backtest-result-analysis), or can be used by the plotting script `plot_dataframe.py` in the scripts folder.
|
||||||
@ -70,7 +70,7 @@ The exported trades can be used for [further analysis](#further-backtest-result-
|
|||||||
#### Exporting trades to file specifying a custom filename
|
#### Exporting trades to file specifying a custom filename
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py backtesting --export trades --export-filename=backtest_teststrategy.json
|
python3 freqtrade backtesting --export trades --export-filename=backtest_teststrategy.json
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Running backtest with smaller testset
|
#### Running backtest with smaller testset
|
||||||
@ -81,7 +81,7 @@ you want to use. The last N ticks/timeframes will be used.
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py backtesting --timerange=-200
|
python3 freqtrade backtesting --timerange=-200
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Advanced use of timerange
|
#### Advanced use of timerange
|
||||||
|
@ -14,7 +14,7 @@ Let assume you have a class called `AwesomeStrategy` in the file `awesome-strate
|
|||||||
2. Start the bot with the param `--strategy AwesomeStrategy` (the parameter is the class name)
|
2. Start the bot with the param `--strategy AwesomeStrategy` (the parameter is the class name)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py --strategy AwesomeStrategy
|
python3 freqtrade --strategy AwesomeStrategy
|
||||||
```
|
```
|
||||||
|
|
||||||
## Change your strategy
|
## Change your strategy
|
||||||
@ -41,13 +41,13 @@ The bot also include a sample strategy called `TestStrategy` you can update: `us
|
|||||||
You can test it with the parameter: `--strategy TestStrategy`
|
You can test it with the parameter: `--strategy TestStrategy`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py --strategy AwesomeStrategy
|
python3 freqtrade --strategy AwesomeStrategy
|
||||||
```
|
```
|
||||||
|
|
||||||
**For the following section we will use the [user_data/strategies/test_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/test_strategy.py)
|
**For the following section we will use the [user_data/strategies/test_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/test_strategy.py)
|
||||||
file as reference.**
|
file as reference.**
|
||||||
|
|
||||||
!!! Note: Strategies and Backtesting
|
!!! Note Strategies and Backtesting
|
||||||
To avoid problems and unexpected differences between Backtesting and dry/live modes, please be aware
|
To avoid problems and unexpected differences between Backtesting and dry/live modes, please be aware
|
||||||
that during backtesting the full time-interval is passed to the `populate_*()` methods at once.
|
that during backtesting the full time-interval is passed to the `populate_*()` methods at once.
|
||||||
It is therefore best to use vectorized operations (across the whole dataframe, not loops) and
|
It is therefore best to use vectorized operations (across the whole dataframe, not loops) and
|
||||||
@ -250,22 +250,19 @@ class Awesomestrategy(IStrategy):
|
|||||||
self.cust_info[metadata["pair"]["crosstime"] = 1
|
self.cust_info[metadata["pair"]["crosstime"] = 1
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! Warning:
|
!!! Warning
|
||||||
The data is not persisted after a bot-restart (or config-reload). Also, the amount of data should be kept smallish (no DataFrames and such), otherwise the bot will start to consume a lot of memory and eventually run out of memory and crash.
|
The data is not persisted after a bot-restart (or config-reload). Also, the amount of data should be kept smallish (no DataFrames and such), otherwise the bot will start to consume a lot of memory and eventually run out of memory and crash.
|
||||||
|
|
||||||
!!! Note:
|
!!! Note
|
||||||
If the data is pair-specific, make sure to use pair as one of the keys in the dictionary.
|
If the data is pair-specific, make sure to use pair as one of the keys in the dictionary.
|
||||||
|
|
||||||
### Additional data (DataProvider)
|
### Additional data (DataProvider)
|
||||||
|
|
||||||
The strategy provides access to the `DataProvider`. This allows you to get additional data to use in your strategy.
|
The strategy provides access to the `DataProvider`. This allows you to get additional data to use in your strategy.
|
||||||
|
|
||||||
!!!Note:
|
|
||||||
The DataProvier is currently not available during backtesting / hyperopt, but this is planned for the future.
|
|
||||||
|
|
||||||
All methods return `None` in case of failure (do not raise an exception).
|
All methods return `None` in case of failure (do not raise an exception).
|
||||||
|
|
||||||
Please always check if the `DataProvider` is available to avoid failures during backtesting.
|
Please always check the mode of operation to select the correct method to get data (samples see below).
|
||||||
|
|
||||||
#### Possible options for DataProvider
|
#### Possible options for DataProvider
|
||||||
|
|
||||||
@ -278,20 +275,23 @@ Please always check if the `DataProvider` is available to avoid failures during
|
|||||||
|
|
||||||
``` python
|
``` python
|
||||||
if self.dp:
|
if self.dp:
|
||||||
if dp.runmode == 'live':
|
if self.dp.runmode in ('live', 'dry_run'):
|
||||||
if ('ETH/BTC', ticker_interval) in self.dp.available_pairs:
|
if (f'{self.stake_currency}/BTC', self.ticker_interval) in self.dp.available_pairs:
|
||||||
data_eth = self.dp.ohlcv(pair='ETH/BTC',
|
data_eth = self.dp.ohlcv(pair='{self.stake_currency}/BTC',
|
||||||
ticker_interval=ticker_interval)
|
ticker_interval=self.ticker_interval)
|
||||||
else:
|
else:
|
||||||
# Get historic ohlcv data (cached on disk).
|
# Get historic ohlcv data (cached on disk).
|
||||||
history_eth = self.dp.historic_ohlcv(pair='ETH/BTC',
|
history_eth = self.dp.historic_ohlcv(pair='{self.stake_currency}/BTC',
|
||||||
ticker_interval='1h')
|
ticker_interval='1h')
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! Warning: Warning about backtesting
|
!!! Warning Warning about backtesting
|
||||||
Be carefull when using dataprovider in backtesting. `historic_ohlcv()` provides the full time-range in one go,
|
Be carefull when using dataprovider in backtesting. `historic_ohlcv()` provides the full time-range in one go,
|
||||||
so please be aware of it and make sure to not "look into the future" to avoid surprises when running in dry/live mode).
|
so please be aware of it and make sure to not "look into the future" to avoid surprises when running in dry/live mode).
|
||||||
|
|
||||||
|
!!! Warning Warning in hyperopt
|
||||||
|
This option cannot currently be used during hyperopt.
|
||||||
|
|
||||||
#### Available Pairs
|
#### Available Pairs
|
||||||
|
|
||||||
``` python
|
``` python
|
||||||
@ -317,7 +317,7 @@ def informative_pairs(self):
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! Warning:
|
!!! Warning
|
||||||
As these pairs will be refreshed as part of the regular whitelist refresh, it's best to keep this list short.
|
As these pairs will be refreshed as part of the regular whitelist refresh, it's best to keep this list short.
|
||||||
All intervals and all pairs can be specified as long as they are available (and active) on the used exchange.
|
All intervals and all pairs can be specified as long as they are available (and active) on the used exchange.
|
||||||
It is however better to use resampling to longer time-intervals when possible
|
It is however better to use resampling to longer time-intervals when possible
|
||||||
@ -327,7 +327,7 @@ def informative_pairs(self):
|
|||||||
|
|
||||||
The strategy provides access to the `Wallets` object. This contains the current balances on the exchange.
|
The strategy provides access to the `Wallets` object. This contains the current balances on the exchange.
|
||||||
|
|
||||||
!!!NOTE:
|
!!! Note
|
||||||
Wallets is not available during backtesting / hyperopt.
|
Wallets is not available during backtesting / hyperopt.
|
||||||
|
|
||||||
Please always check if `Wallets` is available to avoid failures during backtesting.
|
Please always check if `Wallets` is available to avoid failures during backtesting.
|
||||||
@ -355,7 +355,7 @@ The default buy strategy is located in the file
|
|||||||
If you want to use a strategy from a different folder you can pass `--strategy-path`
|
If you want to use a strategy from a different folder you can pass `--strategy-path`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py --strategy AwesomeStrategy --strategy-path /some/folder
|
python3 freqtrade --strategy AwesomeStrategy --strategy-path /some/folder
|
||||||
```
|
```
|
||||||
|
|
||||||
### Further strategy ideas
|
### Further strategy ideas
|
||||||
|
@ -47,7 +47,7 @@ The bot allows you to select which configuration file you want to use. Per
|
|||||||
default, the bot will load the file `./config.json`
|
default, the bot will load the file `./config.json`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py -c path/far/far/away/config.json
|
python3 freqtrade -c path/far/far/away/config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
### How to use multiple configuration files?
|
### How to use multiple configuration files?
|
||||||
@ -63,13 +63,13 @@ empty key and secrete values while running in the Dry Mode (which does not actua
|
|||||||
require them):
|
require them):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py -c ./config.json
|
python3 freqtrade -c ./config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
and specify both configuration files when running in the normal Live Trade Mode:
|
and specify both configuration files when running in the normal Live Trade Mode:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py -c ./config.json -c path/to/secrets/keys.config.json
|
python3 freqtrade -c ./config.json -c path/to/secrets/keys.config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
This could help you hide your private Exchange key and Exchange secrete on you local machine
|
This could help you hide your private Exchange key and Exchange secrete on you local machine
|
||||||
@ -95,7 +95,7 @@ In `user_data/strategies` you have a file `my_awesome_strategy.py` which has
|
|||||||
a strategy class called `AwesomeStrategy` to load it:
|
a strategy class called `AwesomeStrategy` to load it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py --strategy AwesomeStrategy
|
python3 freqtrade --strategy AwesomeStrategy
|
||||||
```
|
```
|
||||||
|
|
||||||
If the bot does not find your strategy file, it will display in an error
|
If the bot does not find your strategy file, it will display in an error
|
||||||
@ -109,7 +109,7 @@ Learn more about strategy file in
|
|||||||
This parameter allows you to add an additional strategy lookup path, which gets
|
This parameter allows you to add an additional strategy lookup path, which gets
|
||||||
checked before the default locations (The passed path must be a folder!):
|
checked before the default locations (The passed path must be a folder!):
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py --strategy AwesomeStrategy --strategy-path /some/folder
|
python3 freqtrade --strategy AwesomeStrategy --strategy-path /some/folder
|
||||||
```
|
```
|
||||||
|
|
||||||
#### How to install a strategy?
|
#### How to install a strategy?
|
||||||
@ -136,7 +136,7 @@ using `--db-url`. This can also be used to specify a custom database
|
|||||||
in production mode. Example command:
|
in production mode. Example command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py -c config.json --db-url sqlite:///tradesv3.dry_run.sqlite
|
python3 freqtrade -c config.json --db-url sqlite:///tradesv3.dry_run.sqlite
|
||||||
```
|
```
|
||||||
|
|
||||||
## Backtesting commands
|
## Backtesting commands
|
||||||
|
@ -14,8 +14,8 @@ Mandatory Parameters are marked as **Required**.
|
|||||||
| Command | Default | Description |
|
| Command | Default | Description |
|
||||||
|----------|---------|-------------|
|
|----------|---------|-------------|
|
||||||
| `max_open_trades` | 3 | **Required.** Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades)
|
| `max_open_trades` | 3 | **Required.** Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades)
|
||||||
| `stake_currency` | BTC | **Required.** Crypto-currency used for trading.
|
| `stake_currency` | BTC | **Required.** Crypto-currency used for trading. [Strategy Override](#parameters-in-the-strategy).
|
||||||
| `stake_amount` | 0.05 | **Required.** Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all available balance.
|
| `stake_amount` | 0.05 | **Required.** Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all available balance. [Strategy Override](#parameters-in-the-strategy).
|
||||||
| `amount_reserve_percent` | 0.05 | Reserve some amount in min pair stake amount. Default is 5%. The bot will reserve `amount_reserve_percent` + stop-loss value when calculating min pair stake amount in order to avoid possible trade refusals.
|
| `amount_reserve_percent` | 0.05 | Reserve some amount in min pair stake amount. Default is 5%. The bot will reserve `amount_reserve_percent` + stop-loss value when calculating min pair stake amount in order to avoid possible trade refusals.
|
||||||
| `ticker_interval` | [1m, 5m, 15m, 30m, 1h, 1d, ...] | The ticker interval to use (1min, 5 min, 15 min, 30 min, 1 hour or 1 day). Default is 5 minutes. [Strategy Override](#parameters-in-the-strategy).
|
| `ticker_interval` | [1m, 5m, 15m, 30m, 1h, 1d, ...] | The ticker interval to use (1min, 5 min, 15 min, 30 min, 1 hour or 1 day). Default is 5 minutes. [Strategy Override](#parameters-in-the-strategy).
|
||||||
| `fiat_display_currency` | USD | **Required.** Fiat currency used to show your profits. More information below.
|
| `fiat_display_currency` | USD | **Required.** Fiat currency used to show your profits. More information below.
|
||||||
@ -77,8 +77,10 @@ Mandatory Parameters are marked as **Required**.
|
|||||||
The following parameters can be set in either configuration file or strategy.
|
The following parameters can be set in either configuration file or strategy.
|
||||||
Values set in the configuration file always overwrite values set in the strategy.
|
Values set in the configuration file always overwrite values set in the strategy.
|
||||||
|
|
||||||
* `minimal_roi`
|
* `stake_currency`
|
||||||
|
* `stake_amount`
|
||||||
* `ticker_interval`
|
* `ticker_interval`
|
||||||
|
* `minimal_roi`
|
||||||
* `stoploss`
|
* `stoploss`
|
||||||
* `trailing_stop`
|
* `trailing_stop`
|
||||||
* `trailing_stop_positive`
|
* `trailing_stop_positive`
|
||||||
|
@ -13,14 +13,14 @@ on BaseVolume. This value can be changed when you run the script.
|
|||||||
Get the 20 currencies based on BaseVolume.
|
Get the 20 currencies based on BaseVolume.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py --dynamic-whitelist
|
python3 freqtrade --dynamic-whitelist
|
||||||
```
|
```
|
||||||
|
|
||||||
**Customize the number of currencies to retrieve**
|
**Customize the number of currencies to retrieve**
|
||||||
Get the 30 currencies based on BaseVolume.
|
Get the 30 currencies based on BaseVolume.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py --dynamic-whitelist 30
|
python3 freqtrade --dynamic-whitelist 30
|
||||||
```
|
```
|
||||||
|
|
||||||
**Exception**
|
**Exception**
|
||||||
|
22
docs/edge.md
22
docs/edge.md
@ -146,16 +146,19 @@ Percentage of allowed risk per trade.
|
|||||||
(defaults to 0.01 so 1%)
|
(defaults to 0.01 so 1%)
|
||||||
|
|
||||||
#### stoploss_range_min
|
#### stoploss_range_min
|
||||||
|
|
||||||
Minimum stoploss.
|
Minimum stoploss.
|
||||||
|
|
||||||
(defaults to -0.01)
|
(defaults to -0.01)
|
||||||
|
|
||||||
#### stoploss_range_max
|
#### stoploss_range_max
|
||||||
|
|
||||||
Maximum stoploss.
|
Maximum stoploss.
|
||||||
|
|
||||||
(defaults to -0.10)
|
(defaults to -0.10)
|
||||||
|
|
||||||
#### stoploss_range_step
|
#### stoploss_range_step
|
||||||
|
|
||||||
As an example if this is set to -0.01 then Edge will test the strategy for \[-0.01, -0,02, -0,03 ..., -0.09, -0.10\] ranges.
|
As an example if this is set to -0.01 then Edge will test the strategy for \[-0.01, -0,02, -0,03 ..., -0.09, -0.10\] ranges.
|
||||||
Note than having a smaller step means having a bigger range which could lead to slow calculation.
|
Note than having a smaller step means having a bigger range which could lead to slow calculation.
|
||||||
|
|
||||||
@ -164,6 +167,7 @@ If you set this parameter to -0.001, you then slow down the Edge calculation by
|
|||||||
(defaults to -0.01)
|
(defaults to -0.01)
|
||||||
|
|
||||||
#### minimum_winrate
|
#### minimum_winrate
|
||||||
|
|
||||||
It filters out pairs which don't have at least minimum_winrate.
|
It filters out pairs which don't have at least minimum_winrate.
|
||||||
|
|
||||||
This comes handy if you want to be conservative and don't comprise win rate in favour of risk reward ratio.
|
This comes handy if you want to be conservative and don't comprise win rate in favour of risk reward ratio.
|
||||||
@ -171,6 +175,7 @@ This comes handy if you want to be conservative and don't comprise win rate in f
|
|||||||
(defaults to 0.60)
|
(defaults to 0.60)
|
||||||
|
|
||||||
#### minimum_expectancy
|
#### minimum_expectancy
|
||||||
|
|
||||||
It filters out pairs which have the expectancy lower than this number.
|
It filters out pairs which have the expectancy lower than this number.
|
||||||
|
|
||||||
Having an expectancy of 0.20 means if you put 10$ on a trade you expect a 12$ return.
|
Having an expectancy of 0.20 means if you put 10$ on a trade you expect a 12$ return.
|
||||||
@ -178,6 +183,7 @@ Having an expectancy of 0.20 means if you put 10$ on a trade you expect a 12$ re
|
|||||||
(defaults to 0.20)
|
(defaults to 0.20)
|
||||||
|
|
||||||
#### min_trade_number
|
#### min_trade_number
|
||||||
|
|
||||||
When calculating *W*, *R* and *E* (expectancy) against historical data, you always want to have a minimum number of trades. The more this number is the more Edge is reliable.
|
When calculating *W*, *R* and *E* (expectancy) against historical data, you always want to have a minimum number of trades. The more this number is the more Edge is reliable.
|
||||||
|
|
||||||
Having a win rate of 100% on a single trade doesn't mean anything at all. But having a win rate of 70% over past 100 trades means clearly something.
|
Having a win rate of 100% on a single trade doesn't mean anything at all. But having a win rate of 70% over past 100 trades means clearly something.
|
||||||
@ -185,6 +191,7 @@ Having a win rate of 100% on a single trade doesn't mean anything at all. But ha
|
|||||||
(defaults to 10, it is highly recommended not to decrease this number)
|
(defaults to 10, it is highly recommended not to decrease this number)
|
||||||
|
|
||||||
#### max_trade_duration_minute
|
#### max_trade_duration_minute
|
||||||
|
|
||||||
Edge will filter out trades with long duration. If a trade is profitable after 1 month, it is hard to evaluate the strategy based on it. But if most of trades are profitable and they have maximum duration of 30 minutes, then it is clearly a good sign.
|
Edge will filter out trades with long duration. If a trade is profitable after 1 month, it is hard to evaluate the strategy based on it. But if most of trades are profitable and they have maximum duration of 30 minutes, then it is clearly a good sign.
|
||||||
|
|
||||||
**NOTICE:** While configuring this value, you should take into consideration your ticker interval. As an example filtering out trades having duration less than one day for a strategy which has 4h interval does not make sense. Default value is set assuming your strategy interval is relatively small (1m or 5m, etc.).
|
**NOTICE:** While configuring this value, you should take into consideration your ticker interval. As an example filtering out trades having duration less than one day for a strategy which has 4h interval does not make sense. Default value is set assuming your strategy interval is relatively small (1m or 5m, etc.).
|
||||||
@ -192,15 +199,17 @@ Edge will filter out trades with long duration. If a trade is profitable after 1
|
|||||||
(defaults to 1 day, i.e. to 60 * 24 = 1440 minutes)
|
(defaults to 1 day, i.e. to 60 * 24 = 1440 minutes)
|
||||||
|
|
||||||
#### remove_pumps
|
#### remove_pumps
|
||||||
|
|
||||||
Edge will remove sudden pumps in a given market while going through historical data. However, given that pumps happen very often in crypto markets, we recommend you keep this off.
|
Edge will remove sudden pumps in a given market while going through historical data. However, given that pumps happen very often in crypto markets, we recommend you keep this off.
|
||||||
|
|
||||||
(defaults to false)
|
(defaults to false)
|
||||||
|
|
||||||
|
|
||||||
## Running Edge independently
|
## Running Edge independently
|
||||||
|
|
||||||
You can run Edge independently in order to see in details the result. Here is an example:
|
You can run Edge independently in order to see in details the result. Here is an example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py edge
|
python3 freqtrade edge
|
||||||
```
|
```
|
||||||
|
|
||||||
An example of its output:
|
An example of its output:
|
||||||
@ -224,18 +233,21 @@ An example of its output:
|
|||||||
| NEBL/BTC | -0.03 | 0.63 | 1.29 | 0.58 | 0.44 | 19 | 59 |
|
| NEBL/BTC | -0.03 | 0.63 | 1.29 | 0.58 | 0.44 | 19 | 59 |
|
||||||
|
|
||||||
### Update cached pairs with the latest data
|
### Update cached pairs with the latest data
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py edge --refresh-pairs-cached
|
python3 freqtrade edge --refresh-pairs-cached
|
||||||
```
|
```
|
||||||
|
|
||||||
### Precising stoploss range
|
### Precising stoploss range
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py edge --stoplosses=-0.01,-0.1,-0.001 #min,max,step
|
python3 freqtrade edge --stoplosses=-0.01,-0.1,-0.001 #min,max,step
|
||||||
```
|
```
|
||||||
|
|
||||||
### Advanced use of timerange
|
### Advanced use of timerange
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py edge --timerange=20181110-20181113
|
python3 freqtrade edge --timerange=20181110-20181113
|
||||||
```
|
```
|
||||||
|
|
||||||
Doing `--timerange=-200` will get the last 200 timeframes from your inputdata. You can also specify specific dates, or a range span indexed by start and stop.
|
Doing `--timerange=-200` will get the last 200 timeframes from your inputdata. You can also specify specific dates, or a range span indexed by start and stop.
|
||||||
|
10
docs/faq.md
10
docs/faq.md
@ -46,22 +46,24 @@ have to run it for 10.000 or more. But it will take an eternity to
|
|||||||
compute.
|
compute.
|
||||||
|
|
||||||
We recommend you to run it at least 10.000 epochs:
|
We recommend you to run it at least 10.000 epochs:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py hyperopt -e 10000
|
python3 freqtrade hyperopt -e 10000
|
||||||
```
|
```
|
||||||
|
|
||||||
or if you want intermediate result to see
|
or if you want intermediate result to see
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
for i in {1..100}; do python3 ./freqtrade/main.py hyperopt -e 100; done
|
for i in {1..100}; do python3 freqtrade hyperopt -e 100; done
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Why it is so long to run hyperopt?
|
#### Why it is so long to run hyperopt?
|
||||||
|
|
||||||
Finding a great Hyperopt results takes time.
|
Finding a great Hyperopt results takes time.
|
||||||
|
|
||||||
If you wonder why it takes a while to find great hyperopt results
|
If you wonder why it takes a while to find great hyperopt results
|
||||||
|
|
||||||
This answer was written during the under the release 0.15.1, when we had
|
This answer was written during the under the release 0.15.1, when we had:
|
||||||
:
|
|
||||||
- 8 triggers
|
- 8 triggers
|
||||||
- 9 guards: let's say we evaluate even 10 values from each
|
- 9 guards: let's say we evaluate even 10 values from each
|
||||||
- 1 stoploss calculation: let's say we want 10 values from that too to
|
- 1 stoploss calculation: let's say we want 10 values from that too to
|
||||||
|
@ -152,7 +152,7 @@ Because hyperopt tries a lot of combinations to find the best parameters it will
|
|||||||
We strongly recommend to use `screen` or `tmux` to prevent any connection loss.
|
We strongly recommend to use `screen` or `tmux` to prevent any connection loss.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py -c config.json hyperopt --customhyperopt <hyperoptname> -e 5000 --spaces all
|
python3 freqtrade -c config.json hyperopt --customhyperopt <hyperoptname> -e 5000 --spaces all
|
||||||
```
|
```
|
||||||
|
|
||||||
Use `<hyperoptname>` as the name of the custom hyperopt used.
|
Use `<hyperoptname>` as the name of the custom hyperopt used.
|
||||||
@ -178,7 +178,7 @@ you want to use. The last N ticks/timeframes will be used.
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./freqtrade/main.py hyperopt --timerange -200
|
python3 freqtrade hyperopt --timerange -200
|
||||||
```
|
```
|
||||||
|
|
||||||
### Running Hyperopt with Smaller Search Space
|
### Running Hyperopt with Smaller Search Space
|
||||||
@ -293,7 +293,7 @@ You can overwrite position stacking in the configuration by explicitly setting `
|
|||||||
|
|
||||||
Enabling the market-position for hyperopt is currently not possible.
|
Enabling the market-position for hyperopt is currently not possible.
|
||||||
|
|
||||||
!!! Note:
|
!!! Note
|
||||||
Dry/live runs will **NOT** use position stacking - therefore it does make sense to also validate the strategy without this as it's closer to reality.
|
Dry/live runs will **NOT** use position stacking - therefore it does make sense to also validate the strategy without this as it's closer to reality.
|
||||||
|
|
||||||
## Next Step
|
## Next Step
|
||||||
|
@ -407,7 +407,7 @@ pip3 install -e .
|
|||||||
If this is the first time you run the bot, ensure you are running it in Dry-run `"dry_run": true,` otherwise it will start to buy and sell coins.
|
If this is the first time you run the bot, ensure you are running it in Dry-run `"dry_run": true,` otherwise it will start to buy and sell coins.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3.6 ./freqtrade/main.py -c config.json
|
python3.6 freqtrade -c config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
*Note*: If you run the bot on a server, you should consider using [Docker](#automatic-installation---docker) a terminal multiplexer like `screen` or [`tmux`](https://en.wikipedia.org/wiki/Tmux) to avoid that the bot is stopped on logout.
|
*Note*: If you run the bot on a server, you should consider using [Docker](#automatic-installation---docker) a terminal multiplexer like `screen` or [`tmux`](https://en.wikipedia.org/wiki/Tmux) to avoid that the bot is stopped on logout.
|
||||||
@ -437,7 +437,7 @@ when it changes.
|
|||||||
The `freqtrade.service.watchdog` file contains an example of the service unit configuration file which uses systemd
|
The `freqtrade.service.watchdog` file contains an example of the service unit configuration file which uses systemd
|
||||||
as the watchdog.
|
as the watchdog.
|
||||||
|
|
||||||
!!! Note:
|
!!! Note
|
||||||
The sd_notify communication between the bot and the systemd service manager will not work if the bot runs in a
|
The sd_notify communication between the bot and the systemd service manager will not work if the bot runs in a
|
||||||
Docker container.
|
Docker container.
|
||||||
|
|
||||||
|
@ -84,5 +84,5 @@ The `-p` pair argument, can be used to plot a single pair
|
|||||||
|
|
||||||
Example
|
Example
|
||||||
```
|
```
|
||||||
python3 scripts/plot_profit.py --datadir ../freqtrade/freqtrade/tests/testdata-20171221/ -p BTC_LTC
|
python3 scripts/plot_profit.py --datadir ../freqtrade/freqtrade/tests/testdata-20171221/ -p LTC/BTC
|
||||||
```
|
```
|
||||||
|
@ -65,11 +65,11 @@ SELECT * FROM trades;
|
|||||||
|
|
||||||
## Fix trade still open after a manual sell on the exchange
|
## Fix trade still open after a manual sell on the exchange
|
||||||
|
|
||||||
!!! Warning:
|
!!! Warning
|
||||||
Manually selling on the exchange should not be done by default, since the bot does not detect this and will try to sell anyway.
|
Manually selling on the exchange should not be done by default, since the bot does not detect this and will try to sell anyway.
|
||||||
/foresell <tradeid> should accomplish the same thing.
|
/foresell <tradeid> should accomplish the same thing.
|
||||||
|
|
||||||
!!! Note:
|
!!! Note
|
||||||
This should not be necessary after /forcesell, as forcesell orders are closed automatically by the bot on the next iteration.
|
This should not be necessary after /forcesell, as forcesell orders are closed automatically by the bot on the next iteration.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
|
@ -28,6 +28,9 @@ official commands. You can ask at any moment for help with `/help`.
|
|||||||
| `/performance` | | Show performance of each finished trade grouped by pair
|
| `/performance` | | Show performance of each finished trade grouped by pair
|
||||||
| `/balance` | | Show account balance per currency
|
| `/balance` | | Show account balance per currency
|
||||||
| `/daily <n>` | 7 | Shows profit or loss per day, over the last n days
|
| `/daily <n>` | 7 | Shows profit or loss per day, over the last n days
|
||||||
|
| `/whitelist` | | Show the current whitelist
|
||||||
|
| `/blacklist [pair]` | | Show the current blacklist, or adds a pair to the blacklist.
|
||||||
|
| `/edge` | | Show validated pairs by Edge if it is enabled.
|
||||||
| `/help` | | Show help message
|
| `/help` | | Show help message
|
||||||
| `/version` | | Show version
|
| `/version` | | Show version
|
||||||
|
|
||||||
@ -55,7 +58,7 @@ Once all positions are sold, run `/stop` to completely stop the bot.
|
|||||||
|
|
||||||
`/reload_conf` resets "max_open_trades" to the value set in the configuration and resets this command.
|
`/reload_conf` resets "max_open_trades" to the value set in the configuration and resets this command.
|
||||||
|
|
||||||
!!! warning:
|
!!! warning
|
||||||
The stop-buy signal is ONLY active while the bot is running, and is not persisted anyway, so restarting the bot will cause this to reset.
|
The stop-buy signal is ONLY active while the bot is running, and is not persisted anyway, so restarting the bot will cause this to reset.
|
||||||
|
|
||||||
### /status
|
### /status
|
||||||
@ -160,6 +163,38 @@ Day Profit BTC Profit USD
|
|||||||
2018-01-01 0.00269130 BTC 34.986 USD
|
2018-01-01 0.00269130 BTC 34.986 USD
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### /whitelist
|
||||||
|
|
||||||
|
Shows the current whitelist
|
||||||
|
|
||||||
|
> Using whitelist `StaticPairList` with 22 pairs
|
||||||
|
> `IOTA/BTC, NEO/BTC, TRX/BTC, VET/BTC, ADA/BTC, ETC/BTC, NCASH/BTC, DASH/BTC, XRP/BTC, XVG/BTC, EOS/BTC, LTC/BTC, OMG/BTC, BTG/BTC, LSK/BTC, ZEC/BTC, HOT/BTC, IOTX/BTC, XMR/BTC, AST/BTC, XLM/BTC, NANO/BTC`
|
||||||
|
|
||||||
|
### /blacklist [pair]
|
||||||
|
|
||||||
|
Shows the current blacklist.
|
||||||
|
If Pair is set, then this pair will be added to the pairlist.
|
||||||
|
Also supports multiple pairs, seperated by a space.
|
||||||
|
Use `/reload_conf` to reset the blacklist.
|
||||||
|
|
||||||
|
> Using blacklist `StaticPairList` with 2 pairs
|
||||||
|
>`DODGE/BTC`, `HOT/BTC`.
|
||||||
|
|
||||||
|
### /edge
|
||||||
|
|
||||||
|
Shows pairs validated by Edge along with their corresponding winrate, expectancy and stoploss values.
|
||||||
|
|
||||||
|
> **Edge only validated following pairs:**
|
||||||
|
```
|
||||||
|
Pair Winrate Expectancy Stoploss
|
||||||
|
-------- --------- ------------ ----------
|
||||||
|
DOCK/ETH 0.522727 0.881821 -0.03
|
||||||
|
PHX/ETH 0.677419 0.560488 -0.03
|
||||||
|
HOT/ETH 0.733333 0.490492 -0.03
|
||||||
|
HC/ETH 0.588235 0.280988 -0.02
|
||||||
|
ARDR/ETH 0.366667 0.143059 -0.01
|
||||||
|
```
|
||||||
|
|
||||||
### /version
|
### /version
|
||||||
|
|
||||||
> **Version:** `0.14.3`
|
> **Version:** `0.14.3`
|
||||||
|
@ -11,6 +11,7 @@ class Binance(Exchange):
|
|||||||
|
|
||||||
_ft_has: Dict = {
|
_ft_has: Dict = {
|
||||||
"stoploss_on_exchange": True,
|
"stoploss_on_exchange": True,
|
||||||
|
"order_time_in_force": ['gtc', 'fok', 'ioc'],
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_order_book(self, pair: str, limit: int = 100) -> dict:
|
def get_order_book(self, pair: str, limit: int = 100) -> dict:
|
||||||
|
@ -13,7 +13,7 @@ import ccxt
|
|||||||
import ccxt.async_support as ccxt_async
|
import ccxt.async_support as ccxt_async
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade import constants, OperationalException, DependencyException, TemporaryError
|
from freqtrade import constants, DependencyException, OperationalException, TemporaryError
|
||||||
from freqtrade.data.converter import parse_ticker_dataframe
|
from freqtrade.data.converter import parse_ticker_dataframe
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -21,13 +21,6 @@ logger = logging.getLogger(__name__)
|
|||||||
API_RETRY_COUNT = 4
|
API_RETRY_COUNT = 4
|
||||||
|
|
||||||
|
|
||||||
# Urls to exchange markets, insert quote and base with .format()
|
|
||||||
_EXCHANGE_URLS = {
|
|
||||||
ccxt.bittrex.__name__: '/Market/Index?MarketName={quote}-{base}',
|
|
||||||
ccxt.binance.__name__: '/tradeDetail.html?symbol={base}_{quote}',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def retrier_async(f):
|
def retrier_async(f):
|
||||||
async def wrapper(*args, **kwargs):
|
async def wrapper(*args, **kwargs):
|
||||||
count = kwargs.pop('count', API_RETRY_COUNT)
|
count = kwargs.pop('count', API_RETRY_COUNT)
|
||||||
@ -72,8 +65,9 @@ class Exchange(object):
|
|||||||
# Dict to specify which options each exchange implements
|
# Dict to specify which options each exchange implements
|
||||||
# TODO: this should be merged with attributes from subclasses
|
# TODO: this should be merged with attributes from subclasses
|
||||||
# To avoid having to copy/paste this to all subclasses.
|
# To avoid having to copy/paste this to all subclasses.
|
||||||
_ft_has = {
|
_ft_has: Dict = {
|
||||||
"stoploss_on_exchange": False,
|
"stoploss_on_exchange": False,
|
||||||
|
"order_time_in_force": ["gtc"],
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, config: dict) -> None:
|
def __init__(self, config: dict) -> None:
|
||||||
@ -275,10 +269,10 @@ class Exchange(object):
|
|||||||
"""
|
"""
|
||||||
Checks if order time in force configured in strategy/config are supported
|
Checks if order time in force configured in strategy/config are supported
|
||||||
"""
|
"""
|
||||||
if any(v != 'gtc' for k, v in order_time_in_force.items()):
|
if any(v not in self._ft_has["order_time_in_force"]
|
||||||
if self.name != 'Binance':
|
for k, v in order_time_in_force.items()):
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
f'Time in force policies are not supporetd for {self.name} yet.')
|
f'Time in force policies are not supported for {self.name} yet.')
|
||||||
|
|
||||||
def exchange_has(self, endpoint: str) -> bool:
|
def exchange_has(self, endpoint: str) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -18,6 +18,7 @@ from freqtrade import DependencyException, constants
|
|||||||
from freqtrade.arguments import Arguments
|
from freqtrade.arguments import Arguments
|
||||||
from freqtrade.configuration import Configuration
|
from freqtrade.configuration import Configuration
|
||||||
from freqtrade.data import history
|
from freqtrade.data import history
|
||||||
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.misc import file_dump_json
|
from freqtrade.misc import file_dump_json
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
|
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
|
||||||
@ -64,6 +65,15 @@ class Backtesting(object):
|
|||||||
self.config['exchange']['uid'] = ''
|
self.config['exchange']['uid'] = ''
|
||||||
self.config['dry_run'] = True
|
self.config['dry_run'] = True
|
||||||
self.strategylist: List[IStrategy] = []
|
self.strategylist: List[IStrategy] = []
|
||||||
|
|
||||||
|
exchange_name = self.config.get('exchange', {}).get('name', 'bittrex').title()
|
||||||
|
self.exchange = ExchangeResolver(exchange_name, self.config).exchange
|
||||||
|
self.fee = self.exchange.get_fee()
|
||||||
|
|
||||||
|
if self.config.get('runmode') != RunMode.HYPEROPT:
|
||||||
|
self.dataprovider = DataProvider(self.config, self.exchange)
|
||||||
|
IStrategy.dp = self.dataprovider
|
||||||
|
|
||||||
if self.config.get('strategy_list', None):
|
if self.config.get('strategy_list', None):
|
||||||
# Force one interval
|
# Force one interval
|
||||||
self.ticker_interval = str(self.config.get('ticker_interval'))
|
self.ticker_interval = str(self.config.get('ticker_interval'))
|
||||||
@ -78,15 +88,13 @@ class Backtesting(object):
|
|||||||
self.strategylist.append(StrategyResolver(self.config).strategy)
|
self.strategylist.append(StrategyResolver(self.config).strategy)
|
||||||
# Load one strategy
|
# Load one strategy
|
||||||
self._set_strategy(self.strategylist[0])
|
self._set_strategy(self.strategylist[0])
|
||||||
exchange_name = self.config.get('exchange', {}).get('name', 'bittrex').title()
|
|
||||||
self.exchange = ExchangeResolver(exchange_name, self.config).exchange
|
|
||||||
self.fee = self.exchange.get_fee()
|
|
||||||
|
|
||||||
def _set_strategy(self, strategy):
|
def _set_strategy(self, strategy):
|
||||||
"""
|
"""
|
||||||
Load strategy into backtesting
|
Load strategy into backtesting
|
||||||
"""
|
"""
|
||||||
self.strategy = strategy
|
self.strategy = strategy
|
||||||
|
|
||||||
self.ticker_interval = self.config.get('ticker_interval')
|
self.ticker_interval = self.config.get('ticker_interval')
|
||||||
self.ticker_interval_mins = constants.TICKER_INTERVAL_MINUTES[self.ticker_interval]
|
self.ticker_interval_mins = constants.TICKER_INTERVAL_MINUTES[self.ticker_interval]
|
||||||
self.tickerdata_to_dataframe = strategy.tickerdata_to_dataframe
|
self.tickerdata_to_dataframe = strategy.tickerdata_to_dataframe
|
||||||
|
@ -56,6 +56,8 @@ class StrategyResolver(IResolver):
|
|||||||
("process_only_new_candles", None, False),
|
("process_only_new_candles", None, False),
|
||||||
("order_types", None, False),
|
("order_types", None, False),
|
||||||
("order_time_in_force", None, False),
|
("order_time_in_force", None, False),
|
||||||
|
("stake_currency", None, False),
|
||||||
|
("stake_amount", None, False),
|
||||||
("use_sell_signal", False, True),
|
("use_sell_signal", False, True),
|
||||||
("sell_profit_only", False, True),
|
("sell_profit_only", False, True),
|
||||||
("ignore_roi_if_buy_signal", False, True),
|
("ignore_roi_if_buy_signal", False, True),
|
||||||
|
@ -456,7 +456,37 @@ class RPC(object):
|
|||||||
def _rpc_whitelist(self) -> Dict:
|
def _rpc_whitelist(self) -> Dict:
|
||||||
""" Returns the currently active whitelist"""
|
""" Returns the currently active whitelist"""
|
||||||
res = {'method': self._freqtrade.pairlists.name,
|
res = {'method': self._freqtrade.pairlists.name,
|
||||||
'length': len(self._freqtrade.pairlists.whitelist),
|
'length': len(self._freqtrade.active_pair_whitelist),
|
||||||
'whitelist': self._freqtrade.active_pair_whitelist
|
'whitelist': self._freqtrade.active_pair_whitelist
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def _rpc_blacklist(self, add: List[str]) -> Dict:
|
||||||
|
""" Returns the currently active blacklist"""
|
||||||
|
if add:
|
||||||
|
stake_currency = self._freqtrade.config.get('stake_currency')
|
||||||
|
for pair in add:
|
||||||
|
if (pair.endswith(stake_currency)
|
||||||
|
and pair not in self._freqtrade.pairlists.blacklist):
|
||||||
|
self._freqtrade.pairlists.blacklist.append(pair)
|
||||||
|
|
||||||
|
res = {'method': self._freqtrade.pairlists.name,
|
||||||
|
'length': len(self._freqtrade.pairlists.blacklist),
|
||||||
|
'blacklist': self._freqtrade.pairlists.blacklist,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _rpc_edge(self) -> List[Dict[str, Any]]:
|
||||||
|
""" Returns information related to Edge """
|
||||||
|
if not self._freqtrade.edge:
|
||||||
|
raise RPCException(f'Edge is not enabled.')
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'Pair': k,
|
||||||
|
'Winrate': v.winrate,
|
||||||
|
'Expectancy': v.expectancy,
|
||||||
|
'Stoploss': v.stoploss,
|
||||||
|
}
|
||||||
|
for k, v in self._freqtrade.edge._cached_pairs.items()
|
||||||
|
]
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
This module manage Telegram communication
|
This module manage Telegram communication
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Callable, Dict
|
from typing import Any, Callable, Dict, List
|
||||||
|
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
from telegram import Bot, ParseMode, ReplyKeyboardMarkup, Update
|
from telegram import Bot, ParseMode, ReplyKeyboardMarkup, Update
|
||||||
@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
|
|||||||
logger.debug('Included module rpc.telegram ...')
|
logger.debug('Included module rpc.telegram ...')
|
||||||
|
|
||||||
|
|
||||||
def authorized_only(command_handler: Callable[[Any, Bot, Update], None]) -> Callable[..., Any]:
|
def authorized_only(command_handler: Callable[..., None]) -> Callable[..., Any]:
|
||||||
"""
|
"""
|
||||||
Decorator to check if the message comes from the correct chat_id
|
Decorator to check if the message comes from the correct chat_id
|
||||||
:param command_handler: Telegram CommandHandler
|
:param command_handler: Telegram CommandHandler
|
||||||
@ -93,6 +93,8 @@ class Telegram(RPC):
|
|||||||
CommandHandler('reload_conf', self._reload_conf),
|
CommandHandler('reload_conf', self._reload_conf),
|
||||||
CommandHandler('stopbuy', self._stopbuy),
|
CommandHandler('stopbuy', self._stopbuy),
|
||||||
CommandHandler('whitelist', self._whitelist),
|
CommandHandler('whitelist', self._whitelist),
|
||||||
|
CommandHandler('blacklist', self._blacklist, pass_args=True),
|
||||||
|
CommandHandler('edge', self._edge),
|
||||||
CommandHandler('help', self._help),
|
CommandHandler('help', self._help),
|
||||||
CommandHandler('version', self._version),
|
CommandHandler('version', self._version),
|
||||||
]
|
]
|
||||||
@ -470,6 +472,39 @@ class Telegram(RPC):
|
|||||||
except RPCException as e:
|
except RPCException as e:
|
||||||
self._send_msg(str(e), bot=bot)
|
self._send_msg(str(e), bot=bot)
|
||||||
|
|
||||||
|
@authorized_only
|
||||||
|
def _blacklist(self, bot: Bot, update: Update, args: List[str]) -> None:
|
||||||
|
"""
|
||||||
|
Handler for /blacklist
|
||||||
|
Shows the currently active blacklist
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
|
||||||
|
blacklist = self._rpc_blacklist(args)
|
||||||
|
|
||||||
|
message = f"Blacklist contains {blacklist['length']} pairs\n"
|
||||||
|
message += f"`{', '.join(blacklist['blacklist'])}`"
|
||||||
|
|
||||||
|
logger.debug(message)
|
||||||
|
self._send_msg(message)
|
||||||
|
except RPCException as e:
|
||||||
|
self._send_msg(str(e), bot=bot)
|
||||||
|
|
||||||
|
@authorized_only
|
||||||
|
def _edge(self, bot: Bot, update: Update) -> None:
|
||||||
|
"""
|
||||||
|
Handler for /edge
|
||||||
|
Shows information related to Edge
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
edge_pairs = self._rpc_edge()
|
||||||
|
print(edge_pairs)
|
||||||
|
edge_pairs_tab = tabulate(edge_pairs, headers='keys', tablefmt='simple')
|
||||||
|
message = f'<b>Edge only validated following pairs:</b>\n<pre>{edge_pairs_tab}</pre>'
|
||||||
|
self._send_msg(message, bot=bot, parse_mode=ParseMode.HTML)
|
||||||
|
except RPCException as e:
|
||||||
|
self._send_msg(str(e), bot=bot)
|
||||||
|
|
||||||
@authorized_only
|
@authorized_only
|
||||||
def _help(self, bot: Bot, update: Update) -> None:
|
def _help(self, bot: Bot, update: Update) -> None:
|
||||||
"""
|
"""
|
||||||
@ -497,6 +532,9 @@ class Telegram(RPC):
|
|||||||
"*/stopbuy:* `Stops buying, but handles open trades gracefully` \n" \
|
"*/stopbuy:* `Stops buying, but handles open trades gracefully` \n" \
|
||||||
"*/reload_conf:* `Reload configuration file` \n" \
|
"*/reload_conf:* `Reload configuration file` \n" \
|
||||||
"*/whitelist:* `Show current whitelist` \n" \
|
"*/whitelist:* `Show current whitelist` \n" \
|
||||||
|
"*/blacklist [pair]:* `Show current blacklist, or adds one or more pairs " \
|
||||||
|
"to the blacklist.` \n" \
|
||||||
|
"*/edge:* `Shows validated pairs by Edge if it is enabeld` \n" \
|
||||||
"*/help:* `This help message`\n" \
|
"*/help:* `This help message`\n" \
|
||||||
"*/version:* `Show version`"
|
"*/version:* `Show version`"
|
||||||
|
|
||||||
|
@ -180,6 +180,10 @@ def default_conf():
|
|||||||
"LTC/BTC",
|
"LTC/BTC",
|
||||||
"XRP/BTC",
|
"XRP/BTC",
|
||||||
"NEO/BTC"
|
"NEO/BTC"
|
||||||
|
],
|
||||||
|
"pair_blacklist": [
|
||||||
|
"DOGE/BTC",
|
||||||
|
"HOT/BTC",
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"telegram": {
|
"telegram": {
|
||||||
|
@ -139,6 +139,28 @@ def test_exchange_resolver(default_conf, mocker, caplog):
|
|||||||
caplog.record_tuples)
|
caplog.record_tuples)
|
||||||
|
|
||||||
|
|
||||||
|
def test_validate_order_time_in_force(default_conf, mocker, caplog):
|
||||||
|
caplog.set_level(logging.INFO)
|
||||||
|
# explicitly test bittrex, exchanges implementing other policies need seperate tests
|
||||||
|
ex = get_patched_exchange(mocker, default_conf, id="bittrex")
|
||||||
|
tif = {
|
||||||
|
"buy": "gtc",
|
||||||
|
"sell": "gtc",
|
||||||
|
}
|
||||||
|
|
||||||
|
ex.validate_order_time_in_force(tif)
|
||||||
|
tif2 = {
|
||||||
|
"buy": "fok",
|
||||||
|
"sell": "ioc",
|
||||||
|
}
|
||||||
|
with pytest.raises(OperationalException, match=r"Time in force.*not supported for .*"):
|
||||||
|
ex.validate_order_time_in_force(tif2)
|
||||||
|
|
||||||
|
# Patch to see if this will pass if the values are in the ft dict
|
||||||
|
ex._ft_has.update({"order_time_in_force": ["gtc", "fok", "ioc"]})
|
||||||
|
ex.validate_order_time_in_force(tif2)
|
||||||
|
|
||||||
|
|
||||||
def test_symbol_amount_prec(default_conf, mocker):
|
def test_symbol_amount_prec(default_conf, mocker):
|
||||||
'''
|
'''
|
||||||
Test rounds down to 4 Decimal places
|
Test rounds down to 4 Decimal places
|
||||||
|
@ -16,6 +16,7 @@ from freqtrade.arguments import Arguments, TimeRange
|
|||||||
from freqtrade.data import history
|
from freqtrade.data import history
|
||||||
from freqtrade.data.btanalysis import evaluate_result_multi
|
from freqtrade.data.btanalysis import evaluate_result_multi
|
||||||
from freqtrade.data.converter import parse_ticker_dataframe
|
from freqtrade.data.converter import parse_ticker_dataframe
|
||||||
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.optimize import get_timeframe
|
from freqtrade.optimize import get_timeframe
|
||||||
from freqtrade.optimize.backtesting import (Backtesting, setup_configuration,
|
from freqtrade.optimize.backtesting import (Backtesting, setup_configuration,
|
||||||
start)
|
start)
|
||||||
@ -346,6 +347,7 @@ def test_backtesting_init(mocker, default_conf, order_types) -> None:
|
|||||||
assert callable(backtesting.strategy.tickerdata_to_dataframe)
|
assert callable(backtesting.strategy.tickerdata_to_dataframe)
|
||||||
assert callable(backtesting.advise_buy)
|
assert callable(backtesting.advise_buy)
|
||||||
assert callable(backtesting.advise_sell)
|
assert callable(backtesting.advise_sell)
|
||||||
|
assert isinstance(backtesting.strategy.dp, DataProvider)
|
||||||
get_fee.assert_called()
|
get_fee.assert_called()
|
||||||
assert backtesting.fee == 0.5
|
assert backtesting.fee == 0.5
|
||||||
assert not backtesting.strategy.order_types["stoploss_on_exchange"]
|
assert not backtesting.strategy.order_types["stoploss_on_exchange"]
|
||||||
|
@ -2,20 +2,21 @@
|
|||||||
# pragma pylint: disable=invalid-sequence-index, invalid-name, too-many-arguments
|
# pragma pylint: disable=invalid-sequence-index, invalid-name, too-many-arguments
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from unittest.mock import MagicMock, ANY, PropertyMock
|
from unittest.mock import ANY, MagicMock, PropertyMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from numpy import isnan
|
from numpy import isnan
|
||||||
|
|
||||||
from freqtrade import TemporaryError, DependencyException
|
from freqtrade import DependencyException, TemporaryError
|
||||||
from freqtrade.worker import Worker
|
from freqtrade.edge import PairInfo
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.rpc import RPC, RPCException
|
from freqtrade.rpc import RPC, RPCException
|
||||||
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
from freqtrade.tests.test_freqtradebot import patch_get_signal
|
|
||||||
from freqtrade.tests.conftest import patch_coinmarketcap, patch_exchange
|
from freqtrade.tests.conftest import patch_coinmarketcap, patch_exchange
|
||||||
|
from freqtrade.tests.test_freqtradebot import patch_get_signal
|
||||||
|
from freqtrade.worker import Worker
|
||||||
|
|
||||||
|
|
||||||
# Functions for recurrent object patching
|
# Functions for recurrent object patching
|
||||||
@ -731,3 +732,54 @@ def test_rpc_whitelist_dynamic(mocker, default_conf) -> None:
|
|||||||
assert ret['method'] == 'VolumePairList'
|
assert ret['method'] == 'VolumePairList'
|
||||||
assert ret['length'] == 4
|
assert ret['length'] == 4
|
||||||
assert ret['whitelist'] == default_conf['exchange']['pair_whitelist']
|
assert ret['whitelist'] == default_conf['exchange']['pair_whitelist']
|
||||||
|
|
||||||
|
|
||||||
|
def test_rpc_blacklist(mocker, default_conf) -> None:
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
|
|
||||||
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
rpc = RPC(freqtradebot)
|
||||||
|
ret = rpc._rpc_blacklist(None)
|
||||||
|
assert ret['method'] == 'StaticPairList'
|
||||||
|
assert len(ret['blacklist']) == 2
|
||||||
|
assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
|
||||||
|
assert ret['blacklist'] == ['DOGE/BTC', 'HOT/BTC']
|
||||||
|
|
||||||
|
ret = rpc._rpc_blacklist(["ETH/BTC"])
|
||||||
|
assert ret['method'] == 'StaticPairList'
|
||||||
|
assert len(ret['blacklist']) == 3
|
||||||
|
assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
|
||||||
|
assert ret['blacklist'] == ['DOGE/BTC', 'HOT/BTC', 'ETH/BTC']
|
||||||
|
|
||||||
|
|
||||||
|
def test_rpc_edge_disabled(mocker, default_conf) -> None:
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
rpc = RPC(freqtradebot)
|
||||||
|
with pytest.raises(RPCException, match=r'Edge is not enabled.'):
|
||||||
|
rpc._rpc_edge()
|
||||||
|
|
||||||
|
|
||||||
|
def test_rpc_edge_enabled(mocker, edge_conf) -> None:
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
|
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
|
||||||
|
return_value={
|
||||||
|
'E/F': PairInfo(-0.02, 0.66, 3.71, 0.50, 1.71, 10, 60),
|
||||||
|
}
|
||||||
|
))
|
||||||
|
freqtradebot = FreqtradeBot(edge_conf)
|
||||||
|
|
||||||
|
rpc = RPC(freqtradebot)
|
||||||
|
ret = rpc._rpc_edge()
|
||||||
|
|
||||||
|
assert len(ret) == 1
|
||||||
|
assert ret[0]['Pair'] == 'E/F'
|
||||||
|
assert ret[0]['Winrate'] == 0.66
|
||||||
|
assert ret[0]['Expectancy'] == 1.71
|
||||||
|
assert ret[0]['Stoploss'] == -0.02
|
||||||
|
@ -13,17 +13,17 @@ from telegram import Chat, Message, Update
|
|||||||
from telegram.error import NetworkError
|
from telegram.error import NetworkError
|
||||||
|
|
||||||
from freqtrade import __version__
|
from freqtrade import __version__
|
||||||
from freqtrade.worker import Worker
|
from freqtrade.edge import PairInfo
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.rpc import RPCMessageType
|
from freqtrade.rpc import RPCMessageType
|
||||||
from freqtrade.rpc.telegram import Telegram, authorized_only
|
from freqtrade.rpc.telegram import Telegram, authorized_only
|
||||||
from freqtrade.strategy.interface import SellType
|
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
from freqtrade.tests.conftest import (get_patched_freqtradebot, get_patched_worker, log_has,
|
from freqtrade.strategy.interface import SellType
|
||||||
patch_exchange)
|
from freqtrade.tests.conftest import (get_patched_freqtradebot, get_patched_worker,
|
||||||
|
log_has, patch_coinmarketcap, patch_exchange)
|
||||||
from freqtrade.tests.test_freqtradebot import patch_get_signal
|
from freqtrade.tests.test_freqtradebot import patch_get_signal
|
||||||
from freqtrade.tests.conftest import patch_coinmarketcap
|
from freqtrade.worker import Worker
|
||||||
|
|
||||||
|
|
||||||
class DummyCls(Telegram):
|
class DummyCls(Telegram):
|
||||||
@ -75,7 +75,7 @@ def test_init(default_conf, mocker, caplog) -> None:
|
|||||||
message_str = "rpc.telegram is listening for following commands: [['status'], ['profit'], " \
|
message_str = "rpc.telegram is listening for following commands: [['status'], ['profit'], " \
|
||||||
"['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], " \
|
"['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], " \
|
||||||
"['performance'], ['daily'], ['count'], ['reload_conf'], " \
|
"['performance'], ['daily'], ['count'], ['reload_conf'], " \
|
||||||
"['stopbuy'], ['whitelist'], ['help'], ['version']]"
|
"['stopbuy'], ['whitelist'], ['blacklist'], ['edge'], ['help'], ['version']]"
|
||||||
|
|
||||||
assert log_has(message_str, caplog.record_tuples)
|
assert log_has(message_str, caplog.record_tuples)
|
||||||
|
|
||||||
@ -1122,6 +1122,73 @@ def test_whitelist_dynamic(default_conf, update, mocker) -> None:
|
|||||||
in msg_mock.call_args_list[0][0][0])
|
in msg_mock.call_args_list[0][0][0])
|
||||||
|
|
||||||
|
|
||||||
|
def test_blacklist_static(default_conf, update, mocker) -> None:
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
msg_mock = MagicMock()
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.rpc.telegram.Telegram',
|
||||||
|
_init=MagicMock(),
|
||||||
|
_send_msg=msg_mock
|
||||||
|
)
|
||||||
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
|
telegram._blacklist(bot=MagicMock(), update=update, args=[])
|
||||||
|
assert msg_mock.call_count == 1
|
||||||
|
assert ("Blacklist contains 2 pairs\n`DOGE/BTC, HOT/BTC`"
|
||||||
|
in msg_mock.call_args_list[0][0][0])
|
||||||
|
|
||||||
|
msg_mock.reset_mock()
|
||||||
|
telegram._blacklist(bot=MagicMock(), update=update, args=["ETH/BTC"])
|
||||||
|
assert msg_mock.call_count == 1
|
||||||
|
assert ("Blacklist contains 3 pairs\n`DOGE/BTC, HOT/BTC, ETH/BTC`"
|
||||||
|
in msg_mock.call_args_list[0][0][0])
|
||||||
|
assert freqtradebot.pairlists.blacklist == ["DOGE/BTC", "HOT/BTC", "ETH/BTC"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_edge_disabled(default_conf, update, mocker) -> None:
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
msg_mock = MagicMock()
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.rpc.telegram.Telegram',
|
||||||
|
_init=MagicMock(),
|
||||||
|
_send_msg=msg_mock
|
||||||
|
)
|
||||||
|
|
||||||
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
|
telegram._edge(bot=MagicMock(), update=update)
|
||||||
|
assert msg_mock.call_count == 1
|
||||||
|
assert "Edge is not enabled." in msg_mock.call_args_list[0][0][0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_edge_enabled(edge_conf, update, mocker) -> None:
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
msg_mock = MagicMock()
|
||||||
|
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
|
||||||
|
return_value={
|
||||||
|
'E/F': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60),
|
||||||
|
}
|
||||||
|
))
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.rpc.telegram.Telegram',
|
||||||
|
_init=MagicMock(),
|
||||||
|
_send_msg=msg_mock
|
||||||
|
)
|
||||||
|
|
||||||
|
freqtradebot = get_patched_freqtradebot(mocker, edge_conf)
|
||||||
|
|
||||||
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
|
telegram._edge(bot=MagicMock(), update=update)
|
||||||
|
assert msg_mock.call_count == 1
|
||||||
|
assert '<b>Edge only validated following pairs:</b>\n<pre>' in msg_mock.call_args_list[0][0][0]
|
||||||
|
assert 'Pair Winrate Expectancy Stoploss' in msg_mock.call_args_list[0][0][0]
|
||||||
|
|
||||||
|
|
||||||
def test_help_handle(default_conf, update, mocker) -> None:
|
def test_help_handle(default_conf, update, mocker) -> None:
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
|
@ -194,11 +194,13 @@ def test_strategy_override_ticker_interval(caplog):
|
|||||||
|
|
||||||
config = {
|
config = {
|
||||||
'strategy': 'DefaultStrategy',
|
'strategy': 'DefaultStrategy',
|
||||||
'ticker_interval': 60
|
'ticker_interval': 60,
|
||||||
|
'stake_currency': 'ETH'
|
||||||
}
|
}
|
||||||
resolver = StrategyResolver(config)
|
resolver = StrategyResolver(config)
|
||||||
|
|
||||||
assert resolver.strategy.ticker_interval == 60
|
assert resolver.strategy.ticker_interval == 60
|
||||||
|
assert resolver.strategy.stake_currency == 'ETH'
|
||||||
assert ('freqtrade.resolvers.strategy_resolver',
|
assert ('freqtrade.resolvers.strategy_resolver',
|
||||||
logging.INFO,
|
logging.INFO,
|
||||||
"Override strategy 'ticker_interval' with value in config file: 60."
|
"Override strategy 'ticker_interval' with value in config file: 60."
|
||||||
|
@ -13,13 +13,15 @@ import requests
|
|||||||
|
|
||||||
from freqtrade import (DependencyException, OperationalException,
|
from freqtrade import (DependencyException, OperationalException,
|
||||||
TemporaryError, constants)
|
TemporaryError, constants)
|
||||||
from freqtrade.worker import Worker
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.rpc import RPCMessageType
|
from freqtrade.rpc import RPCMessageType
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
from freqtrade.strategy.interface import SellType, SellCheckTuple
|
from freqtrade.strategy.interface import SellCheckTuple, SellType
|
||||||
from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange, patch_edge, patch_wallet
|
from freqtrade.tests.conftest import (log_has, log_has_re, patch_edge,
|
||||||
|
patch_exchange, patch_wallet)
|
||||||
|
from freqtrade.worker import Worker
|
||||||
|
|
||||||
|
|
||||||
# Functions for recurrent object patching
|
# Functions for recurrent object patching
|
||||||
@ -109,6 +111,10 @@ def test_worker_running(mocker, default_conf, caplog) -> None:
|
|||||||
assert state is State.RUNNING
|
assert state is State.RUNNING
|
||||||
assert log_has('Changing state to: RUNNING', caplog.record_tuples)
|
assert log_has('Changing state to: RUNNING', caplog.record_tuples)
|
||||||
assert mock_throttle.call_count == 1
|
assert mock_throttle.call_count == 1
|
||||||
|
# Check strategy is loaded, and received a dataprovider object
|
||||||
|
assert freqtrade.strategy
|
||||||
|
assert freqtrade.strategy.dp
|
||||||
|
assert isinstance(freqtrade.strategy.dp, DataProvider)
|
||||||
|
|
||||||
|
|
||||||
def test_worker_stopped(mocker, default_conf, caplog) -> None:
|
def test_worker_stopped(mocker, default_conf, caplog) -> None:
|
||||||
|
@ -5,7 +5,7 @@ flake8==3.7.7
|
|||||||
flake8-type-annotations==0.1.0
|
flake8-type-annotations==0.1.0
|
||||||
flake8-tidy-imports==2.0.0
|
flake8-tidy-imports==2.0.0
|
||||||
pytest==4.3.1
|
pytest==4.3.1
|
||||||
pytest-mock==1.10.1
|
pytest-mock==1.10.2
|
||||||
pytest-asyncio==0.10.0
|
pytest-asyncio==0.10.0
|
||||||
pytest-cov==2.6.1
|
pytest-cov==2.6.1
|
||||||
coveralls==1.7.0
|
coveralls==1.7.0
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
ccxt==1.18.385
|
ccxt==1.18.406
|
||||||
SQLAlchemy==1.3.1
|
SQLAlchemy==1.3.1
|
||||||
python-telegram-bot==11.1.0
|
python-telegram-bot==11.1.0
|
||||||
arrow==0.13.1
|
arrow==0.13.1
|
||||||
|
@ -51,7 +51,6 @@ try:
|
|||||||
|
|
||||||
id = sys.argv[1] # get exchange id from command line arguments
|
id = sys.argv[1] # get exchange id from command line arguments
|
||||||
|
|
||||||
|
|
||||||
# check if the exchange is supported by ccxt
|
# check if the exchange is supported by ccxt
|
||||||
exchange_found = id in ccxt.exchanges
|
exchange_found = id in ccxt.exchanges
|
||||||
|
|
||||||
@ -90,4 +89,3 @@ except Exception as e:
|
|||||||
dump('[' + type(e).__name__ + ']', str(e))
|
dump('[' + type(e).__name__ + ']', str(e))
|
||||||
dump("Usage: python " + sys.argv[0], green('id'))
|
dump("Usage: python " + sys.argv[0], green('id'))
|
||||||
print_supported_exchanges()
|
print_supported_exchanges()
|
||||||
|
|
||||||
|
@ -24,24 +24,22 @@ Example of usage:
|
|||||||
> python3 scripts/plot_dataframe.py --pairs BTC/EUR,XRP/BTC -d user_data/data/
|
> python3 scripts/plot_dataframe.py --pairs BTC/EUR,XRP/BTC -d user_data/data/
|
||||||
--indicators1 sma,ema3 --indicators2 fastk,fastd
|
--indicators1 sma,ema3 --indicators2 fastk,fastd
|
||||||
"""
|
"""
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List, Any
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import plotly.graph_objs as go
|
import plotly.graph_objs as go
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
from plotly import tools
|
from plotly import tools
|
||||||
from plotly.offline import plot
|
from plotly.offline import plot
|
||||||
|
|
||||||
from freqtrade import persistence
|
from freqtrade import persistence
|
||||||
from freqtrade.arguments import Arguments, TimeRange
|
from freqtrade.arguments import Arguments, TimeRange
|
||||||
from freqtrade.data import history
|
from freqtrade.data import history
|
||||||
from freqtrade.data.btanalysis import load_backtest_data, BT_DATA_COLUMNS
|
from freqtrade.data.btanalysis import BT_DATA_COLUMNS, load_backtest_data
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.optimize.backtesting import setup_configuration
|
from freqtrade.optimize.backtesting import setup_configuration
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
@ -148,7 +146,7 @@ def get_tickers_data(strategy, exchange, pairs: List[str], args):
|
|||||||
tickers[pair] = exchange.klines((pair, tick_interval))
|
tickers[pair] = exchange.klines((pair, tick_interval))
|
||||||
else:
|
else:
|
||||||
tickers = history.load_data(
|
tickers = history.load_data(
|
||||||
datadir=Path(_CONF.get("datadir")),
|
datadir=Path(str(_CONF.get("datadir"))),
|
||||||
pairs=pairs,
|
pairs=pairs,
|
||||||
ticker_interval=tick_interval,
|
ticker_interval=tick_interval,
|
||||||
refresh_pairs=_CONF.get('refresh_pairs', False),
|
refresh_pairs=_CONF.get('refresh_pairs', False),
|
||||||
|
@ -12,26 +12,24 @@ Optional Cli parameters
|
|||||||
--timerange: specify what timerange of data to use
|
--timerange: specify what timerange of data to use
|
||||||
--export-filename: Specify where the backtest export is located.
|
--export-filename: Specify where the backtest export is located.
|
||||||
"""
|
"""
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import json
|
|
||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import plotly.graph_objs as go
|
||||||
from plotly import tools
|
from plotly import tools
|
||||||
from plotly.offline import plot
|
from plotly.offline import plot
|
||||||
import plotly.graph_objs as go
|
|
||||||
|
|
||||||
|
from freqtrade import constants, misc
|
||||||
from freqtrade.arguments import Arguments
|
from freqtrade.arguments import Arguments
|
||||||
from freqtrade.configuration import Configuration
|
from freqtrade.configuration import Configuration
|
||||||
from freqtrade import constants
|
|
||||||
from freqtrade.data import history
|
from freqtrade.data import history
|
||||||
from freqtrade.resolvers import StrategyResolver
|
from freqtrade.resolvers import StrategyResolver
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
import freqtrade.misc as misc
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -39,7 +37,7 @@ logger = logging.getLogger(__name__)
|
|||||||
# data:: [ pair, profit-%, enter, exit, time, duration]
|
# data:: [ pair, profit-%, enter, exit, time, duration]
|
||||||
# data:: ["ETH/BTC", 0.0023975, "1515598200", "1515602100", "2018-01-10 07:30:00+00:00", 65]
|
# data:: ["ETH/BTC", 0.0023975, "1515598200", "1515602100", "2018-01-10 07:30:00+00:00", 65]
|
||||||
def make_profit_array(data: List, px: int, min_date: int,
|
def make_profit_array(data: List, px: int, min_date: int,
|
||||||
interval: int,
|
interval: str,
|
||||||
filter_pairs: Optional[List] = None) -> np.ndarray:
|
filter_pairs: Optional[List] = None) -> np.ndarray:
|
||||||
pg = np.zeros(px)
|
pg = np.zeros(px)
|
||||||
filter_pairs = filter_pairs or []
|
filter_pairs = filter_pairs or []
|
||||||
@ -122,7 +120,7 @@ def plot_profit(args: Namespace) -> None:
|
|||||||
logger.info('Filter, keep pairs %s' % pairs)
|
logger.info('Filter, keep pairs %s' % pairs)
|
||||||
|
|
||||||
tickers = history.load_data(
|
tickers = history.load_data(
|
||||||
datadir=Path(config.get('datadir')),
|
datadir=Path(str(config.get('datadir'))),
|
||||||
pairs=pairs,
|
pairs=pairs,
|
||||||
ticker_interval=tick_interval,
|
ticker_interval=tick_interval,
|
||||||
refresh_pairs=False,
|
refresh_pairs=False,
|
||||||
@ -180,7 +178,7 @@ def plot_profit(args: Namespace) -> None:
|
|||||||
fig.append_trace(profit, 2, 1)
|
fig.append_trace(profit, 2, 1)
|
||||||
|
|
||||||
for pair in pairs:
|
for pair in pairs:
|
||||||
pg = make_profit_array(data, num_iterations, min_date, tick_interval, pair)
|
pg = make_profit_array(data, num_iterations, min_date, tick_interval, [pair])
|
||||||
pair_profit = go.Scattergl(
|
pair_profit = go.Scattergl(
|
||||||
x=dates,
|
x=dates,
|
||||||
y=pg,
|
y=pg,
|
||||||
|
2
setup.sh
2
setup.sh
@ -235,7 +235,7 @@ function install() {
|
|||||||
echo "-------------------------"
|
echo "-------------------------"
|
||||||
echo "Run the bot !"
|
echo "Run the bot !"
|
||||||
echo "-------------------------"
|
echo "-------------------------"
|
||||||
echo "You can now use the bot by executing 'source .env/bin/activate; python freqtrade/main.py'."
|
echo "You can now use the bot by executing 'source .env/bin/activate; python freqtrade'."
|
||||||
}
|
}
|
||||||
|
|
||||||
function plot() {
|
function plot() {
|
||||||
|
Loading…
Reference in New Issue
Block a user