Merge with develop
This commit is contained in:
commit
f82b809fcf
@ -2,4 +2,5 @@
|
|||||||
omit =
|
omit =
|
||||||
scripts/*
|
scripts/*
|
||||||
freqtrade/tests/*
|
freqtrade/tests/*
|
||||||
freqtrade/vendor/*
|
freqtrade/vendor/*
|
||||||
|
freqtrade/__main__.py
|
||||||
|
@ -16,15 +16,16 @@ install:
|
|||||||
- pip install --upgrade flake8 coveralls pytest-random-order mypy
|
- pip install --upgrade flake8 coveralls pytest-random-order mypy
|
||||||
- pip install -r requirements.txt
|
- pip install -r requirements.txt
|
||||||
- pip install -e .
|
- pip install -e .
|
||||||
- cp config.json.example config.json
|
|
||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
- script:
|
- script:
|
||||||
- pytest --cov=freqtrade --cov-config=.coveragerc freqtrade/tests/
|
- pytest --cov=freqtrade --cov-config=.coveragerc freqtrade/tests/
|
||||||
- coveralls
|
- coveralls
|
||||||
- script:
|
- script:
|
||||||
|
- cp config.json.example config.json
|
||||||
- python freqtrade/main.py --datadir freqtrade/tests/testdata backtesting
|
- python freqtrade/main.py --datadir freqtrade/tests/testdata backtesting
|
||||||
- script:
|
- script:
|
||||||
|
- cp config.json.example config.json
|
||||||
- python freqtrade/main.py --datadir freqtrade/tests/testdata hyperopt -e 5
|
- python freqtrade/main.py --datadir freqtrade/tests/testdata hyperopt -e 5
|
||||||
- script: flake8 freqtrade
|
- script: flake8 freqtrade
|
||||||
- script: mypy freqtrade
|
- script: mypy freqtrade
|
||||||
|
169
README.md
169
README.md
@ -22,33 +22,10 @@ expect.
|
|||||||
We strongly recommend you to have coding and Python knowledge. Do not
|
We strongly recommend you to have coding and Python knowledge. Do not
|
||||||
hesitate to read the source code and understand the mechanism of this bot.
|
hesitate to read the source code and understand the mechanism of this bot.
|
||||||
|
|
||||||
## Table of Contents
|
## Exchange marketplaces supported
|
||||||
- [Features](#features)
|
- [X] [Bittrex](https://bittrex.com/)
|
||||||
- [Quick start](#quick-start)
|
- [X] [Binance](https://www.binance.com/)
|
||||||
- [Documentations](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md)
|
- [ ] [113 others to tests](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_
|
||||||
- [Installation](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md)
|
|
||||||
- [Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md)
|
|
||||||
- [Strategy Optimization](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md)
|
|
||||||
- [Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md)
|
|
||||||
- [Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md)
|
|
||||||
- [Support](#support)
|
|
||||||
- [Help](#help--slack)
|
|
||||||
- [Bugs](#bugs--issues)
|
|
||||||
- [Feature Requests](#feature-requests)
|
|
||||||
- [Pull Requests](#pull-requests)
|
|
||||||
- [Basic Usage](#basic-usage)
|
|
||||||
- [Bot commands](#bot-commands)
|
|
||||||
- [Telegram RPC commands](#telegram-rpc-commands)
|
|
||||||
- [Requirements](#requirements)
|
|
||||||
- [Min hardware required](#min-hardware-required)
|
|
||||||
- [Software requirements](#software-requirements)
|
|
||||||
|
|
||||||
## Branches
|
|
||||||
The project is currently setup in two main branches:
|
|
||||||
- `develop` - This branch has often new features, but might also cause
|
|
||||||
breaking changes.
|
|
||||||
- `master` - This branch contains the latest stable release. The bot
|
|
||||||
'should' be stable on this branch, and is generally well tested.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- [x] **Based on Python 3.6+**: For botting on any operating system -
|
- [x] **Based on Python 3.6+**: For botting on any operating system -
|
||||||
@ -65,74 +42,50 @@ strategy parameters with real exchange data.
|
|||||||
- [x] **Daily summary of profit/loss**: Provide a daily summary of your profit/loss.
|
- [x] **Daily summary of profit/loss**: Provide a daily summary of your profit/loss.
|
||||||
- [x] **Performance status report**: Provide a performance status of your current trades.
|
- [x] **Performance status report**: Provide a performance status of your current trades.
|
||||||
|
|
||||||
### Exchange marketplaces supported
|
## Table of Contents
|
||||||
- [X] [Bittrex](https://bittrex.com/)
|
- [Quick start](#quick-start)
|
||||||
- [X] [Binance](https://www.binance.com/)
|
- [Documentations](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md)
|
||||||
- [ ] [113 others to tests](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_
|
- [Installation](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md)
|
||||||
|
- [Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md)
|
||||||
|
- [Strategy Optimization](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md)
|
||||||
|
- [Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md)
|
||||||
|
- [Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md)
|
||||||
|
- [Basic Usage](#basic-usage)
|
||||||
|
- [Bot commands](#bot-commands)
|
||||||
|
- [Telegram RPC commands](#telegram-rpc-commands)
|
||||||
|
- [Support](#support)
|
||||||
|
- [Help](#help--slack)
|
||||||
|
- [Bugs](#bugs--issues)
|
||||||
|
- [Feature Requests](#feature-requests)
|
||||||
|
- [Pull Requests](#pull-requests)
|
||||||
|
- [Requirements](#requirements)
|
||||||
|
- [Min hardware required](#min-hardware-required)
|
||||||
|
- [Software requirements](#software-requirements)
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
This quick start section is a very short explanation on how to test the
|
Freqtrade provides a Linux/macOS script to install all dependencies and help you to configure the bot.
|
||||||
bot in dry-run. We invite you to read the
|
|
||||||
[bot documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md)
|
|
||||||
to ensure you understand how the bot is working.
|
|
||||||
|
|
||||||
### Easy installation
|
|
||||||
The script below will install all dependencies and help you to configure the bot.
|
|
||||||
```bash
|
|
||||||
./setup.sh --install
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manual installation
|
|
||||||
The following steps are made for Linux/MacOS environment
|
|
||||||
|
|
||||||
**1. Clone the repo**
|
|
||||||
```bash
|
```bash
|
||||||
git clone git@github.com:freqtrade/freqtrade.git
|
git clone git@github.com:freqtrade/freqtrade.git
|
||||||
git checkout develop
|
git checkout develop
|
||||||
cd freqtrade
|
cd freqtrade
|
||||||
|
./setup.sh --install
|
||||||
```
|
```
|
||||||
**2. Create the config file**
|
_Windows installation is explained in [Installation doc](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md)_
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Help / Slack
|
## Documentation
|
||||||
For any questions not covered by the documentation or for further
|
We invite you to read the bot documentation to ensure you understand how the bot is working.
|
||||||
information about the bot, we encourage you to join our slack channel.
|
- [Index](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md)
|
||||||
- [Click here to join Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE).
|
- [Installation](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md)
|
||||||
|
- [Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md)
|
||||||
|
- [Bot usage](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md)
|
||||||
|
- [How to run the bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#bot-commands)
|
||||||
|
- [How to use Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#backtesting-commands)
|
||||||
|
- [How to use Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#hyperopt-commands)
|
||||||
|
- [Strategy Optimization](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md)
|
||||||
|
- [Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md)
|
||||||
|
- [Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md)
|
||||||
|
|
||||||
### [Bugs / Issues](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue)
|
|
||||||
If you discover a bug in the bot, please
|
|
||||||
[search our issue tracker](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue)
|
|
||||||
first. If it hasn't been reported, please
|
|
||||||
[create a new issue](https://github.com/freqtrade/freqtrade/issues/new) and
|
|
||||||
ensure you follow the template guide so that our team can assist you as
|
|
||||||
quickly as possible.
|
|
||||||
|
|
||||||
### [Feature Requests](https://github.com/freqtrade/freqtrade/labels/enhancement)
|
|
||||||
Have you a great idea to improve the bot you want to share? Please,
|
|
||||||
first search if this feature was not [already discussed](https://github.com/freqtrade/freqtrade/labels/enhancement).
|
|
||||||
If it hasn't been requested, please
|
|
||||||
[create a new request](https://github.com/freqtrade/freqtrade/issues/new)
|
|
||||||
and ensure you follow the template guide so that it does not get lost
|
|
||||||
in the bug reports.
|
|
||||||
|
|
||||||
### [Pull Requests](https://github.com/freqtrade/freqtrade/pulls)
|
|
||||||
Feel like our bot is missing a feature? We welcome your pull requests!
|
|
||||||
Please read our
|
|
||||||
[Contributing document](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md)
|
|
||||||
to understand the requirements before sending your pull-requests.
|
|
||||||
|
|
||||||
**Important:** Always create your PR against the `develop` branch, not
|
|
||||||
`master`.
|
|
||||||
|
|
||||||
## Basic Usage
|
## Basic Usage
|
||||||
|
|
||||||
@ -170,11 +123,7 @@ optional arguments:
|
|||||||
"tradesv3.dry_run.sqlite" instead of memory DB. Work
|
"tradesv3.dry_run.sqlite" instead of memory DB. Work
|
||||||
only if dry_run is enabled.
|
only if dry_run is enabled.
|
||||||
```
|
```
|
||||||
More details on:
|
|
||||||
- [How to run the bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#bot-commands)
|
|
||||||
- [How to use Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#backtesting-commands)
|
|
||||||
- [How to use Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#hyperopt-commands)
|
|
||||||
|
|
||||||
### Telegram RPC commands
|
### Telegram RPC commands
|
||||||
Telegram is not mandatory. However, this is a great way to control your
|
Telegram is not mandatory. However, this is a great way to control your
|
||||||
bot. More details on our
|
bot. More details on our
|
||||||
@ -193,6 +142,46 @@ bot. More details on our
|
|||||||
- `/help`: Show help message
|
- `/help`: Show help message
|
||||||
- `/version`: Show version
|
- `/version`: Show version
|
||||||
|
|
||||||
|
|
||||||
|
## Development branches
|
||||||
|
The project is currently setup in two main branches:
|
||||||
|
- `develop` - This branch has often new features, but might also cause
|
||||||
|
breaking changes.
|
||||||
|
- `master` - This branch contains the latest stable release. The bot
|
||||||
|
'should' be stable on this branch, and is generally well tested.
|
||||||
|
|
||||||
|
|
||||||
|
## Support
|
||||||
|
### Help / Slack
|
||||||
|
For any questions not covered by the documentation or for further
|
||||||
|
information about the bot, we encourage you to join our slack channel.
|
||||||
|
- [Click here to join Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE).
|
||||||
|
|
||||||
|
### [Bugs / Issues](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue)
|
||||||
|
If you discover a bug in the bot, please
|
||||||
|
[search our issue tracker](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue)
|
||||||
|
first. If it hasn't been reported, please
|
||||||
|
[create a new issue](https://github.com/freqtrade/freqtrade/issues/new) and
|
||||||
|
ensure you follow the template guide so that our team can assist you as
|
||||||
|
quickly as possible.
|
||||||
|
|
||||||
|
### [Feature Requests](https://github.com/freqtrade/freqtrade/labels/enhancement)
|
||||||
|
Have you a great idea to improve the bot you want to share? Please,
|
||||||
|
first search if this feature was not [already discussed](https://github.com/freqtrade/freqtrade/labels/enhancement).
|
||||||
|
If it hasn't been requested, please
|
||||||
|
[create a new request](https://github.com/freqtrade/freqtrade/issues/new)
|
||||||
|
and ensure you follow the template guide so that it does not get lost
|
||||||
|
in the bug reports.
|
||||||
|
|
||||||
|
### [Pull Requests](https://github.com/freqtrade/freqtrade/pulls)
|
||||||
|
Feel like our bot is missing a feature? We welcome your pull requests!
|
||||||
|
Please read our
|
||||||
|
[Contributing document](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md)
|
||||||
|
to understand the requirements before sending your pull-requests.
|
||||||
|
|
||||||
|
**Important:** Always create your PR against the `develop` branch, not
|
||||||
|
`master`.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
### Min hardware required
|
### Min hardware required
|
||||||
|
@ -31,7 +31,8 @@
|
|||||||
},
|
},
|
||||||
"experimental": {
|
"experimental": {
|
||||||
"use_sell_signal": false,
|
"use_sell_signal": false,
|
||||||
"sell_profit_only": false
|
"sell_profit_only": false,
|
||||||
|
"ignore_roi_if_buy_signal": false
|
||||||
},
|
},
|
||||||
"telegram": {
|
"telegram": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -38,7 +38,8 @@
|
|||||||
},
|
},
|
||||||
"experimental": {
|
"experimental": {
|
||||||
"use_sell_signal": false,
|
"use_sell_signal": false,
|
||||||
"sell_profit_only": false
|
"sell_profit_only": false,
|
||||||
|
"ignore_roi_if_buy_signal": false
|
||||||
},
|
},
|
||||||
"telegram": {
|
"telegram": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -31,6 +31,7 @@ The table below will list all configuration parameters.
|
|||||||
| `exchange.pair_blacklist` | [] | No | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param.
|
| `exchange.pair_blacklist` | [] | No | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param.
|
||||||
| `experimental.use_sell_signal` | false | No | Use your sell strategy in addition of the `minimal_roi`.
|
| `experimental.use_sell_signal` | false | No | Use your sell strategy in addition of the `minimal_roi`.
|
||||||
| `experimental.sell_profit_only` | false | No | waits until you have made a positive profit before taking a sell decision.
|
| `experimental.sell_profit_only` | false | No | waits until you have made a positive profit before taking a sell decision.
|
||||||
|
| `experimental.ignore_roi_if_buy_signal` | false | No | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal`
|
||||||
| `telegram.enabled` | true | Yes | Enable or not the usage of Telegram.
|
| `telegram.enabled` | true | Yes | Enable or not the usage of Telegram.
|
||||||
| `telegram.token` | token | No | Your Telegram bot token. Only required if `telegram.enabled` is `true`.
|
| `telegram.token` | token | No | Your Telegram bot token. Only required if `telegram.enabled` is `true`.
|
||||||
| `telegram.chat_id` | chat_id | No | Your personal Telegram account id. Only required if `telegram.enabled` is `true`.
|
| `telegram.chat_id` | chat_id | No | Your personal Telegram account id. Only required if `telegram.enabled` is `true`.
|
||||||
|
@ -8,6 +8,7 @@ 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)
|
||||||
@ -55,6 +56,28 @@ 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
|
||||||
@ -184,6 +207,26 @@ 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.
|
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
|
||||||
|
|
||||||
|
The following assumes that the above steps (1-4) have been completed successfully.
|
||||||
|
Also, backtest-data should be available at `~/.freqtrade/user_data/`.
|
||||||
|
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
docker run -d \
|
||||||
|
--name freqtrade \
|
||||||
|
-v /etc/localtime:/etc/localtime:ro \
|
||||||
|
-v ~/.freqtrade/config.json:/freqtrade/config.json \
|
||||||
|
-v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \
|
||||||
|
-v ~/.freqtrade/user_data/:/freqtrade/user_data/ \
|
||||||
|
freqtrade --strategy AwsomelyProfitableStrategy backtesting
|
||||||
|
```
|
||||||
|
|
||||||
|
Head over to the [Backtesting Documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md) for more details.
|
||||||
|
|
||||||
|
*Note*: Additional parameters can be appended after the image name (`freqtrade` in the above example).
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
## Custom Installation
|
## Custom Installation
|
||||||
|
@ -10,7 +10,7 @@ import arrow
|
|||||||
from pandas import DataFrame, to_datetime
|
from pandas import DataFrame, to_datetime
|
||||||
|
|
||||||
from freqtrade import constants
|
from freqtrade import constants
|
||||||
from freqtrade.exchange import get_ticker_history
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.strategy.resolver import StrategyResolver, IStrategy
|
from freqtrade.strategy.resolver import StrategyResolver, IStrategy
|
||||||
|
|
||||||
@ -117,14 +117,14 @@ class Analyze(object):
|
|||||||
dataframe = self.populate_sell_trend(dataframe)
|
dataframe = self.populate_sell_trend(dataframe)
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
def get_signal(self, pair: str, interval: str) -> Tuple[bool, bool]:
|
def get_signal(self, exchange: Exchange, pair: str, interval: str) -> Tuple[bool, bool]:
|
||||||
"""
|
"""
|
||||||
Calculates current signal based several technical analysis indicators
|
Calculates current signal based several technical analysis indicators
|
||||||
:param pair: pair in format ANT/BTC
|
:param pair: pair in format ANT/BTC
|
||||||
:param interval: Interval to use (in min)
|
:param interval: Interval to use (in min)
|
||||||
:return: (Buy, Sell) A bool-tuple indicating buy/sell signal
|
:return: (Buy, Sell) A bool-tuple indicating buy/sell signal
|
||||||
"""
|
"""
|
||||||
ticker_hist = get_ticker_history(pair, interval)
|
ticker_hist = exchange.get_ticker_history(pair, interval)
|
||||||
if not ticker_hist:
|
if not ticker_hist:
|
||||||
logger.warning('Empty ticker history for pair %s', pair)
|
logger.warning('Empty ticker history for pair %s', pair)
|
||||||
return False, False
|
return False, False
|
||||||
@ -155,7 +155,7 @@ class Analyze(object):
|
|||||||
# Check if dataframe is out of date
|
# Check if dataframe is out of date
|
||||||
signal_date = arrow.get(latest['date'])
|
signal_date = arrow.get(latest['date'])
|
||||||
interval_minutes = constants.TICKER_INTERVAL_MINUTES[interval]
|
interval_minutes = constants.TICKER_INTERVAL_MINUTES[interval]
|
||||||
if signal_date < arrow.utcnow() - timedelta(minutes=(interval_minutes + 5)):
|
if signal_date < (arrow.utcnow() - timedelta(minutes=(interval_minutes + 5))):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'Outdated history for pair %s. Last tick is %s minutes old',
|
'Outdated history for pair %s. Last tick is %s minutes old',
|
||||||
pair,
|
pair,
|
||||||
@ -179,33 +179,45 @@ class Analyze(object):
|
|||||||
if the threshold is reached and updates the trade record.
|
if the threshold is reached and updates the trade record.
|
||||||
:return: True if trade should be sold, False otherwise
|
:return: True if trade should be sold, False otherwise
|
||||||
"""
|
"""
|
||||||
|
current_profit = trade.calc_profit_percent(rate)
|
||||||
|
if self.stop_loss_reached(current_profit=current_profit):
|
||||||
|
return True
|
||||||
|
|
||||||
|
experimental = self.config.get('experimental', {})
|
||||||
|
|
||||||
|
if buy and experimental.get('ignore_roi_if_buy_signal', False):
|
||||||
|
logger.debug('Buy signal still active - not selling.')
|
||||||
|
return False
|
||||||
|
|
||||||
# Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee)
|
# Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee)
|
||||||
if self.min_roi_reached(trade=trade, current_rate=rate, current_time=date):
|
if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):
|
||||||
logger.debug('Required profit reached. Selling..')
|
logger.debug('Required profit reached. Selling..')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Experimental: Check if the trade is profitable before selling it (avoid selling at loss)
|
if experimental.get('sell_profit_only', False):
|
||||||
if self.config.get('experimental', {}).get('sell_profit_only', False):
|
|
||||||
logger.debug('Checking if trade is profitable..')
|
logger.debug('Checking if trade is profitable..')
|
||||||
if trade.calc_profit(rate=rate) <= 0:
|
if trade.calc_profit(rate=rate) <= 0:
|
||||||
return False
|
return False
|
||||||
|
if sell and not buy and experimental.get('use_sell_signal', False):
|
||||||
if sell and not buy and self.config.get('experimental', {}).get('use_sell_signal', False):
|
|
||||||
logger.debug('Sell signal received. Selling..')
|
logger.debug('Sell signal received. Selling..')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def min_roi_reached(self, trade: Trade, current_rate: float, current_time: datetime) -> bool:
|
def stop_loss_reached(self, current_profit: float) -> bool:
|
||||||
|
"""Based on current profit of the trade and configured stoploss, decides to sell or not"""
|
||||||
|
|
||||||
|
if self.strategy.stoploss is not None and current_profit < self.strategy.stoploss:
|
||||||
|
logger.debug('Stop loss hit.')
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def min_roi_reached(self, trade: Trade, current_profit: float, current_time: datetime) -> bool:
|
||||||
"""
|
"""
|
||||||
Based an earlier trade and current price and ROI configuration, decides whether bot should
|
Based an earlier trade and current price and ROI configuration, decides whether bot should
|
||||||
sell
|
sell
|
||||||
:return True if bot should sell at current rate
|
:return True if bot should sell at current rate
|
||||||
"""
|
"""
|
||||||
current_profit = trade.calc_profit_percent(current_rate)
|
|
||||||
if self.strategy.stoploss is not None and current_profit < self.strategy.stoploss:
|
|
||||||
logger.debug('Stop loss hit.')
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Check if time matches and current rate is above threshold
|
# Check if time matches and current rate is above threshold
|
||||||
time_diff = (current_time.timestamp() - trade.open_date.timestamp()) / 60
|
time_diff = (current_time.timestamp() - trade.open_date.timestamp()) / 60
|
||||||
|
@ -79,7 +79,8 @@ CONF_SCHEMA = {
|
|||||||
'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'}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'telegram': {
|
'telegram': {
|
||||||
|
@ -12,16 +12,8 @@ from freqtrade import constants, OperationalException, DependencyException, Temp
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Current selected exchange
|
|
||||||
_API: ccxt.Exchange = None
|
|
||||||
|
|
||||||
_CONF: Dict = {}
|
|
||||||
API_RETRY_COUNT = 4
|
API_RETRY_COUNT = 4
|
||||||
|
|
||||||
_CACHED_TICKER: Dict[str, Any] = {}
|
|
||||||
|
|
||||||
# Holds all open sell orders for dry_run
|
|
||||||
_DRY_RUN_OPEN_ORDERS: Dict[str, Any] = {}
|
|
||||||
|
|
||||||
# Urls to exchange markets, insert quote and base with .format()
|
# Urls to exchange markets, insert quote and base with .format()
|
||||||
_EXCHANGE_URLS = {
|
_EXCHANGE_URLS = {
|
||||||
@ -48,390 +40,378 @@ def retrier(f):
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def init_ccxt(exchange_config: dict) -> ccxt.Exchange:
|
class Exchange(object):
|
||||||
"""
|
|
||||||
Initialize ccxt with given config and return valid
|
|
||||||
ccxt instance.
|
|
||||||
:param config: config to use
|
|
||||||
:return: ccxt
|
|
||||||
"""
|
|
||||||
# Find matching class for the given exchange name
|
|
||||||
name = exchange_config['name']
|
|
||||||
|
|
||||||
if name not in ccxt.exchanges:
|
# Current selected exchange
|
||||||
raise OperationalException(f'Exchange {name} is not supported')
|
_api: ccxt.Exchange = None
|
||||||
try:
|
_conf: Dict = {}
|
||||||
api = getattr(ccxt, name.lower())({
|
_cached_ticker: Dict[str, Any] = {}
|
||||||
'apiKey': exchange_config.get('key'),
|
|
||||||
'secret': exchange_config.get('secret'),
|
|
||||||
'password': exchange_config.get('password'),
|
|
||||||
'uid': exchange_config.get('uid', ''),
|
|
||||||
'enableRateLimit': True,
|
|
||||||
})
|
|
||||||
except (KeyError, AttributeError):
|
|
||||||
raise OperationalException(f'Exchange {name} is not supported')
|
|
||||||
|
|
||||||
return api
|
# Holds all open sell orders for dry_run
|
||||||
|
_dry_run_open_orders: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
def __init__(self, config: dict) -> None:
|
||||||
|
"""
|
||||||
|
Initializes this module with the given config,
|
||||||
|
it does basic validation whether the specified
|
||||||
|
exchange and pairs are valid.
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self._conf.update(config)
|
||||||
|
|
||||||
def init(config: dict) -> None:
|
if config['dry_run']:
|
||||||
"""
|
logger.info('Instance is running with dry_run enabled')
|
||||||
Initializes this module with the given config,
|
|
||||||
it does basic validation whether the specified
|
|
||||||
exchange and pairs are valid.
|
|
||||||
:param config: config to use
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
global _CONF, _API
|
|
||||||
|
|
||||||
_CONF.update(config)
|
exchange_config = config['exchange']
|
||||||
|
self._api = self._init_ccxt(exchange_config)
|
||||||
|
|
||||||
if config['dry_run']:
|
logger.info('Using Exchange "%s"', self.name)
|
||||||
logger.info('Instance is running with dry_run enabled')
|
|
||||||
|
|
||||||
exchange_config = config['exchange']
|
# Check if all pairs are available
|
||||||
_API = init_ccxt(exchange_config)
|
self.validate_pairs(config['exchange']['pair_whitelist'])
|
||||||
|
|
||||||
logger.info('Using Exchange "%s"', get_name())
|
def _init_ccxt(self, exchange_config: dict) -> ccxt.Exchange:
|
||||||
|
"""
|
||||||
|
Initialize ccxt with given config and return valid
|
||||||
|
ccxt instance.
|
||||||
|
"""
|
||||||
|
# Find matching class for the given exchange name
|
||||||
|
name = exchange_config['name']
|
||||||
|
|
||||||
# Check if all pairs are available
|
if name not in ccxt.exchanges:
|
||||||
validate_pairs(config['exchange']['pair_whitelist'])
|
raise OperationalException(f'Exchange {name} is not supported')
|
||||||
|
|
||||||
|
|
||||||
def validate_pairs(pairs: List[str]) -> None:
|
|
||||||
"""
|
|
||||||
Checks if all given pairs are tradable on the current exchange.
|
|
||||||
Raises OperationalException if one pair is not available.
|
|
||||||
:param pairs: list of pairs
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
markets = _API.load_markets()
|
|
||||||
except ccxt.BaseError as e:
|
|
||||||
logger.warning('Unable to validate pairs (assuming they are correct). Reason: %s', e)
|
|
||||||
return
|
|
||||||
|
|
||||||
stake_cur = _CONF['stake_currency']
|
|
||||||
for pair in pairs:
|
|
||||||
# Note: ccxt has BaseCurrency/QuoteCurrency format for pairs
|
|
||||||
# TODO: add a support for having coins in BTC/USDT format
|
|
||||||
if not pair.endswith(stake_cur):
|
|
||||||
raise OperationalException(
|
|
||||||
f'Pair {pair} not compatible with stake_currency: {stake_cur}')
|
|
||||||
if pair not in markets:
|
|
||||||
raise OperationalException(
|
|
||||||
f'Pair {pair} is not available at {get_name()}')
|
|
||||||
|
|
||||||
|
|
||||||
def exchange_has(endpoint: str) -> bool:
|
|
||||||
"""
|
|
||||||
Checks if exchange implements a specific API endpoint.
|
|
||||||
Wrapper around ccxt 'has' attribute
|
|
||||||
:param endpoint: Name of endpoint (e.g. 'fetchOHLCV', 'fetchTickers')
|
|
||||||
:return: bool
|
|
||||||
"""
|
|
||||||
return endpoint in _API.has and _API.has[endpoint]
|
|
||||||
|
|
||||||
|
|
||||||
def buy(pair: str, rate: float, amount: float) -> Dict:
|
|
||||||
if _CONF['dry_run']:
|
|
||||||
global _DRY_RUN_OPEN_ORDERS
|
|
||||||
order_id = f'dry_run_buy_{randint(0, 10**6)}'
|
|
||||||
_DRY_RUN_OPEN_ORDERS[order_id] = {
|
|
||||||
'pair': pair,
|
|
||||||
'price': rate,
|
|
||||||
'amount': amount,
|
|
||||||
'type': 'limit',
|
|
||||||
'side': 'buy',
|
|
||||||
'remaining': 0.0,
|
|
||||||
'datetime': arrow.utcnow().isoformat(),
|
|
||||||
'status': 'closed',
|
|
||||||
'fee': None
|
|
||||||
}
|
|
||||||
return {'id': order_id}
|
|
||||||
|
|
||||||
try:
|
|
||||||
return _API.create_limit_buy_order(pair, amount, rate)
|
|
||||||
except ccxt.InsufficientFunds as e:
|
|
||||||
raise DependencyException(
|
|
||||||
f'Insufficient funds to create limit buy order on market {pair}.'
|
|
||||||
f'Tried to buy amount {amount} at rate {rate} (total {rate*amount}).'
|
|
||||||
f'Message: {e}')
|
|
||||||
except ccxt.InvalidOrder as e:
|
|
||||||
raise DependencyException(
|
|
||||||
f'Could not create limit buy order on market {pair}.'
|
|
||||||
f'Tried to buy amount {amount} at rate {rate} (total {rate*amount}).'
|
|
||||||
f'Message: {e}')
|
|
||||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
|
||||||
raise TemporaryError(
|
|
||||||
f'Could not place buy order due to {e.__class__.__name__}. Message: {e}')
|
|
||||||
except ccxt.BaseError as e:
|
|
||||||
raise OperationalException(e)
|
|
||||||
|
|
||||||
|
|
||||||
def sell(pair: str, rate: float, amount: float) -> Dict:
|
|
||||||
if _CONF['dry_run']:
|
|
||||||
global _DRY_RUN_OPEN_ORDERS
|
|
||||||
order_id = f'dry_run_sell_{randint(0, 10**6)}'
|
|
||||||
_DRY_RUN_OPEN_ORDERS[order_id] = {
|
|
||||||
'pair': pair,
|
|
||||||
'price': rate,
|
|
||||||
'amount': amount,
|
|
||||||
'type': 'limit',
|
|
||||||
'side': 'sell',
|
|
||||||
'remaining': 0.0,
|
|
||||||
'datetime': arrow.utcnow().isoformat(),
|
|
||||||
'status': 'closed'
|
|
||||||
}
|
|
||||||
return {'id': order_id}
|
|
||||||
|
|
||||||
try:
|
|
||||||
return _API.create_limit_sell_order(pair, amount, rate)
|
|
||||||
except ccxt.InsufficientFunds as e:
|
|
||||||
raise DependencyException(
|
|
||||||
f'Insufficient funds to create limit sell order on market {pair}.'
|
|
||||||
f'Tried to sell amount {amount} at rate {rate} (total {rate*amount}).'
|
|
||||||
f'Message: {e}')
|
|
||||||
except ccxt.InvalidOrder as e:
|
|
||||||
raise DependencyException(
|
|
||||||
f'Could not create limit sell order on market {pair}.'
|
|
||||||
f'Tried to sell amount {amount} at rate {rate} (total {rate*amount}).'
|
|
||||||
f'Message: {e}')
|
|
||||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
|
||||||
raise TemporaryError(
|
|
||||||
f'Could not place sell order due to {e.__class__.__name__}. Message: {e}')
|
|
||||||
except ccxt.BaseError as e:
|
|
||||||
raise OperationalException(e)
|
|
||||||
|
|
||||||
|
|
||||||
@retrier
|
|
||||||
def get_balance(currency: str) -> float:
|
|
||||||
if _CONF['dry_run']:
|
|
||||||
return 999.9
|
|
||||||
|
|
||||||
# ccxt exception is already handled by get_balances
|
|
||||||
balances = get_balances()
|
|
||||||
balance = balances.get(currency)
|
|
||||||
if balance is None:
|
|
||||||
raise TemporaryError(
|
|
||||||
f'Could not get {currency} balance due to malformed exchange response: {balances}')
|
|
||||||
return balance['free']
|
|
||||||
|
|
||||||
|
|
||||||
@retrier
|
|
||||||
def get_balances() -> dict:
|
|
||||||
if _CONF['dry_run']:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
try:
|
|
||||||
balances = _API.fetch_balance()
|
|
||||||
# Remove additional info from ccxt results
|
|
||||||
balances.pop("info", None)
|
|
||||||
balances.pop("free", None)
|
|
||||||
balances.pop("total", None)
|
|
||||||
balances.pop("used", None)
|
|
||||||
|
|
||||||
return balances
|
|
||||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
|
||||||
raise TemporaryError(
|
|
||||||
f'Could not get balance due to {e.__class__.__name__}. Message: {e}')
|
|
||||||
except ccxt.BaseError as e:
|
|
||||||
raise OperationalException(e)
|
|
||||||
|
|
||||||
|
|
||||||
@retrier
|
|
||||||
def get_tickers() -> Dict:
|
|
||||||
try:
|
|
||||||
return _API.fetch_tickers()
|
|
||||||
except ccxt.NotSupported as e:
|
|
||||||
raise OperationalException(
|
|
||||||
f'Exchange {_API.name} does not support fetching tickers in batch.'
|
|
||||||
f'Message: {e}')
|
|
||||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
|
||||||
raise TemporaryError(
|
|
||||||
f'Could not load tickers due to {e.__class__.__name__}. Message: {e}')
|
|
||||||
except ccxt.BaseError as e:
|
|
||||||
raise OperationalException(e)
|
|
||||||
|
|
||||||
|
|
||||||
@retrier
|
|
||||||
def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict:
|
|
||||||
global _CACHED_TICKER
|
|
||||||
if refresh or pair not in _CACHED_TICKER.keys():
|
|
||||||
try:
|
try:
|
||||||
data = _API.fetch_ticker(pair)
|
api = getattr(ccxt, name.lower())({
|
||||||
|
'apiKey': exchange_config.get('key'),
|
||||||
|
'secret': exchange_config.get('secret'),
|
||||||
|
'password': exchange_config.get('password'),
|
||||||
|
'uid': exchange_config.get('uid', ''),
|
||||||
|
'enableRateLimit': True,
|
||||||
|
})
|
||||||
|
except (KeyError, AttributeError):
|
||||||
|
raise OperationalException(f'Exchange {name} is not supported')
|
||||||
|
|
||||||
|
return api
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
"""exchange Name (from ccxt)"""
|
||||||
|
return self._api.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self) -> str:
|
||||||
|
"""exchange ccxt id"""
|
||||||
|
return self._api.id
|
||||||
|
|
||||||
|
def validate_pairs(self, pairs: List[str]) -> None:
|
||||||
|
"""
|
||||||
|
Checks if all given pairs are tradable on the current exchange.
|
||||||
|
Raises OperationalException if one pair is not available.
|
||||||
|
:param pairs: list of pairs
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
markets = self._api.load_markets()
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
logger.warning('Unable to validate pairs (assuming they are correct). Reason: %s', e)
|
||||||
|
return
|
||||||
|
|
||||||
|
stake_cur = self._conf['stake_currency']
|
||||||
|
for pair in pairs:
|
||||||
|
# Note: ccxt has BaseCurrency/QuoteCurrency format for pairs
|
||||||
|
# TODO: add a support for having coins in BTC/USDT format
|
||||||
|
if not pair.endswith(stake_cur):
|
||||||
|
raise OperationalException(
|
||||||
|
f'Pair {pair} not compatible with stake_currency: {stake_cur}')
|
||||||
|
if pair not in markets:
|
||||||
|
raise OperationalException(
|
||||||
|
f'Pair {pair} is not available at {self.name}')
|
||||||
|
|
||||||
|
def exchange_has(self, endpoint: str) -> bool:
|
||||||
|
"""
|
||||||
|
Checks if exchange implements a specific API endpoint.
|
||||||
|
Wrapper around ccxt 'has' attribute
|
||||||
|
:param endpoint: Name of endpoint (e.g. 'fetchOHLCV', 'fetchTickers')
|
||||||
|
:return: bool
|
||||||
|
"""
|
||||||
|
return endpoint in self._api.has and self._api.has[endpoint]
|
||||||
|
|
||||||
|
def buy(self, pair: str, rate: float, amount: float) -> Dict:
|
||||||
|
if self._conf['dry_run']:
|
||||||
|
order_id = f'dry_run_buy_{randint(0, 10**6)}'
|
||||||
|
self._dry_run_open_orders[order_id] = {
|
||||||
|
'pair': pair,
|
||||||
|
'price': rate,
|
||||||
|
'amount': amount,
|
||||||
|
'type': 'limit',
|
||||||
|
'side': 'buy',
|
||||||
|
'remaining': 0.0,
|
||||||
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
|
'status': 'closed',
|
||||||
|
'fee': None
|
||||||
|
}
|
||||||
|
return {'id': order_id}
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self._api.create_limit_buy_order(pair, amount, rate)
|
||||||
|
except ccxt.InsufficientFunds as e:
|
||||||
|
raise DependencyException(
|
||||||
|
f'Insufficient funds to create limit buy order on market {pair}.'
|
||||||
|
f'Tried to buy amount {amount} at rate {rate} (total {rate*amount}).'
|
||||||
|
f'Message: {e}')
|
||||||
|
except ccxt.InvalidOrder as e:
|
||||||
|
raise DependencyException(
|
||||||
|
f'Could not create limit buy order on market {pair}.'
|
||||||
|
f'Tried to buy amount {amount} at rate {rate} (total {rate*amount}).'
|
||||||
|
f'Message: {e}')
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not place buy order due to {e.__class__.__name__}. Message: {e}')
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e)
|
||||||
|
|
||||||
|
def sell(self, pair: str, rate: float, amount: float) -> Dict:
|
||||||
|
if self._conf['dry_run']:
|
||||||
|
order_id = f'dry_run_sell_{randint(0, 10**6)}'
|
||||||
|
self._dry_run_open_orders[order_id] = {
|
||||||
|
'pair': pair,
|
||||||
|
'price': rate,
|
||||||
|
'amount': amount,
|
||||||
|
'type': 'limit',
|
||||||
|
'side': 'sell',
|
||||||
|
'remaining': 0.0,
|
||||||
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
|
'status': 'closed'
|
||||||
|
}
|
||||||
|
return {'id': order_id}
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self._api.create_limit_sell_order(pair, amount, rate)
|
||||||
|
except ccxt.InsufficientFunds as e:
|
||||||
|
raise DependencyException(
|
||||||
|
f'Insufficient funds to create limit sell order on market {pair}.'
|
||||||
|
f'Tried to sell amount {amount} at rate {rate} (total {rate*amount}).'
|
||||||
|
f'Message: {e}')
|
||||||
|
except ccxt.InvalidOrder as e:
|
||||||
|
raise DependencyException(
|
||||||
|
f'Could not create limit sell order on market {pair}.'
|
||||||
|
f'Tried to sell amount {amount} at rate {rate} (total {rate*amount}).'
|
||||||
|
f'Message: {e}')
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not place sell order due to {e.__class__.__name__}. Message: {e}')
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e)
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def get_balance(self, currency: str) -> float:
|
||||||
|
if self._conf['dry_run']:
|
||||||
|
return 999.9
|
||||||
|
|
||||||
|
# ccxt exception is already handled by get_balances
|
||||||
|
balances = self.get_balances()
|
||||||
|
balance = balances.get(currency)
|
||||||
|
if balance is None:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not get {currency} balance due to malformed exchange response: {balances}')
|
||||||
|
return balance['free']
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def get_balances(self) -> dict:
|
||||||
|
if self._conf['dry_run']:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
balances = self._api.fetch_balance()
|
||||||
|
# Remove additional info from ccxt results
|
||||||
|
balances.pop("info", None)
|
||||||
|
balances.pop("free", None)
|
||||||
|
balances.pop("total", None)
|
||||||
|
balances.pop("used", None)
|
||||||
|
|
||||||
|
return balances
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not get balance due to {e.__class__.__name__}. Message: {e}')
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e)
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def get_tickers(self) -> Dict:
|
||||||
|
try:
|
||||||
|
return self._api.fetch_tickers()
|
||||||
|
except ccxt.NotSupported as e:
|
||||||
|
raise OperationalException(
|
||||||
|
f'Exchange {self._api.name} does not support fetching tickers in batch.'
|
||||||
|
f'Message: {e}')
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not load tickers due to {e.__class__.__name__}. Message: {e}')
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e)
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict:
|
||||||
|
if refresh or pair not in self._cached_ticker.keys():
|
||||||
try:
|
try:
|
||||||
_CACHED_TICKER[pair] = {
|
data = self._api.fetch_ticker(pair)
|
||||||
'bid': float(data['bid']),
|
try:
|
||||||
'ask': float(data['ask']),
|
self._cached_ticker[pair] = {
|
||||||
}
|
'bid': float(data['bid']),
|
||||||
except KeyError:
|
'ask': float(data['ask']),
|
||||||
logger.debug("Could not cache ticker data for %s", pair)
|
}
|
||||||
|
except KeyError:
|
||||||
|
logger.debug("Could not cache ticker data for %s", pair)
|
||||||
|
return data
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not load ticker history due to {e.__class__.__name__}. Message: {e}')
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e)
|
||||||
|
else:
|
||||||
|
logger.info("returning cached ticker-data for %s", pair)
|
||||||
|
return self._cached_ticker[pair]
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def get_ticker_history(self, pair: str, tick_interval: str,
|
||||||
|
since_ms: Optional[int] = None) -> List[Dict]:
|
||||||
|
try:
|
||||||
|
# last item should be in the time interval [now - tick_interval, now]
|
||||||
|
till_time_ms = arrow.utcnow().shift(
|
||||||
|
minutes=-constants.TICKER_INTERVAL_MINUTES[tick_interval]
|
||||||
|
).timestamp * 1000
|
||||||
|
# it looks as if some exchanges return cached data
|
||||||
|
# and they update it one in several minute, so 10 mins interval
|
||||||
|
# is necessary to skeep downloading of an empty array when all
|
||||||
|
# chached data was already downloaded
|
||||||
|
till_time_ms = min(till_time_ms, arrow.utcnow().shift(minutes=-10).timestamp * 1000)
|
||||||
|
|
||||||
|
data: List[Dict[Any, Any]] = []
|
||||||
|
while not since_ms or since_ms < till_time_ms:
|
||||||
|
data_part = self._api.fetch_ohlcv(pair, timeframe=tick_interval, since=since_ms)
|
||||||
|
|
||||||
|
# Because some exchange sort Tickers ASC and other DESC.
|
||||||
|
# Ex: Bittrex returns a list of tickers ASC (oldest first, newest last)
|
||||||
|
# when GDAX returns a list of tickers DESC (newest first, oldest last)
|
||||||
|
data_part = sorted(data_part, key=lambda x: x[0])
|
||||||
|
|
||||||
|
if not data_part:
|
||||||
|
break
|
||||||
|
|
||||||
|
logger.debug('Downloaded data for %s time range [%s, %s]',
|
||||||
|
pair,
|
||||||
|
arrow.get(data_part[0][0] / 1000).format(),
|
||||||
|
arrow.get(data_part[-1][0] / 1000).format())
|
||||||
|
|
||||||
|
data.extend(data_part)
|
||||||
|
since_ms = data[-1][0] + 1
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
except ccxt.NotSupported as e:
|
||||||
|
raise OperationalException(
|
||||||
|
f'Exchange {self._api.name} does not support fetching historical candlestick data.'
|
||||||
|
f'Message: {e}')
|
||||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
raise TemporaryError(
|
raise TemporaryError(
|
||||||
f'Could not load ticker history due to {e.__class__.__name__}. Message: {e}')
|
f'Could not load ticker history due to {e.__class__.__name__}. Message: {e}')
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(f'Could not fetch ticker data. Msg: {e}')
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def cancel_order(self, order_id: str, pair: str) -> None:
|
||||||
|
if self._conf['dry_run']:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self._api.cancel_order(order_id, pair)
|
||||||
|
except ccxt.InvalidOrder as e:
|
||||||
|
raise DependencyException(
|
||||||
|
f'Could not cancel order. Message: {e}')
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not cancel order due to {e.__class__.__name__}. Message: {e}')
|
||||||
except ccxt.BaseError as e:
|
except ccxt.BaseError as e:
|
||||||
raise OperationalException(e)
|
raise OperationalException(e)
|
||||||
else:
|
|
||||||
logger.info("returning cached ticker-data for %s", pair)
|
|
||||||
return _CACHED_TICKER[pair]
|
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def get_order(self, order_id: str, pair: str) -> Dict:
|
||||||
|
if self._conf['dry_run']:
|
||||||
|
order = self._dry_run_open_orders[order_id]
|
||||||
|
order.update({
|
||||||
|
'id': order_id
|
||||||
|
})
|
||||||
|
return order
|
||||||
|
try:
|
||||||
|
return self._api.fetch_order(order_id, pair)
|
||||||
|
except ccxt.InvalidOrder as e:
|
||||||
|
raise DependencyException(
|
||||||
|
f'Could not get order. Message: {e}')
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not get order due to {e.__class__.__name__}. Message: {e}')
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e)
|
||||||
|
|
||||||
@retrier
|
@retrier
|
||||||
def get_ticker_history(pair: str, tick_interval: str, since_ms: Optional[int] = None) -> List[Dict]:
|
def get_trades_for_order(self, order_id: str, pair: str, since: datetime) -> List:
|
||||||
try:
|
if self._conf['dry_run']:
|
||||||
# last item should be in the time interval [now - tick_interval, now]
|
return []
|
||||||
till_time_ms = arrow.utcnow().shift(
|
if not self.exchange_has('fetchMyTrades'):
|
||||||
minutes=-constants.TICKER_INTERVAL_MINUTES[tick_interval]
|
return []
|
||||||
).timestamp * 1000
|
try:
|
||||||
# it looks as if some exchanges return cached data
|
my_trades = self._api.fetch_my_trades(pair, since.timestamp())
|
||||||
# and they update it one in several minute, so 10 mins interval
|
matched_trades = [trade for trade in my_trades if trade['order'] == order_id]
|
||||||
# is necessary to skeep downloading of an empty array when all
|
|
||||||
# chached data was already downloaded
|
|
||||||
till_time_ms = min(till_time_ms, arrow.utcnow().shift(minutes=-10).timestamp * 1000)
|
|
||||||
|
|
||||||
data: List[Dict[Any, Any]] = []
|
return matched_trades
|
||||||
while not since_ms or since_ms < till_time_ms:
|
|
||||||
data_part = _API.fetch_ohlcv(pair, timeframe=tick_interval, since=since_ms)
|
|
||||||
|
|
||||||
# Because some exchange sort Tickers ASC and other DESC.
|
except ccxt.NetworkError as e:
|
||||||
# Ex: Bittrex returns a list of tickers ASC (oldest first, newest last)
|
raise TemporaryError(
|
||||||
# when GDAX returns a list of tickers DESC (newest first, oldest last)
|
f'Could not get trades due to networking error. Message: {e}')
|
||||||
data_part = sorted(data_part, key=lambda x: x[0])
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e)
|
||||||
|
|
||||||
if not data_part:
|
def get_pair_detail_url(self, pair: str) -> str:
|
||||||
break
|
try:
|
||||||
|
url_base = self._api.urls.get('www')
|
||||||
|
base, quote = pair.split('/')
|
||||||
|
|
||||||
logger.debug('Downloaded data for %s time range [%s, %s]',
|
return url_base + _EXCHANGE_URLS[self._api.id].format(base=base, quote=quote)
|
||||||
pair,
|
except KeyError:
|
||||||
arrow.get(data_part[0][0] / 1000).format(),
|
logger.warning('Could not get exchange url for %s', self.name)
|
||||||
arrow.get(data_part[-1][0] / 1000).format())
|
return ""
|
||||||
|
|
||||||
data.extend(data_part)
|
@retrier
|
||||||
since_ms = data[-1][0] + 1
|
def get_markets(self) -> List[dict]:
|
||||||
|
try:
|
||||||
|
return self._api.fetch_markets()
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not load markets due to {e.__class__.__name__}. Message: {e}')
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e)
|
||||||
|
|
||||||
return data
|
@retrier
|
||||||
except ccxt.NotSupported as e:
|
def get_fee(self, symbol='ETH/BTC', type='', side='', amount=1,
|
||||||
raise OperationalException(
|
price=1, taker_or_maker='maker') -> float:
|
||||||
f'Exchange {_API.name} does not support fetching historical candlestick data.'
|
try:
|
||||||
f'Message: {e}')
|
# validate that markets are loaded before trying to get fee
|
||||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
if self._api.markets is None or len(self._api.markets) == 0:
|
||||||
raise TemporaryError(
|
self._api.load_markets()
|
||||||
f'Could not load ticker history due to {e.__class__.__name__}. Message: {e}')
|
|
||||||
except ccxt.BaseError as e:
|
|
||||||
raise OperationalException(f'Could not fetch ticker data. Msg: {e}')
|
|
||||||
|
|
||||||
|
return self._api.calculate_fee(symbol=symbol, type=type, side=side, amount=amount,
|
||||||
|
price=price, takerOrMaker=taker_or_maker)['rate']
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not get fee info due to {e.__class__.__name__}. Message: {e}')
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e)
|
||||||
|
|
||||||
@retrier
|
def get_amount_lots(self, pair: str, amount: float) -> float:
|
||||||
def cancel_order(order_id: str, pair: str) -> None:
|
"""
|
||||||
if _CONF['dry_run']:
|
get buyable amount rounding, ..
|
||||||
return
|
"""
|
||||||
|
|
||||||
try:
|
|
||||||
return _API.cancel_order(order_id, pair)
|
|
||||||
except ccxt.InvalidOrder as e:
|
|
||||||
raise DependencyException(
|
|
||||||
f'Could not cancel order. Message: {e}')
|
|
||||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
|
||||||
raise TemporaryError(
|
|
||||||
f'Could not cancel order due to {e.__class__.__name__}. Message: {e}')
|
|
||||||
except ccxt.BaseError as e:
|
|
||||||
raise OperationalException(e)
|
|
||||||
|
|
||||||
|
|
||||||
@retrier
|
|
||||||
def get_order(order_id: str, pair: str) -> Dict:
|
|
||||||
if _CONF['dry_run']:
|
|
||||||
order = _DRY_RUN_OPEN_ORDERS[order_id]
|
|
||||||
order.update({
|
|
||||||
'id': order_id
|
|
||||||
})
|
|
||||||
return order
|
|
||||||
try:
|
|
||||||
return _API.fetch_order(order_id, pair)
|
|
||||||
except ccxt.InvalidOrder as e:
|
|
||||||
raise DependencyException(
|
|
||||||
f'Could not get order. Message: {e}')
|
|
||||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
|
||||||
raise TemporaryError(
|
|
||||||
f'Could not get order due to {e.__class__.__name__}. Message: {e}')
|
|
||||||
except ccxt.BaseError as e:
|
|
||||||
raise OperationalException(e)
|
|
||||||
|
|
||||||
|
|
||||||
@retrier
|
|
||||||
def get_trades_for_order(order_id: str, pair: str, since: datetime) -> List:
|
|
||||||
if _CONF['dry_run']:
|
|
||||||
return []
|
|
||||||
if not exchange_has('fetchMyTrades'):
|
|
||||||
return []
|
|
||||||
try:
|
|
||||||
my_trades = _API.fetch_my_trades(pair, since.timestamp())
|
|
||||||
matched_trades = [trade for trade in my_trades if trade['order'] == order_id]
|
|
||||||
|
|
||||||
return matched_trades
|
|
||||||
|
|
||||||
except ccxt.NetworkError as e:
|
|
||||||
raise TemporaryError(
|
|
||||||
f'Could not get trades due to networking error. Message: {e}')
|
|
||||||
except ccxt.BaseError as e:
|
|
||||||
raise OperationalException(e)
|
|
||||||
|
|
||||||
|
|
||||||
def get_pair_detail_url(pair: str) -> str:
|
|
||||||
try:
|
|
||||||
url_base = _API.urls.get('www')
|
|
||||||
base, quote = pair.split('/')
|
|
||||||
|
|
||||||
return url_base + _EXCHANGE_URLS[_API.id].format(base=base, quote=quote)
|
|
||||||
except KeyError:
|
|
||||||
logger.warning('Could not get exchange url for %s', get_name())
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
@retrier
|
|
||||||
def get_markets() -> List[dict]:
|
|
||||||
try:
|
|
||||||
return _API.fetch_markets()
|
|
||||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
|
||||||
raise TemporaryError(
|
|
||||||
f'Could not load markets due to {e.__class__.__name__}. Message: {e}')
|
|
||||||
except ccxt.BaseError as e:
|
|
||||||
raise OperationalException(e)
|
|
||||||
|
|
||||||
|
|
||||||
def get_name() -> str:
|
|
||||||
return _API.name
|
|
||||||
|
|
||||||
|
|
||||||
def get_id() -> str:
|
|
||||||
return _API.id
|
|
||||||
|
|
||||||
|
|
||||||
@retrier
|
|
||||||
def get_fee(symbol='ETH/BTC', type='', side='', amount=1,
|
|
||||||
price=1, taker_or_maker='maker') -> float:
|
|
||||||
try:
|
|
||||||
# validate that markets are loaded before trying to get fee
|
# validate that markets are loaded before trying to get fee
|
||||||
if _API.markets is None or len(_API.markets) == 0:
|
if not self._api.markets:
|
||||||
_API.load_markets()
|
self._api.load_markets()
|
||||||
|
return self._api.amount_to_lots(pair, amount)
|
||||||
return _API.calculate_fee(symbol=symbol, type=type, side=side, amount=amount,
|
|
||||||
price=price, takerOrMaker=taker_or_maker)['rate']
|
|
||||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
|
||||||
raise TemporaryError(
|
|
||||||
f'Could not get fee info due to {e.__class__.__name__}. Message: {e}')
|
|
||||||
except ccxt.BaseError as e:
|
|
||||||
raise OperationalException(e)
|
|
||||||
|
|
||||||
|
|
||||||
def get_amount_lots(pair: str, amount: float) -> float:
|
|
||||||
"""
|
|
||||||
get buyable amount rounding, ..
|
|
||||||
"""
|
|
||||||
# validate that markets are loaded before trying to get fee
|
|
||||||
if not _API.markets:
|
|
||||||
_API.load_markets()
|
|
||||||
return _API.amount_to_lots(pair, amount)
|
|
||||||
|
@ -14,11 +14,11 @@ import requests
|
|||||||
from cachetools import TTLCache, cached
|
from cachetools import TTLCache, cached
|
||||||
|
|
||||||
from freqtrade import (
|
from freqtrade import (
|
||||||
DependencyException, OperationalException, TemporaryError,
|
DependencyException, OperationalException, TemporaryError, persistence, __version__,
|
||||||
exchange, persistence, __version__,
|
|
||||||
)
|
)
|
||||||
from freqtrade import constants
|
from freqtrade import constants
|
||||||
from freqtrade.analyze import Analyze
|
from freqtrade.analyze import Analyze
|
||||||
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.fiat_convert import CryptoToFiatConverter
|
from freqtrade.fiat_convert import CryptoToFiatConverter
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.rpc.rpc_manager import RPCManager
|
from freqtrade.rpc.rpc_manager import RPCManager
|
||||||
@ -54,7 +54,7 @@ class FreqtradeBot(object):
|
|||||||
self.fiat_converter = CryptoToFiatConverter()
|
self.fiat_converter = CryptoToFiatConverter()
|
||||||
self.rpc: RPCManager = RPCManager(self)
|
self.rpc: RPCManager = RPCManager(self)
|
||||||
self.persistence = None
|
self.persistence = None
|
||||||
self.exchange = None
|
self.exchange = Exchange(self.config)
|
||||||
|
|
||||||
self._init_modules()
|
self._init_modules()
|
||||||
|
|
||||||
@ -66,7 +66,6 @@ class FreqtradeBot(object):
|
|||||||
# Initialize all modules
|
# Initialize all modules
|
||||||
|
|
||||||
persistence.init(self.config)
|
persistence.init(self.config)
|
||||||
exchange.init(self.config)
|
|
||||||
|
|
||||||
# Set initial application state
|
# Set initial application state
|
||||||
initial_state = self.config.get('initial_state')
|
initial_state = self.config.get('initial_state')
|
||||||
@ -186,13 +185,13 @@ class FreqtradeBot(object):
|
|||||||
:return: List of pairs
|
:return: List of pairs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not exchange.exchange_has('fetchTickers'):
|
if not self.exchange.exchange_has('fetchTickers'):
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
'Exchange does not support dynamic whitelist.'
|
'Exchange does not support dynamic whitelist.'
|
||||||
'Please edit your config and restart the bot'
|
'Please edit your config and restart the bot'
|
||||||
)
|
)
|
||||||
|
|
||||||
tickers = exchange.get_tickers()
|
tickers = self.exchange.get_tickers()
|
||||||
# check length so that we make sure that '/' is actually in the string
|
# check length so that we make sure that '/' is actually in the string
|
||||||
tickers = [v for k, v in tickers.items()
|
tickers = [v for k, v in tickers.items()
|
||||||
if len(k.split('/')) == 2 and k.split('/')[1] == base_currency]
|
if len(k.split('/')) == 2 and k.split('/')[1] == base_currency]
|
||||||
@ -210,7 +209,7 @@ class FreqtradeBot(object):
|
|||||||
black_listed
|
black_listed
|
||||||
"""
|
"""
|
||||||
sanitized_whitelist = whitelist
|
sanitized_whitelist = whitelist
|
||||||
markets = exchange.get_markets()
|
markets = self.exchange.get_markets()
|
||||||
|
|
||||||
markets = [m for m in markets if m['quote'] == self.config['stake_currency']]
|
markets = [m for m in markets if m['quote'] == self.config['stake_currency']]
|
||||||
known_pairs = set()
|
known_pairs = set()
|
||||||
@ -247,7 +246,7 @@ class FreqtradeBot(object):
|
|||||||
|
|
||||||
def _get_trade_stake_amount(self) -> Optional[float]:
|
def _get_trade_stake_amount(self) -> Optional[float]:
|
||||||
stake_amount = self.config['stake_amount']
|
stake_amount = self.config['stake_amount']
|
||||||
avaliable_amount = exchange.get_balance(self.config['stake_currency'])
|
avaliable_amount = self.exchange.get_balance(self.config['stake_currency'])
|
||||||
|
|
||||||
if stake_amount == constants.UNLIMITED_STAKE_AMOUNT:
|
if stake_amount == constants.UNLIMITED_STAKE_AMOUNT:
|
||||||
open_trades = len(Trade.query.filter(Trade.is_open.is_(True)).all())
|
open_trades = len(Trade.query.filter(Trade.is_open.is_(True)).all())
|
||||||
@ -267,7 +266,7 @@ class FreqtradeBot(object):
|
|||||||
return stake_amount
|
return stake_amount
|
||||||
|
|
||||||
def _get_min_pair_stake_amount(self, pair: str, price: float) -> Optional[float]:
|
def _get_min_pair_stake_amount(self, pair: str, price: float) -> Optional[float]:
|
||||||
markets = exchange.get_markets()
|
markets = self.exchange.get_markets()
|
||||||
markets = [m for m in markets if m['symbol'] == pair]
|
markets = [m for m in markets if m['symbol'] == pair]
|
||||||
if not markets:
|
if not markets:
|
||||||
raise ValueError(f'Can\'t get market information for symbol {pair}')
|
raise ValueError(f'Can\'t get market information for symbol {pair}')
|
||||||
@ -307,7 +306,7 @@ class FreqtradeBot(object):
|
|||||||
return False
|
return False
|
||||||
stake_currency = self.config['stake_currency']
|
stake_currency = self.config['stake_currency']
|
||||||
fiat_currency = self.config['fiat_display_currency']
|
fiat_currency = self.config['fiat_display_currency']
|
||||||
exc_name = exchange.get_name()
|
exc_name = self.exchange.name
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
'Checking buy signals to create a new trade with stake_amount: %f ...',
|
'Checking buy signals to create a new trade with stake_amount: %f ...',
|
||||||
@ -326,16 +325,17 @@ class FreqtradeBot(object):
|
|||||||
|
|
||||||
# Pick pair based on buy signals
|
# Pick pair based on buy signals
|
||||||
for _pair in whitelist:
|
for _pair in whitelist:
|
||||||
(buy, sell) = self.analyze.get_signal(_pair, interval)
|
(buy, sell) = self.analyze.get_signal(self.exchange, _pair, interval)
|
||||||
if buy and not sell:
|
if buy and not sell:
|
||||||
pair = _pair
|
pair = _pair
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
pair_s = pair.replace('_', '/')
|
pair_s = pair.replace('_', '/')
|
||||||
pair_url = exchange.get_pair_detail_url(pair)
|
pair_url = self.exchange.get_pair_detail_url(pair)
|
||||||
|
|
||||||
# Calculate amount
|
# Calculate amount
|
||||||
buy_limit = self.get_target_bid(exchange.get_ticker(pair))
|
buy_limit = self.get_target_bid(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:
|
||||||
@ -346,7 +346,8 @@ class FreqtradeBot(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
amount = stake_amount / buy_limit
|
amount = stake_amount / buy_limit
|
||||||
order_id = exchange.buy(pair, buy_limit, amount)['id']
|
|
||||||
|
order_id = self.exchange.buy(pair, buy_limit, amount)['id']
|
||||||
|
|
||||||
stake_amount_fiat = self.fiat_converter.convert_amount(
|
stake_amount_fiat = self.fiat_converter.convert_amount(
|
||||||
stake_amount,
|
stake_amount,
|
||||||
@ -361,7 +362,7 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||||||
{stake_currency}, {stake_amount_fiat:.3f} {fiat_currency})`"""
|
{stake_currency}, {stake_amount_fiat:.3f} {fiat_currency})`"""
|
||||||
)
|
)
|
||||||
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
|
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
|
||||||
fee = exchange.get_fee(symbol=pair, taker_or_maker='maker')
|
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair=pair,
|
pair=pair,
|
||||||
stake_amount=stake_amount,
|
stake_amount=stake_amount,
|
||||||
@ -371,7 +372,7 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||||||
open_rate=buy_limit,
|
open_rate=buy_limit,
|
||||||
open_rate_requested=buy_limit,
|
open_rate_requested=buy_limit,
|
||||||
open_date=datetime.utcnow(),
|
open_date=datetime.utcnow(),
|
||||||
exchange=exchange.get_id(),
|
exchange=self.exchange.id,
|
||||||
open_order_id=order_id
|
open_order_id=order_id
|
||||||
)
|
)
|
||||||
Trade.session.add(trade)
|
Trade.session.add(trade)
|
||||||
@ -404,7 +405,7 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||||||
if trade.open_order_id:
|
if trade.open_order_id:
|
||||||
# Update trade with order values
|
# Update trade with order values
|
||||||
logger.info('Found open order for %s', trade)
|
logger.info('Found open order for %s', trade)
|
||||||
order = exchange.get_order(trade.open_order_id, trade.pair)
|
order = self.exchange.get_order(trade.open_order_id, trade.pair)
|
||||||
# Try update amount (binance-fix)
|
# Try update amount (binance-fix)
|
||||||
try:
|
try:
|
||||||
new_amount = self.get_real_amount(trade, order)
|
new_amount = self.get_real_amount(trade, order)
|
||||||
@ -428,7 +429,7 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||||||
def get_real_amount(self, trade: Trade, order: Dict) -> float:
|
def get_real_amount(self, trade: Trade, order: Dict) -> float:
|
||||||
"""
|
"""
|
||||||
Get real amount for the trade
|
Get real amount for the trade
|
||||||
Necessary for exchanges which charge fees in base currency (e.g. binance)
|
Necessary for self.exchanges which charge fees in base currency (e.g. binance)
|
||||||
"""
|
"""
|
||||||
order_amount = order['amount']
|
order_amount = order['amount']
|
||||||
# Only run for closed orders
|
# Only run for closed orders
|
||||||
@ -444,7 +445,8 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||||||
return new_amount
|
return new_amount
|
||||||
|
|
||||||
# Fallback to Trades
|
# Fallback to Trades
|
||||||
trades = exchange.get_trades_for_order(trade.open_order_id, trade.pair, trade.open_date)
|
trades = self.exchange.get_trades_for_order(trade.open_order_id, trade.pair,
|
||||||
|
trade.open_date)
|
||||||
|
|
||||||
if len(trades) == 0:
|
if len(trades) == 0:
|
||||||
logger.info("Applying fee on amount for %s failed: myTrade-Dict empty found", trade)
|
logger.info("Applying fee on amount for %s failed: myTrade-Dict empty found", trade)
|
||||||
@ -476,12 +478,13 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||||||
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 = exchange.get_ticker(trade.pair)['bid']
|
current_rate = self.exchange.get_ticker(trade.pair)['bid']
|
||||||
|
|
||||||
(buy, sell) = (False, False)
|
(buy, sell) = (False, False)
|
||||||
|
experimental = self.config.get('experimental', {})
|
||||||
if self.config.get('experimental', {}).get('use_sell_signal'):
|
if experimental.get('use_sell_signal') or experimental.get('ignore_roi_if_buy_signal'):
|
||||||
(buy, sell) = self.analyze.get_signal(trade.pair, self.analyze.get_ticker_interval())
|
(buy, sell) = self.analyze.get_signal(self.exchange,
|
||||||
|
trade.pair, self.analyze.get_ticker_interval())
|
||||||
|
|
||||||
if self.analyze.should_sell(trade, current_rate, datetime.utcnow(), buy, sell):
|
if self.analyze.should_sell(trade, current_rate, datetime.utcnow(), buy, sell):
|
||||||
self.execute_sell(trade, current_rate)
|
self.execute_sell(trade, current_rate)
|
||||||
@ -505,7 +508,7 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||||||
# updated via /forcesell in a different thread.
|
# updated via /forcesell in a different thread.
|
||||||
if not trade.open_order_id:
|
if not trade.open_order_id:
|
||||||
continue
|
continue
|
||||||
order = exchange.get_order(trade.open_order_id, trade.pair)
|
order = self.exchange.get_order(trade.open_order_id, trade.pair)
|
||||||
except requests.exceptions.RequestException:
|
except requests.exceptions.RequestException:
|
||||||
logger.info(
|
logger.info(
|
||||||
'Cannot query order for %s due to %s',
|
'Cannot query order for %s due to %s',
|
||||||
@ -531,7 +534,7 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||||||
:return: True if order was fully cancelled
|
:return: True if order was fully cancelled
|
||||||
"""
|
"""
|
||||||
pair_s = trade.pair.replace('_', '/')
|
pair_s = trade.pair.replace('_', '/')
|
||||||
exchange.cancel_order(trade.open_order_id, trade.pair)
|
self.exchange.cancel_order(trade.open_order_id, trade.pair)
|
||||||
if order['remaining'] == order['amount']:
|
if order['remaining'] == order['amount']:
|
||||||
# if trade is not partially completed, just delete the trade
|
# if trade is not partially completed, just delete the trade
|
||||||
Trade.session.delete(trade)
|
Trade.session.delete(trade)
|
||||||
@ -558,7 +561,7 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||||||
pair_s = trade.pair.replace('_', '/')
|
pair_s = trade.pair.replace('_', '/')
|
||||||
if order['remaining'] == order['amount']:
|
if order['remaining'] == order['amount']:
|
||||||
# if trade is not partially completed, just cancel the trade
|
# if trade is not partially completed, just cancel the trade
|
||||||
exchange.cancel_order(trade.open_order_id, trade.pair)
|
self.exchange.cancel_order(trade.open_order_id, trade.pair)
|
||||||
trade.close_rate = None
|
trade.close_rate = None
|
||||||
trade.close_profit = None
|
trade.close_profit = None
|
||||||
trade.close_date = None
|
trade.close_date = None
|
||||||
@ -581,15 +584,15 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||||||
exc = trade.exchange
|
exc = trade.exchange
|
||||||
pair = trade.pair
|
pair = trade.pair
|
||||||
# Execute sell and update trade record
|
# Execute sell and update trade record
|
||||||
order_id = exchange.sell(str(trade.pair), limit, trade.amount)['id']
|
order_id = self.exchange.sell(str(trade.pair), limit, trade.amount)['id']
|
||||||
trade.open_order_id = order_id
|
trade.open_order_id = order_id
|
||||||
trade.close_rate_requested = limit
|
trade.close_rate_requested = limit
|
||||||
|
|
||||||
fmt_exp_profit = round(trade.calc_profit_percent(rate=limit) * 100, 2)
|
fmt_exp_profit = round(trade.calc_profit_percent(rate=limit) * 100, 2)
|
||||||
profit_trade = trade.calc_profit(rate=limit)
|
profit_trade = trade.calc_profit(rate=limit)
|
||||||
current_rate = exchange.get_ticker(trade.pair)['bid']
|
current_rate = self.exchange.get_ticker(trade.pair)['bid']
|
||||||
profit = trade.calc_profit_percent(limit)
|
profit = trade.calc_profit_percent(limit)
|
||||||
pair_url = exchange.get_pair_detail_url(trade.pair)
|
pair_url = self.exchange.get_pair_detail_url(trade.pair)
|
||||||
gain = "profit" if fmt_exp_profit > 0 else "loss"
|
gain = "profit" if fmt_exp_profit > 0 else "loss"
|
||||||
|
|
||||||
message = f"*{exc}:* Selling\n" \
|
message = f"*{exc}:* Selling\n" \
|
||||||
|
@ -7,8 +7,8 @@ import os
|
|||||||
from typing import Optional, List, Dict, Tuple, Any
|
from typing import Optional, List, Dict, Tuple, Any
|
||||||
import arrow
|
import arrow
|
||||||
|
|
||||||
from freqtrade import misc, constants
|
from freqtrade import misc, constants, OperationalException
|
||||||
from freqtrade.exchange import get_ticker_history
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.arguments import TimeRange
|
from freqtrade.arguments import TimeRange
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -83,6 +83,7 @@ def load_data(datadir: str,
|
|||||||
ticker_interval: str,
|
ticker_interval: str,
|
||||||
pairs: List[str],
|
pairs: List[str],
|
||||||
refresh_pairs: Optional[bool] = False,
|
refresh_pairs: Optional[bool] = False,
|
||||||
|
exchange: Optional[Exchange] = None,
|
||||||
timerange: TimeRange = TimeRange(None, None, 0, 0)) -> Dict[str, List]:
|
timerange: TimeRange = TimeRange(None, None, 0, 0)) -> Dict[str, List]:
|
||||||
"""
|
"""
|
||||||
Loads ticker history data for the given parameters
|
Loads ticker history data for the given parameters
|
||||||
@ -93,7 +94,10 @@ def load_data(datadir: str,
|
|||||||
# If the user force the refresh of pairs
|
# If the user force the refresh of pairs
|
||||||
if refresh_pairs:
|
if refresh_pairs:
|
||||||
logger.info('Download data for all pairs and store them in %s', datadir)
|
logger.info('Download data for all pairs and store them in %s', datadir)
|
||||||
download_pairs(datadir, pairs, ticker_interval, timerange=timerange)
|
if not exchange:
|
||||||
|
raise OperationalException("Exchange needs to be initialized when "
|
||||||
|
"calling load_data with refresh_pairs=True")
|
||||||
|
download_pairs(datadir, exchange, pairs, ticker_interval, timerange=timerange)
|
||||||
|
|
||||||
for pair in pairs:
|
for pair in pairs:
|
||||||
pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange)
|
pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange)
|
||||||
@ -119,13 +123,14 @@ def make_testdata_path(datadir: str) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def download_pairs(datadir, pairs: List[str],
|
def download_pairs(datadir, exchange: Exchange, pairs: List[str],
|
||||||
ticker_interval: str,
|
ticker_interval: str,
|
||||||
timerange: TimeRange = TimeRange(None, None, 0, 0)) -> bool:
|
timerange: TimeRange = TimeRange(None, None, 0, 0)) -> bool:
|
||||||
"""For each pairs passed in parameters, download the ticker intervals"""
|
"""For each pairs passed in parameters, download the ticker intervals"""
|
||||||
for pair in pairs:
|
for pair in pairs:
|
||||||
try:
|
try:
|
||||||
download_backtesting_testdata(datadir,
|
download_backtesting_testdata(datadir,
|
||||||
|
exchange=exchange,
|
||||||
pair=pair,
|
pair=pair,
|
||||||
tick_interval=ticker_interval,
|
tick_interval=ticker_interval,
|
||||||
timerange=timerange)
|
timerange=timerange)
|
||||||
@ -183,6 +188,7 @@ def load_cached_data_for_updating(filename: str,
|
|||||||
|
|
||||||
|
|
||||||
def download_backtesting_testdata(datadir: str,
|
def download_backtesting_testdata(datadir: str,
|
||||||
|
exchange: Exchange,
|
||||||
pair: str,
|
pair: str,
|
||||||
tick_interval: str = '5m',
|
tick_interval: str = '5m',
|
||||||
timerange: Optional[TimeRange] = None) -> None:
|
timerange: Optional[TimeRange] = None) -> None:
|
||||||
@ -216,7 +222,8 @@ def download_backtesting_testdata(datadir: str,
|
|||||||
logger.debug("Current Start: %s", misc.format_ms_time(data[1][0]) if data else 'None')
|
logger.debug("Current Start: %s", misc.format_ms_time(data[1][0]) if data else 'None')
|
||||||
logger.debug("Current End: %s", misc.format_ms_time(data[-1][0]) if data else 'None')
|
logger.debug("Current End: %s", misc.format_ms_time(data[-1][0]) if data else 'None')
|
||||||
|
|
||||||
new_data = get_ticker_history(pair=pair, tick_interval=tick_interval, since_ms=since_ms)
|
new_data = exchange.get_ticker_history(pair=pair, tick_interval=tick_interval,
|
||||||
|
since_ms=since_ms)
|
||||||
data.extend(new_data)
|
data.extend(new_data)
|
||||||
|
|
||||||
logger.debug("New Start: %s", misc.format_ms_time(data[0][0]))
|
logger.debug("New Start: %s", misc.format_ms_time(data[0][0]))
|
||||||
|
@ -14,7 +14,8 @@ from pandas import DataFrame
|
|||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
import freqtrade.optimize as optimize
|
import freqtrade.optimize as optimize
|
||||||
from freqtrade import exchange, constants, DependencyException
|
from freqtrade import constants, DependencyException
|
||||||
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.analyze import Analyze
|
from freqtrade.analyze import Analyze
|
||||||
from freqtrade.arguments import Arguments
|
from freqtrade.arguments import Arguments
|
||||||
from freqtrade.configuration import Configuration
|
from freqtrade.configuration import Configuration
|
||||||
@ -61,7 +62,8 @@ class Backtesting(object):
|
|||||||
self.config['exchange']['password'] = ''
|
self.config['exchange']['password'] = ''
|
||||||
self.config['exchange']['uid'] = ''
|
self.config['exchange']['uid'] = ''
|
||||||
self.config['dry_run'] = True
|
self.config['dry_run'] = True
|
||||||
exchange.init(self.config)
|
self.exchange = Exchange(self.config)
|
||||||
|
self.fee = self.exchange.get_fee()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_timeframe(data: Dict[str, DataFrame]) -> Tuple[arrow.Arrow, arrow.Arrow]:
|
def get_timeframe(data: Dict[str, DataFrame]) -> Tuple[arrow.Arrow, arrow.Arrow]:
|
||||||
@ -130,14 +132,13 @@ class Backtesting(object):
|
|||||||
|
|
||||||
stake_amount = args['stake_amount']
|
stake_amount = args['stake_amount']
|
||||||
max_open_trades = args.get('max_open_trades', 0)
|
max_open_trades = args.get('max_open_trades', 0)
|
||||||
fee = exchange.get_fee()
|
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
open_rate=buy_row.close,
|
open_rate=buy_row.close,
|
||||||
open_date=buy_row.date,
|
open_date=buy_row.date,
|
||||||
stake_amount=stake_amount,
|
stake_amount=stake_amount,
|
||||||
amount=stake_amount / buy_row.open,
|
amount=stake_amount / buy_row.open,
|
||||||
fee_open=fee,
|
fee_open=self.fee,
|
||||||
fee_close=fee
|
fee_close=self.fee
|
||||||
)
|
)
|
||||||
|
|
||||||
# calculate win/lose forwards from buy point
|
# calculate win/lose forwards from buy point
|
||||||
@ -256,7 +257,7 @@ class Backtesting(object):
|
|||||||
if self.config.get('live'):
|
if self.config.get('live'):
|
||||||
logger.info('Downloading data for all pairs in whitelist ...')
|
logger.info('Downloading data for all pairs in whitelist ...')
|
||||||
for pair in pairs:
|
for pair in pairs:
|
||||||
data[pair] = exchange.get_ticker_history(pair, self.ticker_interval)
|
data[pair] = self.exchange.get_ticker_history(pair, self.ticker_interval)
|
||||||
else:
|
else:
|
||||||
logger.info('Using local backtesting data (using whitelist in given config) ...')
|
logger.info('Using local backtesting data (using whitelist in given config) ...')
|
||||||
|
|
||||||
@ -267,6 +268,7 @@ class Backtesting(object):
|
|||||||
pairs=pairs,
|
pairs=pairs,
|
||||||
ticker_interval=self.ticker_interval,
|
ticker_interval=self.ticker_interval,
|
||||||
refresh_pairs=self.config.get('refresh_pairs', False),
|
refresh_pairs=self.config.get('refresh_pairs', False),
|
||||||
|
exchange=self.exchange,
|
||||||
timerange=timerange
|
timerange=timerange
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@ class Hyperopt(Backtesting):
|
|||||||
hyperopt.start()
|
hyperopt.start()
|
||||||
"""
|
"""
|
||||||
def __init__(self, config: Dict[str, Any]) -> None:
|
def __init__(self, config: Dict[str, Any]) -> None:
|
||||||
|
|
||||||
super().__init__(config)
|
super().__init__(config)
|
||||||
# set TARGET_TRADES to suit your number concurrent trades so its realistic
|
# set TARGET_TRADES to suit your number concurrent trades so its realistic
|
||||||
# to the number of days
|
# to the number of days
|
||||||
|
@ -21,7 +21,6 @@ from freqtrade import OperationalException
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_CONF = {}
|
|
||||||
_DECL_BASE: Any = declarative_base()
|
_DECL_BASE: Any = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
@ -33,9 +32,7 @@ def init(config: Dict) -> None:
|
|||||||
:param config: config to use
|
:param config: config to use
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
_CONF.update(config)
|
db_url = config.get('db_url', None)
|
||||||
|
|
||||||
db_url = _CONF.get('db_url', None)
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
||||||
# Take care of thread ownership if in-memory db
|
# Take care of thread ownership if in-memory db
|
||||||
@ -61,7 +58,7 @@ def init(config: Dict) -> None:
|
|||||||
check_migrate(engine)
|
check_migrate(engine)
|
||||||
|
|
||||||
# Clean dry_run DB if the db is not in-memory
|
# Clean dry_run DB if the db is not in-memory
|
||||||
if _CONF.get('dry_run', False) and db_url != 'sqlite://':
|
if config.get('dry_run', False) and db_url != 'sqlite://':
|
||||||
clean_dry_run_db()
|
clean_dry_run_db()
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ import sqlalchemy as sql
|
|||||||
from numpy import mean, nan_to_num
|
from numpy import mean, nan_to_num
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade import exchange
|
|
||||||
from freqtrade.misc import shorten_date
|
from freqtrade.misc import shorten_date
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
@ -71,9 +70,9 @@ class RPC(object):
|
|||||||
for trade in trades:
|
for trade in trades:
|
||||||
order = None
|
order = None
|
||||||
if trade.open_order_id:
|
if trade.open_order_id:
|
||||||
order = exchange.get_order(trade.open_order_id, trade.pair)
|
order = self._freqtrade.exchange.get_order(trade.open_order_id, trade.pair)
|
||||||
# calculate profit and send message to user
|
# calculate profit and send message to user
|
||||||
current_rate = exchange.get_ticker(trade.pair, False)['bid']
|
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
|
||||||
current_profit = trade.calc_profit_percent(current_rate)
|
current_profit = trade.calc_profit_percent(current_rate)
|
||||||
fmt_close_profit = '{:.2f}%'.format(
|
fmt_close_profit = '{:.2f}%'.format(
|
||||||
round(trade.close_profit * 100, 2)
|
round(trade.close_profit * 100, 2)
|
||||||
@ -91,7 +90,7 @@ class RPC(object):
|
|||||||
.format(
|
.format(
|
||||||
trade_id=trade.id,
|
trade_id=trade.id,
|
||||||
pair=trade.pair,
|
pair=trade.pair,
|
||||||
market_url=exchange.get_pair_detail_url(trade.pair),
|
market_url=self._freqtrade.exchange.get_pair_detail_url(trade.pair),
|
||||||
date=arrow.get(trade.open_date).humanize(),
|
date=arrow.get(trade.open_date).humanize(),
|
||||||
open_rate=trade.open_rate,
|
open_rate=trade.open_rate,
|
||||||
close_rate=trade.close_rate,
|
close_rate=trade.close_rate,
|
||||||
@ -116,7 +115,7 @@ class RPC(object):
|
|||||||
trades_list = []
|
trades_list = []
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
# calculate profit and send message to user
|
# calculate profit and send message to user
|
||||||
current_rate = exchange.get_ticker(trade.pair, False)['bid']
|
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
|
||||||
trades_list.append([
|
trades_list.append([
|
||||||
trade.id,
|
trade.id,
|
||||||
trade.pair,
|
trade.pair,
|
||||||
@ -201,7 +200,7 @@ class RPC(object):
|
|||||||
profit_closed_percent.append(profit_percent)
|
profit_closed_percent.append(profit_percent)
|
||||||
else:
|
else:
|
||||||
# Get current rate
|
# Get current rate
|
||||||
current_rate = exchange.get_ticker(trade.pair, False)['bid']
|
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
|
||||||
profit_percent = trade.calc_profit_percent(rate=current_rate)
|
profit_percent = trade.calc_profit_percent(rate=current_rate)
|
||||||
|
|
||||||
profit_all_coin.append(
|
profit_all_coin.append(
|
||||||
@ -258,7 +257,7 @@ class RPC(object):
|
|||||||
""" Returns current account balance per crypto """
|
""" Returns current account balance per crypto """
|
||||||
output = []
|
output = []
|
||||||
total = 0.0
|
total = 0.0
|
||||||
for coin, balance in exchange.get_balances().items():
|
for coin, balance in self._freqtrade.exchange.get_balances().items():
|
||||||
if not balance['total']:
|
if not balance['total']:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -266,9 +265,9 @@ class RPC(object):
|
|||||||
rate = 1.0
|
rate = 1.0
|
||||||
else:
|
else:
|
||||||
if coin == 'USDT':
|
if coin == 'USDT':
|
||||||
rate = 1.0 / exchange.get_ticker('BTC/USDT', False)['bid']
|
rate = 1.0 / self._freqtrade.exchange.get_ticker('BTC/USDT', False)['bid']
|
||||||
else:
|
else:
|
||||||
rate = exchange.get_ticker(coin + '/BTC', False)['bid']
|
rate = self._freqtrade.exchange.get_ticker(coin + '/BTC', False)['bid']
|
||||||
est_btc: float = rate * balance['total']
|
est_btc: float = rate * balance['total']
|
||||||
total = total + est_btc
|
total = total + est_btc
|
||||||
output.append(
|
output.append(
|
||||||
@ -318,13 +317,13 @@ class RPC(object):
|
|||||||
def _exec_forcesell(trade: Trade) -> None:
|
def _exec_forcesell(trade: Trade) -> None:
|
||||||
# Check if there is there is an open order
|
# Check if there is there is an open order
|
||||||
if trade.open_order_id:
|
if trade.open_order_id:
|
||||||
order = exchange.get_order(trade.open_order_id, trade.pair)
|
order = self._freqtrade.exchange.get_order(trade.open_order_id, trade.pair)
|
||||||
|
|
||||||
# Cancel open LIMIT_BUY orders and close trade
|
# Cancel open LIMIT_BUY orders and close trade
|
||||||
if order and order['status'] == 'open' \
|
if order and order['status'] == 'open' \
|
||||||
and order['type'] == 'limit' \
|
and order['type'] == 'limit' \
|
||||||
and order['side'] == 'buy':
|
and order['side'] == 'buy':
|
||||||
exchange.cancel_order(trade.open_order_id, trade.pair)
|
self._freqtrade.exchange.cancel_order(trade.open_order_id, trade.pair)
|
||||||
trade.close(order.get('price') or trade.open_rate)
|
trade.close(order.get('price') or trade.open_rate)
|
||||||
# Do the best effort, if we don't know 'filled' amount, don't try selling
|
# Do the best effort, if we don't know 'filled' amount, don't try selling
|
||||||
if order['filled'] is None:
|
if order['filled'] is None:
|
||||||
@ -338,7 +337,7 @@ class RPC(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Get current rate and execute sell
|
# Get current rate and execute sell
|
||||||
current_rate = exchange.get_ticker(trade.pair, False)['bid']
|
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
|
||||||
self._freqtrade.execute_sell(trade, current_rate)
|
self._freqtrade.execute_sell(trade, current_rate)
|
||||||
# ---- EOF def _exec_forcesell ----
|
# ---- EOF def _exec_forcesell ----
|
||||||
|
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
import logging
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
from freqtrade.strategy.interface import IStrategy
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def import_strategy(strategy: IStrategy) -> IStrategy:
|
||||||
|
"""
|
||||||
|
Imports given Strategy instance to global scope
|
||||||
|
of freqtrade.strategy and returns an instance of it
|
||||||
|
"""
|
||||||
|
# Copy all attributes from base class and class
|
||||||
|
attr = deepcopy({**strategy.__class__.__dict__, **strategy.__dict__})
|
||||||
|
# Adjust module name
|
||||||
|
attr['__module__'] = 'freqtrade.strategy'
|
||||||
|
|
||||||
|
name = strategy.__class__.__name__
|
||||||
|
clazz = type(name, (IStrategy,), attr)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
'Imported strategy %s.%s as %s.%s',
|
||||||
|
strategy.__module__, strategy.__class__.__name__,
|
||||||
|
clazz.__module__, strategy.__class__.__name__,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Modify global scope to declare class
|
||||||
|
globals()[name] = clazz
|
||||||
|
|
||||||
|
return clazz()
|
@ -11,6 +11,7 @@ from collections import OrderedDict
|
|||||||
from typing import Optional, Dict, Type
|
from typing import Optional, Dict, Type
|
||||||
|
|
||||||
from freqtrade import constants
|
from freqtrade import constants
|
||||||
|
from freqtrade.strategy import import_strategy
|
||||||
from freqtrade.strategy.interface import IStrategy
|
from freqtrade.strategy.interface import IStrategy
|
||||||
|
|
||||||
|
|
||||||
@ -83,7 +84,7 @@ class StrategyResolver(object):
|
|||||||
strategy = self._search_strategy(path, strategy_name)
|
strategy = self._search_strategy(path, strategy_name)
|
||||||
if strategy:
|
if strategy:
|
||||||
logger.info('Using resolved strategy %s from \'%s\'', strategy_name, path)
|
logger.info('Using resolved strategy %s from \'%s\'', strategy_name, path)
|
||||||
return strategy
|
return import_strategy(strategy)
|
||||||
|
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
"Impossible to load Strategy '{}'. This class does not exist"
|
"Impossible to load Strategy '{}'. This class does not exist"
|
||||||
@ -100,7 +101,7 @@ class StrategyResolver(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Generate spec based on absolute path
|
# Generate spec based on absolute path
|
||||||
spec = importlib.util.spec_from_file_location('user_data.strategies', module_path)
|
spec = importlib.util.spec_from_file_location('unknown', module_path)
|
||||||
module = importlib.util.module_from_spec(spec)
|
module = importlib.util.module_from_spec(spec)
|
||||||
spec.loader.exec_module(module) # type: ignore # importlib does not use typehints
|
spec.loader.exec_module(module) # type: ignore # importlib does not use typehints
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ from telegram import Chat, Message, Update
|
|||||||
|
|
||||||
from freqtrade.analyze import Analyze
|
from freqtrade.analyze import Analyze
|
||||||
from freqtrade import constants
|
from freqtrade import constants
|
||||||
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
|
|
||||||
logging.getLogger('').setLevel(logging.INFO)
|
logging.getLogger('').setLevel(logging.INFO)
|
||||||
@ -26,6 +27,20 @@ def log_has(line, logs):
|
|||||||
False)
|
False)
|
||||||
|
|
||||||
|
|
||||||
|
def patch_exchange(mocker, api_mock=None) -> None:
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||||
|
if api_mock:
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||||
|
else:
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock())
|
||||||
|
|
||||||
|
|
||||||
|
def get_patched_exchange(mocker, config, api_mock=None) -> Exchange:
|
||||||
|
patch_exchange(mocker, api_mock)
|
||||||
|
exchange = Exchange(config)
|
||||||
|
return exchange
|
||||||
|
|
||||||
|
|
||||||
# Functions for recurrent object patching
|
# Functions for recurrent object patching
|
||||||
def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
|
def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
|
||||||
"""
|
"""
|
||||||
@ -39,7 +54,7 @@ def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
|
|||||||
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
patch_exchange(mocker, None)
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.get_signal', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.Analyze.get_signal', MagicMock())
|
||||||
|
@ -3,33 +3,20 @@
|
|||||||
import logging
|
import logging
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from random import randint
|
from random import randint
|
||||||
|
from datetime import datetime
|
||||||
from unittest.mock import MagicMock, PropertyMock
|
from unittest.mock import MagicMock, PropertyMock
|
||||||
|
|
||||||
import ccxt
|
import ccxt
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import freqtrade.exchange as exchange
|
|
||||||
from freqtrade import OperationalException, DependencyException, TemporaryError
|
from freqtrade import OperationalException, DependencyException, TemporaryError
|
||||||
from freqtrade.exchange import (init, validate_pairs, buy, sell, get_balance, get_balances,
|
from freqtrade.exchange import Exchange, API_RETRY_COUNT
|
||||||
get_ticker, get_ticker_history, cancel_order, get_name, get_fee,
|
from freqtrade.tests.conftest import log_has, get_patched_exchange
|
||||||
get_id, get_pair_detail_url, get_amount_lots)
|
|
||||||
from freqtrade.tests.conftest import log_has
|
|
||||||
|
|
||||||
API_INIT = False
|
|
||||||
|
|
||||||
|
|
||||||
def maybe_init_api(conf, mocker, force=False):
|
|
||||||
global API_INIT
|
|
||||||
if force or not API_INIT:
|
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs',
|
|
||||||
side_effect=lambda s: True)
|
|
||||||
init(config=conf)
|
|
||||||
API_INIT = True
|
|
||||||
|
|
||||||
|
|
||||||
def test_init(default_conf, mocker, caplog):
|
def test_init(default_conf, mocker, caplog):
|
||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
maybe_init_api(default_conf, mocker, True)
|
get_patched_exchange(mocker, default_conf)
|
||||||
assert log_has('Instance is running with dry_run enabled', caplog.record_tuples)
|
assert log_has('Instance is running with dry_run enabled', caplog.record_tuples)
|
||||||
|
|
||||||
|
|
||||||
@ -39,7 +26,7 @@ def test_init_exception(default_conf):
|
|||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
OperationalException,
|
OperationalException,
|
||||||
match='Exchange {} is not supported'.format(default_conf['exchange']['name'])):
|
match='Exchange {} is not supported'.format(default_conf['exchange']['name'])):
|
||||||
init(config=default_conf)
|
Exchange(default_conf)
|
||||||
|
|
||||||
|
|
||||||
def test_validate_pairs(default_conf, mocker):
|
def test_validate_pairs(default_conf, mocker):
|
||||||
@ -50,18 +37,17 @@ def test_validate_pairs(default_conf, mocker):
|
|||||||
id_mock = PropertyMock(return_value='test_exchange')
|
id_mock = PropertyMock(return_value='test_exchange')
|
||||||
type(api_mock).id = id_mock
|
type(api_mock).id = id_mock
|
||||||
|
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
Exchange(default_conf)
|
||||||
validate_pairs(default_conf['exchange']['pair_whitelist'])
|
|
||||||
|
|
||||||
|
|
||||||
def test_validate_pairs_not_available(default_conf, mocker):
|
def test_validate_pairs_not_available(default_conf, mocker):
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.load_markets = MagicMock(return_value={})
|
api_mock.load_markets = MagicMock(return_value={})
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
|
||||||
with pytest.raises(OperationalException, match=r'not available'):
|
with pytest.raises(OperationalException, match=r'not available'):
|
||||||
validate_pairs(default_conf['exchange']['pair_whitelist'])
|
Exchange(default_conf)
|
||||||
|
|
||||||
|
|
||||||
def test_validate_pairs_not_compatible(default_conf, mocker):
|
def test_validate_pairs_not_compatible(default_conf, mocker):
|
||||||
@ -71,25 +57,27 @@ def test_validate_pairs_not_compatible(default_conf, mocker):
|
|||||||
})
|
})
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf['stake_currency'] = 'ETH'
|
conf['stake_currency'] = 'ETH'
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', conf)
|
|
||||||
with pytest.raises(OperationalException, match=r'not compatible'):
|
with pytest.raises(OperationalException, match=r'not compatible'):
|
||||||
validate_pairs(conf['exchange']['pair_whitelist'])
|
Exchange(conf)
|
||||||
|
|
||||||
|
|
||||||
def test_validate_pairs_exception(default_conf, mocker, caplog):
|
def test_validate_pairs_exception(default_conf, mocker, caplog):
|
||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.name = 'Binance'
|
mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value='Binance'))
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
|
||||||
|
|
||||||
api_mock.load_markets = MagicMock(return_value={})
|
api_mock.load_markets = MagicMock(return_value={})
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', api_mock)
|
||||||
|
|
||||||
with pytest.raises(OperationalException, match=r'Pair ETH/BTC is not available at Binance'):
|
with pytest.raises(OperationalException, match=r'Pair ETH/BTC is not available at Binance'):
|
||||||
validate_pairs(default_conf['exchange']['pair_whitelist'])
|
Exchange(default_conf)
|
||||||
|
|
||||||
api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError())
|
api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError())
|
||||||
validate_pairs(default_conf['exchange']['pair_whitelist'])
|
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||||
|
Exchange(default_conf)
|
||||||
assert log_has('Unable to validate pairs (assuming they are correct). Reason: ',
|
assert log_has('Unable to validate pairs (assuming they are correct). Reason: ',
|
||||||
caplog.record_tuples)
|
caplog.record_tuples)
|
||||||
|
|
||||||
@ -99,22 +87,21 @@ def test_validate_pairs_stake_exception(default_conf, mocker, caplog):
|
|||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf['stake_currency'] = 'ETH'
|
conf['stake_currency'] = 'ETH'
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.name = 'binance'
|
api_mock.name = MagicMock(return_value='binance')
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', api_mock)
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', conf)
|
|
||||||
|
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
OperationalException,
|
OperationalException,
|
||||||
match=r'Pair ETH/BTC not compatible with stake_currency: ETH'
|
match=r'Pair ETH/BTC not compatible with stake_currency: ETH'
|
||||||
):
|
):
|
||||||
validate_pairs(default_conf['exchange']['pair_whitelist'])
|
Exchange(conf)
|
||||||
|
|
||||||
|
|
||||||
def test_buy_dry_run(default_conf, mocker):
|
def test_buy_dry_run(default_conf, mocker):
|
||||||
default_conf['dry_run'] = True
|
default_conf['dry_run'] = True
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
|
|
||||||
order = buy(pair='ETH/BTC', rate=200, amount=1)
|
order = exchange.buy(pair='ETH/BTC', rate=200, amount=1)
|
||||||
assert 'id' in order
|
assert 'id' in order
|
||||||
assert 'dry_run_buy_' in order['id']
|
assert 'dry_run_buy_' in order['id']
|
||||||
|
|
||||||
@ -128,12 +115,10 @@ def test_buy_prod(default_conf, mocker):
|
|||||||
'foo': 'bar'
|
'foo': 'bar'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
|
||||||
|
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
|
||||||
order = buy(pair='ETH/BTC', rate=200, amount=1)
|
order = exchange.buy(pair='ETH/BTC', rate=200, amount=1)
|
||||||
assert 'id' in order
|
assert 'id' in order
|
||||||
assert 'info' in order
|
assert 'info' in order
|
||||||
assert order['id'] == order_id
|
assert order['id'] == order_id
|
||||||
@ -141,30 +126,30 @@ def test_buy_prod(default_conf, mocker):
|
|||||||
# test exception handling
|
# test exception handling
|
||||||
with pytest.raises(DependencyException):
|
with pytest.raises(DependencyException):
|
||||||
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.InsufficientFunds)
|
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.InsufficientFunds)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
buy(pair='ETH/BTC', rate=200, amount=1)
|
exchange.buy(pair='ETH/BTC', rate=200, amount=1)
|
||||||
|
|
||||||
with pytest.raises(DependencyException):
|
with pytest.raises(DependencyException):
|
||||||
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
buy(pair='ETH/BTC', rate=200, amount=1)
|
exchange.buy(pair='ETH/BTC', rate=200, amount=1)
|
||||||
|
|
||||||
with pytest.raises(TemporaryError):
|
with pytest.raises(TemporaryError):
|
||||||
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.NetworkError)
|
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.NetworkError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
buy(pair='ETH/BTC', rate=200, amount=1)
|
exchange.buy(pair='ETH/BTC', rate=200, amount=1)
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.BaseError)
|
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.BaseError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
buy(pair='ETH/BTC', rate=200, amount=1)
|
exchange.buy(pair='ETH/BTC', rate=200, amount=1)
|
||||||
|
|
||||||
|
|
||||||
def test_sell_dry_run(default_conf, mocker):
|
def test_sell_dry_run(default_conf, mocker):
|
||||||
default_conf['dry_run'] = True
|
default_conf['dry_run'] = True
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
|
|
||||||
order = sell(pair='ETH/BTC', rate=200, amount=1)
|
order = exchange.sell(pair='ETH/BTC', rate=200, amount=1)
|
||||||
assert 'id' in order
|
assert 'id' in order
|
||||||
assert 'dry_run_sell_' in order['id']
|
assert 'dry_run_sell_' in order['id']
|
||||||
|
|
||||||
@ -178,12 +163,11 @@ def test_sell_prod(default_conf, mocker):
|
|||||||
'foo': 'bar'
|
'foo': 'bar'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
|
||||||
|
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
|
||||||
|
|
||||||
order = sell(pair='ETH/BTC', rate=200, amount=1)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
|
||||||
|
order = exchange.sell(pair='ETH/BTC', rate=200, amount=1)
|
||||||
assert 'id' in order
|
assert 'id' in order
|
||||||
assert 'info' in order
|
assert 'info' in order
|
||||||
assert order['id'] == order_id
|
assert order['id'] == order_id
|
||||||
@ -191,53 +175,52 @@ def test_sell_prod(default_conf, mocker):
|
|||||||
# test exception handling
|
# test exception handling
|
||||||
with pytest.raises(DependencyException):
|
with pytest.raises(DependencyException):
|
||||||
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.InsufficientFunds)
|
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.InsufficientFunds)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
sell(pair='ETH/BTC', rate=200, amount=1)
|
exchange.sell(pair='ETH/BTC', rate=200, amount=1)
|
||||||
|
|
||||||
with pytest.raises(DependencyException):
|
with pytest.raises(DependencyException):
|
||||||
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
sell(pair='ETH/BTC', rate=200, amount=1)
|
exchange.sell(pair='ETH/BTC', rate=200, amount=1)
|
||||||
|
|
||||||
with pytest.raises(TemporaryError):
|
with pytest.raises(TemporaryError):
|
||||||
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.NetworkError)
|
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.NetworkError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
sell(pair='ETH/BTC', rate=200, amount=1)
|
exchange.sell(pair='ETH/BTC', rate=200, amount=1)
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.BaseError)
|
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.BaseError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
sell(pair='ETH/BTC', rate=200, amount=1)
|
exchange.sell(pair='ETH/BTC', rate=200, amount=1)
|
||||||
|
|
||||||
|
|
||||||
def test_get_balance_dry_run(default_conf, mocker):
|
def test_get_balance_dry_run(default_conf, mocker):
|
||||||
default_conf['dry_run'] = True
|
default_conf['dry_run'] = True
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
|
||||||
|
|
||||||
assert get_balance(currency='BTC') == 999.9
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
|
assert exchange.get_balance(currency='BTC') == 999.9
|
||||||
|
|
||||||
|
|
||||||
def test_get_balance_prod(default_conf, mocker):
|
def test_get_balance_prod(default_conf, mocker):
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.fetch_balance = MagicMock(return_value={'BTC': {'free': 123.4}})
|
api_mock.fetch_balance = MagicMock(return_value={'BTC': {'free': 123.4}})
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
|
||||||
|
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
|
||||||
|
|
||||||
assert get_balance(currency='BTC') == 123.4
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
|
||||||
|
assert exchange.get_balance(currency='BTC') == 123.4
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.fetch_balance = MagicMock(side_effect=ccxt.BaseError)
|
api_mock.fetch_balance = MagicMock(side_effect=ccxt.BaseError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
get_balance(currency='BTC')
|
|
||||||
|
exchange.get_balance(currency='BTC')
|
||||||
|
|
||||||
|
|
||||||
def test_get_balances_dry_run(default_conf, mocker):
|
def test_get_balances_dry_run(default_conf, mocker):
|
||||||
default_conf['dry_run'] = True
|
default_conf['dry_run'] = True
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
|
assert exchange.get_balances() == {}
|
||||||
assert get_balances() == {}
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_balances_prod(default_conf, mocker):
|
def test_get_balances_prod(default_conf, mocker):
|
||||||
@ -253,33 +236,73 @@ def test_get_balances_prod(default_conf, mocker):
|
|||||||
'2ST': balance_item,
|
'2ST': balance_item,
|
||||||
'3ST': balance_item
|
'3ST': balance_item
|
||||||
})
|
})
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
|
||||||
|
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
assert len(exchange.get_balances()) == 3
|
||||||
assert len(get_balances()) == 3
|
assert exchange.get_balances()['1ST']['free'] == 10.0
|
||||||
assert get_balances()['1ST']['free'] == 10.0
|
assert exchange.get_balances()['1ST']['total'] == 10.0
|
||||||
assert get_balances()['1ST']['total'] == 10.0
|
assert exchange.get_balances()['1ST']['used'] == 0.0
|
||||||
assert get_balances()['1ST']['used'] == 0.0
|
|
||||||
|
|
||||||
with pytest.raises(TemporaryError):
|
with pytest.raises(TemporaryError):
|
||||||
api_mock.fetch_balance = MagicMock(side_effect=ccxt.NetworkError)
|
api_mock.fetch_balance = MagicMock(side_effect=ccxt.NetworkError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
get_balances()
|
exchange.get_balances()
|
||||||
assert api_mock.fetch_balance.call_count == exchange.API_RETRY_COUNT + 1
|
assert api_mock.fetch_balance.call_count == API_RETRY_COUNT + 1
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.fetch_balance = MagicMock(side_effect=ccxt.BaseError)
|
api_mock.fetch_balance = MagicMock(side_effect=ccxt.BaseError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
get_balances()
|
exchange.get_balances()
|
||||||
assert api_mock.fetch_balance.call_count == 1
|
assert api_mock.fetch_balance.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
# This test is somewhat redundant with
|
def test_get_tickers(default_conf, mocker):
|
||||||
# test_exchange_bittrex.py::test_exchange_bittrex_get_ticker
|
api_mock = MagicMock()
|
||||||
|
tick = {'ETH/BTC': {
|
||||||
|
'symbol': 'ETH/BTC',
|
||||||
|
'bid': 0.5,
|
||||||
|
'ask': 1,
|
||||||
|
'last': 42,
|
||||||
|
}, 'BCH/BTC': {
|
||||||
|
'symbol': 'BCH/BTC',
|
||||||
|
'bid': 0.6,
|
||||||
|
'ask': 0.5,
|
||||||
|
'last': 41,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
api_mock.fetch_tickers = MagicMock(return_value=tick)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
# retrieve original ticker
|
||||||
|
tickers = exchange.get_tickers()
|
||||||
|
|
||||||
|
assert 'ETH/BTC' in tickers
|
||||||
|
assert 'BCH/BTC' in tickers
|
||||||
|
assert tickers['ETH/BTC']['bid'] == 0.5
|
||||||
|
assert tickers['ETH/BTC']['ask'] == 1
|
||||||
|
assert tickers['BCH/BTC']['bid'] == 0.6
|
||||||
|
assert tickers['BCH/BTC']['ask'] == 0.5
|
||||||
|
|
||||||
|
with pytest.raises(TemporaryError): # test retrier
|
||||||
|
api_mock.fetch_tickers = MagicMock(side_effect=ccxt.NetworkError)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_tickers()
|
||||||
|
|
||||||
|
with pytest.raises(OperationalException):
|
||||||
|
api_mock.fetch_tickers = MagicMock(side_effect=ccxt.BaseError)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_tickers()
|
||||||
|
|
||||||
|
with pytest.raises(OperationalException):
|
||||||
|
api_mock.fetch_tickers = MagicMock(side_effect=ccxt.NotSupported)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_tickers()
|
||||||
|
|
||||||
|
api_mock.fetch_tickers = MagicMock(return_value={})
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_tickers()
|
||||||
|
|
||||||
|
|
||||||
def test_get_ticker(default_conf, mocker):
|
def test_get_ticker(default_conf, mocker):
|
||||||
maybe_init_api(default_conf, mocker)
|
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
tick = {
|
tick = {
|
||||||
'symbol': 'ETH/BTC',
|
'symbol': 'ETH/BTC',
|
||||||
@ -288,10 +311,9 @@ def test_get_ticker(default_conf, mocker):
|
|||||||
'last': 0.0001,
|
'last': 0.0001,
|
||||||
}
|
}
|
||||||
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
|
||||||
# retrieve original ticker
|
# retrieve original ticker
|
||||||
ticker = get_ticker(pair='ETH/BTC')
|
ticker = exchange.get_ticker(pair='ETH/BTC')
|
||||||
|
|
||||||
assert ticker['bid'] == 0.00001098
|
assert ticker['bid'] == 0.00001098
|
||||||
assert ticker['ask'] == 0.00001099
|
assert ticker['ask'] == 0.00001099
|
||||||
@ -304,38 +326,38 @@ def test_get_ticker(default_conf, mocker):
|
|||||||
'last': 42,
|
'last': 42,
|
||||||
}
|
}
|
||||||
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
|
||||||
# if not caching the result we should get the same ticker
|
# if not caching the result we should get the same ticker
|
||||||
# if not fetching a new result we should get the cached ticker
|
# if not fetching a new result we should get the cached ticker
|
||||||
ticker = get_ticker(pair='ETH/BTC')
|
ticker = exchange.get_ticker(pair='ETH/BTC')
|
||||||
|
|
||||||
assert api_mock.fetch_ticker.call_count == 1
|
assert api_mock.fetch_ticker.call_count == 1
|
||||||
assert ticker['bid'] == 0.5
|
assert ticker['bid'] == 0.5
|
||||||
assert ticker['ask'] == 1
|
assert ticker['ask'] == 1
|
||||||
|
|
||||||
assert 'ETH/BTC' in exchange._CACHED_TICKER
|
assert 'ETH/BTC' in exchange._cached_ticker
|
||||||
assert exchange._CACHED_TICKER['ETH/BTC']['bid'] == 0.5
|
assert exchange._cached_ticker['ETH/BTC']['bid'] == 0.5
|
||||||
assert exchange._CACHED_TICKER['ETH/BTC']['ask'] == 1
|
assert exchange._cached_ticker['ETH/BTC']['ask'] == 1
|
||||||
|
|
||||||
# Test caching
|
# Test caching
|
||||||
api_mock.fetch_ticker = MagicMock()
|
api_mock.fetch_ticker = MagicMock()
|
||||||
get_ticker(pair='ETH/BTC', refresh=False)
|
exchange.get_ticker(pair='ETH/BTC', refresh=False)
|
||||||
assert api_mock.fetch_ticker.call_count == 0
|
assert api_mock.fetch_ticker.call_count == 0
|
||||||
|
|
||||||
with pytest.raises(TemporaryError): # test retrier
|
with pytest.raises(TemporaryError): # test retrier
|
||||||
api_mock.fetch_ticker = MagicMock(side_effect=ccxt.NetworkError)
|
api_mock.fetch_ticker = MagicMock(side_effect=ccxt.NetworkError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
get_ticker(pair='ETH/BTC', refresh=True)
|
exchange.get_ticker(pair='ETH/BTC', refresh=True)
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.fetch_ticker = MagicMock(side_effect=ccxt.BaseError)
|
api_mock.fetch_ticker = MagicMock(side_effect=ccxt.BaseError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
get_ticker(pair='ETH/BTC', refresh=True)
|
exchange.get_ticker(pair='ETH/BTC', refresh=True)
|
||||||
|
|
||||||
api_mock.fetch_ticker = MagicMock(return_value={})
|
api_mock.fetch_ticker = MagicMock(return_value={})
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
get_ticker(pair='ETH/BTC', refresh=True)
|
exchange.get_ticker(pair='ETH/BTC', refresh=True)
|
||||||
|
|
||||||
|
|
||||||
def make_fetch_ohlcv_mock(data):
|
def make_fetch_ohlcv_mock(data):
|
||||||
@ -361,10 +383,10 @@ def test_get_ticker_history(default_conf, mocker):
|
|||||||
]
|
]
|
||||||
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
|
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
|
||||||
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick))
|
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick))
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
|
||||||
# retrieve original ticker
|
# retrieve original ticker
|
||||||
ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
ticks = exchange.get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||||
assert ticks[0][0] == 1511686200000
|
assert ticks[0][0] == 1511686200000
|
||||||
assert ticks[0][1] == 1
|
assert ticks[0][1] == 1
|
||||||
assert ticks[0][2] == 2
|
assert ticks[0][2] == 2
|
||||||
@ -384,9 +406,9 @@ def test_get_ticker_history(default_conf, mocker):
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(new_tick))
|
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(new_tick))
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
|
||||||
ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
ticks = exchange.get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||||
assert ticks[0][0] == 1511686210000
|
assert ticks[0][0] == 1511686210000
|
||||||
assert ticks[0][1] == 6
|
assert ticks[0][1] == 6
|
||||||
assert ticks[0][2] == 7
|
assert ticks[0][2] == 7
|
||||||
@ -396,15 +418,15 @@ def test_get_ticker_history(default_conf, mocker):
|
|||||||
|
|
||||||
with pytest.raises(TemporaryError): # test retrier
|
with pytest.raises(TemporaryError): # test retrier
|
||||||
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.NetworkError)
|
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.NetworkError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
# new symbol to get around cache
|
# new symbol to get around cache
|
||||||
get_ticker_history('ABCD/BTC', default_conf['ticker_interval'])
|
exchange.get_ticker_history('ABCD/BTC', default_conf['ticker_interval'])
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.BaseError)
|
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.BaseError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
# new symbol to get around cache
|
# new symbol to get around cache
|
||||||
get_ticker_history('EFGH/BTC', default_conf['ticker_interval'])
|
exchange.get_ticker_history('EFGH/BTC', default_conf['ticker_interval'])
|
||||||
|
|
||||||
|
|
||||||
def test_get_ticker_history_sort(default_conf, mocker):
|
def test_get_ticker_history_sort(default_conf, mocker):
|
||||||
@ -426,10 +448,11 @@ def test_get_ticker_history_sort(default_conf, mocker):
|
|||||||
]
|
]
|
||||||
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
|
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
|
||||||
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick))
|
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick))
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
|
||||||
# Test the ticker history sort
|
# Test the ticker history sort
|
||||||
ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
ticks = exchange.get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||||
assert ticks[0][0] == 1527830400000
|
assert ticks[0][0] == 1527830400000
|
||||||
assert ticks[0][1] == 0.07649
|
assert ticks[0][1] == 0.07649
|
||||||
assert ticks[0][2] == 0.07651
|
assert ticks[0][2] == 0.07651
|
||||||
@ -460,10 +483,9 @@ def test_get_ticker_history_sort(default_conf, mocker):
|
|||||||
]
|
]
|
||||||
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
|
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
|
||||||
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick))
|
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick))
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
|
||||||
# Test the ticker history sort
|
# Test the ticker history sort
|
||||||
ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
ticks = exchange.get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||||
assert ticks[0][0] == 1527827700000
|
assert ticks[0][0] == 1527827700000
|
||||||
assert ticks[0][1] == 0.07659999
|
assert ticks[0][1] == 0.07659999
|
||||||
assert ticks[0][2] == 0.0766
|
assert ticks[0][2] == 0.0766
|
||||||
@ -481,117 +503,194 @@ def test_get_ticker_history_sort(default_conf, mocker):
|
|||||||
|
|
||||||
def test_cancel_order_dry_run(default_conf, mocker):
|
def test_cancel_order_dry_run(default_conf, mocker):
|
||||||
default_conf['dry_run'] = True
|
default_conf['dry_run'] = True
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
|
assert exchange.cancel_order(order_id='123', pair='TKN/BTC') is None
|
||||||
assert cancel_order(order_id='123', pair='TKN/BTC') is None
|
|
||||||
|
|
||||||
|
|
||||||
# Ensure that if not dry_run, we should call API
|
# Ensure that if not dry_run, we should call API
|
||||||
def test_cancel_order(default_conf, mocker):
|
def test_cancel_order(default_conf, mocker):
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.cancel_order = MagicMock(return_value=123)
|
api_mock.cancel_order = MagicMock(return_value=123)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
assert cancel_order(order_id='_', pair='TKN/BTC') == 123
|
assert exchange.cancel_order(order_id='_', pair='TKN/BTC') == 123
|
||||||
|
|
||||||
with pytest.raises(TemporaryError):
|
with pytest.raises(TemporaryError):
|
||||||
api_mock.cancel_order = MagicMock(side_effect=ccxt.NetworkError)
|
api_mock.cancel_order = MagicMock(side_effect=ccxt.NetworkError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
|
||||||
cancel_order(order_id='_', pair='TKN/BTC')
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
assert api_mock.cancel_order.call_count == exchange.API_RETRY_COUNT + 1
|
exchange.cancel_order(order_id='_', pair='TKN/BTC')
|
||||||
|
assert api_mock.cancel_order.call_count == API_RETRY_COUNT + 1
|
||||||
|
|
||||||
with pytest.raises(DependencyException):
|
with pytest.raises(DependencyException):
|
||||||
api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
cancel_order(order_id='_', pair='TKN/BTC')
|
exchange.cancel_order(order_id='_', pair='TKN/BTC')
|
||||||
assert api_mock.cancel_order.call_count == exchange.API_RETRY_COUNT + 1
|
assert api_mock.cancel_order.call_count == API_RETRY_COUNT + 1
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.cancel_order = MagicMock(side_effect=ccxt.BaseError)
|
api_mock.cancel_order = MagicMock(side_effect=ccxt.BaseError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
cancel_order(order_id='_', pair='TKN/BTC')
|
exchange.cancel_order(order_id='_', pair='TKN/BTC')
|
||||||
assert api_mock.cancel_order.call_count == 1
|
assert api_mock.cancel_order.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
def test_get_order(default_conf, mocker):
|
def test_get_order(default_conf, mocker):
|
||||||
default_conf['dry_run'] = True
|
default_conf['dry_run'] = True
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
|
||||||
order = MagicMock()
|
order = MagicMock()
|
||||||
order.myid = 123
|
order.myid = 123
|
||||||
exchange._DRY_RUN_OPEN_ORDERS['X'] = order
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
|
exchange._dry_run_open_orders['X'] = order
|
||||||
print(exchange.get_order('X', 'TKN/BTC'))
|
print(exchange.get_order('X', 'TKN/BTC'))
|
||||||
assert exchange.get_order('X', 'TKN/BTC').myid == 123
|
assert exchange.get_order('X', 'TKN/BTC').myid == 123
|
||||||
|
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.fetch_order = MagicMock(return_value=456)
|
api_mock.fetch_order = MagicMock(return_value=456)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
assert exchange.get_order('X', 'TKN/BTC') == 456
|
assert exchange.get_order('X', 'TKN/BTC') == 456
|
||||||
|
|
||||||
with pytest.raises(TemporaryError):
|
with pytest.raises(TemporaryError):
|
||||||
api_mock.fetch_order = MagicMock(side_effect=ccxt.NetworkError)
|
api_mock.fetch_order = MagicMock(side_effect=ccxt.NetworkError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
exchange.get_order(order_id='_', pair='TKN/BTC')
|
exchange.get_order(order_id='_', pair='TKN/BTC')
|
||||||
assert api_mock.fetch_order.call_count == exchange.API_RETRY_COUNT + 1
|
assert api_mock.fetch_order.call_count == API_RETRY_COUNT + 1
|
||||||
|
|
||||||
with pytest.raises(DependencyException):
|
with pytest.raises(DependencyException):
|
||||||
api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
exchange.get_order(order_id='_', pair='TKN/BTC')
|
exchange.get_order(order_id='_', pair='TKN/BTC')
|
||||||
assert api_mock.fetch_order.call_count == exchange.API_RETRY_COUNT + 1
|
assert api_mock.fetch_order.call_count == API_RETRY_COUNT + 1
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.fetch_order = MagicMock(side_effect=ccxt.BaseError)
|
api_mock.fetch_order = MagicMock(side_effect=ccxt.BaseError)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
exchange.get_order(order_id='_', pair='TKN/BTC')
|
exchange.get_order(order_id='_', pair='TKN/BTC')
|
||||||
assert api_mock.fetch_order.call_count == 1
|
assert api_mock.fetch_order.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
def test_get_name(default_conf, mocker):
|
def test_name(default_conf, mocker):
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs',
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs',
|
||||||
side_effect=lambda s: True)
|
side_effect=lambda s: True)
|
||||||
default_conf['exchange']['name'] = 'binance'
|
default_conf['exchange']['name'] = 'binance'
|
||||||
init(default_conf)
|
exchange = Exchange(default_conf)
|
||||||
|
|
||||||
assert get_name() == 'Binance'
|
assert exchange.name == 'Binance'
|
||||||
|
|
||||||
|
|
||||||
def test_get_id(default_conf, mocker):
|
def test_id(default_conf, mocker):
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs',
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs',
|
||||||
side_effect=lambda s: True)
|
side_effect=lambda s: True)
|
||||||
default_conf['exchange']['name'] = 'binance'
|
default_conf['exchange']['name'] = 'binance'
|
||||||
init(default_conf)
|
exchange = Exchange(default_conf)
|
||||||
|
assert exchange.id == 'binance'
|
||||||
assert get_id() == 'binance'
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_pair_detail_url(default_conf, mocker):
|
def test_get_pair_detail_url(default_conf, mocker, caplog):
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs',
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs',
|
||||||
side_effect=lambda s: True)
|
side_effect=lambda s: True)
|
||||||
default_conf['exchange']['name'] = 'binance'
|
default_conf['exchange']['name'] = 'binance'
|
||||||
init(default_conf)
|
exchange = Exchange(default_conf)
|
||||||
|
|
||||||
url = get_pair_detail_url('TKN/ETH')
|
url = exchange.get_pair_detail_url('TKN/ETH')
|
||||||
assert 'TKN' in url
|
assert 'TKN' in url
|
||||||
assert 'ETH' in url
|
assert 'ETH' in url
|
||||||
|
|
||||||
url = get_pair_detail_url('LOOONG/BTC')
|
url = exchange.get_pair_detail_url('LOOONG/BTC')
|
||||||
assert 'LOOONG' in url
|
assert 'LOOONG' in url
|
||||||
assert 'BTC' in url
|
assert 'BTC' in url
|
||||||
|
|
||||||
default_conf['exchange']['name'] = 'bittrex'
|
default_conf['exchange']['name'] = 'bittrex'
|
||||||
init(default_conf)
|
exchange = Exchange(default_conf)
|
||||||
|
|
||||||
url = get_pair_detail_url('TKN/ETH')
|
url = exchange.get_pair_detail_url('TKN/ETH')
|
||||||
assert 'TKN' in url
|
assert 'TKN' in url
|
||||||
assert 'ETH' in url
|
assert 'ETH' in url
|
||||||
|
|
||||||
url = get_pair_detail_url('LOOONG/BTC')
|
url = exchange.get_pair_detail_url('LOOONG/BTC')
|
||||||
assert 'LOOONG' in url
|
assert 'LOOONG' in url
|
||||||
assert 'BTC' in url
|
assert 'BTC' in url
|
||||||
|
|
||||||
|
default_conf['exchange']['name'] = 'poloniex'
|
||||||
|
exchange = Exchange(default_conf)
|
||||||
|
url = exchange.get_pair_detail_url('LOOONG/BTC')
|
||||||
|
assert '' == url
|
||||||
|
assert log_has('Could not get exchange url for Poloniex', caplog.record_tuples)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_trades_for_order(default_conf, mocker):
|
||||||
|
order_id = 'ABCD-ABCD'
|
||||||
|
since = datetime(2018, 5, 5)
|
||||||
|
default_conf["dry_run"] = False
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True)
|
||||||
|
api_mock = MagicMock()
|
||||||
|
|
||||||
|
api_mock.fetch_my_trades = MagicMock(return_value=[{'id': 'TTR67E-3PFBD-76IISV',
|
||||||
|
'order': 'ABCD-ABCD',
|
||||||
|
'info': {'pair': 'XLTCZBTC',
|
||||||
|
'time': 1519860024.4388,
|
||||||
|
'type': 'buy',
|
||||||
|
'ordertype': 'limit',
|
||||||
|
'price': '20.00000',
|
||||||
|
'cost': '38.62000',
|
||||||
|
'fee': '0.06179',
|
||||||
|
'vol': '5',
|
||||||
|
'id': 'ABCD-ABCD'},
|
||||||
|
'timestamp': 1519860024438,
|
||||||
|
'datetime': '2018-02-28T23:20:24.438Z',
|
||||||
|
'symbol': 'LTC/BTC',
|
||||||
|
'type': 'limit',
|
||||||
|
'side': 'buy',
|
||||||
|
'price': 165.0,
|
||||||
|
'amount': 0.2340606,
|
||||||
|
'fee': {'cost': 0.06179, 'currency': 'BTC'}
|
||||||
|
}])
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
|
||||||
|
orders = exchange.get_trades_for_order(order_id, 'LTC/BTC', since)
|
||||||
|
assert len(orders) == 1
|
||||||
|
assert orders[0]['price'] == 165
|
||||||
|
|
||||||
|
# test Exceptions
|
||||||
|
with pytest.raises(OperationalException):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
api_mock.fetch_my_trades = MagicMock(side_effect=ccxt.BaseError)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_trades_for_order(order_id, 'LTC/BTC', since)
|
||||||
|
|
||||||
|
with pytest.raises(TemporaryError):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
api_mock.fetch_my_trades = MagicMock(side_effect=ccxt.NetworkError)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_trades_for_order(order_id, 'LTC/BTC', since)
|
||||||
|
assert api_mock.fetch_my_trades.call_count == API_RETRY_COUNT + 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_markets(default_conf, mocker, markets):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
api_mock.fetch_markets = markets
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
ret = exchange.get_markets()
|
||||||
|
assert isinstance(ret, list)
|
||||||
|
assert len(ret) == 6
|
||||||
|
|
||||||
|
assert ret[0]["id"] == "ethbtc"
|
||||||
|
assert ret[0]["symbol"] == "ETH/BTC"
|
||||||
|
|
||||||
|
# test Exceptions
|
||||||
|
with pytest.raises(OperationalException):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
api_mock.fetch_markets = MagicMock(side_effect=ccxt.BaseError)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_markets()
|
||||||
|
|
||||||
|
with pytest.raises(TemporaryError):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
api_mock.fetch_markets = MagicMock(side_effect=ccxt.NetworkError)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_markets()
|
||||||
|
assert api_mock.fetch_markets.call_count == API_RETRY_COUNT + 1
|
||||||
|
|
||||||
|
|
||||||
def test_get_fee(default_conf, mocker):
|
def test_get_fee(default_conf, mocker):
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
@ -601,12 +700,32 @@ def test_get_fee(default_conf, mocker):
|
|||||||
'rate': 0.025,
|
'rate': 0.025,
|
||||||
'cost': 0.05
|
'cost': 0.05
|
||||||
})
|
})
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
assert get_fee() == 0.025
|
|
||||||
|
assert exchange.get_fee() == 0.025
|
||||||
|
|
||||||
|
# test Exceptions
|
||||||
|
with pytest.raises(OperationalException):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
api_mock.calculate_fee = MagicMock(side_effect=ccxt.BaseError)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_fee()
|
||||||
|
|
||||||
|
with pytest.raises(TemporaryError):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
api_mock.calculate_fee = MagicMock(side_effect=ccxt.NetworkError)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange.get_fee()
|
||||||
|
assert api_mock.calculate_fee.call_count == API_RETRY_COUNT + 1
|
||||||
|
|
||||||
|
|
||||||
def test_get_amount_lots(default_conf, mocker):
|
def test_get_amount_lots(default_conf, mocker):
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.amount_to_lots = MagicMock(return_value=1.0)
|
api_mock.amount_to_lots = MagicMock(return_value=1.0)
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
api_mock.markets = None
|
||||||
assert get_amount_lots('LTC/BTC', 1.54) == 1
|
marketmock = MagicMock()
|
||||||
|
api_mock.load_markets = marketmock
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
|
||||||
|
assert exchange.get_amount_lots('LTC/BTC', 1.54) == 1
|
||||||
|
assert marketmock.call_count == 1
|
||||||
|
@ -16,7 +16,7 @@ from freqtrade import optimize, constants, DependencyException
|
|||||||
from freqtrade.analyze import Analyze
|
from freqtrade.analyze import Analyze
|
||||||
from freqtrade.arguments import Arguments, TimeRange
|
from freqtrade.arguments import Arguments, TimeRange
|
||||||
from freqtrade.optimize.backtesting import Backtesting, start, setup_configuration
|
from freqtrade.optimize.backtesting import Backtesting, start, setup_configuration
|
||||||
from freqtrade.tests.conftest import log_has
|
from freqtrade.tests.conftest import log_has, patch_exchange
|
||||||
|
|
||||||
|
|
||||||
def get_args(args) -> List[str]:
|
def get_args(args) -> List[str]:
|
||||||
@ -84,7 +84,7 @@ def load_data_test(what):
|
|||||||
|
|
||||||
|
|
||||||
def simple_backtest(config, contour, num_results, mocker) -> None:
|
def simple_backtest(config, contour, num_results, mocker) -> None:
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
backtesting = Backtesting(config)
|
backtesting = Backtesting(config)
|
||||||
|
|
||||||
data = load_data_test(contour)
|
data = load_data_test(contour)
|
||||||
@ -102,7 +102,8 @@ def simple_backtest(config, contour, num_results, mocker) -> None:
|
|||||||
assert len(results) == num_results
|
assert len(results) == num_results
|
||||||
|
|
||||||
|
|
||||||
def mocked_load_data(datadir, pairs=[], ticker_interval='0m', refresh_pairs=False, timerange=None):
|
def mocked_load_data(datadir, pairs=[], ticker_interval='0m', refresh_pairs=False,
|
||||||
|
timerange=None, exchange=None):
|
||||||
tickerdata = optimize.load_tickerdata_file(datadir, 'UNITTEST/BTC', '1m', timerange=timerange)
|
tickerdata = optimize.load_tickerdata_file(datadir, 'UNITTEST/BTC', '1m', timerange=timerange)
|
||||||
pairdata = {'UNITTEST/BTC': tickerdata}
|
pairdata = {'UNITTEST/BTC': tickerdata}
|
||||||
return pairdata
|
return pairdata
|
||||||
@ -119,7 +120,7 @@ def _load_pair_as_ticks(pair, tickfreq):
|
|||||||
def _make_backtest_conf(mocker, conf=None, pair='UNITTEST/BTC', record=None):
|
def _make_backtest_conf(mocker, conf=None, pair='UNITTEST/BTC', record=None):
|
||||||
data = optimize.load_data(None, ticker_interval='8m', pairs=[pair])
|
data = optimize.load_data(None, ticker_interval='8m', pairs=[pair])
|
||||||
data = trim_dictlist(data, -201)
|
data = trim_dictlist(data, -201)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
backtesting = Backtesting(conf)
|
backtesting = Backtesting(conf)
|
||||||
return {
|
return {
|
||||||
'stake_amount': conf['stake_amount'],
|
'stake_amount': conf['stake_amount'],
|
||||||
@ -295,8 +296,8 @@ def test_start(mocker, fee, default_conf, caplog) -> None:
|
|||||||
Test start() function
|
Test start() function
|
||||||
"""
|
"""
|
||||||
start_mock = MagicMock()
|
start_mock = MagicMock()
|
||||||
mocker.patch('freqtrade.exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.start', start_mock)
|
mocker.patch('freqtrade.optimize.backtesting.Backtesting.start', start_mock)
|
||||||
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||||
read_data=json.dumps(default_conf)
|
read_data=json.dumps(default_conf)
|
||||||
@ -319,7 +320,8 @@ def test_backtesting_init(mocker, default_conf) -> None:
|
|||||||
"""
|
"""
|
||||||
Test Backtesting._init() method
|
Test Backtesting._init() method
|
||||||
"""
|
"""
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
|
get_fee = mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5))
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
assert backtesting.config == default_conf
|
assert backtesting.config == default_conf
|
||||||
assert isinstance(backtesting.analyze, Analyze)
|
assert isinstance(backtesting.analyze, Analyze)
|
||||||
@ -327,13 +329,15 @@ def test_backtesting_init(mocker, default_conf) -> None:
|
|||||||
assert callable(backtesting.tickerdata_to_dataframe)
|
assert callable(backtesting.tickerdata_to_dataframe)
|
||||||
assert callable(backtesting.populate_buy_trend)
|
assert callable(backtesting.populate_buy_trend)
|
||||||
assert callable(backtesting.populate_sell_trend)
|
assert callable(backtesting.populate_sell_trend)
|
||||||
|
get_fee.assert_called()
|
||||||
|
assert backtesting.fee == 0.5
|
||||||
|
|
||||||
|
|
||||||
def test_tickerdata_to_dataframe(default_conf, mocker) -> None:
|
def test_tickerdata_to_dataframe(default_conf, mocker) -> None:
|
||||||
"""
|
"""
|
||||||
Test Backtesting.tickerdata_to_dataframe() method
|
Test Backtesting.tickerdata_to_dataframe() method
|
||||||
"""
|
"""
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
timerange = TimeRange(None, 'line', 0, -100)
|
timerange = TimeRange(None, 'line', 0, -100)
|
||||||
tick = optimize.load_tickerdata_file(None, 'UNITTEST/BTC', '1m', timerange=timerange)
|
tick = optimize.load_tickerdata_file(None, 'UNITTEST/BTC', '1m', timerange=timerange)
|
||||||
tickerlist = {'UNITTEST/BTC': tick}
|
tickerlist = {'UNITTEST/BTC': tick}
|
||||||
@ -352,7 +356,7 @@ def test_get_timeframe(default_conf, mocker) -> None:
|
|||||||
"""
|
"""
|
||||||
Test Backtesting.get_timeframe() method
|
Test Backtesting.get_timeframe() method
|
||||||
"""
|
"""
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
|
|
||||||
data = backtesting.tickerdata_to_dataframe(
|
data = backtesting.tickerdata_to_dataframe(
|
||||||
@ -371,7 +375,7 @@ def test_generate_text_table(default_conf, mocker):
|
|||||||
"""
|
"""
|
||||||
Test Backtesting.generate_text_table() method
|
Test Backtesting.generate_text_table() method
|
||||||
"""
|
"""
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
|
|
||||||
results = pd.DataFrame(
|
results = pd.DataFrame(
|
||||||
@ -408,8 +412,8 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None:
|
|||||||
|
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
||||||
mocker.patch('freqtrade.optimize.load_data', mocked_load_data)
|
mocker.patch('freqtrade.optimize.load_data', mocked_load_data)
|
||||||
mocker.patch('freqtrade.exchange.get_ticker_history')
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history')
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.optimize.backtesting.Backtesting',
|
'freqtrade.optimize.backtesting.Backtesting',
|
||||||
backtest=MagicMock(),
|
backtest=MagicMock(),
|
||||||
@ -449,8 +453,8 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None:
|
|||||||
|
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
||||||
mocker.patch('freqtrade.optimize.load_data', MagicMock(return_value={}))
|
mocker.patch('freqtrade.optimize.load_data', MagicMock(return_value={}))
|
||||||
mocker.patch('freqtrade.exchange.get_ticker_history')
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history')
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.optimize.backtesting.Backtesting',
|
'freqtrade.optimize.backtesting.Backtesting',
|
||||||
backtest=MagicMock(),
|
backtest=MagicMock(),
|
||||||
@ -477,8 +481,8 @@ def test_backtest(default_conf, fee, mocker) -> None:
|
|||||||
"""
|
"""
|
||||||
Test Backtesting.backtest() method
|
Test Backtesting.backtest() method
|
||||||
"""
|
"""
|
||||||
mocker.patch('freqtrade.exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
|
|
||||||
data = optimize.load_data(None, ticker_interval='5m', pairs=['UNITTEST/BTC'])
|
data = optimize.load_data(None, ticker_interval='5m', pairs=['UNITTEST/BTC'])
|
||||||
@ -499,8 +503,8 @@ def test_backtest_1min_ticker_interval(default_conf, fee, mocker) -> None:
|
|||||||
"""
|
"""
|
||||||
Test Backtesting.backtest() method with 1 min ticker
|
Test Backtesting.backtest() method with 1 min ticker
|
||||||
"""
|
"""
|
||||||
mocker.patch('freqtrade.exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
|
|
||||||
# Run a backtesting for an exiting 5min ticker_interval
|
# Run a backtesting for an exiting 5min ticker_interval
|
||||||
@ -522,7 +526,7 @@ def test_processed(default_conf, mocker) -> None:
|
|||||||
"""
|
"""
|
||||||
Test Backtesting.backtest() method with offline data
|
Test Backtesting.backtest() method with offline data
|
||||||
"""
|
"""
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
|
|
||||||
dict_of_tickerrows = load_data_test('raise')
|
dict_of_tickerrows = load_data_test('raise')
|
||||||
@ -536,7 +540,7 @@ def test_processed(default_conf, mocker) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_backtest_pricecontours(default_conf, fee, mocker) -> None:
|
def test_backtest_pricecontours(default_conf, fee, mocker) -> None:
|
||||||
mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
tests = [['raise', 18], ['lower', 0], ['sine', 16]]
|
tests = [['raise', 18], ['lower', 0], ['sine', 16]]
|
||||||
for [contour, numres] in tests:
|
for [contour, numres] in tests:
|
||||||
simple_backtest(default_conf, contour, numres, mocker)
|
simple_backtest(default_conf, contour, numres, mocker)
|
||||||
@ -544,8 +548,8 @@ def test_backtest_pricecontours(default_conf, fee, mocker) -> None:
|
|||||||
|
|
||||||
# Test backtest using offline data (testdata directory)
|
# Test backtest using offline data (testdata directory)
|
||||||
def test_backtest_ticks(default_conf, fee, mocker):
|
def test_backtest_ticks(default_conf, fee, mocker):
|
||||||
mocker.patch('freqtrade.exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
ticks = [1, 5]
|
ticks = [1, 5]
|
||||||
fun = Backtesting(default_conf).populate_buy_trend
|
fun = Backtesting(default_conf).populate_buy_trend
|
||||||
for _ in ticks:
|
for _ in ticks:
|
||||||
@ -564,7 +568,6 @@ def test_backtest_clash_buy_sell(mocker, default_conf):
|
|||||||
sell_value = 1
|
sell_value = 1
|
||||||
return _trend(dataframe, buy_value, sell_value)
|
return _trend(dataframe, buy_value, sell_value)
|
||||||
|
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
|
||||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf)
|
backtest_conf = _make_backtest_conf(mocker, conf=default_conf)
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
backtesting.populate_buy_trend = fun # Override
|
backtesting.populate_buy_trend = fun # Override
|
||||||
@ -580,7 +583,6 @@ def test_backtest_only_sell(mocker, default_conf):
|
|||||||
sell_value = 1
|
sell_value = 1
|
||||||
return _trend(dataframe, buy_value, sell_value)
|
return _trend(dataframe, buy_value, sell_value)
|
||||||
|
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
|
||||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf)
|
backtest_conf = _make_backtest_conf(mocker, conf=default_conf)
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
backtesting.populate_buy_trend = fun # Override
|
backtesting.populate_buy_trend = fun # Override
|
||||||
@ -590,8 +592,7 @@ def test_backtest_only_sell(mocker, default_conf):
|
|||||||
|
|
||||||
|
|
||||||
def test_backtest_alternate_buy_sell(default_conf, fee, mocker):
|
def test_backtest_alternate_buy_sell(default_conf, fee, mocker):
|
||||||
mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
|
||||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, pair='UNITTEST/BTC')
|
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, pair='UNITTEST/BTC')
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
backtesting.populate_buy_trend = _trend_alternate # Override
|
backtesting.populate_buy_trend = _trend_alternate # Override
|
||||||
@ -606,8 +607,8 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker):
|
|||||||
def test_backtest_record(default_conf, fee, mocker):
|
def test_backtest_record(default_conf, fee, mocker):
|
||||||
names = []
|
names = []
|
||||||
records = []
|
records = []
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.optimize.backtesting.file_dump_json',
|
'freqtrade.optimize.backtesting.file_dump_json',
|
||||||
new=lambda n, r: (names.append(n), records.append(r))
|
new=lambda n, r: (names.append(n), records.append(r))
|
||||||
@ -655,9 +656,9 @@ def test_backtest_record(default_conf, fee, mocker):
|
|||||||
def test_backtest_start_live(default_conf, mocker, caplog):
|
def test_backtest_start_live(default_conf, mocker, caplog):
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
|
conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
|
||||||
mocker.patch('freqtrade.exchange.get_ticker_history',
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history',
|
||||||
new=lambda n, i: _load_pair_as_ticks(n, i))
|
new=lambda s, n, i: _load_pair_as_ticks(n, i))
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock())
|
patch_exchange(mocker)
|
||||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', MagicMock())
|
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', MagicMock())
|
||||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table', MagicMock())
|
mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table', MagicMock())
|
||||||
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||||
|
@ -10,7 +10,7 @@ import pytest
|
|||||||
from freqtrade.optimize.__init__ import load_tickerdata_file
|
from freqtrade.optimize.__init__ import load_tickerdata_file
|
||||||
from freqtrade.optimize.hyperopt import Hyperopt, start
|
from freqtrade.optimize.hyperopt import Hyperopt, start
|
||||||
from freqtrade.strategy.resolver import StrategyResolver
|
from freqtrade.strategy.resolver import StrategyResolver
|
||||||
from freqtrade.tests.conftest import log_has
|
from freqtrade.tests.conftest import log_has, patch_exchange
|
||||||
from freqtrade.tests.optimize.test_backtesting import get_args
|
from freqtrade.tests.optimize.test_backtesting import get_args
|
||||||
|
|
||||||
# Avoid to reinit the same object again and again
|
# Avoid to reinit the same object again and again
|
||||||
@ -22,8 +22,7 @@ _HYPEROPT = None
|
|||||||
def init_hyperopt(default_conf, mocker):
|
def init_hyperopt(default_conf, mocker):
|
||||||
global _HYPEROPT_INITIALIZED, _HYPEROPT
|
global _HYPEROPT_INITIALIZED, _HYPEROPT
|
||||||
if not _HYPEROPT_INITIALIZED:
|
if not _HYPEROPT_INITIALIZED:
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
patch_exchange(mocker)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock())
|
|
||||||
_HYPEROPT = Hyperopt(default_conf)
|
_HYPEROPT = Hyperopt(default_conf)
|
||||||
_HYPEROPT_INITIALIZED = True
|
_HYPEROPT_INITIALIZED = True
|
||||||
|
|
||||||
@ -61,8 +60,12 @@ def test_start(mocker, default_conf, caplog) -> None:
|
|||||||
Test start() function
|
Test start() function
|
||||||
"""
|
"""
|
||||||
start_mock = MagicMock()
|
start_mock = MagicMock()
|
||||||
|
mocker.patch(
|
||||||
|
'freqtrade.configuration.Configuration._load_config_file',
|
||||||
|
lambda *args, **kwargs: default_conf
|
||||||
|
)
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
|
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
|
patch_exchange(mocker)
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
'--config', 'config.json',
|
'--config', 'config.json',
|
||||||
@ -178,7 +181,7 @@ def test_fmin_best_results(mocker, init_hyperopt, default_conf, caplog) -> None:
|
|||||||
|
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
|
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value=fmin_result)
|
mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value=fmin_result)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
|
patch_exchange(mocker)
|
||||||
|
|
||||||
StrategyResolver({'strategy': 'DefaultStrategy'})
|
StrategyResolver({'strategy': 'DefaultStrategy'})
|
||||||
hyperopt = Hyperopt(conf)
|
hyperopt = Hyperopt(conf)
|
||||||
@ -222,7 +225,7 @@ def test_fmin_throw_value_error(mocker, init_hyperopt, default_conf, caplog) ->
|
|||||||
conf.update({'epochs': 1})
|
conf.update({'epochs': 1})
|
||||||
conf.update({'timerange': None})
|
conf.update({'timerange': None})
|
||||||
conf.update({'spaces': 'all'})
|
conf.update({'spaces': 'all'})
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
|
patch_exchange(mocker)
|
||||||
|
|
||||||
StrategyResolver({'strategy': 'DefaultStrategy'})
|
StrategyResolver({'strategy': 'DefaultStrategy'})
|
||||||
hyperopt = Hyperopt(conf)
|
hyperopt = Hyperopt(conf)
|
||||||
@ -263,7 +266,7 @@ def test_resuming_previous_hyperopt_results_succeeds(mocker, init_hyperopt, defa
|
|||||||
mocker.patch('freqtrade.optimize.hyperopt.sorted', return_value=trials.results)
|
mocker.patch('freqtrade.optimize.hyperopt.sorted', return_value=trials.results)
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
|
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={})
|
mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={})
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock())
|
patch_exchange(mocker)
|
||||||
|
|
||||||
StrategyResolver({'strategy': 'DefaultStrategy'})
|
StrategyResolver({'strategy': 'DefaultStrategy'})
|
||||||
hyperopt = Hyperopt(conf)
|
hyperopt = Hyperopt(conf)
|
||||||
@ -334,7 +337,7 @@ def test_start_calls_fmin(mocker, init_hyperopt, default_conf) -> None:
|
|||||||
trials = create_trials(mocker)
|
trials = create_trials(mocker)
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.sorted', return_value=trials.results)
|
mocker.patch('freqtrade.optimize.hyperopt.sorted', return_value=trials.results)
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
|
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock())
|
patch_exchange(mocker)
|
||||||
mock_fmin = mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={})
|
mock_fmin = mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={})
|
||||||
|
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
@ -499,7 +502,7 @@ def test_generate_optimizer(mocker, init_hyperopt, default_conf) -> None:
|
|||||||
'freqtrade.optimize.hyperopt.Hyperopt.backtest',
|
'freqtrade.optimize.hyperopt.Hyperopt.backtest',
|
||||||
MagicMock(return_value=backtest_result)
|
MagicMock(return_value=backtest_result)
|
||||||
)
|
)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock())
|
patch_exchange(mocker)
|
||||||
|
|
||||||
optimizer_param = {
|
optimizer_param = {
|
||||||
'adx': {'enabled': False},
|
'adx': {'enabled': False},
|
||||||
|
@ -12,7 +12,7 @@ from freqtrade.optimize.__init__ import make_testdata_path, download_pairs, \
|
|||||||
download_backtesting_testdata, load_tickerdata_file, trim_tickerlist, \
|
download_backtesting_testdata, load_tickerdata_file, trim_tickerlist, \
|
||||||
load_cached_data_for_updating
|
load_cached_data_for_updating
|
||||||
from freqtrade.arguments import TimeRange
|
from freqtrade.arguments import TimeRange
|
||||||
from freqtrade.tests.conftest import log_has
|
from freqtrade.tests.conftest import log_has, get_patched_exchange
|
||||||
|
|
||||||
# Change this if modifying UNITTEST/BTC testdatafile
|
# Change this if modifying UNITTEST/BTC testdatafile
|
||||||
_BTC_UNITTEST_LENGTH = 13681
|
_BTC_UNITTEST_LENGTH = 13681
|
||||||
@ -49,12 +49,11 @@ def _clean_test_file(file: str) -> None:
|
|||||||
os.rename(file_swp, file)
|
os.rename(file_swp, file)
|
||||||
|
|
||||||
|
|
||||||
def test_load_data_30min_ticker(ticker_history, mocker, caplog) -> None:
|
def test_load_data_30min_ticker(ticker_history, mocker, caplog, default_conf) -> None:
|
||||||
"""
|
"""
|
||||||
Test load_data() with 30 min ticker
|
Test load_data() with 30 min ticker
|
||||||
"""
|
"""
|
||||||
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||||
|
|
||||||
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-30m.json')
|
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-30m.json')
|
||||||
_backup_file(file, copy_file=True)
|
_backup_file(file, copy_file=True)
|
||||||
optimize.load_data(None, pairs=['UNITTEST/BTC'], ticker_interval='30m')
|
optimize.load_data(None, pairs=['UNITTEST/BTC'], ticker_interval='30m')
|
||||||
@ -63,11 +62,11 @@ def test_load_data_30min_ticker(ticker_history, mocker, caplog) -> None:
|
|||||||
_clean_test_file(file)
|
_clean_test_file(file)
|
||||||
|
|
||||||
|
|
||||||
def test_load_data_5min_ticker(ticker_history, mocker, caplog) -> None:
|
def test_load_data_5min_ticker(ticker_history, mocker, caplog, default_conf) -> None:
|
||||||
"""
|
"""
|
||||||
Test load_data() with 5 min ticker
|
Test load_data() with 5 min ticker
|
||||||
"""
|
"""
|
||||||
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||||
|
|
||||||
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-5m.json')
|
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-5m.json')
|
||||||
_backup_file(file, copy_file=True)
|
_backup_file(file, copy_file=True)
|
||||||
@ -81,7 +80,7 @@ def test_load_data_1min_ticker(ticker_history, mocker, caplog) -> None:
|
|||||||
"""
|
"""
|
||||||
Test load_data() with 1 min ticker
|
Test load_data() with 1 min ticker
|
||||||
"""
|
"""
|
||||||
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||||
|
|
||||||
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-1m.json')
|
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-1m.json')
|
||||||
_backup_file(file, copy_file=True)
|
_backup_file(file, copy_file=True)
|
||||||
@ -91,12 +90,12 @@ def test_load_data_1min_ticker(ticker_history, mocker, caplog) -> None:
|
|||||||
_clean_test_file(file)
|
_clean_test_file(file)
|
||||||
|
|
||||||
|
|
||||||
def test_load_data_with_new_pair_1min(ticker_history, mocker, caplog) -> None:
|
def test_load_data_with_new_pair_1min(ticker_history, mocker, caplog, default_conf) -> None:
|
||||||
"""
|
"""
|
||||||
Test load_data() with 1 min ticker
|
Test load_data() with 1 min ticker
|
||||||
"""
|
"""
|
||||||
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
|
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
|
||||||
|
|
||||||
_backup_file(file)
|
_backup_file(file)
|
||||||
@ -114,6 +113,7 @@ def test_load_data_with_new_pair_1min(ticker_history, mocker, caplog) -> None:
|
|||||||
optimize.load_data(None,
|
optimize.load_data(None,
|
||||||
ticker_interval='1m',
|
ticker_interval='1m',
|
||||||
refresh_pairs=True,
|
refresh_pairs=True,
|
||||||
|
exchange=exchange,
|
||||||
pairs=['MEME/BTC'])
|
pairs=['MEME/BTC'])
|
||||||
assert os.path.isfile(file) is True
|
assert os.path.isfile(file) is True
|
||||||
assert log_has('Download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples)
|
assert log_has('Download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples)
|
||||||
@ -124,9 +124,9 @@ def test_testdata_path() -> None:
|
|||||||
assert os.path.join('freqtrade', 'tests', 'testdata') in make_testdata_path(None)
|
assert os.path.join('freqtrade', 'tests', 'testdata') in make_testdata_path(None)
|
||||||
|
|
||||||
|
|
||||||
def test_download_pairs(ticker_history, mocker) -> None:
|
def test_download_pairs(ticker_history, mocker, default_conf) -> None:
|
||||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
|
file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
|
||||||
file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json')
|
file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json')
|
||||||
file2_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'CFI_BTC-1m.json')
|
file2_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'CFI_BTC-1m.json')
|
||||||
@ -140,7 +140,8 @@ def test_download_pairs(ticker_history, mocker) -> None:
|
|||||||
assert os.path.isfile(file1_1) is False
|
assert os.path.isfile(file1_1) is False
|
||||||
assert os.path.isfile(file2_1) is False
|
assert os.path.isfile(file2_1) is False
|
||||||
|
|
||||||
assert download_pairs(None, pairs=['MEME/BTC', 'CFI/BTC'], ticker_interval='1m') is True
|
assert download_pairs(None, exchange,
|
||||||
|
pairs=['MEME/BTC', 'CFI/BTC'], ticker_interval='1m') is True
|
||||||
|
|
||||||
assert os.path.isfile(file1_1) is True
|
assert os.path.isfile(file1_1) is True
|
||||||
assert os.path.isfile(file2_1) is True
|
assert os.path.isfile(file2_1) is True
|
||||||
@ -152,7 +153,8 @@ def test_download_pairs(ticker_history, mocker) -> None:
|
|||||||
assert os.path.isfile(file1_5) is False
|
assert os.path.isfile(file1_5) is False
|
||||||
assert os.path.isfile(file2_5) is False
|
assert os.path.isfile(file2_5) is False
|
||||||
|
|
||||||
assert download_pairs(None, pairs=['MEME/BTC', 'CFI/BTC'], ticker_interval='5m') is True
|
assert download_pairs(None, exchange,
|
||||||
|
pairs=['MEME/BTC', 'CFI/BTC'], ticker_interval='5m') is True
|
||||||
|
|
||||||
assert os.path.isfile(file1_5) is True
|
assert os.path.isfile(file1_5) is True
|
||||||
assert os.path.isfile(file2_5) is True
|
assert os.path.isfile(file2_5) is True
|
||||||
@ -265,30 +267,32 @@ def test_load_cached_data_for_updating(mocker) -> None:
|
|||||||
assert start_ts is None
|
assert start_ts is None
|
||||||
|
|
||||||
|
|
||||||
def test_download_pairs_exception(ticker_history, mocker, caplog) -> None:
|
def test_download_pairs_exception(ticker_history, mocker, caplog, default_conf) -> None:
|
||||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||||
mocker.patch('freqtrade.optimize.__init__.download_backtesting_testdata',
|
mocker.patch('freqtrade.optimize.__init__.download_backtesting_testdata',
|
||||||
side_effect=BaseException('File Error'))
|
side_effect=BaseException('File Error'))
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
|
|
||||||
file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
|
file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
|
||||||
file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json')
|
file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json')
|
||||||
_backup_file(file1_1)
|
_backup_file(file1_1)
|
||||||
_backup_file(file1_5)
|
_backup_file(file1_5)
|
||||||
|
|
||||||
download_pairs(None, pairs=['MEME/BTC'], ticker_interval='1m')
|
download_pairs(None, exchange, pairs=['MEME/BTC'], ticker_interval='1m')
|
||||||
# clean files freshly downloaded
|
# clean files freshly downloaded
|
||||||
_clean_test_file(file1_1)
|
_clean_test_file(file1_1)
|
||||||
_clean_test_file(file1_5)
|
_clean_test_file(file1_5)
|
||||||
assert log_has('Failed to download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples)
|
assert log_has('Failed to download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples)
|
||||||
|
|
||||||
|
|
||||||
def test_download_backtesting_testdata(ticker_history, mocker) -> None:
|
def test_download_backtesting_testdata(ticker_history, mocker, default_conf) -> None:
|
||||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
|
|
||||||
# Download a 1 min ticker file
|
# Download a 1 min ticker file
|
||||||
file1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'XEL_BTC-1m.json')
|
file1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'XEL_BTC-1m.json')
|
||||||
_backup_file(file1)
|
_backup_file(file1)
|
||||||
download_backtesting_testdata(None, pair="XEL/BTC", tick_interval='1m')
|
download_backtesting_testdata(None, exchange, pair="XEL/BTC", tick_interval='1m')
|
||||||
assert os.path.isfile(file1) is True
|
assert os.path.isfile(file1) is True
|
||||||
_clean_test_file(file1)
|
_clean_test_file(file1)
|
||||||
|
|
||||||
@ -296,21 +300,21 @@ def test_download_backtesting_testdata(ticker_history, mocker) -> None:
|
|||||||
file2 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'STORJ_BTC-5m.json')
|
file2 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'STORJ_BTC-5m.json')
|
||||||
_backup_file(file2)
|
_backup_file(file2)
|
||||||
|
|
||||||
download_backtesting_testdata(None, pair="STORJ/BTC", tick_interval='5m')
|
download_backtesting_testdata(None, exchange, pair="STORJ/BTC", tick_interval='5m')
|
||||||
assert os.path.isfile(file2) is True
|
assert os.path.isfile(file2) is True
|
||||||
_clean_test_file(file2)
|
_clean_test_file(file2)
|
||||||
|
|
||||||
|
|
||||||
def test_download_backtesting_testdata2(mocker) -> None:
|
def test_download_backtesting_testdata2(mocker, default_conf) -> None:
|
||||||
tick = [
|
tick = [
|
||||||
[1509836520000, 0.00162008, 0.00162008, 0.00162008, 0.00162008, 108.14853839],
|
[1509836520000, 0.00162008, 0.00162008, 0.00162008, 0.00162008, 108.14853839],
|
||||||
[1509836580000, 0.00161, 0.00161, 0.00161, 0.00161, 82.390199]
|
[1509836580000, 0.00161, 0.00161, 0.00161, 0.00161, 82.390199]
|
||||||
]
|
]
|
||||||
json_dump_mock = mocker.patch('freqtrade.misc.file_dump_json', return_value=None)
|
json_dump_mock = mocker.patch('freqtrade.misc.file_dump_json', return_value=None)
|
||||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=tick)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=tick)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
download_backtesting_testdata(None, pair="UNITTEST/BTC", tick_interval='1m')
|
download_backtesting_testdata(None, exchange, pair="UNITTEST/BTC", tick_interval='1m')
|
||||||
download_backtesting_testdata(None, pair="UNITTEST/BTC", tick_interval='3m')
|
download_backtesting_testdata(None, exchange, pair="UNITTEST/BTC", tick_interval='3m')
|
||||||
assert json_dump_mock.call_count == 2
|
assert json_dump_mock.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
@ -326,8 +330,10 @@ def test_load_tickerdata_file() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_init(default_conf, mocker) -> None:
|
def test_init(default_conf, mocker) -> None:
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
assert {} == optimize.load_data(
|
assert {} == optimize.load_data(
|
||||||
'',
|
'',
|
||||||
|
exchange=exchange,
|
||||||
pairs=[],
|
pairs=[],
|
||||||
refresh_pairs=True,
|
refresh_pairs=True,
|
||||||
ticker_interval=default_conf['ticker_interval']
|
ticker_interval=default_conf['ticker_interval']
|
||||||
|
@ -33,7 +33,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -80,7 +80,7 @@ def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None:
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -114,7 +114,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
|||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -170,7 +170,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
|||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -194,7 +194,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
|||||||
|
|
||||||
# Update the ticker with a market going up
|
# Update the ticker with a market going up
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker_sell_up
|
get_ticker=ticker_sell_up
|
||||||
)
|
)
|
||||||
@ -209,7 +209,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
|||||||
|
|
||||||
# Update the ticker with a market going up
|
# Update the ticker with a market going up
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker_sell_up
|
get_ticker=ticker_sell_up
|
||||||
)
|
)
|
||||||
@ -247,7 +247,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets,
|
|||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -267,7 +267,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets,
|
|||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
# Update the ticker with a market going up
|
# Update the ticker with a market going up
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker_sell_up,
|
get_ticker=ticker_sell_up,
|
||||||
get_fee=fee
|
get_fee=fee
|
||||||
@ -319,7 +319,7 @@ def test_rpc_balance_handle(default_conf, mocker):
|
|||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_balances=MagicMock(return_value=mock_balance)
|
get_balances=MagicMock(return_value=mock_balance)
|
||||||
)
|
)
|
||||||
@ -347,7 +347,7 @@ def test_rpc_start(mocker, default_conf) -> None:
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=MagicMock()
|
get_ticker=MagicMock()
|
||||||
)
|
)
|
||||||
@ -373,7 +373,7 @@ def test_rpc_stop(mocker, default_conf) -> None:
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=MagicMock()
|
get_ticker=MagicMock()
|
||||||
)
|
)
|
||||||
@ -401,7 +401,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None:
|
|||||||
|
|
||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
cancel_order=cancel_order_mock,
|
cancel_order=cancel_order_mock,
|
||||||
@ -447,7 +447,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None:
|
|||||||
trade = Trade.query.filter(Trade.id == '1').first()
|
trade = Trade.query.filter(Trade.id == '1').first()
|
||||||
filled_amount = trade.amount / 2
|
filled_amount = trade.amount / 2
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.freqtradebot.exchange.get_order',
|
'freqtrade.exchange.Exchange.get_order',
|
||||||
return_value={
|
return_value={
|
||||||
'status': 'open',
|
'status': 'open',
|
||||||
'type': 'limit',
|
'type': 'limit',
|
||||||
@ -466,7 +466,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None:
|
|||||||
amount = trade.amount
|
amount = trade.amount
|
||||||
# make an limit-buy open trade, if there is no 'filled', don't sell it
|
# make an limit-buy open trade, if there is no 'filled', don't sell it
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.freqtradebot.exchange.get_order',
|
'freqtrade.exchange.Exchange.get_order',
|
||||||
return_value={
|
return_value={
|
||||||
'status': 'open',
|
'status': 'open',
|
||||||
'type': 'limit',
|
'type': 'limit',
|
||||||
@ -482,7 +482,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None:
|
|||||||
freqtradebot.create_trade()
|
freqtradebot.create_trade()
|
||||||
# make an limit-sell open trade
|
# make an limit-sell open trade
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.freqtradebot.exchange.get_order',
|
'freqtrade.exchange.Exchange.get_order',
|
||||||
return_value={
|
return_value={
|
||||||
'status': 'open',
|
'status': 'open',
|
||||||
'type': 'limit',
|
'type': 'limit',
|
||||||
@ -503,7 +503,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_balances=MagicMock(return_value=ticker),
|
get_balances=MagicMock(return_value=ticker),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
@ -542,7 +542,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None:
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_balances=MagicMock(return_value=ticker),
|
get_balances=MagicMock(return_value=ticker),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
|
@ -20,7 +20,7 @@ from freqtrade.persistence import Trade
|
|||||||
from freqtrade.rpc.telegram import Telegram
|
from freqtrade.rpc.telegram import Telegram
|
||||||
from freqtrade.rpc.telegram import authorized_only
|
from freqtrade.rpc.telegram import authorized_only
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
from freqtrade.tests.conftest import get_patched_freqtradebot, log_has
|
from freqtrade.tests.conftest import get_patched_freqtradebot, patch_exchange, log_has
|
||||||
from freqtrade.tests.test_freqtradebot import patch_get_signal, patch_coinmarketcap
|
from freqtrade.tests.test_freqtradebot import patch_get_signal, patch_coinmarketcap
|
||||||
|
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ def test_authorized_only(default_conf, mocker, caplog) -> None:
|
|||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
patch_get_signal(mocker, (True, False))
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
patch_exchange(mocker, None)
|
||||||
|
|
||||||
chat = Chat(0, 0)
|
chat = Chat(0, 0)
|
||||||
update = Update(randint(1, 100))
|
update = Update(randint(1, 100))
|
||||||
@ -131,8 +131,7 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
|
|||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
patch_get_signal(mocker, (True, False))
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
patch_exchange(mocker, None)
|
||||||
|
|
||||||
chat = Chat(0xdeadbeef, 0)
|
chat = Chat(0xdeadbeef, 0)
|
||||||
update = Update(randint(1, 100))
|
update = Update(randint(1, 100))
|
||||||
update.message = Message(randint(1, 100), 0, datetime.utcnow(), chat)
|
update.message = Message(randint(1, 100), 0, datetime.utcnow(), chat)
|
||||||
@ -162,7 +161,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None:
|
|||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
patch_get_signal(mocker, (True, False))
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
patch_exchange(mocker)
|
||||||
|
|
||||||
update = Update(randint(1, 100))
|
update = Update(randint(1, 100))
|
||||||
update.message = Message(randint(1, 100), 0, datetime.utcnow(), Chat(0, 0))
|
update.message = Message(randint(1, 100), 0, datetime.utcnow(), Chat(0, 0))
|
||||||
@ -198,7 +197,7 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None:
|
|||||||
patch_get_signal(mocker, (True, False))
|
patch_get_signal(mocker, (True, False))
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_pair_detail_url=MagicMock(),
|
get_pair_detail_url=MagicMock(),
|
||||||
@ -239,7 +238,7 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No
|
|||||||
patch_get_signal(mocker, (True, False))
|
patch_get_signal(mocker, (True, False))
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -286,7 +285,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker)
|
|||||||
patch_get_signal(mocker, (True, False))
|
patch_get_signal(mocker, (True, False))
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=MagicMock(return_value={'id': 'mocked_order_id'}),
|
buy=MagicMock(return_value={'id': 'mocked_order_id'}),
|
||||||
@ -344,7 +343,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
|
|||||||
return_value=15000.0
|
return_value=15000.0
|
||||||
)
|
)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -414,7 +413,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
|
|||||||
patch_get_signal(mocker, (True, False))
|
patch_get_signal(mocker, (True, False))
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker
|
get_ticker=ticker
|
||||||
)
|
)
|
||||||
@ -454,7 +453,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
|||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -489,7 +488,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
|||||||
msg_mock.reset_mock()
|
msg_mock.reset_mock()
|
||||||
|
|
||||||
# Update the ticker with a market going up
|
# Update the ticker with a market going up
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.get_ticker', ticker_sell_up)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker', ticker_sell_up)
|
||||||
trade.update(limit_sell_order)
|
trade.update(limit_sell_order)
|
||||||
|
|
||||||
trade.close_date = datetime.utcnow()
|
trade.close_date = datetime.utcnow()
|
||||||
@ -554,9 +553,8 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None:
|
|||||||
|
|
||||||
patch_get_signal(mocker, (True, False))
|
patch_get_signal(mocker, (True, False))
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=mock_balance)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.get_balances', return_value=mock_balance)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker', side_effect=mock_ticker)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.get_ticker', side_effect=mock_ticker)
|
|
||||||
|
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -565,7 +563,7 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None:
|
|||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
telegram._balance(bot=MagicMock(), update=update)
|
telegram._balance(bot=MagicMock(), update=update)
|
||||||
@ -584,9 +582,7 @@ def test_zero_balance_handle(default_conf, update, mocker) -> None:
|
|||||||
Test _balance() method when the Exchange platform returns nothing
|
Test _balance() method when the Exchange platform returns nothing
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
patch_get_signal(mocker, (True, False))
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={})
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.get_balances', return_value={})
|
|
||||||
|
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -595,7 +591,7 @@ def test_zero_balance_handle(default_conf, update, mocker) -> None:
|
|||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
telegram._balance(bot=MagicMock(), update=update)
|
telegram._balance(bot=MagicMock(), update=update)
|
||||||
@ -608,17 +604,14 @@ def test_start_handle(default_conf, update, mocker) -> None:
|
|||||||
"""
|
"""
|
||||||
Test _start() method
|
Test _start() method
|
||||||
"""
|
"""
|
||||||
patch_coinmarketcap(mocker)
|
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.rpc.telegram.Telegram',
|
'freqtrade.rpc.telegram.Telegram',
|
||||||
_init=MagicMock(),
|
_init=MagicMock(),
|
||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
@ -632,17 +625,14 @@ def test_start_handle_already_running(default_conf, update, mocker) -> None:
|
|||||||
"""
|
"""
|
||||||
Test _start() method
|
Test _start() method
|
||||||
"""
|
"""
|
||||||
patch_coinmarketcap(mocker)
|
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.rpc.telegram.Telegram',
|
'freqtrade.rpc.telegram.Telegram',
|
||||||
_init=MagicMock(),
|
_init=MagicMock(),
|
||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.RUNNING
|
freqtradebot.state = State.RUNNING
|
||||||
@ -658,16 +648,14 @@ def test_stop_handle(default_conf, update, mocker) -> None:
|
|||||||
Test _stop() method
|
Test _stop() method
|
||||||
"""
|
"""
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.rpc.telegram.Telegram',
|
'freqtrade.rpc.telegram.Telegram',
|
||||||
_init=MagicMock(),
|
_init=MagicMock(),
|
||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.RUNNING
|
freqtradebot.state = State.RUNNING
|
||||||
@ -683,16 +671,14 @@ def test_stop_handle_already_stopped(default_conf, update, mocker) -> None:
|
|||||||
Test _stop() method
|
Test _stop() method
|
||||||
"""
|
"""
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.rpc.telegram.Telegram',
|
'freqtrade.rpc.telegram.Telegram',
|
||||||
_init=MagicMock(),
|
_init=MagicMock(),
|
||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
@ -706,16 +692,14 @@ def test_stop_handle_already_stopped(default_conf, update, mocker) -> None:
|
|||||||
def test_reload_conf_handle(default_conf, update, mocker) -> None:
|
def test_reload_conf_handle(default_conf, update, mocker) -> None:
|
||||||
""" Test _reload_conf() method """
|
""" Test _reload_conf() method """
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.rpc.telegram.Telegram',
|
'freqtrade.rpc.telegram.Telegram',
|
||||||
_init=MagicMock(),
|
_init=MagicMock(),
|
||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.RUNNING
|
freqtradebot.state = State.RUNNING
|
||||||
@ -737,7 +721,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
|
|||||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -754,7 +738,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
|
|||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
# Increase the price and sell it
|
# Increase the price and sell it
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.get_ticker', ticker_sell_up)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker', ticker_sell_up)
|
||||||
|
|
||||||
update.message.text = '/forcesell 1'
|
update.message.text = '/forcesell 1'
|
||||||
telegram._forcesell(bot=MagicMock(), update=update)
|
telegram._forcesell(bot=MagicMock(), update=update)
|
||||||
@ -779,7 +763,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
|
|||||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -794,7 +778,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
|
|||||||
|
|
||||||
# Decrease the price and sell it
|
# Decrease the price and sell it
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker_sell_down
|
get_ticker=ticker_sell_down
|
||||||
)
|
)
|
||||||
@ -823,9 +807,9 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker
|
|||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||||
mocker.patch('freqtrade.exchange.get_pair_detail_url', MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.get_pair_detail_url', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -863,7 +847,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
|||||||
_init=MagicMock(),
|
_init=MagicMock(),
|
||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
@ -906,7 +890,7 @@ def test_performance_handle(default_conf, update, ticker, fee,
|
|||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -947,7 +931,7 @@ def test_performance_handle_invalid(default_conf, update, mocker) -> None:
|
|||||||
_init=MagicMock(),
|
_init=MagicMock(),
|
||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
@ -971,13 +955,13 @@ def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> Non
|
|||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=MagicMock(return_value={'id': 'mocked_order_id'}),
|
buy=MagicMock(return_value={'id': 'mocked_order_id'}),
|
||||||
get_markets=markets
|
get_markets=markets
|
||||||
)
|
)
|
||||||
mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
@ -1007,14 +991,14 @@ def test_help_handle(default_conf, update, mocker) -> None:
|
|||||||
Test _help() method
|
Test _help() method
|
||||||
"""
|
"""
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.rpc.telegram.Telegram',
|
'freqtrade.rpc.telegram.Telegram',
|
||||||
_init=MagicMock(),
|
_init=MagicMock(),
|
||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
telegram._help(bot=MagicMock(), update=update)
|
telegram._help(bot=MagicMock(), update=update)
|
||||||
@ -1027,14 +1011,13 @@ def test_version_handle(default_conf, update, mocker) -> None:
|
|||||||
Test _version() method
|
Test _version() method
|
||||||
"""
|
"""
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.rpc.telegram.Telegram',
|
'freqtrade.rpc.telegram.Telegram',
|
||||||
_init=MagicMock(),
|
_init=MagicMock(),
|
||||||
_send_msg=msg_mock
|
_send_msg=msg_mock
|
||||||
)
|
)
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
telegram._version(bot=MagicMock(), update=update)
|
telegram._version(bot=MagicMock(), update=update)
|
||||||
@ -1047,11 +1030,10 @@ def test_send_msg(default_conf, mocker) -> None:
|
|||||||
Test send_msg() method
|
Test send_msg() method
|
||||||
"""
|
"""
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
bot = MagicMock()
|
bot = MagicMock()
|
||||||
freqtradebot = FreqtradeBot(conf)
|
freqtradebot = get_patched_freqtradebot(mocker, conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
telegram._config['telegram']['enabled'] = True
|
telegram._config['telegram']['enabled'] = True
|
||||||
@ -1064,12 +1046,11 @@ def test_send_msg_network_error(default_conf, mocker, caplog) -> None:
|
|||||||
Test send_msg() method
|
Test send_msg() method
|
||||||
"""
|
"""
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
bot = MagicMock()
|
bot = MagicMock()
|
||||||
bot.send_message = MagicMock(side_effect=NetworkError('Oh snap'))
|
bot.send_message = MagicMock(side_effect=NetworkError('Oh snap'))
|
||||||
freqtradebot = FreqtradeBot(conf)
|
freqtradebot = get_patched_freqtradebot(mocker, conf)
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
telegram._config['telegram']['enabled'] = True
|
telegram._config['telegram']['enabled'] = True
|
||||||
|
@ -1,14 +1,39 @@
|
|||||||
# pragma pylint: disable=missing-docstring, protected-access, C0103
|
# pragma pylint: disable=missing-docstring, protected-access, C0103
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from freqtrade.strategy import import_strategy
|
||||||
|
from freqtrade.strategy.default_strategy import DefaultStrategy
|
||||||
from freqtrade.strategy.interface import IStrategy
|
from freqtrade.strategy.interface import IStrategy
|
||||||
from freqtrade.strategy.resolver import StrategyResolver
|
from freqtrade.strategy.resolver import StrategyResolver
|
||||||
|
|
||||||
|
|
||||||
|
def test_import_strategy(caplog):
|
||||||
|
caplog.set_level(logging.DEBUG)
|
||||||
|
|
||||||
|
strategy = DefaultStrategy()
|
||||||
|
strategy.some_method = lambda *args, **kwargs: 42
|
||||||
|
|
||||||
|
assert strategy.__module__ == 'freqtrade.strategy.default_strategy'
|
||||||
|
assert strategy.some_method() == 42
|
||||||
|
|
||||||
|
imported_strategy = import_strategy(strategy)
|
||||||
|
|
||||||
|
assert dir(strategy) == dir(imported_strategy)
|
||||||
|
|
||||||
|
assert imported_strategy.__module__ == 'freqtrade.strategy'
|
||||||
|
assert imported_strategy.some_method() == 42
|
||||||
|
|
||||||
|
assert (
|
||||||
|
'freqtrade.strategy',
|
||||||
|
logging.DEBUG,
|
||||||
|
'Imported strategy freqtrade.strategy.default_strategy.DefaultStrategy '
|
||||||
|
'as freqtrade.strategy.DefaultStrategy',
|
||||||
|
) in caplog.record_tuples
|
||||||
|
|
||||||
|
|
||||||
def test_search_strategy():
|
def test_search_strategy():
|
||||||
default_location = os.path.join(os.path.dirname(
|
default_location = os.path.join(os.path.dirname(
|
||||||
os.path.realpath(__file__)), '..', '..', 'strategy'
|
os.path.realpath(__file__)), '..', '..', 'strategy'
|
||||||
@ -20,8 +45,7 @@ def test_search_strategy():
|
|||||||
|
|
||||||
|
|
||||||
def test_load_strategy(result):
|
def test_load_strategy(result):
|
||||||
resolver = StrategyResolver()
|
resolver = StrategyResolver({'strategy': 'TestStrategy'})
|
||||||
resolver._load_strategy('TestStrategy')
|
|
||||||
assert hasattr(resolver.strategy, 'populate_indicators')
|
assert hasattr(resolver.strategy, 'populate_indicators')
|
||||||
assert 'adx' in resolver.strategy.populate_indicators(result)
|
assert 'adx' in resolver.strategy.populate_indicators(result)
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ def test_refresh_market_pair_not_in_whitelist(mocker, markets):
|
|||||||
|
|
||||||
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
|
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
|
||||||
|
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.get_markets', markets)
|
mocker.patch('freqtrade.exchange.Exchange.get_markets', markets)
|
||||||
refreshedwhitelist = freqtradebot._refresh_whitelist(
|
refreshedwhitelist = freqtradebot._refresh_whitelist(
|
||||||
conf['exchange']['pair_whitelist'] + ['XXX/BTC']
|
conf['exchange']['pair_whitelist'] + ['XXX/BTC']
|
||||||
)
|
)
|
||||||
@ -46,7 +46,7 @@ def test_refresh_whitelist(mocker, markets):
|
|||||||
conf = whitelist_conf()
|
conf = whitelist_conf()
|
||||||
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
|
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
|
||||||
|
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.get_markets', markets)
|
mocker.patch('freqtrade.exchange.Exchange.get_markets', markets)
|
||||||
refreshedwhitelist = freqtradebot._refresh_whitelist(conf['exchange']['pair_whitelist'])
|
refreshedwhitelist = freqtradebot._refresh_whitelist(conf['exchange']['pair_whitelist'])
|
||||||
|
|
||||||
# List ordered by BaseVolume
|
# List ordered by BaseVolume
|
||||||
@ -59,7 +59,7 @@ def test_refresh_whitelist_dynamic(mocker, markets, tickers):
|
|||||||
conf = whitelist_conf()
|
conf = whitelist_conf()
|
||||||
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
|
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
get_markets=markets,
|
get_markets=markets,
|
||||||
get_tickers=tickers,
|
get_tickers=tickers,
|
||||||
exchange_has=MagicMock(return_value=True)
|
exchange_has=MagicMock(return_value=True)
|
||||||
@ -78,7 +78,7 @@ def test_refresh_whitelist_dynamic(mocker, markets, tickers):
|
|||||||
def test_refresh_whitelist_dynamic_empty(mocker, markets_empty):
|
def test_refresh_whitelist_dynamic_empty(mocker, markets_empty):
|
||||||
conf = whitelist_conf()
|
conf = whitelist_conf()
|
||||||
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
|
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.get_markets', markets_empty)
|
mocker.patch('freqtrade.exchange.Exchange.get_markets', markets_empty)
|
||||||
|
|
||||||
# argument: use the whitelist dynamically by exchange-volume
|
# argument: use the whitelist dynamically by exchange-volume
|
||||||
whitelist = []
|
whitelist = []
|
||||||
|
@ -14,7 +14,7 @@ from pandas import DataFrame
|
|||||||
from freqtrade.analyze import Analyze, SignalType
|
from freqtrade.analyze import Analyze, SignalType
|
||||||
from freqtrade.optimize.__init__ import load_tickerdata_file
|
from freqtrade.optimize.__init__ import load_tickerdata_file
|
||||||
from freqtrade.arguments import TimeRange
|
from freqtrade.arguments import TimeRange
|
||||||
from freqtrade.tests.conftest import log_has
|
from freqtrade.tests.conftest import log_has, get_patched_exchange
|
||||||
|
|
||||||
# Avoid to reinit the same object again and again
|
# Avoid to reinit the same object again and again
|
||||||
_ANALYZE = Analyze({'strategy': 'DefaultStrategy'})
|
_ANALYZE = Analyze({'strategy': 'DefaultStrategy'})
|
||||||
@ -66,16 +66,16 @@ def test_populates_sell_trend(result):
|
|||||||
assert 'sell' in dataframe.columns
|
assert 'sell' in dataframe.columns
|
||||||
|
|
||||||
|
|
||||||
def test_returns_latest_buy_signal(mocker):
|
def test_returns_latest_buy_signal(mocker, default_conf):
|
||||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock())
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.analyze.Analyze',
|
'freqtrade.analyze.Analyze',
|
||||||
analyze_ticker=MagicMock(
|
analyze_ticker=MagicMock(
|
||||||
return_value=DataFrame([{'buy': 1, 'sell': 0, 'date': arrow.utcnow()}])
|
return_value=DataFrame([{'buy': 1, 'sell': 0, 'date': arrow.utcnow()}])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert _ANALYZE.get_signal('ETH/BTC', '5m') == (True, False)
|
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (True, False)
|
||||||
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.analyze.Analyze',
|
'freqtrade.analyze.Analyze',
|
||||||
@ -83,11 +83,12 @@ def test_returns_latest_buy_signal(mocker):
|
|||||||
return_value=DataFrame([{'buy': 0, 'sell': 1, 'date': arrow.utcnow()}])
|
return_value=DataFrame([{'buy': 0, 'sell': 1, 'date': arrow.utcnow()}])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert _ANALYZE.get_signal('ETH/BTC', '5m') == (False, True)
|
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, True)
|
||||||
|
|
||||||
|
|
||||||
def test_returns_latest_sell_signal(mocker):
|
def test_returns_latest_sell_signal(mocker, default_conf):
|
||||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock())
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.analyze.Analyze',
|
'freqtrade.analyze.Analyze',
|
||||||
analyze_ticker=MagicMock(
|
analyze_ticker=MagicMock(
|
||||||
@ -95,7 +96,7 @@ def test_returns_latest_sell_signal(mocker):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert _ANALYZE.get_signal('ETH/BTC', '5m') == (False, True)
|
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, True)
|
||||||
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.analyze.Analyze',
|
'freqtrade.analyze.Analyze',
|
||||||
@ -103,45 +104,49 @@ def test_returns_latest_sell_signal(mocker):
|
|||||||
return_value=DataFrame([{'sell': 0, 'buy': 1, 'date': arrow.utcnow()}])
|
return_value=DataFrame([{'sell': 0, 'buy': 1, 'date': arrow.utcnow()}])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert _ANALYZE.get_signal('ETH/BTC', '5m') == (True, False)
|
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (True, False)
|
||||||
|
|
||||||
|
|
||||||
def test_get_signal_empty(default_conf, mocker, caplog):
|
def test_get_signal_empty(default_conf, mocker, caplog):
|
||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=None)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=None)
|
||||||
assert (False, False) == _ANALYZE.get_signal('foo', default_conf['ticker_interval'])
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
|
assert (False, False) == _ANALYZE.get_signal(exchange, 'foo', default_conf['ticker_interval'])
|
||||||
assert log_has('Empty ticker history for pair foo', caplog.record_tuples)
|
assert log_has('Empty ticker history for pair foo', caplog.record_tuples)
|
||||||
|
|
||||||
|
|
||||||
def test_get_signal_exception_valueerror(default_conf, mocker, caplog):
|
def test_get_signal_exception_valueerror(default_conf, mocker, caplog):
|
||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=1)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.analyze.Analyze',
|
'freqtrade.analyze.Analyze',
|
||||||
analyze_ticker=MagicMock(
|
analyze_ticker=MagicMock(
|
||||||
side_effect=ValueError('xyz')
|
side_effect=ValueError('xyz')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert (False, False) == _ANALYZE.get_signal('foo', default_conf['ticker_interval'])
|
assert (False, False) == _ANALYZE.get_signal(exchange, 'foo', default_conf['ticker_interval'])
|
||||||
assert log_has('Unable to analyze ticker for pair foo: xyz', caplog.record_tuples)
|
assert log_has('Unable to analyze ticker for pair foo: xyz', caplog.record_tuples)
|
||||||
|
|
||||||
|
|
||||||
def test_get_signal_empty_dataframe(default_conf, mocker, caplog):
|
def test_get_signal_empty_dataframe(default_conf, mocker, caplog):
|
||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=1)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.analyze.Analyze',
|
'freqtrade.analyze.Analyze',
|
||||||
analyze_ticker=MagicMock(
|
analyze_ticker=MagicMock(
|
||||||
return_value=DataFrame([])
|
return_value=DataFrame([])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert (False, False) == _ANALYZE.get_signal('xyz', default_conf['ticker_interval'])
|
assert (False, False) == _ANALYZE.get_signal(exchange, 'xyz', default_conf['ticker_interval'])
|
||||||
assert log_has('Empty dataframe for pair xyz', caplog.record_tuples)
|
assert log_has('Empty dataframe for pair xyz', caplog.record_tuples)
|
||||||
|
|
||||||
|
|
||||||
def test_get_signal_old_dataframe(default_conf, mocker, caplog):
|
def test_get_signal_old_dataframe(default_conf, mocker, caplog):
|
||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=1)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
# FIX: The get_signal function has hardcoded 10, which we must inturn hardcode
|
# FIX: The get_signal function has hardcoded 10, which we must inturn hardcode
|
||||||
oldtime = arrow.utcnow() - datetime.timedelta(minutes=11)
|
oldtime = arrow.utcnow() - datetime.timedelta(minutes=11)
|
||||||
ticks = DataFrame([{'buy': 1, 'date': oldtime}])
|
ticks = DataFrame([{'buy': 1, 'date': oldtime}])
|
||||||
@ -151,15 +156,16 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog):
|
|||||||
return_value=DataFrame(ticks)
|
return_value=DataFrame(ticks)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert (False, False) == _ANALYZE.get_signal('xyz', default_conf['ticker_interval'])
|
assert (False, False) == _ANALYZE.get_signal(exchange, 'xyz', default_conf['ticker_interval'])
|
||||||
assert log_has(
|
assert log_has(
|
||||||
'Outdated history for pair xyz. Last tick is 11 minutes old',
|
'Outdated history for pair xyz. Last tick is 11 minutes old',
|
||||||
caplog.record_tuples
|
caplog.record_tuples
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_get_signal_handles_exceptions(mocker):
|
def test_get_signal_handles_exceptions(mocker, default_conf):
|
||||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock())
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.analyze.Analyze',
|
'freqtrade.analyze.Analyze',
|
||||||
analyze_ticker=MagicMock(
|
analyze_ticker=MagicMock(
|
||||||
@ -167,7 +173,7 @@ def test_get_signal_handles_exceptions(mocker):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert _ANALYZE.get_signal('ETH/BTC', '5m') == (False, False)
|
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, False)
|
||||||
|
|
||||||
|
|
||||||
def test_parse_ticker_dataframe(ticker_history):
|
def test_parse_ticker_dataframe(ticker_history):
|
||||||
|
@ -13,6 +13,7 @@ from jsonschema import ValidationError
|
|||||||
|
|
||||||
from freqtrade.arguments import Arguments
|
from freqtrade.arguments import Arguments
|
||||||
from freqtrade.configuration import Configuration
|
from freqtrade.configuration import Configuration
|
||||||
|
from freqtrade.constants import DEFAULT_DB_PROD_URL, DEFAULT_DB_DRYRUN_URL
|
||||||
from freqtrade.tests.conftest import log_has
|
from freqtrade.tests.conftest import log_has
|
||||||
from freqtrade import OperationalException
|
from freqtrade import OperationalException
|
||||||
|
|
||||||
@ -152,6 +153,43 @@ def test_load_config_with_params(default_conf, mocker) -> None:
|
|||||||
assert validated_conf.get('strategy_path') == '/some/path'
|
assert validated_conf.get('strategy_path') == '/some/path'
|
||||||
assert validated_conf.get('db_url') == 'sqlite:///someurl'
|
assert validated_conf.get('db_url') == 'sqlite:///someurl'
|
||||||
|
|
||||||
|
conf = default_conf.copy()
|
||||||
|
conf["dry_run"] = False
|
||||||
|
del conf["db_url"]
|
||||||
|
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||||
|
read_data=json.dumps(conf)
|
||||||
|
))
|
||||||
|
|
||||||
|
arglist = [
|
||||||
|
'--dynamic-whitelist', '10',
|
||||||
|
'--strategy', 'TestStrategy',
|
||||||
|
'--strategy-path', '/some/path'
|
||||||
|
]
|
||||||
|
args = Arguments(arglist, '').get_parsed_arg()
|
||||||
|
|
||||||
|
configuration = Configuration(args)
|
||||||
|
validated_conf = configuration.load_config()
|
||||||
|
assert validated_conf.get('db_url') == DEFAULT_DB_PROD_URL
|
||||||
|
|
||||||
|
# Test dry=run with ProdURL
|
||||||
|
conf = default_conf.copy()
|
||||||
|
conf["dry_run"] = True
|
||||||
|
conf["db_url"] = DEFAULT_DB_PROD_URL
|
||||||
|
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||||
|
read_data=json.dumps(conf)
|
||||||
|
))
|
||||||
|
|
||||||
|
arglist = [
|
||||||
|
'--dynamic-whitelist', '10',
|
||||||
|
'--strategy', 'TestStrategy',
|
||||||
|
'--strategy-path', '/some/path'
|
||||||
|
]
|
||||||
|
args = Arguments(arglist, '').get_parsed_arg()
|
||||||
|
|
||||||
|
configuration = Configuration(args)
|
||||||
|
validated_conf = configuration.load_config()
|
||||||
|
assert validated_conf.get('db_url') == DEFAULT_DB_DRYRUN_URL
|
||||||
|
|
||||||
|
|
||||||
def test_load_custom_strategy(default_conf, mocker) -> None:
|
def test_load_custom_strategy(default_conf, mocker) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -40,7 +40,8 @@ def test_pair_convertion_object():
|
|||||||
assert pair_convertion.price == 30000.123
|
assert pair_convertion.price == 30000.123
|
||||||
|
|
||||||
|
|
||||||
def test_fiat_convert_is_supported():
|
def test_fiat_convert_is_supported(mocker):
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
fiat_convert = CryptoToFiatConverter()
|
fiat_convert = CryptoToFiatConverter()
|
||||||
assert fiat_convert._is_supported_fiat(fiat='USD') is True
|
assert fiat_convert._is_supported_fiat(fiat='USD') is True
|
||||||
assert fiat_convert._is_supported_fiat(fiat='usd') is True
|
assert fiat_convert._is_supported_fiat(fiat='usd') is True
|
||||||
@ -48,7 +49,9 @@ def test_fiat_convert_is_supported():
|
|||||||
assert fiat_convert._is_supported_fiat(fiat='ABC') is False
|
assert fiat_convert._is_supported_fiat(fiat='ABC') is False
|
||||||
|
|
||||||
|
|
||||||
def test_fiat_convert_add_pair():
|
def test_fiat_convert_add_pair(mocker):
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
|
||||||
fiat_convert = CryptoToFiatConverter()
|
fiat_convert = CryptoToFiatConverter()
|
||||||
|
|
||||||
pair_len = len(fiat_convert._pairs)
|
pair_len = len(fiat_convert._pairs)
|
||||||
@ -70,11 +73,8 @@ def test_fiat_convert_add_pair():
|
|||||||
|
|
||||||
|
|
||||||
def test_fiat_convert_find_price(mocker):
|
def test_fiat_convert_find_price(mocker):
|
||||||
api_mock = MagicMock(return_value={
|
patch_coinmarketcap(mocker)
|
||||||
'price_usd': 12345.0,
|
|
||||||
'price_eur': 13000.2
|
|
||||||
})
|
|
||||||
mocker.patch('freqtrade.fiat_convert.Market.ticker', api_mock)
|
|
||||||
fiat_convert = CryptoToFiatConverter()
|
fiat_convert = CryptoToFiatConverter()
|
||||||
|
|
||||||
with pytest.raises(ValueError, match=r'The fiat ABC is not supported.'):
|
with pytest.raises(ValueError, match=r'The fiat ABC is not supported.'):
|
||||||
@ -92,17 +92,15 @@ def test_fiat_convert_find_price(mocker):
|
|||||||
|
|
||||||
def test_fiat_convert_unsupported_crypto(mocker, caplog):
|
def test_fiat_convert_unsupported_crypto(mocker, caplog):
|
||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._cryptomap', return_value=[])
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._cryptomap', return_value=[])
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
fiat_convert = CryptoToFiatConverter()
|
fiat_convert = CryptoToFiatConverter()
|
||||||
assert fiat_convert._find_price(crypto_symbol='CRYPTO_123', fiat_symbol='EUR') == 0.0
|
assert fiat_convert._find_price(crypto_symbol='CRYPTO_123', fiat_symbol='EUR') == 0.0
|
||||||
assert log_has('unsupported crypto-symbol CRYPTO_123 - returning 0.0', caplog.record_tuples)
|
assert log_has('unsupported crypto-symbol CRYPTO_123 - returning 0.0', caplog.record_tuples)
|
||||||
|
|
||||||
|
|
||||||
def test_fiat_convert_get_price(mocker):
|
def test_fiat_convert_get_price(mocker):
|
||||||
api_mock = MagicMock(return_value={
|
patch_coinmarketcap(mocker)
|
||||||
'price_usd': 28000.0,
|
|
||||||
'price_eur': 15000.0
|
|
||||||
})
|
|
||||||
mocker.patch('freqtrade.fiat_convert.Market.ticker', api_mock)
|
|
||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=28000.0)
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=28000.0)
|
||||||
|
|
||||||
fiat_convert = CryptoToFiatConverter()
|
fiat_convert = CryptoToFiatConverter()
|
||||||
@ -172,8 +170,9 @@ def test_fiat_init_network_exception(mocker):
|
|||||||
assert length_cryptomap == 0
|
assert length_cryptomap == 0
|
||||||
|
|
||||||
|
|
||||||
def test_fiat_convert_without_network():
|
def test_fiat_convert_without_network(mocker):
|
||||||
# Because CryptoToFiatConverter is a Singleton we reset the value of _coinmarketcap
|
# Because CryptoToFiatConverter is a Singleton we reset the value of _coinmarketcap
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
|
||||||
fiat_convert = CryptoToFiatConverter()
|
fiat_convert = CryptoToFiatConverter()
|
||||||
|
|
||||||
@ -186,6 +185,7 @@ def test_fiat_convert_without_network():
|
|||||||
|
|
||||||
|
|
||||||
def test_convert_amount(mocker):
|
def test_convert_amount(mocker):
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter.get_price', return_value=12345.0)
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter.get_price', return_value=12345.0)
|
||||||
|
|
||||||
fiat_convert = CryptoToFiatConverter()
|
fiat_convert = CryptoToFiatConverter()
|
||||||
|
@ -18,7 +18,7 @@ from freqtrade import constants, DependencyException, OperationalException, Temp
|
|||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
from freqtrade.tests.conftest import log_has, patch_coinmarketcap
|
from freqtrade.tests.conftest import log_has, patch_coinmarketcap, patch_exchange
|
||||||
|
|
||||||
|
|
||||||
# Functions for recurrent object patching
|
# Functions for recurrent object patching
|
||||||
@ -32,7 +32,7 @@ def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
|
|||||||
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
patch_exchange(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
|
|
||||||
return FreqtradeBot(config)
|
return FreqtradeBot(config)
|
||||||
@ -47,7 +47,7 @@ def patch_get_signal(mocker, value=(True, False)) -> None:
|
|||||||
"""
|
"""
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.freqtradebot.Analyze.get_signal',
|
'freqtrade.freqtradebot.Analyze.get_signal',
|
||||||
side_effect=lambda s, t: value
|
side_effect=lambda e, s, t: value
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -187,9 +187,9 @@ def test_gen_pair_whitelist(mocker, default_conf, tickers) -> None:
|
|||||||
Test _gen_pair_whitelist() method
|
Test _gen_pair_whitelist() method
|
||||||
"""
|
"""
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.get_tickers', tickers)
|
mocker.patch('freqtrade.exchange.Exchange.get_tickers', tickers)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.exchange_has', MagicMock(return_value=True))
|
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
# mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||||
|
|
||||||
# Test to retrieved BTC sorted on quoteVolume (default)
|
# Test to retrieved BTC sorted on quoteVolume (default)
|
||||||
whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC')
|
whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC')
|
||||||
@ -223,7 +223,7 @@ def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mock
|
|||||||
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2)
|
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2)
|
||||||
)
|
)
|
||||||
@ -244,7 +244,7 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf,
|
|||||||
"""
|
"""
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5)
|
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5)
|
||||||
)
|
)
|
||||||
@ -269,7 +269,7 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf,
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
@ -313,13 +313,13 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.get_stoploss', MagicMock(return_value=-0.05))
|
mocker.patch('freqtrade.freqtradebot.Analyze.get_stoploss', MagicMock(return_value=-0.05))
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
|
||||||
# no pair found
|
# no pair found
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.freqtradebot.exchange.get_markets',
|
'freqtrade.exchange.Exchange.get_markets',
|
||||||
MagicMock(return_value=[{
|
MagicMock(return_value=[{
|
||||||
'symbol': 'ETH/BTC'
|
'symbol': 'ETH/BTC'
|
||||||
}])
|
}])
|
||||||
@ -329,7 +329,7 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
|
|
||||||
# no 'limits' section
|
# no 'limits' section
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.freqtradebot.exchange.get_markets',
|
'freqtrade.exchange.Exchange.get_markets',
|
||||||
MagicMock(return_value=[{
|
MagicMock(return_value=[{
|
||||||
'symbol': 'ETH/BTC'
|
'symbol': 'ETH/BTC'
|
||||||
}])
|
}])
|
||||||
@ -339,7 +339,7 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
|
|
||||||
# empty 'limits' section
|
# empty 'limits' section
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.freqtradebot.exchange.get_markets',
|
'freqtrade.exchange.Exchange.get_markets',
|
||||||
MagicMock(return_value=[{
|
MagicMock(return_value=[{
|
||||||
'symbol': 'ETH/BTC',
|
'symbol': 'ETH/BTC',
|
||||||
'limits': {}
|
'limits': {}
|
||||||
@ -350,7 +350,7 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
|
|
||||||
# empty 'cost'/'amount' section
|
# empty 'cost'/'amount' section
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.freqtradebot.exchange.get_markets',
|
'freqtrade.exchange.Exchange.get_markets',
|
||||||
MagicMock(return_value=[{
|
MagicMock(return_value=[{
|
||||||
'symbol': 'ETH/BTC',
|
'symbol': 'ETH/BTC',
|
||||||
'limits': {
|
'limits': {
|
||||||
@ -364,7 +364,7 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
|
|
||||||
# min cost is set
|
# min cost is set
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.freqtradebot.exchange.get_markets',
|
'freqtrade.exchange.Exchange.get_markets',
|
||||||
MagicMock(return_value=[{
|
MagicMock(return_value=[{
|
||||||
'symbol': 'ETH/BTC',
|
'symbol': 'ETH/BTC',
|
||||||
'limits': {
|
'limits': {
|
||||||
@ -378,7 +378,7 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
|
|
||||||
# min amount is set
|
# min amount is set
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.freqtradebot.exchange.get_markets',
|
'freqtrade.exchange.Exchange.get_markets',
|
||||||
MagicMock(return_value=[{
|
MagicMock(return_value=[{
|
||||||
'symbol': 'ETH/BTC',
|
'symbol': 'ETH/BTC',
|
||||||
'limits': {
|
'limits': {
|
||||||
@ -392,7 +392,7 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
|
|
||||||
# min amount and cost are set (cost is minimal)
|
# min amount and cost are set (cost is minimal)
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.freqtradebot.exchange.get_markets',
|
'freqtrade.exchange.Exchange.get_markets',
|
||||||
MagicMock(return_value=[{
|
MagicMock(return_value=[{
|
||||||
'symbol': 'ETH/BTC',
|
'symbol': 'ETH/BTC',
|
||||||
'limits': {
|
'limits': {
|
||||||
@ -406,7 +406,7 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
|
|
||||||
# min amount and cost are set (amount is minial)
|
# min amount and cost are set (amount is minial)
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.freqtradebot.exchange.get_markets',
|
'freqtrade.exchange.Exchange.get_markets',
|
||||||
MagicMock(return_value=[{
|
MagicMock(return_value=[{
|
||||||
'symbol': 'ETH/BTC',
|
'symbol': 'ETH/BTC',
|
||||||
'limits': {
|
'limits': {
|
||||||
@ -427,7 +427,7 @@ def test_create_trade(default_conf, ticker, limit_buy_order, fee, markets, mocke
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
@ -465,7 +465,7 @@ def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order,
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
@ -489,7 +489,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order,
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
buy_mock = MagicMock(return_value={'id': limit_buy_order['id']})
|
buy_mock = MagicMock(return_value={'id': limit_buy_order['id']})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=buy_mock,
|
buy=buy_mock,
|
||||||
@ -516,7 +516,7 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
buy_mock = MagicMock(return_value={'id': limit_buy_order['id']})
|
buy_mock = MagicMock(return_value={'id': limit_buy_order['id']})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=buy_mock,
|
buy=buy_mock,
|
||||||
@ -541,7 +541,7 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order,
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
@ -567,7 +567,7 @@ def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, marke
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
@ -595,7 +595,7 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker,
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
@ -625,7 +625,7 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None:
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker_history=MagicMock(return_value=20),
|
get_ticker_history=MagicMock(return_value=20),
|
||||||
get_balance=MagicMock(return_value=20),
|
get_balance=MagicMock(return_value=20),
|
||||||
@ -650,7 +650,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_markets=markets,
|
get_markets=markets,
|
||||||
@ -691,7 +691,7 @@ def test_process_exchange_failures(default_conf, ticker, markets, mocker) -> Non
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_markets=markets,
|
get_markets=markets,
|
||||||
@ -713,7 +713,7 @@ def test_process_operational_exception(default_conf, ticker, markets, mocker) ->
|
|||||||
msg_mock = patch_RPCManager(mocker)
|
msg_mock = patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_markets=markets,
|
get_markets=markets,
|
||||||
@ -737,7 +737,7 @@ def test_process_trade_handling(
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_markets=markets,
|
get_markets=markets,
|
||||||
@ -758,29 +758,32 @@ def test_process_trade_handling(
|
|||||||
assert result is False
|
assert result is False
|
||||||
|
|
||||||
|
|
||||||
def test_balance_fully_ask_side(mocker) -> None:
|
def test_balance_fully_ask_side(mocker, default_conf) -> None:
|
||||||
"""
|
"""
|
||||||
Test get_target_bid() method
|
Test get_target_bid() method
|
||||||
"""
|
"""
|
||||||
freqtrade = get_patched_freqtradebot(mocker, {'bid_strategy': {'ask_last_balance': 0.0}})
|
default_conf['bid_strategy']['ask_last_balance'] = 0.0
|
||||||
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
assert freqtrade.get_target_bid({'ask': 20, 'last': 10}) == 20
|
assert freqtrade.get_target_bid({'ask': 20, 'last': 10}) == 20
|
||||||
|
|
||||||
|
|
||||||
def test_balance_fully_last_side(mocker) -> None:
|
def test_balance_fully_last_side(mocker, default_conf) -> None:
|
||||||
"""
|
"""
|
||||||
Test get_target_bid() method
|
Test get_target_bid() method
|
||||||
"""
|
"""
|
||||||
freqtrade = get_patched_freqtradebot(mocker, {'bid_strategy': {'ask_last_balance': 1.0}})
|
default_conf['bid_strategy']['ask_last_balance'] = 1.0
|
||||||
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
assert freqtrade.get_target_bid({'ask': 20, 'last': 10}) == 10
|
assert freqtrade.get_target_bid({'ask': 20, 'last': 10}) == 10
|
||||||
|
|
||||||
|
|
||||||
def test_balance_bigger_last_ask(mocker) -> None:
|
def test_balance_bigger_last_ask(mocker, default_conf) -> None:
|
||||||
"""
|
"""
|
||||||
Test get_target_bid() method
|
Test get_target_bid() method
|
||||||
"""
|
"""
|
||||||
freqtrade = get_patched_freqtradebot(mocker, {'bid_strategy': {'ask_last_balance': 1.0}})
|
default_conf['bid_strategy']['ask_last_balance'] = 1.0
|
||||||
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
assert freqtrade.get_target_bid({'ask': 5, 'last': 10}) == 5
|
assert freqtrade.get_target_bid({'ask': 5, 'last': 10}) == 5
|
||||||
|
|
||||||
@ -819,8 +822,8 @@ def test_process_maybe_execute_sell(mocker, default_conf, limit_buy_order, caplo
|
|||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
|
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.get_order', return_value=limit_buy_order)
|
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.get_trades_for_order', return_value=[])
|
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
|
||||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
|
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
|
||||||
return_value=limit_buy_order['amount'])
|
return_value=limit_buy_order['amount'])
|
||||||
|
|
||||||
@ -853,7 +856,7 @@ def test_process_maybe_execute_sell_exception(mocker, default_conf,
|
|||||||
Test the exceptions in process_maybe_execute_sell()
|
Test the exceptions in process_maybe_execute_sell()
|
||||||
"""
|
"""
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
mocker.patch('freqtrade.freqtradebot.exchange.get_order', return_value=limit_buy_order)
|
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
|
||||||
|
|
||||||
trade = MagicMock()
|
trade = MagicMock()
|
||||||
trade.open_order_id = '123'
|
trade.open_order_id = '123'
|
||||||
@ -884,7 +887,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order,
|
|||||||
patch_get_signal(mocker)
|
patch_get_signal(mocker)
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=MagicMock(return_value={
|
get_ticker=MagicMock(return_value={
|
||||||
'bid': 0.00001172,
|
'bid': 0.00001172,
|
||||||
@ -935,7 +938,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order,
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
@ -995,7 +998,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
@ -1033,7 +1036,7 @@ def test_handle_trade_experimental(
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
@ -1065,7 +1068,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order,
|
|||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
@ -1096,7 +1099,7 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, fe
|
|||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_order=MagicMock(return_value=limit_buy_order_old),
|
get_order=MagicMock(return_value=limit_buy_order_old),
|
||||||
@ -1137,7 +1140,7 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old,
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_order=MagicMock(return_value=limit_sell_order_old),
|
get_order=MagicMock(return_value=limit_sell_order_old),
|
||||||
@ -1177,7 +1180,7 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_order=MagicMock(return_value=limit_buy_order_old_partial),
|
get_order=MagicMock(return_value=limit_buy_order_old_partial),
|
||||||
@ -1225,7 +1228,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
|
|||||||
handle_timedout_limit_sell=MagicMock(),
|
handle_timedout_limit_sell=MagicMock(),
|
||||||
)
|
)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_order=MagicMock(side_effect=requests.exceptions.RequestException('Oh snap')),
|
get_order=MagicMock(side_effect=requests.exceptions.RequestException('Oh snap')),
|
||||||
@ -1265,7 +1268,7 @@ def test_handle_timedout_limit_buy(mocker, default_conf) -> None:
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
cancel_order=cancel_order_mock
|
cancel_order=cancel_order_mock
|
||||||
)
|
)
|
||||||
@ -1291,7 +1294,7 @@ def test_handle_timedout_limit_sell(mocker, default_conf) -> None:
|
|||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
cancel_order=cancel_order_mock
|
cancel_order=cancel_order_mock
|
||||||
)
|
)
|
||||||
@ -1317,7 +1320,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc
|
|||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -1334,7 +1337,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc
|
|||||||
|
|
||||||
# Increase the price and sell it
|
# Increase the price and sell it
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker_sell_up
|
get_ticker=ticker_sell_up
|
||||||
)
|
)
|
||||||
@ -1360,7 +1363,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets,
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -1376,7 +1379,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets,
|
|||||||
|
|
||||||
# Decrease the price and sell it
|
# Decrease the price and sell it
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker_sell_down
|
get_ticker=ticker_sell_down
|
||||||
)
|
)
|
||||||
@ -1401,7 +1404,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee,
|
|||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -1417,7 +1420,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee,
|
|||||||
|
|
||||||
# Increase the price and sell it
|
# Increase the price and sell it
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker_sell_up
|
get_ticker=ticker_sell_up
|
||||||
)
|
)
|
||||||
@ -1443,7 +1446,7 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee,
|
|||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker,
|
get_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
@ -1459,7 +1462,7 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee,
|
|||||||
|
|
||||||
# Decrease the price and sell it
|
# Decrease the price and sell it
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=ticker_sell_down
|
get_ticker=ticker_sell_down
|
||||||
)
|
)
|
||||||
@ -1484,7 +1487,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=MagicMock(return_value={
|
get_ticker=MagicMock(return_value={
|
||||||
'bid': 0.00002172,
|
'bid': 0.00002172,
|
||||||
@ -1519,7 +1522,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order,
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=MagicMock(return_value={
|
get_ticker=MagicMock(return_value={
|
||||||
'bid': 0.00002172,
|
'bid': 0.00002172,
|
||||||
@ -1551,9 +1554,9 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, market
|
|||||||
patch_get_signal(mocker)
|
patch_get_signal(mocker)
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
mocker.patch('freqtrade.freqtradebot.Analyze.stop_loss_reached', return_value=False)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=MagicMock(return_value={
|
get_ticker=MagicMock(return_value={
|
||||||
'bid': 0.00000172,
|
'bid': 0.00000172,
|
||||||
@ -1587,16 +1590,15 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, marke
|
|||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
get_ticker=MagicMock(return_value={
|
get_ticker=MagicMock(return_value={
|
||||||
'bid': 0.00000172,
|
'bid': 0.0000172,
|
||||||
'ask': 0.00000173,
|
'ask': 0.0000173,
|
||||||
'last': 0.00000172
|
'last': 0.0000172
|
||||||
}),
|
}),
|
||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
get_markets=markets
|
|
||||||
)
|
)
|
||||||
|
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
@ -1614,17 +1616,96 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, marke
|
|||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, mocker) -> None:
|
||||||
|
"""
|
||||||
|
Test sell_profit_only feature when enabled and we have a loss
|
||||||
|
"""
|
||||||
|
patch_get_signal(mocker)
|
||||||
|
patch_RPCManager(mocker)
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=True)
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
validate_pairs=MagicMock(),
|
||||||
|
get_ticker=MagicMock(return_value={
|
||||||
|
'bid': 0.0000172,
|
||||||
|
'ask': 0.0000173,
|
||||||
|
'last': 0.0000172
|
||||||
|
}),
|
||||||
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
|
get_fee=fee,
|
||||||
|
)
|
||||||
|
|
||||||
|
conf = deepcopy(default_conf)
|
||||||
|
conf['experimental'] = {
|
||||||
|
'ignore_roi_if_buy_signal': True
|
||||||
|
}
|
||||||
|
|
||||||
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
freqtrade.create_trade()
|
||||||
|
|
||||||
|
trade = Trade.query.first()
|
||||||
|
trade.update(limit_buy_order)
|
||||||
|
patch_get_signal(mocker, value=(True, True))
|
||||||
|
assert freqtrade.handle_trade(trade) is False
|
||||||
|
|
||||||
|
# Test if buy-signal is absent (should sell due to roi = true)
|
||||||
|
patch_get_signal(mocker, value=(False, True))
|
||||||
|
assert freqtrade.handle_trade(trade) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order,
|
||||||
|
fee, markets, mocker) -> None:
|
||||||
|
"""
|
||||||
|
Test sell_profit_only feature when enabled and we have a loss
|
||||||
|
"""
|
||||||
|
patch_get_signal(mocker)
|
||||||
|
patch_RPCManager(mocker)
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=True)
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
validate_pairs=MagicMock(),
|
||||||
|
get_ticker=MagicMock(return_value={
|
||||||
|
'bid': 0.00000172,
|
||||||
|
'ask': 0.00000173,
|
||||||
|
'last': 0.00000172
|
||||||
|
}),
|
||||||
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
|
get_fee=fee,
|
||||||
|
get_markets=markets
|
||||||
|
)
|
||||||
|
|
||||||
|
conf = deepcopy(default_conf)
|
||||||
|
conf['experimental'] = {
|
||||||
|
'ignore_roi_if_buy_signal': False
|
||||||
|
}
|
||||||
|
|
||||||
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
freqtrade.create_trade()
|
||||||
|
|
||||||
|
trade = Trade.query.first()
|
||||||
|
trade.update(limit_buy_order)
|
||||||
|
# Sell due to min_roi_reached
|
||||||
|
patch_get_signal(mocker, value=(True, True))
|
||||||
|
assert freqtrade.handle_trade(trade) is True
|
||||||
|
|
||||||
|
# Test if buy-signal is absent
|
||||||
|
patch_get_signal(mocker, value=(False, True))
|
||||||
|
assert freqtrade.handle_trade(trade) is True
|
||||||
|
|
||||||
|
|
||||||
def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, caplog, mocker):
|
def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, caplog, mocker):
|
||||||
"""
|
"""
|
||||||
Test get_real_amount - fee in quote currency
|
Test get_real_amount - fee in quote currency
|
||||||
"""
|
"""
|
||||||
|
|
||||||
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order)
|
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
|
||||||
|
|
||||||
patch_get_signal(mocker)
|
patch_get_signal(mocker)
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||||
amount = sum(x['amount'] for x in trades_for_order)
|
amount = sum(x['amount'] for x in trades_for_order)
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair='LTC/ETH',
|
pair='LTC/ETH',
|
||||||
@ -1646,12 +1727,12 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker):
|
|||||||
Test get_real_amount - fee in quote currency
|
Test get_real_amount - fee in quote currency
|
||||||
"""
|
"""
|
||||||
|
|
||||||
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=[])
|
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
|
||||||
|
|
||||||
patch_get_signal(mocker)
|
patch_get_signal(mocker)
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||||
amount = buy_order_fee['amount']
|
amount = buy_order_fee['amount']
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair='LTC/ETH',
|
pair='LTC/ETH',
|
||||||
@ -1677,8 +1758,8 @@ def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, mo
|
|||||||
patch_get_signal(mocker)
|
patch_get_signal(mocker)
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||||
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order)
|
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
|
||||||
amount = sum(x['amount'] for x in trades_for_order)
|
amount = sum(x['amount'] for x in trades_for_order)
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair='LTC/ETH',
|
pair='LTC/ETH',
|
||||||
@ -1703,8 +1784,8 @@ def test_get_real_amount_BNB(default_conf, trades_for_order, buy_order_fee, mock
|
|||||||
patch_get_signal(mocker)
|
patch_get_signal(mocker)
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||||
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order)
|
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
|
||||||
amount = sum(x['amount'] for x in trades_for_order)
|
amount = sum(x['amount'] for x in trades_for_order)
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair='LTC/ETH',
|
pair='LTC/ETH',
|
||||||
@ -1726,8 +1807,8 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, c
|
|||||||
patch_get_signal(mocker)
|
patch_get_signal(mocker)
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||||
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order2)
|
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order2)
|
||||||
amount = float(sum(x['amount'] for x in trades_for_order2))
|
amount = float(sum(x['amount'] for x in trades_for_order2))
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair='LTC/ETH',
|
pair='LTC/ETH',
|
||||||
@ -1754,8 +1835,9 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee
|
|||||||
patch_get_signal(mocker)
|
patch_get_signal(mocker)
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||||
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=[trades_for_order])
|
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order',
|
||||||
|
return_value=[trades_for_order])
|
||||||
amount = float(sum(x['amount'] for x in trades_for_order))
|
amount = float(sum(x['amount'] for x in trades_for_order))
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair='LTC/ETH',
|
pair='LTC/ETH',
|
||||||
@ -1782,8 +1864,8 @@ def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order
|
|||||||
patch_get_signal(mocker)
|
patch_get_signal(mocker)
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||||
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=[])
|
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
|
||||||
amount = float(sum(x['amount'] for x in trades_for_order))
|
amount = float(sum(x['amount'] for x in trades_for_order))
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair='LTC/ETH',
|
pair='LTC/ETH',
|
||||||
@ -1807,8 +1889,8 @@ def test_get_real_amount_invalid(default_conf, trades_for_order, buy_order_fee,
|
|||||||
patch_get_signal(mocker)
|
patch_get_signal(mocker)
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||||
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order)
|
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
|
||||||
amount = sum(x['amount'] for x in trades_for_order)
|
amount = sum(x['amount'] for x in trades_for_order)
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair='LTC/ETH',
|
pair='LTC/ETH',
|
||||||
@ -1829,7 +1911,7 @@ def test_get_real_amount_open_trade(default_conf, mocker):
|
|||||||
patch_get_signal(mocker)
|
patch_get_signal(mocker)
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||||
amount = 12345
|
amount = 12345
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair='LTC/ETH',
|
pair='LTC/ETH',
|
||||||
|
@ -13,7 +13,7 @@ from freqtrade.arguments import Arguments
|
|||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
from freqtrade.main import main, set_loggers, reconfigure
|
from freqtrade.main import main, set_loggers, reconfigure
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
from freqtrade.tests.conftest import log_has
|
from freqtrade.tests.conftest import log_has, patch_exchange
|
||||||
|
|
||||||
|
|
||||||
def test_parse_args_backtesting(mocker) -> None:
|
def test_parse_args_backtesting(mocker) -> None:
|
||||||
@ -70,6 +70,7 @@ def test_main_fatal_exception(mocker, default_conf, caplog) -> None:
|
|||||||
Test main() function
|
Test main() function
|
||||||
In this test we are skipping the while True loop by throwing an exception.
|
In this test we are skipping the while True loop by throwing an exception.
|
||||||
"""
|
"""
|
||||||
|
patch_exchange(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.FreqtradeBot',
|
'freqtrade.freqtradebot.FreqtradeBot',
|
||||||
_init_modules=MagicMock(),
|
_init_modules=MagicMock(),
|
||||||
@ -97,6 +98,7 @@ def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None:
|
|||||||
Test main() function
|
Test main() function
|
||||||
In this test we are skipping the while True loop by throwing an exception.
|
In this test we are skipping the while True loop by throwing an exception.
|
||||||
"""
|
"""
|
||||||
|
patch_exchange(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.FreqtradeBot',
|
'freqtrade.freqtradebot.FreqtradeBot',
|
||||||
_init_modules=MagicMock(),
|
_init_modules=MagicMock(),
|
||||||
@ -124,6 +126,7 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None:
|
|||||||
Test main() function
|
Test main() function
|
||||||
In this test we are skipping the while True loop by throwing an exception.
|
In this test we are skipping the while True loop by throwing an exception.
|
||||||
"""
|
"""
|
||||||
|
patch_exchange(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.FreqtradeBot',
|
'freqtrade.freqtradebot.FreqtradeBot',
|
||||||
_init_modules=MagicMock(),
|
_init_modules=MagicMock(),
|
||||||
@ -151,6 +154,7 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None:
|
|||||||
Test main() function
|
Test main() function
|
||||||
In this test we are skipping the while True loop by throwing an exception.
|
In this test we are skipping the while True loop by throwing an exception.
|
||||||
"""
|
"""
|
||||||
|
patch_exchange(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.FreqtradeBot',
|
'freqtrade.freqtradebot.FreqtradeBot',
|
||||||
_init_modules=MagicMock(),
|
_init_modules=MagicMock(),
|
||||||
@ -178,6 +182,7 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None:
|
|||||||
|
|
||||||
def test_reconfigure(mocker, default_conf) -> None:
|
def test_reconfigure(mocker, default_conf) -> None:
|
||||||
""" Test recreate() function """
|
""" Test recreate() function """
|
||||||
|
patch_exchange(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.freqtradebot.FreqtradeBot',
|
'freqtrade.freqtradebot.FreqtradeBot',
|
||||||
_init_modules=MagicMock(),
|
_init_modules=MagicMock(),
|
||||||
|
@ -14,9 +14,7 @@ def init_persistence(default_conf):
|
|||||||
init(default_conf)
|
init(default_conf)
|
||||||
|
|
||||||
|
|
||||||
def test_init_create_session(default_conf, mocker):
|
def test_init_create_session(default_conf):
|
||||||
mocker.patch.dict('freqtrade.persistence._CONF', default_conf)
|
|
||||||
|
|
||||||
# Check if init create a session
|
# Check if init create a session
|
||||||
init(default_conf)
|
init(default_conf)
|
||||||
assert hasattr(Trade, 'session')
|
assert hasattr(Trade, 'session')
|
||||||
@ -29,20 +27,17 @@ def test_init_custom_db_url(default_conf, mocker):
|
|||||||
# Update path to a value other than default, but still in-memory
|
# Update path to a value other than default, but still in-memory
|
||||||
conf.update({'db_url': 'sqlite:///tmp/freqtrade2_test.sqlite'})
|
conf.update({'db_url': 'sqlite:///tmp/freqtrade2_test.sqlite'})
|
||||||
create_engine_mock = mocker.patch('freqtrade.persistence.create_engine', MagicMock())
|
create_engine_mock = mocker.patch('freqtrade.persistence.create_engine', MagicMock())
|
||||||
mocker.patch.dict('freqtrade.persistence._CONF', conf)
|
|
||||||
|
|
||||||
init(conf)
|
init(conf)
|
||||||
assert create_engine_mock.call_count == 1
|
assert create_engine_mock.call_count == 1
|
||||||
assert create_engine_mock.mock_calls[0][1][0] == 'sqlite:///tmp/freqtrade2_test.sqlite'
|
assert create_engine_mock.mock_calls[0][1][0] == 'sqlite:///tmp/freqtrade2_test.sqlite'
|
||||||
|
|
||||||
|
|
||||||
def test_init_invalid_db_url(default_conf, mocker):
|
def test_init_invalid_db_url(default_conf):
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
|
|
||||||
# Update path to a value other than default, but still in-memory
|
# Update path to a value other than default, but still in-memory
|
||||||
conf.update({'db_url': 'unknown:///some.url'})
|
conf.update({'db_url': 'unknown:///some.url'})
|
||||||
mocker.patch.dict('freqtrade.persistence._CONF', conf)
|
|
||||||
|
|
||||||
with pytest.raises(OperationalException, match=r'.*no valid database URL*'):
|
with pytest.raises(OperationalException, match=r'.*no valid database URL*'):
|
||||||
init(conf)
|
init(conf)
|
||||||
|
|
||||||
@ -53,7 +48,6 @@ def test_init_prod_db(default_conf, mocker):
|
|||||||
conf.update({'db_url': constants.DEFAULT_DB_PROD_URL})
|
conf.update({'db_url': constants.DEFAULT_DB_PROD_URL})
|
||||||
|
|
||||||
create_engine_mock = mocker.patch('freqtrade.persistence.create_engine', MagicMock())
|
create_engine_mock = mocker.patch('freqtrade.persistence.create_engine', MagicMock())
|
||||||
mocker.patch.dict('freqtrade.persistence._CONF', conf)
|
|
||||||
|
|
||||||
init(conf)
|
init(conf)
|
||||||
assert create_engine_mock.call_count == 1
|
assert create_engine_mock.call_count == 1
|
||||||
@ -66,7 +60,6 @@ def test_init_dryrun_db(default_conf, mocker):
|
|||||||
conf.update({'db_url': constants.DEFAULT_DB_DRYRUN_URL})
|
conf.update({'db_url': constants.DEFAULT_DB_DRYRUN_URL})
|
||||||
|
|
||||||
create_engine_mock = mocker.patch('freqtrade.persistence.create_engine', MagicMock())
|
create_engine_mock = mocker.patch('freqtrade.persistence.create_engine', MagicMock())
|
||||||
mocker.patch.dict('freqtrade.persistence._CONF', conf)
|
|
||||||
|
|
||||||
init(conf)
|
init(conf)
|
||||||
assert create_engine_mock.call_count == 1
|
assert create_engine_mock.call_count == 1
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
ccxt==1.14.201
|
ccxt==1.14.256
|
||||||
SQLAlchemy==1.2.8
|
SQLAlchemy==1.2.8
|
||||||
python-telegram-bot==10.1.0
|
python-telegram-bot==10.1.0
|
||||||
arrow==0.12.1
|
arrow==0.12.1
|
||||||
@ -12,7 +12,7 @@ scipy==1.1.0
|
|||||||
jsonschema==2.6.0
|
jsonschema==2.6.0
|
||||||
numpy==1.14.5
|
numpy==1.14.5
|
||||||
TA-Lib==0.4.17
|
TA-Lib==0.4.17
|
||||||
pytest==3.6.1
|
pytest==3.6.2
|
||||||
pytest-mock==1.10.0
|
pytest-mock==1.10.0
|
||||||
pytest-cov==2.5.1
|
pytest-cov==2.5.1
|
||||||
hyperopt==0.1
|
hyperopt==0.1
|
||||||
@ -22,4 +22,4 @@ tabulate==0.8.2
|
|||||||
coinmarketcap==5.0.3
|
coinmarketcap==5.0.3
|
||||||
|
|
||||||
# Required for plotting data
|
# Required for plotting data
|
||||||
#plotly==2.3.0
|
#plotly==2.7.0
|
||||||
|
@ -6,7 +6,8 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import arrow
|
import arrow
|
||||||
|
|
||||||
from freqtrade import (exchange, arguments, misc)
|
from freqtrade import (arguments, misc)
|
||||||
|
from freqtrade.exchange import Exchange
|
||||||
|
|
||||||
DEFAULT_DL_PATH = 'user_data/data'
|
DEFAULT_DL_PATH = 'user_data/data'
|
||||||
|
|
||||||
@ -39,16 +40,21 @@ if args.days:
|
|||||||
|
|
||||||
print(f'About to download pairs: {PAIRS} to {dl_path}')
|
print(f'About to download pairs: {PAIRS} to {dl_path}')
|
||||||
|
|
||||||
|
|
||||||
# Init exchange
|
# Init exchange
|
||||||
exchange._API = exchange.init_ccxt({'key': '',
|
exchange = Exchange({'key': '',
|
||||||
'secret': '',
|
'secret': '',
|
||||||
'name': args.exchange})
|
'stake_currency': '',
|
||||||
|
'dry_run': True,
|
||||||
|
'exchange': {
|
||||||
|
'name': args.exchange,
|
||||||
|
'pair_whitelist': []
|
||||||
|
}
|
||||||
|
})
|
||||||
pairs_not_available = []
|
pairs_not_available = []
|
||||||
# Make sure API markets is initialized
|
|
||||||
exchange._API.load_markets()
|
|
||||||
|
|
||||||
for pair in PAIRS:
|
for pair in PAIRS:
|
||||||
if pair not in exchange._API.markets:
|
if pair not in exchange._api.markets:
|
||||||
pairs_not_available.append(pair)
|
pairs_not_available.append(pair)
|
||||||
print(f"skipping pair {pair}")
|
print(f"skipping pair {pair}")
|
||||||
continue
|
continue
|
||||||
|
@ -35,10 +35,10 @@ from plotly import tools
|
|||||||
from plotly.offline import plot
|
from plotly.offline import plot
|
||||||
|
|
||||||
import freqtrade.optimize as optimize
|
import freqtrade.optimize as optimize
|
||||||
from freqtrade import exchange
|
|
||||||
from freqtrade import persistence
|
from freqtrade import persistence
|
||||||
from freqtrade.analyze import Analyze
|
from freqtrade.analyze import Analyze
|
||||||
from freqtrade.arguments import Arguments
|
from freqtrade.arguments import Arguments
|
||||||
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.optimize.backtesting import setup_configuration
|
from freqtrade.optimize.backtesting import setup_configuration
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
|
|||||||
# Load the strategy
|
# Load the strategy
|
||||||
try:
|
try:
|
||||||
analyze = Analyze(_CONF)
|
analyze = Analyze(_CONF)
|
||||||
exchange.init(_CONF)
|
exchange = Exchange(_CONF)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logger.critical(
|
logger.critical(
|
||||||
'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"',
|
'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"',
|
||||||
|
Loading…
Reference in New Issue
Block a user