Merge branch 'develop' into main_refactoring

This commit is contained in:
hroff-1902 2019-03-28 11:10:21 +03:00 committed by GitHub
commit d5254dff7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 390 additions and 117 deletions

View File

@ -33,15 +33,15 @@ jobs:
name: pytest
- script:
- cp config.json.example config.json
- python freqtrade/main.py --datadir freqtrade/tests/testdata backtesting
- python freqtrade --datadir freqtrade/tests/testdata backtesting
name: backtest
- script:
- 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
- script: flake8 freqtrade
- script: flake8 freqtrade scripts
name: flake8
- script: mypy freqtrade
- script: mypy freqtrade scripts
name: mypy
- stage: docker

View File

@ -24,37 +24,37 @@ The backtesting is very easy with freqtrade.
#### With 5 min tickers (Per default)
```bash
python3 ./freqtrade/main.py backtesting
python3 freqtrade backtesting
```
#### With 1 min tickers
```bash
python3 ./freqtrade/main.py backtesting --ticker-interval 1m
python3 freqtrade backtesting --ticker-interval 1m
```
#### Update cached pairs with the latest data
```bash
python3 ./freqtrade/main.py backtesting --refresh-pairs-cached
python3 freqtrade backtesting --refresh-pairs-cached
```
#### With live data (do not alter your testdata files)
```bash
python3 ./freqtrade/main.py backtesting --live
python3 freqtrade backtesting --live
```
#### Using a different on-disk ticker-data source
```bash
python3 ./freqtrade/main.py backtesting --datadir freqtrade/tests/testdata-20180101
python3 freqtrade backtesting --datadir freqtrade/tests/testdata-20180101
```
#### With a (custom) strategy file
```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
@ -62,7 +62,7 @@ Where `-s TestStrategy` refers to the class name within the strategy file `test_
#### Exporting trades to file
```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.
@ -70,7 +70,7 @@ The exported trades can be used for [further analysis](#further-backtest-result-
#### Exporting trades to file specifying a custom filename
```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
@ -81,7 +81,7 @@ you want to use. The last N ticks/timeframes will be used.
Example:
```bash
python3 ./freqtrade/main.py backtesting --timerange=-200
python3 freqtrade backtesting --timerange=-200
```
#### Advanced use of timerange

View File

