Merge branch 'develop' of https://github.com/freqtrade/freqtrade into develop
This commit is contained in:
commit
77935b833b
@ -11,7 +11,18 @@
|
|||||||
"sell": 30
|
"sell": 30
|
||||||
},
|
},
|
||||||
"bid_strategy": {
|
"bid_strategy": {
|
||||||
"ask_last_balance": 0.0
|
"ask_last_balance": 0.0,
|
||||||
|
"use_order_book": false,
|
||||||
|
"order_book_top": 1,
|
||||||
|
"check_depth_of_market": {
|
||||||
|
"enabled": false,
|
||||||
|
"bids_to_ask_delta": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ask_strategy":{
|
||||||
|
"use_order_book": false,
|
||||||
|
"order_book_min": 1,
|
||||||
|
"order_book_max": 9
|
||||||
},
|
},
|
||||||
"exchange": {
|
"exchange": {
|
||||||
"name": "bittrex",
|
"name": "bittrex",
|
||||||
|
@ -20,7 +20,18 @@
|
|||||||
"sell": 30
|
"sell": 30
|
||||||
},
|
},
|
||||||
"bid_strategy": {
|
"bid_strategy": {
|
||||||
"ask_last_balance": 0.0
|
"ask_last_balance": 0.0,
|
||||||
|
"use_order_book": false,
|
||||||
|
"order_book_top": 1,
|
||||||
|
"check_depth_of_market": {
|
||||||
|
"enabled": false,
|
||||||
|
"bids_to_ask_delta": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ask_strategy":{
|
||||||
|
"use_order_book": false,
|
||||||
|
"order_book_min": 1,
|
||||||
|
"order_book_max": 9
|
||||||
},
|
},
|
||||||
"exchange": {
|
"exchange": {
|
||||||
"name": "bittrex",
|
"name": "bittrex",
|
||||||
|
@ -25,12 +25,19 @@ The table below will list all configuration parameters.
|
|||||||
| `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode.
|
| `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode.
|
||||||
| `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file.
|
| `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file.
|
||||||
| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file.
|
| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file.
|
||||||
| `trailing_stoploss` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file).
|
| `trailing_stop` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file).
|
||||||
| `trailing_stoploss_positve` | 0 | No | Changes stop-loss once profit has been reached.
|
| `trailing_stop_positve` | 0 | No | Changes stop-loss once profit has been reached.
|
||||||
| `trailing_stoploss_positve_offset` | 0 | No | Offset on when to apply `trailing_stoploss_positive`. Percentage value which should be positive.
|
| `trailing_stop_positve_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive.
|
||||||
| `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled.
|
| `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled.
|
||||||
| `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled.
|
| `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled.
|
||||||
| `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below.
|
| `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below.
|
||||||
|
| `bid_strategy.use_order_book` | false | No | Allows buying of pair using the rates in Order Book Bids.
|
||||||
|
| `bid_strategy.order_book_top` | 0 | No | Bot will use the top N rate in Order Book Bids. Ie. a value of 2 will allow the bot to pick the 2nd bid rate in Order Book Bids.
|
||||||
|
| `bid_strategy.check_depth_of_market.enabled` | false | No | Does not buy if the % difference of buy orders and sell orders is met in Order Book.
|
||||||
|
| `bid_strategy.check_depth_of_market.bids_to_ask_delta` | 0 | No | The % difference of buy orders and sell orders found in Order Book. A value lesser than 1 means sell orders is greater, while value greater than 1 means buy orders is higher.
|
||||||
|
| `ask_strategy.use_order_book` | false | No | Allows selling of open traded pair using the rates in Order Book Asks.
|
||||||
|
| `ask_strategy.order_book_min` | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate.
|
||||||
|
| `ask_strategy.order_book_max` | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate.
|
||||||
| `exchange.name` | bittrex | Yes | Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename).
|
| `exchange.name` | bittrex | Yes | Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename).
|
||||||
| `exchange.key` | key | No | API key to use for the exchange. Only required when you are in production mode.
|
| `exchange.key` | key | No | API key to use for the exchange. Only required when you are in production mode.
|
||||||
| `exchange.secret` | secret | No | API secret to use for the exchange. Only required when you are in production mode.
|
| `exchange.secret` | secret | No | API secret to use for the exchange. Only required when you are in production mode.
|
||||||
|
@ -8,7 +8,6 @@ To understand how to set up the bot please read the [Bot Configuration](https://
|
|||||||
|
|
||||||
* [Table of Contents](#table-of-contents)
|
* [Table of Contents](#table-of-contents)
|
||||||
* [Easy Installation - Linux Script](#easy-installation---linux-script)
|
* [Easy Installation - Linux Script](#easy-installation---linux-script)
|
||||||
* [Manual installation](#manual-installation)
|
|
||||||
* [Automatic Installation - Docker](#automatic-installation---docker)
|
* [Automatic Installation - Docker](#automatic-installation---docker)
|
||||||
* [Custom Linux MacOS Installation](#custom-installation)
|
* [Custom Linux MacOS Installation](#custom-installation)
|
||||||
- [Requirements](#requirements)
|
- [Requirements](#requirements)
|
||||||
@ -56,34 +55,6 @@ Reset parameter will hard reset your branch (only if you are on `master` or `dev
|
|||||||
|
|
||||||
Config parameter is a `config.json` configurator. This script will ask you questions to setup your bot and create your `config.json`.
|
Config parameter is a `config.json` configurator. This script will ask you questions to setup your bot and create your `config.json`.
|
||||||
|
|
||||||
## Manual installation - Linux/MacOS
|
|
||||||
|
|
||||||
The following steps are made for Linux/MacOS environment
|
|
||||||
|
|
||||||
### 1. Clone the repo
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone git@github.com:freqtrade/freqtrade.git
|
|
||||||
git checkout develop
|
|
||||||
cd freqtrade
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Create the config file
|
|
||||||
|
|
||||||
Switch `"dry_run": true,`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cp config.json.example config.json
|
|
||||||
vi config.json
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Build your docker image and run it
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker build -t freqtrade .
|
|
||||||
docker run --rm -v /etc/localtime:/etc/localtime:ro -v `pwd`/config.json:/freqtrade/config.json -it freqtrade
|
|
||||||
```
|
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
## Automatic Installation - Docker
|
## Automatic Installation - Docker
|
||||||
@ -196,7 +167,7 @@ docker run -d \
|
|||||||
freqtrade --db-url sqlite:///tradesv3.sqlite
|
freqtrade --db-url sqlite:///tradesv3.sqlite
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: db-url defaults to `sqlite:///tradesv3.sqlite` but it defaults to `sqlite://` if `dry_run=True` is being used.
|
*Note*: db-url defaults to `sqlite:///tradesv3.sqlite` but it defaults to `sqlite://` if `dry_run=True` is being used.
|
||||||
To override this behaviour use a custom db-url value: i.e.: `--db-url sqlite:///tradesv3.dryrun.sqlite`
|
To override this behaviour use a custom db-url value: i.e.: `--db-url sqlite:///tradesv3.dryrun.sqlite`
|
||||||
|
|
||||||
### 6. Monitor your Docker instance
|
### 6. Monitor your Docker instance
|
||||||
@ -211,14 +182,15 @@ docker stop freqtrade
|
|||||||
docker start freqtrade
|
docker start freqtrade
|
||||||
```
|
```
|
||||||
|
|
||||||
You do not need to rebuild the image for configuration changes, it will suffice to edit `config.json` and restart the container.
|
For more information on how to operate Docker, please refer to the [official Docker documentation](https://docs.docker.com/).
|
||||||
|
|
||||||
|
*Note*: You do not need to rebuild the image for configuration changes, it will suffice to edit `config.json` and restart the container.
|
||||||
|
|
||||||
### 7. Backtest with docker
|
### 7. Backtest with docker
|
||||||
|
|
||||||
The following assumes that the above steps (1-4) have been completed successfully.
|
The following assumes that the above steps (1-4) have been completed successfully.
|
||||||
Also, backtest-data should be available at `~/.freqtrade/user_data/`.
|
Also, backtest-data should be available at `~/.freqtrade/user_data/`.
|
||||||
|
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name freqtrade \
|
--name freqtrade \
|
||||||
@ -238,12 +210,13 @@ Head over to the [Backtesting Documentation](https://github.com/freqtrade/freqtr
|
|||||||
## Custom Installation
|
## Custom Installation
|
||||||
|
|
||||||
We've included/collected install instructions for Ubuntu 16.04, MacOS, and Windows. These are guidelines and your success may vary with other distros.
|
We've included/collected install instructions for Ubuntu 16.04, MacOS, and Windows. These are guidelines and your success may vary with other distros.
|
||||||
|
OS Specific steps are listed first, the [common](#common) section below is necessary for all systems.
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
Click each one for install guide:
|
Click each one for install guide:
|
||||||
|
|
||||||
* [Python 3.6.x](http://docs.python-guide.org/en/latest/starting/installation/), note the bot was not tested on Python >= 3.7.x
|
* [Python >= 3.6.x](http://docs.python-guide.org/en/latest/starting/installation/)
|
||||||
* [pip](https://pip.pypa.io/en/stable/installing/)
|
* [pip](https://pip.pypa.io/en/stable/installing/)
|
||||||
* [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
|
* [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
|
||||||
* [virtualenv](https://virtualenv.pypa.io/en/stable/installation/) (Recommended)
|
* [virtualenv](https://virtualenv.pypa.io/en/stable/installation/) (Recommended)
|
||||||
@ -251,7 +224,7 @@ Click each one for install guide:
|
|||||||
|
|
||||||
### Linux - Ubuntu 16.04
|
### Linux - Ubuntu 16.04
|
||||||
|
|
||||||
#### 1. Install Python 3.6, Git, and wget
|
#### Install Python 3.6, Git, and wget
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo add-apt-repository ppa:jonathonf/python-3.6
|
sudo add-apt-repository ppa:jonathonf/python-3.6
|
||||||
@ -259,7 +232,34 @@ sudo apt-get update
|
|||||||
sudo apt-get install python3.6 python3.6-venv python3.6-dev build-essential autoconf libtool pkg-config make wget git
|
sudo apt-get install python3.6 python3.6-venv python3.6-dev build-essential autoconf libtool pkg-config make wget git
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. Install TA-Lib
|
#### Raspberry Pi / Raspbian
|
||||||
|
|
||||||
|
Before installing FreqTrade on a Raspberry Pi running the official Raspbian Image, make sure you have at least Python 3.6 installed. The default image only provides Python 3.5. Probably the easiest way to get a recent version of python is [miniconda](https://repo.continuum.io/miniconda/).
|
||||||
|
|
||||||
|
The following assumes that miniconda3 is installed and available in your environment, and is installed.
|
||||||
|
It's recommended to use (mini)conda for this as installation/compilation of `scipy` and `pandas` takes a long time.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
conda config --add channels rpi
|
||||||
|
conda install python=3.6
|
||||||
|
conda create -n freqtrade python=3.6
|
||||||
|
conda install scipy pandas
|
||||||
|
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pip install -e .
|
||||||
|
```
|
||||||
|
|
||||||
|
### MacOS
|
||||||
|
|
||||||
|
#### Install Python 3.6, git, wget and ta-lib
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install python3 git wget
|
||||||
|
```
|
||||||
|
|
||||||
|
### common
|
||||||
|
|
||||||
|
#### 1. Install TA-Lib
|
||||||
|
|
||||||
Official webpage: https://mrjbq7.github.io/ta-lib/install.html
|
Official webpage: https://mrjbq7.github.io/ta-lib/install.html
|
||||||
|
|
||||||
@ -275,15 +275,60 @@ cd ..
|
|||||||
rm -rf ./ta-lib*
|
rm -rf ./ta-lib*
|
||||||
```
|
```
|
||||||
|
|
||||||
|
*Note*: An already downloaded version of ta-lib is included in the repository, as the sourceforge.net source seems to have problems frequently.
|
||||||
|
|
||||||
|
#### 2. Setup your Python virtual environment (virtualenv)
|
||||||
|
|
||||||
|
*Note*: This step is optional but strongly recommended to keep your system organized
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m venv .env
|
||||||
|
source .env/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
#### 3. Install FreqTrade
|
#### 3. Install FreqTrade
|
||||||
|
|
||||||
Clone the git repository:
|
Clone the git repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/freqtrade/freqtrade.git
|
git clone https://github.com/freqtrade/freqtrade.git
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 4. Configure `freqtrade` as a `systemd` service
|
Optionally checkout the stable/master branch:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout master
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Initialize the configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd freqtrade
|
||||||
|
cp config.json.example config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
> *To edit the config please refer to [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md).*
|
||||||
|
|
||||||
|
#### 5. Install python dependencies
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
pip3 install --upgrade pip
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
pip3 install -e .
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6. Run the Bot
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
*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.
|
||||||
|
|
||||||
|
#### 7. [Optional] Configure `freqtrade` as a `systemd` service
|
||||||
|
|
||||||
From the freqtrade repo... copy `freqtrade.service` to your systemd user directory (usually `~/.config/systemd/user`) and update `WorkingDirectory` and `ExecStart` to match your setup.
|
From the freqtrade repo... copy `freqtrade.service` to your systemd user directory (usually `~/.config/systemd/user`) and update `WorkingDirectory` and `ExecStart` to match your setup.
|
||||||
|
|
||||||
@ -299,57 +344,6 @@ For this to be persistent (run when user is logged out) you'll need to enable `l
|
|||||||
sudo loginctl enable-linger "$USER"
|
sudo loginctl enable-linger "$USER"
|
||||||
```
|
```
|
||||||
|
|
||||||
### MacOS
|
|
||||||
|
|
||||||
#### 1. Install Python 3.6, git, wget and ta-lib
|
|
||||||
|
|
||||||
```bash
|
|
||||||
brew install python3 git wget ta-lib
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. Install FreqTrade
|
|
||||||
|
|
||||||
Clone the git repository:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/freqtrade/freqtrade.git
|
|
||||||
```
|
|
||||||
|
|
||||||
Optionally checkout the develop branch:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git checkout develop
|
|
||||||
```
|
|
||||||
|
|
||||||
### Setup Config and virtual env
|
|
||||||
|
|
||||||
#### 1. Initialize the configuration
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd freqtrade
|
|
||||||
cp config.json.example config.json
|
|
||||||
```
|
|
||||||
|
|
||||||
> *To edit the config please refer to [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md).*
|
|
||||||
|
|
||||||
#### 2. Setup your Python virtual environment (virtualenv)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3.6 -m venv .env
|
|
||||||
source .env/bin/activate
|
|
||||||
pip3.6 install --upgrade pip
|
|
||||||
pip3.6 install -r requirements.txt
|
|
||||||
pip3.6 install -e .
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3. Run the Bot
|
|
||||||
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
## Windows
|
## Windows
|
||||||
@ -369,7 +363,7 @@ git clone https://github.com/freqtrade/freqtrade.git
|
|||||||
|
|
||||||
copy paste `config.json` to ``\path\freqtrade-develop\freqtrade`
|
copy paste `config.json` to ``\path\freqtrade-develop\freqtrade`
|
||||||
|
|
||||||
#### install ta-lib
|
#### Install ta-lib
|
||||||
|
|
||||||
Install ta-lib according to the [ta-lib documentation](https://github.com/mrjbq7/ta-lib#windows).
|
Install ta-lib according to the [ta-lib documentation](https://github.com/mrjbq7/ta-lib#windows).
|
||||||
|
|
||||||
@ -390,5 +384,17 @@ REM >pip install TA_Lib‑0.4.17‑cp36‑cp36m‑win32.whl
|
|||||||
|
|
||||||
> Thanks [Owdr](https://github.com/Owdr) for the commands. Source: [Issue #222](https://github.com/freqtrade/freqtrade/issues/222)
|
> Thanks [Owdr](https://github.com/Owdr) for the commands. Source: [Issue #222](https://github.com/freqtrade/freqtrade/issues/222)
|
||||||
|
|
||||||
|
#### Error during installation under Windows
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools
|
||||||
|
```
|
||||||
|
|
||||||
|
Unfortunately, many packages requiring compilation don't provide a pre-build wheel. It is therefore mandatory to have a C/C++ compiler installed and available for your python environment to use.
|
||||||
|
|
||||||
|
The easiest way is to download install Microsoft Visual Studio Community [here](https://visualstudio.microsoft.com/downloads/) and make sure to install "Common Tools for Visual C++" to enable building c code on Windows. Unfortunately, this is a heavy download / dependency (~4Gb) so you might want to consider WSL or docker first.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
Now you have an environment ready, the next step is
|
Now you have an environment ready, the next step is
|
||||||
[Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md)...
|
[Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md)...
|
||||||
|
@ -78,18 +78,35 @@ CONF_SCHEMA = {
|
|||||||
'type': 'number',
|
'type': 'number',
|
||||||
'minimum': 0,
|
'minimum': 0,
|
||||||
'maximum': 1,
|
'maximum': 1,
|
||||||
'exclusiveMaximum': False
|
'exclusiveMaximum': False,
|
||||||
|
'use_order_book': {'type': 'boolean'},
|
||||||
|
'order_book_top': {'type': 'number', 'maximum': 20, 'minimum': 1},
|
||||||
|
'check_depth_of_market': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'enabled': {'type': 'boolean'},
|
||||||
|
'bids_to_ask_delta': {'type': 'number', 'minimum': 0},
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'required': ['ask_last_balance']
|
'required': ['ask_last_balance']
|
||||||
},
|
},
|
||||||
|
'ask_strategy': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'use_order_book': {'type': 'boolean'},
|
||||||
|
'order_book_min': {'type': 'number', 'minimum': 1},
|
||||||
|
'order_book_max': {'type': 'number', 'minimum': 1, 'maximum': 50}
|
||||||
|
}
|
||||||
|
},
|
||||||
'exchange': {'$ref': '#/definitions/exchange'},
|
'exchange': {'$ref': '#/definitions/exchange'},
|
||||||
'experimental': {
|
'experimental': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
'use_sell_signal': {'type': 'boolean'},
|
'use_sell_signal': {'type': 'boolean'},
|
||||||
'sell_profit_only': {'type': 'boolean'},
|
'sell_profit_only': {'type': 'boolean'},
|
||||||
"ignore_roi_if_buy_signal_true": {'type': 'boolean'}
|
'ignore_roi_if_buy_signal_true': {'type': 'boolean'}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'telegram': {
|
'telegram': {
|
||||||
|
@ -116,7 +116,7 @@ class Exchange(object):
|
|||||||
api.urls['api'] = api.urls['test']
|
api.urls['api'] = api.urls['test']
|
||||||
logger.info("Enabled Sandbox API on %s", name)
|
logger.info("Enabled Sandbox API on %s", name)
|
||||||
else:
|
else:
|
||||||
logger.warning(self._api.name, "No Sandbox URL in CCXT, exiting. "
|
logger.warning(name, "No Sandbox URL in CCXT, exiting. "
|
||||||
"Please check your config.json")
|
"Please check your config.json")
|
||||||
raise OperationalException(f'Exchange {name} does not provide a sandbox api')
|
raise OperationalException(f'Exchange {name} does not provide a sandbox api')
|
||||||
|
|
||||||
@ -409,6 +409,37 @@ class Exchange(object):
|
|||||||
except ccxt.BaseError as e:
|
except ccxt.BaseError as e:
|
||||||
raise OperationalException(e)
|
raise OperationalException(e)
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def get_order_book(self, pair: str, limit: int = 100) -> dict:
|
||||||
|
"""
|
||||||
|
get order book level 2 from exchange
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
20180619: bittrex doesnt support limits -.-
|
||||||
|
20180619: binance support limits but only on specific range
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if self._api.name == 'Binance':
|
||||||
|
limit_range = [5, 10, 20, 50, 100, 500, 1000]
|
||||||
|
# get next-higher step in the limit_range list
|
||||||
|
limit = min(list(filter(lambda x: limit <= x, limit_range)))
|
||||||
|
# above script works like loop below (but with slightly better performance):
|
||||||
|
# for limitx in limit_range:
|
||||||
|
# if limit <= limitx:
|
||||||
|
# limit = limitx
|
||||||
|
# break
|
||||||
|
|
||||||
|
return self._api.fetch_l2_order_book(pair, limit)
|
||||||
|
except ccxt.NotSupported as e:
|
||||||
|
raise OperationalException(
|
||||||
|
f'Exchange {self._api.name} does not support fetching order book.'
|
||||||
|
f'Message: {e}')
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not get order book due to {e.__class__.__name__}. Message: {e}')
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e)
|
||||||
|
|
||||||
@retrier
|
@retrier
|
||||||
def get_trades_for_order(self, order_id: str, pair: str, since: datetime) -> List:
|
def get_trades_for_order(self, order_id: str, pair: str, since: datetime) -> List:
|
||||||
if self._conf['dry_run']:
|
if self._conf['dry_run']:
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
Functions to analyze ticker data with indicators and produce buy and sell signals
|
Functions to analyze ticker data with indicators and produce buy and sell signals
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
import pandas as pd
|
||||||
from pandas import DataFrame, to_datetime
|
from pandas import DataFrame, to_datetime
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -31,3 +32,27 @@ def parse_ticker_dataframe(ticker: list) -> DataFrame:
|
|||||||
})
|
})
|
||||||
frame.drop(frame.tail(1).index, inplace=True) # eliminate partial candle
|
frame.drop(frame.tail(1).index, inplace=True) # eliminate partial candle
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
|
|
||||||
|
def order_book_to_dataframe(bids: list, asks: list) -> DataFrame:
|
||||||
|
"""
|
||||||
|
Gets order book list, returns dataframe with below format per suggested by creslin
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
b_sum b_size bids asks a_size a_sum
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
"""
|
||||||
|
cols = ['bids', 'b_size']
|
||||||
|
|
||||||
|
bids_frame = DataFrame(bids, columns=cols)
|
||||||
|
# add cumulative sum column
|
||||||
|
bids_frame['b_sum'] = bids_frame['b_size'].cumsum()
|
||||||
|
cols2 = ['asks', 'a_size']
|
||||||
|
asks_frame = DataFrame(asks, columns=cols2)
|
||||||
|
# add cumulative sum column
|
||||||
|
asks_frame['a_sum'] = asks_frame['a_size'].cumsum()
|
||||||
|
|
||||||
|
frame = pd.concat([bids_frame['b_sum'], bids_frame['b_size'], bids_frame['bids'],
|
||||||
|
asks_frame['asks'], asks_frame['a_size'], asks_frame['a_sum']], axis=1,
|
||||||
|
keys=['b_sum', 'b_size', 'bids', 'asks', 'a_size', 'a_sum'])
|
||||||
|
# logger.info('order book %s', frame )
|
||||||
|
return frame
|
||||||
|
@ -21,6 +21,7 @@ from freqtrade.rpc import RPCManager, RPCMessageType
|
|||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
from freqtrade.strategy.interface import SellType
|
from freqtrade.strategy.interface import SellType
|
||||||
from freqtrade.strategy.resolver import IStrategy, StrategyResolver
|
from freqtrade.strategy.resolver import IStrategy, StrategyResolver
|
||||||
|
from freqtrade.exchange.exchange_helpers import order_book_to_dataframe
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -267,16 +268,40 @@ class FreqtradeBot(object):
|
|||||||
|
|
||||||
return final_list
|
return final_list
|
||||||
|
|
||||||
def get_target_bid(self, ticker: Dict[str, float]) -> float:
|
def get_target_bid(self, pair: str, ticker: Dict[str, float]) -> float:
|
||||||
"""
|
"""
|
||||||
Calculates bid target between current ask price and last price
|
Calculates bid target between current ask price and last price
|
||||||
:param ticker: Ticker to use for getting Ask and Last Price
|
:param ticker: Ticker to use for getting Ask and Last Price
|
||||||
:return: float: Price
|
:return: float: Price
|
||||||
"""
|
"""
|
||||||
if ticker['ask'] < ticker['last']:
|
if ticker['ask'] < ticker['last']:
|
||||||
return ticker['ask']
|
ticker_rate = ticker['ask']
|
||||||
|
else:
|
||||||
balance = self.config['bid_strategy']['ask_last_balance']
|
balance = self.config['bid_strategy']['ask_last_balance']
|
||||||
return ticker['ask'] + balance * (ticker['last'] - ticker['ask'])
|
ticker_rate = ticker['ask'] + balance * (ticker['last'] - ticker['ask'])
|
||||||
|
|
||||||
|
used_rate = ticker_rate
|
||||||
|
config_bid_strategy = self.config.get('bid_strategy', {})
|
||||||
|
if 'use_order_book' in config_bid_strategy and\
|
||||||
|
config_bid_strategy.get('use_order_book', False):
|
||||||
|
logger.info('Getting price from order book')
|
||||||
|
order_book_top = config_bid_strategy.get('order_book_top', 1)
|
||||||
|
order_book = self.exchange.get_order_book(pair, order_book_top)
|
||||||
|
logger.debug('order_book %s', order_book)
|
||||||
|
# top 1 = index 0
|
||||||
|
order_book_rate = order_book['bids'][order_book_top - 1][0]
|
||||||
|
# if ticker has lower rate, then use ticker ( usefull if down trending )
|
||||||
|
logger.info('...top %s order book buy rate %0.8f', order_book_top, order_book_rate)
|
||||||
|
if ticker_rate < order_book_rate:
|
||||||
|
logger.info('...using ticker rate instead %0.8f', ticker_rate)
|
||||||
|
used_rate = ticker_rate
|
||||||
|
else:
|
||||||
|
used_rate = order_book_rate
|
||||||
|
else:
|
||||||
|
logger.info('Using Last Ask / Last Price')
|
||||||
|
used_rate = ticker_rate
|
||||||
|
|
||||||
|
return used_rate
|
||||||
|
|
||||||
def _get_trade_stake_amount(self) -> Optional[float]:
|
def _get_trade_stake_amount(self) -> Optional[float]:
|
||||||
"""
|
"""
|
||||||
@ -368,7 +393,32 @@ class FreqtradeBot(object):
|
|||||||
(buy, sell) = self.strategy.get_signal(_pair, interval, thistory)
|
(buy, sell) = self.strategy.get_signal(_pair, interval, thistory)
|
||||||
|
|
||||||
if buy and not sell:
|
if buy and not sell:
|
||||||
|
bidstrat_check_depth_of_market = self.config.get('bid_strategy', {}).\
|
||||||
|
get('check_depth_of_market', {})
|
||||||
|
if (bidstrat_check_depth_of_market.get('enabled', False)) and\
|
||||||
|
(bidstrat_check_depth_of_market.get('bids_to_ask_delta', 0) > 0):
|
||||||
|
if self._check_depth_of_market_buy(_pair, bidstrat_check_depth_of_market):
|
||||||
return self.execute_buy(_pair, stake_amount)
|
return self.execute_buy(_pair, stake_amount)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
return self.execute_buy(_pair, stake_amount)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _check_depth_of_market_buy(self, pair: str, conf: Dict) -> bool:
|
||||||
|
"""
|
||||||
|
Checks depth of market before executing a buy
|
||||||
|
"""
|
||||||
|
conf_bids_to_ask_delta = conf.get('bids_to_ask_delta', 0)
|
||||||
|
logger.info('checking depth of market for %s', pair)
|
||||||
|
order_book = self.exchange.get_order_book(pair, 1000)
|
||||||
|
order_book_data_frame = order_book_to_dataframe(order_book['bids'], order_book['asks'])
|
||||||
|
order_book_bids = order_book_data_frame['b_size'].sum()
|
||||||
|
order_book_asks = order_book_data_frame['a_size'].sum()
|
||||||
|
bids_ask_delta = order_book_bids / order_book_asks
|
||||||
|
logger.info('bids: %s, asks: %s, delta: %s', order_book_bids,
|
||||||
|
order_book_asks, bids_ask_delta)
|
||||||
|
if bids_ask_delta >= conf_bids_to_ask_delta:
|
||||||
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def execute_buy(self, pair: str, stake_amount: float) -> bool:
|
def execute_buy(self, pair: str, stake_amount: float) -> bool:
|
||||||
@ -383,7 +433,7 @@ class FreqtradeBot(object):
|
|||||||
fiat_currency = self.config.get('fiat_display_currency', None)
|
fiat_currency = self.config.get('fiat_display_currency', None)
|
||||||
|
|
||||||
# Calculate amount
|
# Calculate amount
|
||||||
buy_limit = self.get_target_bid(self.exchange.get_ticker(pair))
|
buy_limit = self.get_target_bid(pair, self.exchange.get_ticker(pair))
|
||||||
|
|
||||||
min_stake_amount = self._get_min_pair_stake_amount(pair_s, buy_limit)
|
min_stake_amount = self._get_min_pair_stake_amount(pair_s, buy_limit)
|
||||||
if min_stake_amount is not None and min_stake_amount > stake_amount:
|
if min_stake_amount is not None and min_stake_amount > stake_amount:
|
||||||
@ -526,7 +576,7 @@ class FreqtradeBot(object):
|
|||||||
raise ValueError(f'attempt to handle closed trade: {trade}')
|
raise ValueError(f'attempt to handle closed trade: {trade}')
|
||||||
|
|
||||||
logger.debug('Handling %s ...', trade)
|
logger.debug('Handling %s ...', trade)
|
||||||
current_rate = self.exchange.get_ticker(trade.pair)['bid']
|
sell_rate = self.exchange.get_ticker(trade.pair)['bid']
|
||||||
|
|
||||||
(buy, sell) = (False, False)
|
(buy, sell) = (False, False)
|
||||||
experimental = self.config.get('experimental', {})
|
experimental = self.config.get('experimental', {})
|
||||||
@ -535,13 +585,43 @@ class FreqtradeBot(object):
|
|||||||
(buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.ticker_interval,
|
(buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.ticker_interval,
|
||||||
ticker)
|
ticker)
|
||||||
|
|
||||||
should_sell = self.strategy.should_sell(trade, current_rate, datetime.utcnow(), buy, sell)
|
config_ask_strategy = self.config.get('ask_strategy', {})
|
||||||
if should_sell.sell_flag:
|
if config_ask_strategy.get('use_order_book', False):
|
||||||
self.execute_sell(trade, current_rate, should_sell.sell_type)
|
logger.info('Using order book for selling...')
|
||||||
|
# logger.debug('Order book %s',orderBook)
|
||||||
|
order_book_min = config_ask_strategy.get('order_book_min', 1)
|
||||||
|
order_book_max = config_ask_strategy.get('order_book_max', 1)
|
||||||
|
|
||||||
|
order_book = self.exchange.get_order_book(trade.pair, order_book_max)
|
||||||
|
|
||||||
|
for i in range(order_book_min, order_book_max + 1):
|
||||||
|
order_book_rate = order_book['asks'][i - 1][0]
|
||||||
|
|
||||||
|
# if orderbook has higher rate (high profit),
|
||||||
|
# use orderbook, otherwise just use bids rate
|
||||||
|
logger.info(' order book asks top %s: %0.8f', i, order_book_rate)
|
||||||
|
if sell_rate < order_book_rate:
|
||||||
|
sell_rate = order_book_rate
|
||||||
|
|
||||||
|
if self.check_sell(trade, sell_rate, buy, sell):
|
||||||
return True
|
return True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logger.info('checking sell')
|
||||||
|
if self.check_sell(trade, sell_rate, buy, sell):
|
||||||
|
return True
|
||||||
|
|
||||||
logger.info('Found no sell signals for whitelisted currencies. Trying again..')
|
logger.info('Found no sell signals for whitelisted currencies. Trying again..')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def check_sell(self, trade: Trade, sell_rate: float, buy: bool, sell: bool) -> bool:
|
||||||
|
should_sell = self.strategy.should_sell(trade, sell_rate, datetime.utcnow(), buy, sell)
|
||||||
|
if should_sell.sell_flag:
|
||||||
|
self.execute_sell(trade, sell_rate, should_sell.sell_type)
|
||||||
|
logger.info('excuted sell')
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def check_handle_timedout(self) -> None:
|
def check_handle_timedout(self) -> None:
|
||||||
"""
|
"""
|
||||||
Check if any orders are timed out and cancel if neccessary
|
Check if any orders are timed out and cancel if neccessary
|
||||||
|
@ -102,7 +102,18 @@ def default_conf():
|
|||||||
"sell": 30
|
"sell": 30
|
||||||
},
|
},
|
||||||
"bid_strategy": {
|
"bid_strategy": {
|
||||||
"ask_last_balance": 0.0
|
"ask_last_balance": 0.0,
|
||||||
|
"use_order_book": False,
|
||||||
|
"order_book_top": 1,
|
||||||
|
"check_depth_of_market": {
|
||||||
|
"enabled": False,
|
||||||
|
"bids_to_ask_delta": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ask_strategy": {
|
||||||
|
"use_order_book": False,
|
||||||
|
"order_book_min": 1,
|
||||||
|
"order_book_max": 1
|
||||||
},
|
},
|
||||||
"exchange": {
|
"exchange": {
|
||||||
"name": "bittrex",
|
"name": "bittrex",
|
||||||
@ -403,6 +414,39 @@ def limit_sell_order():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def order_book_l2():
|
||||||
|
return MagicMock(return_value={
|
||||||
|
'bids': [
|
||||||
|
[0.043936, 10.442],
|
||||||
|
[0.043935, 31.865],
|
||||||
|
[0.043933, 11.212],
|
||||||
|
[0.043928, 0.088],
|
||||||
|
[0.043925, 10.0],
|
||||||
|
[0.043921, 10.0],
|
||||||
|
[0.04392, 37.64],
|
||||||
|
[0.043899, 0.066],
|
||||||
|
[0.043885, 0.676],
|
||||||
|
[0.04387, 22.758]
|
||||||
|
],
|
||||||
|
'asks': [
|
||||||
|
[0.043949, 0.346],
|
||||||
|
[0.04395, 0.608],
|
||||||
|
[0.043951, 3.948],
|
||||||
|
[0.043954, 0.288],
|
||||||
|
[0.043958, 9.277],
|
||||||
|
[0.043995, 1.566],
|
||||||
|
[0.044, 0.588],
|
||||||
|
[0.044002, 0.992],
|
||||||
|
[0.044003, 0.095],
|
||||||
|
[0.04402, 37.64]
|
||||||
|
],
|
||||||
|
'timestamp': None,
|
||||||
|
'datetime': None,
|
||||||
|
'nonce': 288004540
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def ticker_history():
|
def ticker_history():
|
||||||
return [
|
return [
|
||||||
|
@ -515,6 +515,35 @@ def test_get_ticker(default_conf, mocker):
|
|||||||
exchange.get_ticker(pair='ETH/BTC', refresh=True)
|
exchange.get_ticker(pair='ETH/BTC', refresh=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_order_book(default_conf, mocker, order_book_l2):
|
||||||
|
default_conf['exchange']['name'] = 'binance'
|
||||||
|
api_mock = MagicMock()
|
||||||
|
|
||||||
|
api_mock.fetch_l2_order_book = order_book_l2
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
order_book = exchange.get_order_book(pair='ETH/BTC', limit=10)
|
||||||
|
assert 'bids' in order_book
|
||||||
|
assert 'asks' in order_book
|
||||||
|
assert len(order_book['bids']) == 10
|
||||||
|
assert len(order_book['asks']) == 10
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_order_book_exception(default_conf, mocker):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
with pytest.raises(OperationalException):
|
||||||
|
api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.NotSupported)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_order_book(pair='ETH/BTC', limit=50)
|
||||||
|
with pytest.raises(TemporaryError):
|
||||||
|
api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.NetworkError)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_order_book(pair='ETH/BTC', limit=50)
|
||||||
|
with pytest.raises(OperationalException):
|
||||||
|
api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.BaseError)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_order_book(pair='ETH/BTC', limit=50)
|
||||||
|
|
||||||
|
|
||||||
def make_fetch_ohlcv_mock(data):
|
def make_fetch_ohlcv_mock(data):
|
||||||
def fetch_ohlcv_mock(pair, timeframe, since):
|
def fetch_ohlcv_mock(pair, timeframe, since):
|
||||||
if since:
|
if since:
|
||||||
|
@ -159,6 +159,15 @@ def test_gen_pair_whitelist(mocker, default_conf, tickers) -> None:
|
|||||||
assert whitelist == []
|
assert whitelist == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_gen_pair_whitelist_not_supported(mocker, default_conf, tickers) -> None:
|
||||||
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.get_tickers', tickers)
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=False))
|
||||||
|
|
||||||
|
with pytest.raises(OperationalException):
|
||||||
|
freqtrade._gen_pair_whitelist(base_currency='BTC')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Test not implemented")
|
@pytest.mark.skip(reason="Test not implemented")
|
||||||
def test_refresh_whitelist() -> None:
|
def test_refresh_whitelist() -> None:
|
||||||
pass
|
pass
|
||||||
@ -664,21 +673,21 @@ def test_balance_fully_ask_side(mocker, default_conf) -> None:
|
|||||||
default_conf['bid_strategy']['ask_last_balance'] = 0.0
|
default_conf['bid_strategy']['ask_last_balance'] = 0.0
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
assert freqtrade.get_target_bid({'ask': 20, 'last': 10}) == 20
|
assert freqtrade.get_target_bid('ETH/BTC', {'ask': 20, 'last': 10}) == 20
|
||||||
|
|
||||||
|
|
||||||
def test_balance_fully_last_side(mocker, default_conf) -> None:
|
def test_balance_fully_last_side(mocker, default_conf) -> None:
|
||||||
default_conf['bid_strategy']['ask_last_balance'] = 1.0
|
default_conf['bid_strategy']['ask_last_balance'] = 1.0
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
assert freqtrade.get_target_bid({'ask': 20, 'last': 10}) == 10
|
assert freqtrade.get_target_bid('ETH/BTC', {'ask': 20, 'last': 10}) == 10
|
||||||
|
|
||||||
|
|
||||||
def test_balance_bigger_last_ask(mocker, default_conf) -> None:
|
def test_balance_bigger_last_ask(mocker, default_conf) -> None:
|
||||||
default_conf['bid_strategy']['ask_last_balance'] = 1.0
|
default_conf['bid_strategy']['ask_last_balance'] = 1.0
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
assert freqtrade.get_target_bid({'ask': 5, 'last': 10}) == 5
|
assert freqtrade.get_target_bid('ETH/BTC', {'ask': 5, 'last': 10}) == 5
|
||||||
|
|
||||||
|
|
||||||
def test_process_maybe_execute_buy(mocker, default_conf) -> None:
|
def test_process_maybe_execute_buy(mocker, default_conf) -> None:
|
||||||
@ -1878,6 +1887,191 @@ def test_get_real_amount_open_trade(default_conf, mocker):
|
|||||||
assert freqtrade.get_real_amount(trade, order) == amount
|
assert freqtrade.get_real_amount(trade, order) == amount
|
||||||
|
|
||||||
|
|
||||||
|
def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order, fee, markets, mocker,
|
||||||
|
order_book_l2):
|
||||||
|
default_conf['bid_strategy']['check_depth_of_market']['enabled'] = True
|
||||||
|
default_conf['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = 0.1
|
||||||
|
patch_RPCManager(mocker)
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.get_order_book', order_book_l2)
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
validate_pairs=MagicMock(),
|
||||||
|
get_ticker=ticker,
|
||||||
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
|
get_fee=fee,
|
||||||
|
get_markets=markets
|
||||||
|
)
|
||||||
|
|
||||||
|
# Save state of current whitelist
|
||||||
|
whitelist = deepcopy(default_conf['exchange']['pair_whitelist'])
|
||||||
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.create_trade()
|
||||||
|
|
||||||
|
trade = Trade.query.first()
|
||||||
|
assert trade is not None
|
||||||
|
assert trade.stake_amount == 0.001
|
||||||
|
assert trade.is_open
|
||||||
|
assert trade.open_date is not None
|
||||||
|
assert trade.exchange == 'bittrex'
|
||||||
|
|
||||||
|
# Simulate fulfilled LIMIT_BUY order for trade
|
||||||
|
trade.update(limit_buy_order)
|
||||||
|
|
||||||
|
assert trade.open_rate == 0.00001099
|
||||||
|
assert whitelist == default_conf['exchange']['pair_whitelist']
|
||||||
|
|
||||||
|
|
||||||
|
def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_order,
|
||||||
|
fee, markets, mocker, order_book_l2):
|
||||||
|
default_conf['bid_strategy']['check_depth_of_market']['enabled'] = True
|
||||||
|
# delta is 100 which is impossible to reach. hence check_depth_of_market will return false
|
||||||
|
default_conf['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = 100
|
||||||
|
patch_RPCManager(mocker)
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.get_order_book', order_book_l2)
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
validate_pairs=MagicMock(),
|
||||||
|
get_ticker=ticker,
|
||||||
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
|
get_fee=fee,
|
||||||
|
get_markets=markets
|
||||||
|
)
|
||||||
|
# Save state of current whitelist
|
||||||
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.create_trade()
|
||||||
|
|
||||||
|
trade = Trade.query.first()
|
||||||
|
assert trade is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2, markets) -> None:
|
||||||
|
"""
|
||||||
|
test if function get_target_bid will return the order book price
|
||||||
|
instead of the ask rate
|
||||||
|
"""
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
validate_pairs=MagicMock(),
|
||||||
|
get_markets=markets,
|
||||||
|
get_order_book=order_book_l2
|
||||||
|
)
|
||||||
|
default_conf['exchange']['name'] = 'binance'
|
||||||
|
default_conf['bid_strategy']['use_order_book'] = True
|
||||||
|
default_conf['bid_strategy']['order_book_top'] = 2
|
||||||
|
default_conf['bid_strategy']['ask_last_balance'] = 0
|
||||||
|
default_conf['telegram']['enabled'] = False
|
||||||
|
|
||||||
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
assert freqtrade.get_target_bid('ETH/BTC', {'ask': 0.045, 'last': 0.046}) == 0.043935
|
||||||
|
|
||||||
|
|
||||||
|
def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2, markets) -> None:
|
||||||
|
"""
|
||||||
|
test if function get_target_bid will return the ask rate (since its value is lower)
|
||||||
|
instead of the order book rate (even if enabled)
|
||||||
|
"""
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
validate_pairs=MagicMock(),
|
||||||
|
get_markets=markets,
|
||||||
|
get_order_book=order_book_l2
|
||||||
|
)
|
||||||
|
default_conf['exchange']['name'] = 'binance'
|
||||||
|
default_conf['bid_strategy']['use_order_book'] = True
|
||||||
|
default_conf['bid_strategy']['order_book_top'] = 2
|
||||||
|
default_conf['bid_strategy']['ask_last_balance'] = 0
|
||||||
|
default_conf['telegram']['enabled'] = False
|
||||||
|
|
||||||
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
assert freqtrade.get_target_bid('ETH/BTC', {'ask': 0.042, 'last': 0.046}) == 0.042
|
||||||
|
|
||||||
|
|
||||||
|
def test_order_book_bid_strategy3(default_conf, mocker, order_book_l2, markets) -> None:
|
||||||
|
"""
|
||||||
|
test if function get_target_bid will return ask rate instead
|
||||||
|
of the order book rate
|
||||||
|
"""
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
validate_pairs=MagicMock(),
|
||||||
|
get_markets=markets,
|
||||||
|
get_order_book=order_book_l2
|
||||||
|
)
|
||||||
|
default_conf['exchange']['name'] = 'binance'
|
||||||
|
default_conf['bid_strategy']['use_order_book'] = True
|
||||||
|
default_conf['bid_strategy']['order_book_top'] = 1
|
||||||
|
default_conf['bid_strategy']['ask_last_balance'] = 0
|
||||||
|
default_conf['telegram']['enabled'] = False
|
||||||
|
|
||||||
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
|
||||||
|
assert freqtrade.get_target_bid('ETH/BTC', {'ask': 0.03, 'last': 0.029}) == 0.03
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2, markets) -> None:
|
||||||
|
"""
|
||||||
|
test check depth of market
|
||||||
|
"""
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
validate_pairs=MagicMock(),
|
||||||
|
get_markets=markets,
|
||||||
|
get_order_book=order_book_l2
|
||||||
|
)
|
||||||
|
default_conf['telegram']['enabled'] = False
|
||||||
|
default_conf['exchange']['name'] = 'binance'
|
||||||
|
default_conf['bid_strategy']['check_depth_of_market']['enabled'] = True
|
||||||
|
# delta is 100 which is impossible to reach. hence function will return false
|
||||||
|
default_conf['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = 100
|
||||||
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
|
||||||
|
conf = default_conf['bid_strategy']['check_depth_of_market']
|
||||||
|
assert freqtrade._check_depth_of_market_buy('ETH/BTC', conf) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order,
|
||||||
|
fee, markets, mocker, order_book_l2) -> None:
|
||||||
|
"""
|
||||||
|
test order book ask strategy
|
||||||
|
"""
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.get_order_book', order_book_l2)
|
||||||
|
default_conf['exchange']['name'] = 'binance'
|
||||||
|
default_conf['ask_strategy']['use_order_book'] = True
|
||||||
|
default_conf['ask_strategy']['order_book_min'] = 1
|
||||||
|
default_conf['ask_strategy']['order_book_max'] = 2
|
||||||
|
default_conf['telegram']['enabled'] = False
|
||||||
|
patch_RPCManager(mocker)
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
validate_pairs=MagicMock(),
|
||||||
|
get_ticker=MagicMock(return_value={
|
||||||
|
'bid': 0.00001172,
|
||||||
|
'ask': 0.00001173,
|
||||||
|
'last': 0.00001172
|
||||||
|
}),
|
||||||
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
|
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
|
||||||
|
get_fee=fee,
|
||||||
|
get_markets=markets
|
||||||
|
)
|
||||||
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
|
freqtrade.create_trade()
|
||||||
|
|
||||||
|
trade = Trade.query.first()
|
||||||
|
assert trade
|
||||||
|
|
||||||
|
time.sleep(0.01) # Race condition fix
|
||||||
|
trade.update(limit_buy_order)
|
||||||
|
assert trade.is_open is True
|
||||||
|
|
||||||
|
patch_get_signal(freqtrade, value=(False, True))
|
||||||
|
assert freqtrade.handle_trade(trade) is True
|
||||||
|
|
||||||
|
|
||||||
def test_startup_messages(default_conf, mocker):
|
def test_startup_messages(default_conf, mocker):
|
||||||
default_conf['dynamic_whitelist'] = 20
|
default_conf['dynamic_whitelist'] = 20
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
ccxt==1.17.199
|
ccxt==1.17.216
|
||||||
SQLAlchemy==1.2.11
|
SQLAlchemy==1.2.11
|
||||||
python-telegram-bot==10.1.0
|
python-telegram-bot==11.0.0
|
||||||
arrow==0.12.1
|
arrow==0.12.1
|
||||||
cachetools==2.1.0
|
cachetools==2.1.0
|
||||||
requests==2.19.1
|
requests==2.19.1
|
||||||
@ -12,7 +12,7 @@ scipy==1.1.0
|
|||||||
jsonschema==2.6.0
|
jsonschema==2.6.0
|
||||||
numpy==1.15.1
|
numpy==1.15.1
|
||||||
TA-Lib==0.4.17
|
TA-Lib==0.4.17
|
||||||
pytest==3.7.3
|
pytest==3.7.4
|
||||||
pytest-mock==1.10.0
|
pytest-mock==1.10.0
|
||||||
pytest-cov==2.5.1
|
pytest-cov==2.5.1
|
||||||
tabulate==0.8.2
|
tabulate==0.8.2
|
||||||
|
Loading…
Reference in New Issue
Block a user