diff --git a/.travis.yml b/.travis.yml index d24ffcf1b..d5cd52df2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,19 +29,19 @@ jobs: script: - pytest --cov=freqtrade --cov-config=.coveragerc freqtrade/tests/ # Allow failure for coveralls - - coveralls || true + - coveralls || true 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 diff --git a/docs/backtesting.md b/docs/backtesting.md index 932783160..a25d3c1d5 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -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 diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index 8592f6cca..9e754c213 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -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 diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 739a89c07..35e4a776d 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -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 diff --git a/docs/configuration.md b/docs/configuration.md index 4b8d990fe..11b941220 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -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` diff --git a/docs/deprecated.md b/docs/deprecated.md index 25043d981..c218bd360 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -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** diff --git a/docs/edge.md b/docs/edge.md index 7372e3373..b0e0b2d42 100644 --- a/docs/edge.md +++ b/docs/edge.md @@ -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. diff --git a/docs/faq.md b/docs/faq.md index 127f69e9f..60c1509e0 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -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 diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 6e52be47f..e25f35c35 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -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 -e 5000 --spaces all +python3 freqtrade -c config.json hyperopt --customhyperopt -e 5000 --spaces all ``` Use `` 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,8 +293,8 @@ You can overwrite position stacking in the configuration by explicitly setting ` Enabling the market-position for hyperopt is currently not possible. -!!! 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. +!!! 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 diff --git a/docs/installation.md b/docs/installation.md index bd6c50c5a..995bc561b 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -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,9 +437,9 @@ when it changes. The `freqtrade.service.watchdog` file contains an example of the service unit configuration file which uses systemd as the watchdog. -!!! Note: -The sd_notify communication between the bot and the systemd service manager will not work if the bot runs in a -Docker container. +!!! Note + The sd_notify communication between the bot and the systemd service manager will not work if the bot runs in a + Docker container. ------ diff --git a/docs/plotting.md b/docs/plotting.md index a9b191e75..60c642ab3 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -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 ``` diff --git a/docs/sql_cheatsheet.md b/docs/sql_cheatsheet.md index e85aceec8..54f9b8213 100644 --- a/docs/sql_cheatsheet.md +++ b/docs/sql_cheatsheet.md @@ -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 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 diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index face22404..1ca61e54a 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -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 ` | 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,8 +58,8 @@ 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: -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. +!!! 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` diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 127f4e916..18e754e3f 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -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: diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index ea8bcfac1..011be58e5 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -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': - raise OperationalException( - f'Time in force policies are not supporetd for {self.name} yet.') + 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 supported for {self.name} yet.') def exchange_has(self, endpoint: str) -> bool: """ diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 031b490c8..293511fc0 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -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 diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index cece5a5d1..44cc3fe76 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -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), diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index a0ffff107..1203ad7a6 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -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() + ] diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 6771ec803..06bf5efe9 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -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'Edge only validated following pairs:\n
{edge_pairs_tab}
' + 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`" diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 772602f6d..63309506d 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -180,6 +180,10 @@ def default_conf(): "LTC/BTC", "XRP/BTC", "NEO/BTC" + ], + "pair_blacklist": [ + "DOGE/BTC", + "HOT/BTC", ] }, "telegram": { diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 736f2298a..eed16d39b 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -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 diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 40754cfbc..d0b21b8f4 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -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"] diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index 69b428693..46033ec23 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -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 diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index 578e45e24..b44c19688 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -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 'Edge only validated following pairs:\n
' 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()
diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py
index 602ea5dbe..b63180d1e 100644
--- a/freqtrade/tests/strategy/test_strategy.py
+++ b/freqtrade/tests/strategy/test_strategy.py
@@ -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."
diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py
index ece07f6fd..ceeae6865 100644
--- a/freqtrade/tests/test_freqtradebot.py
+++ b/freqtrade/tests/test_freqtradebot.py
@@ -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:
diff --git a/requirements-dev.txt b/requirements-dev.txt
index e0aaf9461..56d7964c3 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -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
diff --git a/requirements.txt b/requirements.txt
index 3a25bb8bc..2bf329bd8 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -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
diff --git a/scripts/get_market_pairs.py b/scripts/get_market_pairs.py
index 6ee6464d3..1a4c593f9 100644
--- a/scripts/get_market_pairs.py
+++ b/scripts/get_market_pairs.py
@@ -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()
-
diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py
index 581518b12..14d57265e 100755
--- a/scripts/plot_dataframe.py
+++ b/scripts/plot_dataframe.py
@@ -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),
diff --git a/scripts/plot_profit.py b/scripts/plot_profit.py
index e2f85932f..394f02116 100755
--- a/scripts/plot_profit.py
+++ b/scripts/plot_profit.py
@@ -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,
diff --git a/setup.sh b/setup.sh
index 66d449037..11df6820e 100755
--- a/setup.sh
+++ b/setup.sh
@@ -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() {