@ -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)
```bash
python3 ./freqtrade/main.py --strategy AwesomeStrategy
python3 freqtrade --strategy AwesomeStrategy
```
## 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`
```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)
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
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
@ -250,22 +250,19 @@ class Awesomestrategy(IStrategy):
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.
!!! Note:
!!! Note
If the data is pair-specific, make sure to use pair as one of the keys in the dictionary.
### Additional data (DataProvider)
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).
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
@ -278,20 +275,23 @@ Please always check if the `DataProvider` is available to avoid failures during
``` python
if self.dp:
if dp.runmode == 'live':
if ('ETH/BTC', ticker_interval) in self.dp.available_pairs:
data_eth = self.dp.ohlcv(pair='ETH/BTC',
ticker_interval=ticker_interval)
if self.dp.runmode in ('live', 'dry_run'):
if (f'{self.stake_currency}/BTC', self.ticker_interval) in self.dp.available_pairs:
data_eth = self.dp.ohlcv(pair='{self.stake_currency}/BTC',
ticker_interval=self.ticker_interval)
else:
# 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')
```
!!! 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,
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
``` 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.
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
@ -327,7 +327,7 @@ def informative_pairs(self):
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.
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`
```bash
python3 ./freqtrade/main.py --strategy AwesomeStrategy --strategy-path /some/folder
python3 freqtrade --strategy AwesomeStrategy --strategy-path /some/folder
```
### Further strategy ideas

View File

@ -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`
```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?
@ -63,13 +63,13 @@ empty key and secrete values while running in the Dry Mode (which does not actua
require them):
```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:
```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
@ -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:
```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
@ -109,7 +109,7 @@ Learn more about strategy file in
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!):
```bash
python3 ./freqtrade/main.py --strategy AwesomeStrategy --strategy-path /some/folder
python3 freqtrade --strategy AwesomeStrategy --strategy-path /some/folder
```
#### 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:
```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

View File

@ -14,8 +14,8 @@ Mandatory Parameters are marked as **Required**.
| 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)
| `stake_currency` | BTC | **Required.** Crypto-currency used for trading.
| `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_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. [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.
| `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.
@ -77,8 +77,10 @@ Mandatory Parameters are marked as **Required**.
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.
* `minimal_roi`
* `stake_currency`
* `stake_amount`
* `ticker_interval`
* `minimal_roi`
* `stoploss`
* `trailing_stop`
* `trailing_stop_positive`

View File

@ -13,14 +13,14 @@ on BaseVolume. This value can be changed when you run the script.
Get the 20 currencies based on BaseVolume.
```bash
python3 ./freqtrade/main.py --dynamic-whitelist
python3 freqtrade --dynamic-whitelist
```
**Customize the number of currencies to retrieve**
Get the 30 currencies based on BaseVolume.
```bash
python3 ./freqtrade/main.py --dynamic-whitelist 30
python3 freqtrade --dynamic-whitelist 30
```
**Exception**

View File

@ -146,16 +146,19 @@ Percentage of allowed risk per trade.
(defaults to 0.01 so 1%)
#### stoploss_range_min
Minimum stoploss.
(defaults to -0.01)
#### stoploss_range_max
Maximum stoploss.
(defaults to -0.10)
#### 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.
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)
#### 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.
@ -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)
#### minimum_expectancy
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.
@ -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)
#### 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.
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)
#### 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.
**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)
#### 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.
(defaults to false)
## Running Edge independently
You can run Edge independently in order to see in details the result. Here is an example:
```bash
python3 ./freqtrade/main.py edge
python3 freqtrade edge
```
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 |
### Update cached pairs with the latest data
```bash
python3 ./freqtrade/main.py edge --refresh-pairs-cached
python3 freqtrade edge --refresh-pairs-cached
```
### Precising stoploss range
```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
```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.

View File

@ -46,22 +46,24 @@ have to run it for 10.000 or more. But it will take an eternity to
compute.
We recommend you to run it at least 10.000 epochs:
```bash
python3 ./freqtrade/main.py hyperopt -e 10000
python3 freqtrade hyperopt -e 10000
```
or if you want intermediate result to see
```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?
Finding a great Hyperopt results takes time.
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
- 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

View File

@ -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.
```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.
@ -178,7 +178,7 @@ you want to use. The last N ticks/timeframes will be used.
Example:
```bash
python3 ./freqtrade/main.py hyperopt --timerange -200
python3 freqtrade hyperopt --timerange -200
```
### 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.
!!! 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.
## Next Step

View File

@ -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.
```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.
@ -437,7 +437,7 @@ when it changes.
The `freqtrade.service.watchdog` file contains an example of the service unit configuration file which uses systemd
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
Docker container.

View File

@ -84,5 +84,5 @@ The `-p` pair argument, can be used to plot a single pair
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
```

View File

@ -65,11 +65,11 @@ SELECT * FROM trades;
## 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.
/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.
```sql

View File

@ -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
| `/balance` | | Show account balance per currency
| `/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
| `/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.
!!! 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.
### /status
@ -160,6 +163,38 @@ Day Profit BTC Profit 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:** `0.14.3`

View File

@ -11,6 +11,7 @@ class Binance(Exchange):
_ft_has: Dict = {
"stoploss_on_exchange": True,
"order_time_in_force": ['gtc', 'fok', 'ioc'],
}
def get_order_book(self, pair: str, limit: int = 100) -> dict:

View File

@ -13,7 +13,7 @@ import ccxt
import ccxt.async_support as ccxt_async
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
logger = logging.getLogger(__name__)
@ -21,13 +21,6 @@ logger = logging.getLogger(__name__)
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):
async def wrapper(*args, **kwargs):
count = kwargs.pop('count', API_RETRY_COUNT)
@ -72,8 +65,9 @@ class Exchange(object):
# Dict to specify which options each exchange implements
# TODO: this should be merged with attributes from subclasses
# To avoid having to copy/paste this to all subclasses.
_ft_has = {
_ft_has: Dict = {
"stoploss_on_exchange": False,
"order_time_in_force": ["gtc"],
}
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
"""
if any(v != 'gtc' for k, v in order_time_in_force.items()):
if self.name != 'Binance':
if any(v not in self._ft_has["order_time_in_force"]
for k, v in order_time_in_force.items()):
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:
"""

View File

@ -18,6 +18,7 @@ from freqtrade import DependencyException, constants
from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration
from freqtrade.data import history
from freqtrade.data.dataprovider import DataProvider
from freqtrade.misc import file_dump_json
from freqtrade.persistence import Trade
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
@ -64,6 +65,15 @@ class Backtesting(object):
self.config['exchange']['uid'] = ''
self.config['dry_run'] = True
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):
# Force one interval
self.ticker_interval = str(self.config.get('ticker_interval'))
@ -78,15 +88,13 @@ class Backtesting(object):
self.strategylist.append(StrategyResolver(self.config).strategy)
# Load one strategy
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):
"""
Load strategy into backtesting
"""
self.strategy = strategy
self.ticker_interval = self.config.get('ticker_interval')
self.ticker_interval_mins = constants.TICKER_INTERVAL_MINUTES[self.ticker_interval]
self.tickerdata_to_dataframe = strategy.tickerdata_to_dataframe

View File

@ -56,6 +56,8 @@ class StrategyResolver(IResolver):
("process_only_new_candles", None, False),
("order_types", None, False),
("order_time_in_force", None, False),
("stake_currency", None, False),
("stake_amount", None, False),
("use_sell_signal", False, True),
("sell_profit_only", False, True),
("ignore_roi_if_buy_signal", False, True),

View File

@ -456,7 +456,37 @@ class RPC(object):
def _rpc_whitelist(self) -> Dict:
""" Returns the currently active whitelist"""
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
}
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()
]

View File

@ -4,7 +4,7 @@
This module manage Telegram communication
"""
import logging
from typing import Any, Callable, Dict
from typing import Any, Callable, Dict, List
from tabulate import tabulate
from telegram import Bot, ParseMode, ReplyKeyboardMarkup, Update
@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
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
:param command_handler: Telegram CommandHandler
@ -93,6 +93,8 @@ class Telegram(RPC):
CommandHandler('reload_conf', self._reload_conf),
CommandHandler('stopbuy', self._stopbuy),
CommandHandler('whitelist', self._whitelist),
CommandHandler('blacklist', self._blacklist, pass_args=True),
CommandHandler('edge', self._edge),
CommandHandler('help', self._help),
CommandHandler('version', self._version),
]
@ -470,6 +472,39 @@ class Telegram(RPC):
except RPCException as e:
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
def _help(self, bot: Bot, update: Update) -> None:
"""
@ -497,6 +532,9 @@ class Telegram(RPC):
"*/stopbuy:* `Stops buying, but handles open trades gracefully` \n" \
"*/reload_conf:* `Reload configuration file` \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" \
"*/version:* `Show version`"

View File

@ -180,6 +180,10 @@ def default_conf():
"LTC/BTC",
"XRP/BTC",
"NEO/BTC"
],
"pair_blacklist": [
"DOGE/BTC",
"HOT/BTC",
]
},
"telegram": {

View File

@ -139,6 +139,28 @@ def test_exchange_resolver(default_conf, mocker, caplog):
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):
'''
Test rounds down to 4 Decimal places

View File

@ -16,6 +16,7 @@ from freqtrade.arguments import Arguments, TimeRange
from freqtrade.data import history
from freqtrade.data.btanalysis import evaluate_result_multi
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.data.dataprovider import DataProvider
from freqtrade.optimize import get_timeframe
from freqtrade.optimize.backtesting import (Backtesting, setup_configuration,
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.advise_buy)
assert callable(backtesting.advise_sell)
assert isinstance(backtesting.strategy.dp, DataProvider)
get_fee.assert_called()
assert backtesting.fee == 0.5
assert not backtesting.strategy.order_types["stoploss_on_exchange"]

View File

@ -2,20 +2,21 @@
# pragma pylint: disable=invalid-sequence-index, invalid-name, too-many-arguments
from datetime import datetime
from unittest.mock import MagicMock, ANY, PropertyMock
from unittest.mock import ANY, MagicMock, PropertyMock
import pytest
from numpy import isnan
from freqtrade import TemporaryError, DependencyException
from freqtrade.worker import Worker
from freqtrade import DependencyException, TemporaryError
from freqtrade.edge import PairInfo
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import Trade
from freqtrade.rpc import RPC, RPCException
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
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.test_freqtradebot import patch_get_signal
from freqtrade.worker import Worker
# Functions for recurrent object patching
@ -731,3 +732,54 @@ def test_rpc_whitelist_dynamic(mocker, default_conf) -> None:
assert ret['method'] == 'VolumePairList'
assert ret['length'] == 4
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

View File

@ -13,17 +13,17 @@ from telegram import Chat, Message, Update
from telegram.error import NetworkError
from freqtrade import __version__
from freqtrade.worker import Worker
from freqtrade.edge import PairInfo
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import Trade
from freqtrade.rpc import RPCMessageType
from freqtrade.rpc.telegram import Telegram, authorized_only
from freqtrade.strategy.interface import SellType
from freqtrade.state import State
from freqtrade.tests.conftest import (get_patched_freqtradebot, get_patched_worker, log_has,
patch_exchange)
from freqtrade.strategy.interface import SellType
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.conftest import patch_coinmarketcap
from freqtrade.worker import Worker
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'], " \
"['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], " \
"['performance'], ['daily'], ['count'], ['reload_conf'], " \
"['stopbuy'], ['whitelist'], ['help'], ['version']]"
"['stopbuy'], ['whitelist'], ['blacklist'], ['edge'], ['help'], ['version']]"
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])
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:
patch_coinmarketcap(mocker)
msg_mock = MagicMock()

View File

@ -194,11 +194,13 @@ def test_strategy_override_ticker_interval(caplog):
config = {
'strategy': 'DefaultStrategy',
'ticker_interval': 60
'ticker_interval': 60,
'stake_currency': 'ETH'
}
resolver = StrategyResolver(config)
assert resolver.strategy.ticker_interval == 60
assert resolver.strategy.stake_currency == 'ETH'
assert ('freqtrade.resolvers.strategy_resolver',
logging.INFO,
"Override strategy 'ticker_interval' with value in config file: 60."

View File

@ -13,13 +13,15 @@ import requests
from freqtrade import (DependencyException, OperationalException,
TemporaryError, constants)
from freqtrade.worker import Worker
from freqtrade.data.dataprovider import DataProvider
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import Trade
from freqtrade.rpc import RPCMessageType
from freqtrade.state import State
from freqtrade.strategy.interface import SellType, SellCheckTuple
from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange, patch_edge, patch_wallet
from freqtrade.strategy.interface import SellCheckTuple, SellType
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
@ -109,6 +111,10 @@ def test_worker_running(mocker, default_conf, caplog) -> None:
assert state is State.RUNNING
assert log_has('Changing state to: RUNNING', caplog.record_tuples)
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:

View File

@ -5,7 +5,7 @@ flake8==3.7.7
flake8-type-annotations==0.1.0
flake8-tidy-imports==2.0.0
pytest==4.3.1
pytest-mock==1.10.1
pytest-mock==1.10.2
pytest-asyncio==0.10.0
pytest-cov==2.6.1
coveralls==1.7.0

View File

@ -1,4 +1,4 @@
ccxt==1.18.385
ccxt==1.18.406
SQLAlchemy==1.3.1
python-telegram-bot==11.1.0
arrow==0.13.1

View File

@ -51,7 +51,6 @@ try:
id = sys.argv[1] # get exchange id from command line arguments
# check if the exchange is supported by ccxt
exchange_found = id in ccxt.exchanges
@ -90,4 +89,3 @@ except Exception as e:
dump('[' + type(e).__name__ + ']', str(e))
dump("Usage: python " + sys.argv[0], green('id'))
print_supported_exchanges()

View File

@ -24,24 +24,22 @@ Example of usage:
> python3 scripts/plot_dataframe.py --pairs BTC/EUR,XRP/BTC -d user_data/data/
--indicators1 sma,ema3 --indicators2 fastk,fastd
"""
import json
import logging
import sys
from argparse import Namespace
from pathlib import Path
from typing import Dict, List, Any
from typing import Any, Dict, List
import pandas as pd
import plotly.graph_objs as go
import pytz
from plotly import tools
from plotly.offline import plot
from freqtrade import persistence
from freqtrade.arguments import Arguments, TimeRange
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.optimize.backtesting import setup_configuration
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))
else:
tickers = history.load_data(
datadir=Path(_CONF.get("datadir")),
datadir=Path(str(_CONF.get("datadir"))),
pairs=pairs,
ticker_interval=tick_interval,
refresh_pairs=_CONF.get('refresh_pairs', False),

View File

@ -12,26 +12,24 @@ Optional Cli parameters
--timerange: specify what timerange of data to use
--export-filename: Specify where the backtest export is located.
"""
import json
import logging
import sys
import json
from argparse import Namespace
from pathlib import Path
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.offline import plot
import plotly.graph_objs as go
from freqtrade import constants, misc
from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration
from freqtrade import constants
from freqtrade.data import history
from freqtrade.resolvers import StrategyResolver
from freqtrade.state import RunMode
import freqtrade.misc as misc
logger = logging.getLogger(__name__)
@ -39,7 +37,7 @@ logger = logging.getLogger(__name__)
# data:: [ pair, profit-%, enter, exit, time, duration]
# 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,
interval: int,
interval: str,
filter_pairs: Optional[List] = None) -> np.ndarray:
pg = np.zeros(px)
filter_pairs = filter_pairs or []
@ -122,7 +120,7 @@ def plot_profit(args: Namespace) -> None:
logger.info('Filter, keep pairs %s' % pairs)
tickers = history.load_data(
datadir=Path(config.get('datadir')),
datadir=Path(str(config.get('datadir'))),
pairs=pairs,
ticker_interval=tick_interval,
refresh_pairs=False,
@ -180,7 +178,7 @@ def plot_profit(args: Namespace) -> None:
fig.append_trace(profit, 2, 1)
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(
x=dates,
y=pg,

View File

@ -235,7 +235,7 @@ function install() {
echo "-------------------------"
echo "Run the bot !"
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() {