From 9be98cd8f77c503f303d021eb62b531c9be698b3 Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 23 May 2018 13:15:03 +0300 Subject: [PATCH 01/11] Add ability to set unlimited stake_amount --- docs/configuration.md | 2 +- freqtrade/constants.py | 5 +- freqtrade/freqtradebot.py | 25 ++++-- freqtrade/optimize/backtesting.py | 5 +- freqtrade/tests/test_freqtradebot.py | 130 ++++++++++++++++++++++----- 5 files changed, 137 insertions(+), 30 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 3d36947c3..e75775f3f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -16,7 +16,7 @@ The table below will list all configuration parameters. |----------|---------|----------|-------------| | `max_open_trades` | 3 | Yes | Number of trades open your bot will have. | `stake_currency` | BTC | Yes | Crypto-currency used for trading. -| `stake_amount` | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. +| `stake_amount` | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. 'unlimited' is used to allow a bot to use all avaliable balance. | `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | No | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes | `fiat_display_currency` | USD | Yes | Fiat currency used to show your profits. More information below. | `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode. diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 7731ea610..20c8a2287 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -32,7 +32,10 @@ CONF_SCHEMA = { 'max_open_trades': {'type': 'integer', 'minimum': 0}, 'ticker_interval': {'type': 'string', 'enum': list(TICKER_INTERVAL_MINUTES.keys())}, 'stake_currency': {'type': 'string', 'enum': ['BTC', 'ETH', 'USDT']}, - 'stake_amount': {'type': 'number', 'minimum': 0.0005}, + 'stake_amount': {'anyOf': [ + {'type': 'integer', 'minimum': 0.0005}, + {'constant': 'unlimited'} + ]}, 'fiat_display_currency': {'type': 'string', 'enum': ['AUD', 'BRL', 'CAD', 'CHF', 'CLP', 'CNY', 'CZK', 'DKK', 'EUR', 'GBP', 'HKD', 'HUF', diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7c955d423..af3f9287e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -255,6 +255,24 @@ class FreqtradeBot(object): balance = self.config['bid_strategy']['ask_last_balance'] return ticker['ask'] + balance * (ticker['last'] - ticker['ask']) + def _get_trade_stake_amount(self) -> float: + stake_amount = self.config['stake_amount'] + avaliable_amount = exchange.get_balance(self.config['stake_currency']) + + if stake_amount == 'unlimited': + open_trades = len(Trade.query.filter(Trade.is_open.is_(True)).all()) + if open_trades == self.config['max_open_trades']: + return 0 + return avaliable_amount / (self.config['max_open_trades'] - open_trades) + + # Check if stake_amount is fulfilled + if avaliable_amount < stake_amount: + raise DependencyException( + 'stake amount is not fulfilled (currency={})'.format(self.config['stake_currency']) + ) + + return stake_amount + def create_trade(self) -> bool: """ Checks the implemented trading indicator(s) for a randomly picked pair, @@ -263,19 +281,14 @@ class FreqtradeBot(object): :param interval: Ticker interval used for Analyze :return: True if a trade object has been created and persisted, False otherwise """ - stake_amount = self.config['stake_amount'] interval = self.analyze.get_ticker_interval() + stake_amount = self._get_trade_stake_amount() logger.info( 'Checking buy signals to create a new trade with stake_amount: %f ...', stake_amount ) whitelist = copy.deepcopy(self.config['exchange']['pair_whitelist']) - # Check if stake_amount is fulfilled - if exchange.get_balance(self.config['stake_currency']) < stake_amount: - raise DependencyException( - 'stake amount is not fulfilled (currency={})'.format(self.config['stake_currency']) - ) # Remove currently opened and latest pairs from whitelist for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 376730d0f..eb6b25e02 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -13,7 +13,7 @@ from pandas import DataFrame from tabulate import tabulate import freqtrade.optimize as optimize -from freqtrade import exchange +from freqtrade import exchange, DependencyException from freqtrade.analyze import Analyze from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration @@ -296,6 +296,9 @@ def setup_configuration(args: Namespace) -> Dict[str, Any]: config['exchange']['key'] = '' config['exchange']['secret'] = '' + if config['stake_amount'] == 'unlimited': + raise DependencyException('stake amount could not be "unlimited" for backtesting') + return config diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index ebabc0187..d9d009a47 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -220,6 +220,115 @@ def test_refresh_whitelist() -> None: pass +def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: + """ + Test get_trade_stake_amount() method + """ + patch_get_signal(mocker) + patch_RPCManager(mocker) + patch_coinmarketcap(mocker) + mocker.patch.multiple( + 'freqtrade.freqtradebot.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker, + buy=MagicMock(return_value={'id': limit_buy_order['id']}), + get_fee=fee, + ) + + conf = deepcopy(default_conf) + + freqtrade = FreqtradeBot(conf, create_engine('sqlite://')) + + result = freqtrade._get_trade_stake_amount() + assert(result == conf['stake_amount']) + + +def test_get_trade_stake_amount_no_stake_amount(default_conf, + ticker, + limit_buy_order, + fee, + mocker) -> None: + """ + Test get_trade_stake_amount() method + """ + patch_get_signal(mocker) + patch_RPCManager(mocker) + patch_coinmarketcap(mocker) + mocker.patch.multiple( + 'freqtrade.freqtradebot.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker, + buy=MagicMock(return_value={'id': limit_buy_order['id']}), + get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5), + get_fee=fee, + ) + + conf = deepcopy(default_conf) + + freqtrade = FreqtradeBot(conf, create_engine('sqlite://')) + + with pytest.raises(DependencyException, match=r'.*stake amount.*'): + freqtrade._get_trade_stake_amount() + + +def test_get_trade_stake_amount_unlimited_amount(default_conf, + ticker, + limit_buy_order, + fee, + mocker) -> None: + """ + Test get_trade_stake_amount() method + """ + patch_get_signal(mocker) + patch_RPCManager(mocker) + patch_coinmarketcap(mocker) + mocker.patch.multiple( + 'freqtrade.freqtradebot.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker, + buy=MagicMock(return_value={'id': limit_buy_order['id']}), + get_balance=MagicMock(return_value=default_conf['stake_amount']), + get_fee=fee, + ) + + conf = deepcopy(default_conf) + conf['stake_amount'] = 'unlimited' + conf['max_open_trades'] = 2 + + freqtrade = FreqtradeBot(conf, create_engine('sqlite://')) + + # no open trades, order amount should be 'balance / max_open_trades' + result = freqtrade._get_trade_stake_amount() + assert(result == default_conf['stake_amount'] / conf['max_open_trades']) + + # create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)' + freqtrade.create_trade() + + result = freqtrade._get_trade_stake_amount() + assert(result == default_conf['stake_amount'] / (conf['max_open_trades'] - 1)) + + +def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: + """ + Test create_trade() method + """ + patch_get_signal(mocker) + patch_RPCManager(mocker) + patch_coinmarketcap(mocker) + mocker.patch.multiple( + 'freqtrade.freqtradebot.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker, + buy=MagicMock(return_value={'id': limit_buy_order['id']}), + get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5), + get_fee=fee, + ) + freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) + + with pytest.raises(DependencyException, match=r'.*stake amount.*'): + freqtrade.create_trade() + + def test_create_trade(default_conf, ticker, limit_buy_order, fee, mocker) -> None: """ Test create_trade() method @@ -281,27 +390,6 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order, fee, assert rate * amount >= conf['stake_amount'] -def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: - """ - Test create_trade() method - """ - patch_get_signal(mocker) - patch_RPCManager(mocker) - patch_coinmarketcap(mocker) - mocker.patch.multiple( - 'freqtrade.freqtradebot.exchange', - validate_pairs=MagicMock(), - get_ticker=ticker, - buy=MagicMock(return_value={'id': limit_buy_order['id']}), - get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5), - get_fee=fee, - ) - freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) - - with pytest.raises(DependencyException, match=r'.*stake amount.*'): - freqtrade.create_trade() - - def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, mocker) -> None: """ Test create_trade() method From cf5d691950c99c8c055048e1b5aa4150b136a765 Mon Sep 17 00:00:00 2001 From: Anton Date: Fri, 25 May 2018 00:46:08 +0300 Subject: [PATCH 02/11] Clean the tests --- freqtrade/tests/test_freqtradebot.py | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index d9d009a47..e47bfd007 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -224,23 +224,18 @@ def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mock """ Test get_trade_stake_amount() method """ - patch_get_signal(mocker) + patch_RPCManager(mocker) - patch_coinmarketcap(mocker) mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker, - buy=MagicMock(return_value={'id': limit_buy_order['id']}), - get_fee=fee, + get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2) ) - conf = deepcopy(default_conf) - - freqtrade = FreqtradeBot(conf, create_engine('sqlite://')) + freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) result = freqtrade._get_trade_stake_amount() - assert(result == conf['stake_amount']) + assert(result == default_conf['stake_amount']) def test_get_trade_stake_amount_no_stake_amount(default_conf, @@ -251,21 +246,14 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf, """ Test get_trade_stake_amount() method """ - patch_get_signal(mocker) patch_RPCManager(mocker) - patch_coinmarketcap(mocker) mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker, - buy=MagicMock(return_value={'id': limit_buy_order['id']}), - get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5), - get_fee=fee, + get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5) ) - conf = deepcopy(default_conf) - - freqtrade = FreqtradeBot(conf, create_engine('sqlite://')) + freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) with pytest.raises(DependencyException, match=r'.*stake amount.*'): freqtrade._get_trade_stake_amount() From 3427c7eb540ac7b73e7ed52578ec1c281b4b2500 Mon Sep 17 00:00:00 2001 From: Anton Date: Fri, 25 May 2018 17:04:08 +0300 Subject: [PATCH 03/11] Use constants --- docs/configuration.md | 2 +- freqtrade/constants.py | 3 ++- freqtrade/freqtradebot.py | 2 +- freqtrade/optimize/backtesting.py | 7 ++++--- freqtrade/tests/test_freqtradebot.py | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index e75775f3f..5b527ddf7 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -16,7 +16,7 @@ The table below will list all configuration parameters. |----------|---------|----------|-------------| | `max_open_trades` | 3 | Yes | Number of trades open your bot will have. | `stake_currency` | BTC | Yes | Crypto-currency used for trading. -| `stake_amount` | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. 'unlimited' is used to allow a bot to use all avaliable balance. +| `stake_amount` | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to 'unlimited' to allow the bot to use all avaliable balance. | `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | No | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes | `fiat_display_currency` | USD | Yes | Fiat currency used to show your profits. More information below. | `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode. diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 20c8a2287..1039b9da4 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -9,6 +9,7 @@ TICKER_INTERVAL = 5 # min HYPEROPT_EPOCH = 100 # epochs RETRY_TIMEOUT = 30 # sec DEFAULT_STRATEGY = 'DefaultStrategy' +UNLIMITED_STAKE_AMOUNT = 'unlimited' TICKER_INTERVAL_MINUTES = { '1m': 1, @@ -34,7 +35,7 @@ CONF_SCHEMA = { 'stake_currency': {'type': 'string', 'enum': ['BTC', 'ETH', 'USDT']}, 'stake_amount': {'anyOf': [ {'type': 'integer', 'minimum': 0.0005}, - {'constant': 'unlimited'} + {'constant': UNLIMITED_STAKE_AMOUNT} ]}, 'fiat_display_currency': {'type': 'string', 'enum': ['AUD', 'BRL', 'CAD', 'CHF', 'CLP', 'CNY', 'CZK', 'DKK', diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index af3f9287e..431a146dd 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -259,7 +259,7 @@ class FreqtradeBot(object): stake_amount = self.config['stake_amount'] avaliable_amount = exchange.get_balance(self.config['stake_currency']) - if stake_amount == 'unlimited': + if stake_amount == constants.UNLIMITED_STAKE_AMOUNT: open_trades = len(Trade.query.filter(Trade.is_open.is_(True)).all()) if open_trades == self.config['max_open_trades']: return 0 diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index eb6b25e02..520765853 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -13,7 +13,7 @@ from pandas import DataFrame from tabulate import tabulate import freqtrade.optimize as optimize -from freqtrade import exchange, DependencyException +from freqtrade import exchange, constants, DependencyException from freqtrade.analyze import Analyze from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration @@ -296,8 +296,9 @@ def setup_configuration(args: Namespace) -> Dict[str, Any]: config['exchange']['key'] = '' config['exchange']['secret'] = '' - if config['stake_amount'] == 'unlimited': - raise DependencyException('stake amount could not be "unlimited" for backtesting') + if config['stake_amount'] == constants.UNLIMITED_STAKE_AMOUNT: + raise DependencyException('stake amount could not be "%s" for backtesting' % + constants.UNLIMITED_STAKE_AMOUNT) return config diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index e47bfd007..eeab3bf37 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -15,7 +15,7 @@ import pytest import requests from sqlalchemy import create_engine -from freqtrade import DependencyException, OperationalException, TemporaryError +from freqtrade import constants, DependencyException, OperationalException, TemporaryError from freqtrade.freqtradebot import FreqtradeBot from freqtrade.persistence import Trade from freqtrade.state import State @@ -280,7 +280,7 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ) conf = deepcopy(default_conf) - conf['stake_amount'] = 'unlimited' + conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT conf['max_open_trades'] = 2 freqtrade = FreqtradeBot(conf, create_engine('sqlite://')) From daa9c0c02607cf5db0a860b133ddfbf91fa6dde9 Mon Sep 17 00:00:00 2001 From: Anton Date: Mon, 4 Jun 2018 01:48:26 +0300 Subject: [PATCH 04/11] Fix review comments --- docs/configuration.md | 7 ++++++ freqtrade/constants.py | 9 +++---- freqtrade/tests/optimize/test_backtesting.py | 25 +++++++++++++++++++- freqtrade/tests/test_configuration.py | 12 ++++++++++ freqtrade/tests/test_freqtradebot.py | 6 +++++ 5 files changed, 54 insertions(+), 5 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 5b527ddf7..0bb417338 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -42,6 +42,13 @@ The table below will list all configuration parameters. The definition of each config parameters is in [misc.py](https://github.com/gcarq/freqtrade/blob/develop/freqtrade/misc.py#L205). +### Understand stake_amount +`stake_amount` is an amount of crypto-currency your bot will use for each trade. +The minimal value is 0.0005. If there is not enough crypto-currency in +the account an exception is generated. +To allow the bot to trade all the avaliable `stake_currency` in your account set `stake_amount` = `unlimited`. +In this case a trade amount is calclulated as `currency_balanse / (max_open_trades - current_open_trades)`. + ### Understand minimal_roi `minimal_roi` is a JSON object where the key is a duration in minutes and the value is the minimum ROI in percent. diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 1039b9da4..ea4fc478c 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -33,10 +33,11 @@ CONF_SCHEMA = { 'max_open_trades': {'type': 'integer', 'minimum': 0}, 'ticker_interval': {'type': 'string', 'enum': list(TICKER_INTERVAL_MINUTES.keys())}, 'stake_currency': {'type': 'string', 'enum': ['BTC', 'ETH', 'USDT']}, - 'stake_amount': {'anyOf': [ - {'type': 'integer', 'minimum': 0.0005}, - {'constant': UNLIMITED_STAKE_AMOUNT} - ]}, + 'stake_amount': { + "type": ["number", "string"], + "minimum": 0.0005, + "pattern": UNLIMITED_STAKE_AMOUNT + }, 'fiat_display_currency': {'type': 'string', 'enum': ['AUD', 'BRL', 'CAD', 'CHF', 'CLP', 'CNY', 'CZK', 'DKK', 'EUR', 'GBP', 'HKD', 'HUF', diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index f17a0115e..4d02cfe5f 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -3,6 +3,7 @@ import json import math import random +import pytest from copy import deepcopy from typing import List from unittest.mock import MagicMock @@ -11,7 +12,7 @@ import numpy as np import pandas as pd from arrow import Arrow -from freqtrade import optimize +from freqtrade import optimize, constants, DependencyException from freqtrade.analyze import Analyze from freqtrade.arguments import Arguments from freqtrade.optimize.backtesting import Backtesting, start, setup_configuration @@ -261,6 +262,28 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non ) +def test_setup_configuration_unlimited_stake_amount(mocker, default_conf, caplog) -> None: + """ + Test setup_configuration() function + """ + + conf = deepcopy(default_conf) + conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT + + mocker.patch('freqtrade.configuration.open', mocker.mock_open( + read_data=json.dumps(conf) + )) + + args = [ + '--config', 'config.json', + '--strategy', 'DefaultStrategy', + 'backtesting' + ] + + with pytest.raises(DependencyException, match=r'.*stake amount.*'): + setup_configuration(get_args(args)) + + def test_start(mocker, fee, default_conf, caplog) -> None: """ Test start() function diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index d27405d91..fb63ca2d9 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -53,6 +53,18 @@ def test_load_config_missing_attributes(default_conf) -> None: configuration._validate_config(conf) +def test_load_config_incorrect_stake_amount(default_conf) -> None: + """ + Test the configuration validator with a missing attribute + """ + conf = deepcopy(default_conf) + conf['stake_amount'] = 'fake' + + with pytest.raises(ValidationError, match=r'.*\'fake\' does not match \'unlimited\'.*'): + configuration = Configuration([]) + configuration._validate_config(conf) + + def test_load_config_file(default_conf, mocker, caplog) -> None: """ Test Configuration._load_config_file() method diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index eeab3bf37..d91dd4720 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -295,6 +295,12 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, result = freqtrade._get_trade_stake_amount() assert(result == default_conf['stake_amount'] / (conf['max_open_trades'] - 1)) + # create 2 trades, order amount should be 0 + freqtrade.create_trade() + + result = freqtrade._get_trade_stake_amount() + assert(result == 0) + def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: """ From 3030bf9778f4d23d2237432e5c840c6e5c74f374 Mon Sep 17 00:00:00 2001 From: Anton Date: Mon, 4 Jun 2018 01:52:54 +0300 Subject: [PATCH 05/11] Fix types --- freqtrade/tests/test_configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index df4d3f5ef..98adf19e9 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -62,7 +62,7 @@ def test_load_config_incorrect_stake_amount(default_conf) -> None: conf['stake_amount'] = 'fake' with pytest.raises(ValidationError, match=r'.*\'fake\' does not match \'unlimited\'.*'): - configuration = Configuration([]) + configuration = Configuration(Namespace()) configuration._validate_config(conf) From 12d8a8b1a35b463fc90a0af0c9ca6d1d0560538e Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 6 Jun 2018 00:14:28 +0300 Subject: [PATCH 06/11] Fix review comments --- freqtrade/arguments.py | 10 ++++------ freqtrade/freqtradebot.py | 6 ++++-- freqtrade/tests/test_freqtradebot.py | 6 ++++++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index 97c3d8cb2..1ae805ca5 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -239,17 +239,15 @@ class Arguments(object): stop: Optional[int] = None if stype[0]: starts = rvals[index] - if stype[0] == 'date': - start = int(starts) if len(starts) == 10 \ - else arrow.get(starts, 'YYYYMMDD').timestamp + if stype[0] == 'date' and len(starts) == 8: + start = arrow.get(starts, 'YYYYMMDD').timestamp else: start = int(starts) index += 1 if stype[1]: stops = rvals[index] - if stype[1] == 'date': - stop = int(stops) if len(stops) == 10 \ - else arrow.get(stops, 'YYYYMMDD').timestamp + if stype[1] == 'date' and len(stops) == 8: + stop = arrow.get(stops, 'YYYYMMDD').timestamp else: stop = int(stops) return stype, start, stop diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index b1928a4d5..49c179907 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -258,14 +258,16 @@ class FreqtradeBot(object): if stake_amount == constants.UNLIMITED_STAKE_AMOUNT: open_trades = len(Trade.query.filter(Trade.is_open.is_(True)).all()) - if open_trades == self.config['max_open_trades']: + if open_trades >= self.config['max_open_trades']: return 0 return avaliable_amount / (self.config['max_open_trades'] - open_trades) # Check if stake_amount is fulfilled if avaliable_amount < stake_amount: raise DependencyException( - 'stake amount is not fulfilled (currency={})'.format(self.config['stake_currency']) + 'Available balance(%f %s) is lower than stake amount(%f %s)' % ( + avaliable_amount, self.config['stake_currency'], + stake_amount, self.config['stake_currency']) ) return stake_amount diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index d91dd4720..581e76406 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -301,6 +301,12 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, result = freqtrade._get_trade_stake_amount() assert(result == 0) + # set max_open_trades = 0, so do not trade + conf['max_open_trades'] = 0 + freqtrade = FreqtradeBot(conf, create_engine('sqlite://')) + result = freqtrade._get_trade_stake_amount() + assert(result == 0) + def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: """ From b1b87731b1a97de0fd8d2cd262c22c80fed87f30 Mon Sep 17 00:00:00 2001 From: Anton Date: Fri, 8 Jun 2018 00:54:46 +0300 Subject: [PATCH 07/11] Support case when _get_trade_stake_amount returns None --- freqtrade/freqtradebot.py | 6 ++++- freqtrade/tests/test_freqtradebot.py | 36 +++++++++++++++------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 9392f5b39..70c74755e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -257,7 +257,8 @@ class FreqtradeBot(object): if stake_amount == constants.UNLIMITED_STAKE_AMOUNT: open_trades = len(Trade.query.filter(Trade.is_open.is_(True)).all()) if open_trades >= self.config['max_open_trades']: - return 0 + logger.warning('Can\'t open a new trade: max number of trades is reached') + return None return avaliable_amount / (self.config['max_open_trades'] - open_trades) # Check if stake_amount is fulfilled @@ -281,6 +282,9 @@ class FreqtradeBot(object): interval = self.analyze.get_ticker_interval() stake_amount = self._get_trade_stake_amount() + if not stake_amount: + return False + logger.info( 'Checking buy signals to create a new trade with stake_amount: %f ...', stake_amount diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index f7e84ebdd..a90b1f983 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -231,7 +231,7 @@ def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mock get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2) ) - freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) + freqtrade = FreqtradeBot(default_conf) result = freqtrade._get_trade_stake_amount() assert(result == default_conf['stake_amount']) @@ -252,7 +252,7 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf, get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5) ) - freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) + freqtrade = FreqtradeBot(default_conf) with pytest.raises(DependencyException, match=r'.*stake amount.*'): freqtrade._get_trade_stake_amount() @@ -282,29 +282,29 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT conf['max_open_trades'] = 2 - freqtrade = FreqtradeBot(conf, create_engine('sqlite://')) + freqtrade = FreqtradeBot(conf) # no open trades, order amount should be 'balance / max_open_trades' result = freqtrade._get_trade_stake_amount() - assert(result == default_conf['stake_amount'] / conf['max_open_trades']) + assert result == default_conf['stake_amount'] / conf['max_open_trades'] # create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)' freqtrade.create_trade() result = freqtrade._get_trade_stake_amount() - assert(result == default_conf['stake_amount'] / (conf['max_open_trades'] - 1)) + assert result == default_conf['stake_amount'] / (conf['max_open_trades'] - 1) - # create 2 trades, order amount should be 0 + # create 2 trades, order amount should be None freqtrade.create_trade() result = freqtrade._get_trade_stake_amount() - assert(result == 0) + assert result is None - # set max_open_trades = 0, so do not trade + # set max_open_trades = None, so do not trade conf['max_open_trades'] = 0 - freqtrade = FreqtradeBot(conf, create_engine('sqlite://')) + freqtrade = FreqtradeBot(conf) result = freqtrade._get_trade_stake_amount() - assert(result == 0) + assert result is None def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: @@ -322,7 +322,7 @@ def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, fee get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5), get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) + freqtrade = FreqtradeBot(default_conf) with pytest.raises(DependencyException, match=r'.*stake amount.*'): freqtrade.create_trade() @@ -389,7 +389,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order, fee, assert rate * amount >= conf['stake_amount'] -def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: +def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order, fee, mocker) -> None: """ Test create_trade() method """ @@ -401,13 +401,17 @@ def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, fee validate_pairs=MagicMock(), get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), - get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5), + get_balance=MagicMock(return_value=default_conf['stake_amount']), get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + conf = deepcopy(default_conf) + conf['max_open_trades'] = 0 + conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT - with pytest.raises(DependencyException, match=r'.*stake amount.*'): - freqtrade.create_trade() + freqtrade = FreqtradeBot(conf) + + assert freqtrade.create_trade() is False + assert freqtrade._get_trade_stake_amount() is None def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, mocker) -> None: From 367601518481092ae92d320e0eeabaa4b2bf7b8a Mon Sep 17 00:00:00 2001 From: Anton Date: Mon, 11 Jun 2018 16:21:57 +0300 Subject: [PATCH 08/11] Fix check --- freqtrade/freqtradebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 70c74755e..8c8017d0b 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -7,7 +7,7 @@ import logging import time import traceback from datetime import datetime -from typing import Dict, List, Optional, Any, Callable +from typing import Dict, List, Optional, Any, Callable, Optional import arrow import requests @@ -239,7 +239,7 @@ class FreqtradeBot(object): return final_list - def get_target_bid(self, ticker: Dict[str, float]) -> float: + def get_target_bid(self, ticker: Dict[str, float]) -> Optional[float]: """ Calculates bid target between current ask price and last price :param ticker: Ticker to use for getting Ask and Last Price From 90025d0ac437fafe261df6d588f0732f3698bbb4 Mon Sep 17 00:00:00 2001 From: Anton Date: Mon, 11 Jun 2018 16:38:10 +0300 Subject: [PATCH 09/11] Fix check --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ab22b2826..1e32aecdf 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -7,7 +7,7 @@ import logging import time import traceback from datetime import datetime -from typing import Dict, List, Optional, Any, Callable, Optional +from typing import Dict, List, Optional, Any, Callable import arrow import requests From 708320318c5a340d70a161177725f670764d0bb9 Mon Sep 17 00:00:00 2001 From: Anton Date: Tue, 12 Jun 2018 01:05:43 +0300 Subject: [PATCH 10/11] Check minimal amount --- freqtrade/freqtradebot.py | 12 +++++++++--- freqtrade/tests/test_freqtradebot.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 1e32aecdf..1477a931e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -234,7 +234,7 @@ class FreqtradeBot(object): return final_list - def get_target_bid(self, ticker: Dict[str, float]) -> Optional[float]: + def get_target_bid(self, ticker: Dict[str, float]) -> float: """ Calculates bid target between current ask price and last price :param ticker: Ticker to use for getting Ask and Last Price @@ -245,7 +245,7 @@ class FreqtradeBot(object): balance = self.config['bid_strategy']['ask_last_balance'] return ticker['ask'] + balance * (ticker['last'] - ticker['ask']) - def _get_trade_stake_amount(self) -> float: + def _get_trade_stake_amount(self) -> Optional[float]: stake_amount = self.config['stake_amount'] avaliable_amount = exchange.get_balance(self.config['stake_currency']) @@ -254,7 +254,13 @@ class FreqtradeBot(object): if open_trades >= self.config['max_open_trades']: logger.warning('Can\'t open a new trade: max number of trades is reached') return None - return avaliable_amount / (self.config['max_open_trades'] - open_trades) + trade_stake_amount = avaliable_amount / (self.config['max_open_trades'] - open_trades) + if trade_stake_amount < 0.0005: + raise DependencyException( + 'Available balance(%f %s) is lower than minimal' % ( + avaliable_amount, self.config['stake_currency']) + ) + return trade_stake_amount # Check if stake_amount is fulfilled if avaliable_amount < stake_amount: diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 314d880b6..80b66735d 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -249,11 +249,21 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf, get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5) ) + # test defined stake amount freqtrade = FreqtradeBot(default_conf) with pytest.raises(DependencyException, match=r'.*stake amount.*'): freqtrade._get_trade_stake_amount() + # test UNLIMITED_STAKE_AMOUNT + conf = deepcopy(default_conf) + conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT + conf['max_open_trades'] = 2 + freqtrade = FreqtradeBot(conf) + + with pytest.raises(DependencyException, match=r'.*is lower than minimal.*'): + freqtrade._get_trade_stake_amount() + def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, From eb909068c5c5d37b8a574b722529f47aab276ffc Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 17 Jun 2018 02:23:12 +0300 Subject: [PATCH 11/11] Add minimal pair stake amount check --- freqtrade/analyze.py | 7 + freqtrade/freqtradebot.py | 46 ++++- freqtrade/tests/conftest.py | 90 +++++++- freqtrade/tests/rpc/test_rpc.py | 36 ++-- freqtrade/tests/rpc/test_rpc_telegram.py | 46 +++-- freqtrade/tests/test_freqtradebot.py | 251 ++++++++++++++++++----- 6 files changed, 388 insertions(+), 88 deletions(-) diff --git a/freqtrade/analyze.py b/freqtrade/analyze.py index f18ae291c..2ef3a7515 100644 --- a/freqtrade/analyze.py +++ b/freqtrade/analyze.py @@ -98,6 +98,13 @@ class Analyze(object): """ return self.strategy.ticker_interval + def get_stoploss(self) -> float: + """ + Return stoploss to use + :return: Strategy stoploss value to use + """ + return self.strategy.stoploss + def analyze_ticker(self, ticker_history: List[Dict]) -> DataFrame: """ Parses the given ticker history and returns a populated DataFrame diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 1477a931e..a5612371d 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -254,13 +254,7 @@ class FreqtradeBot(object): if open_trades >= self.config['max_open_trades']: logger.warning('Can\'t open a new trade: max number of trades is reached') return None - trade_stake_amount = avaliable_amount / (self.config['max_open_trades'] - open_trades) - if trade_stake_amount < 0.0005: - raise DependencyException( - 'Available balance(%f %s) is lower than minimal' % ( - avaliable_amount, self.config['stake_currency']) - ) - return trade_stake_amount + return avaliable_amount / (self.config['max_open_trades'] - open_trades) # Check if stake_amount is fulfilled if avaliable_amount < stake_amount: @@ -272,6 +266,34 @@ class FreqtradeBot(object): return stake_amount + def _get_min_pair_stake_amount(self, pair: str, price: float) -> Optional[float]: + markets = exchange.get_markets() + markets = [m for m in markets if m['symbol'] == pair] + if not markets: + raise ValueError(f'Can\'t get market information for symbol {pair}') + + market = markets[0] + + if 'limits' not in market: + return None + + min_stake_amounts = [] + if 'cost' in market['limits'] and 'min' in market['limits']['cost']: + min_stake_amounts.append(market['limits']['cost']['min']) + + if 'amount' in market['limits'] and 'min' in market['limits']['amount']: + min_stake_amounts.append(market['limits']['amount']['min'] * price) + + if not min_stake_amounts: + return None + + amount_reserve_percent = 1 - 0.05 # reserve 5% + stoploss + if self.analyze.get_stoploss() is not None: + amount_reserve_percent += self.analyze.get_stoploss() + # it should not be more than 50% + amount_reserve_percent = max(amount_reserve_percent, 0.5) + return min(min_stake_amounts)/amount_reserve_percent + def create_trade(self) -> bool: """ Checks the implemented trading indicator(s) for a randomly picked pair, @@ -314,8 +336,16 @@ class FreqtradeBot(object): pair_url = exchange.get_pair_detail_url(pair) # Calculate amount buy_limit = self.get_target_bid(exchange.get_ticker(pair)) - amount = stake_amount / 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: + logger.warning( + f'Can\'t open a new trade for {pair_s}: stake amount' + f' is too small ({stake_amount} < {min_stake_amount})' + ) + return False + + amount = stake_amount / buy_limit order_id = exchange.buy(pair, buy_limit, amount)['id'] stake_amount_fiat = self.fiat_converter.convert_amount( diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 1311687b7..5eed0a08f 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -174,7 +174,10 @@ def markets(): 'max': 1000, }, 'price': 500000, - 'cost': 500000, + 'cost': { + 'min': 1, + 'max': 500000, + }, }, 'info': '', }, @@ -196,7 +199,10 @@ def markets(): 'max': 1000, }, 'price': 500000, - 'cost': 500000, + 'cost': { + 'min': 1, + 'max': 500000, + }, }, 'info': '', }, @@ -218,7 +224,85 @@ def markets(): 'max': 1000, }, 'price': 500000, - 'cost': 500000, + 'cost': { + 'min': 1, + 'max': 500000, + }, + }, + 'info': '', + }, + { + 'id': 'ltcbtc', + 'symbol': 'LTC/BTC', + 'base': 'LTC', + 'quote': 'BTC', + 'active': False, + 'precision': { + 'price': 8, + 'amount': 8, + 'cost': 8, + }, + 'lot': 0.00000001, + 'limits': { + 'amount': { + 'min': 0.01, + 'max': 1000, + }, + 'price': 500000, + 'cost': { + 'min': 1, + 'max': 500000, + }, + }, + 'info': '', + }, + { + 'id': 'xrpbtc', + 'symbol': 'XRP/BTC', + 'base': 'XRP', + 'quote': 'BTC', + 'active': False, + 'precision': { + 'price': 8, + 'amount': 8, + 'cost': 8, + }, + 'lot': 0.00000001, + 'limits': { + 'amount': { + 'min': 0.01, + 'max': 1000, + }, + 'price': 500000, + 'cost': { + 'min': 1, + 'max': 500000, + }, + }, + 'info': '', + }, + { + 'id': 'neobtc', + 'symbol': 'NEO/BTC', + 'base': 'NEO', + 'quote': 'BTC', + 'active': False, + 'precision': { + 'price': 8, + 'amount': 8, + 'cost': 8, + }, + 'lot': 0.00000001, + 'limits': { + 'amount': { + 'min': 0.01, + 'max': 1000, + }, + 'price': 500000, + 'cost': { + 'min': 1, + 'max': 500000, + }, }, 'info': '', } diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index 5cdd22c7a..f947cc9f0 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -23,7 +23,7 @@ def prec_satoshi(a, b) -> float: # Unit tests -def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: +def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: """ Test rpc_trade_status() method """ @@ -34,7 +34,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) freqtradebot = FreqtradeBot(default_conf) @@ -72,7 +73,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: assert trade.find('[ETH/BTC]') >= 0 -def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: +def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None: """ Test rpc_status_table() method """ @@ -83,7 +84,8 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) freqtradebot = FreqtradeBot(default_conf) @@ -107,7 +109,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: def test_rpc_daily_profit(default_conf, update, ticker, fee, - limit_buy_order, limit_sell_order, mocker) -> None: + limit_buy_order, limit_sell_order, markets, mocker) -> None: """ Test rpc_daily_profit() method """ @@ -118,7 +120,8 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) freqtradebot = FreqtradeBot(default_conf) @@ -160,7 +163,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, - limit_buy_order, limit_sell_order, mocker) -> None: + limit_buy_order, limit_sell_order, markets, mocker) -> None: """ Test rpc_trade_statistics() method """ @@ -175,7 +178,8 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) freqtradebot = FreqtradeBot(default_conf) @@ -237,7 +241,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, # Test that rpc_trade_statistics can handle trades that lacks # trade.open_rate (it is set to None) -def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, +def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets, ticker_sell_up, limit_buy_order, limit_sell_order): """ Test rpc_trade_statistics() method @@ -253,7 +257,8 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) freqtradebot = FreqtradeBot(default_conf) @@ -400,7 +405,7 @@ def test_rpc_stop(mocker, default_conf) -> None: assert freqtradebot.state == State.STOPPED -def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: +def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None: """ Test rpc_forcesell() method """ @@ -422,6 +427,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: } ), get_fee=fee, + get_markets=markets ) freqtradebot = FreqtradeBot(default_conf) @@ -519,7 +525,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: def test_performance_handle(default_conf, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: + limit_sell_order, markets, mocker) -> None: """ Test rpc_performance() method """ @@ -531,7 +537,8 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, validate_pairs=MagicMock(), get_balances=MagicMock(return_value=ticker), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) freqtradebot = FreqtradeBot(default_conf) @@ -558,7 +565,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, assert prec_satoshi(res[0]['profit'], 6.2) -def test_rpc_count(mocker, default_conf, ticker, fee) -> None: +def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None: """ Test rpc_count() method """ @@ -571,6 +578,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None: get_balances=MagicMock(return_value=ticker), get_ticker=ticker, get_fee=fee, + get_markets=markets ) freqtradebot = FreqtradeBot(default_conf) diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index 0919455ad..d10abd8b5 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -233,7 +233,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None: ) -def test_status(default_conf, update, mocker, fee, ticker) -> None: +def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: """ Test _status() method """ @@ -250,6 +250,7 @@ def test_status(default_conf, update, mocker, fee, ticker) -> None: get_ticker=ticker, get_pair_detail_url=MagicMock(), get_fee=fee, + get_markets=markets ) msg_mock = MagicMock() status_table = MagicMock() @@ -278,7 +279,7 @@ def test_status(default_conf, update, mocker, fee, ticker) -> None: assert status_table.call_count == 1 -def test_status_handle(default_conf, update, ticker, fee, mocker) -> None: +def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> None: """ Test _status() method """ @@ -289,6 +290,7 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None: validate_pairs=MagicMock(), get_ticker=ticker, get_fee=fee, + get_markets=markets ) msg_mock = MagicMock() status_table = MagicMock() @@ -324,7 +326,7 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None: assert '[ETH/BTC]' in msg_mock.call_args_list[0][0][0] -def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: +def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) -> None: """ Test _status_table() method """ @@ -336,6 +338,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: get_ticker=ticker, buy=MagicMock(return_value={'id': 'mocked_order_id'}), get_fee=fee, + get_markets=markets ) msg_mock = MagicMock() mocker.patch.multiple( @@ -377,7 +380,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: + limit_sell_order, markets, mocker) -> None: """ Test _daily() method """ @@ -391,7 +394,8 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) msg_mock = MagicMock() mocker.patch.multiple( @@ -489,7 +493,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, - limit_buy_order, limit_sell_order, mocker) -> None: + limit_buy_order, limit_sell_order, markets, mocker) -> None: """ Test _profit() method """ @@ -500,7 +504,8 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) msg_mock = MagicMock() mocker.patch.multiple( @@ -768,7 +773,8 @@ def test_reload_conf_handle(default_conf, update, mocker) -> None: assert 'Reloading config' in msg_mock.call_args_list[0][0][0] -def test_forcesell_handle(default_conf, update, ticker, fee, ticker_sell_up, mocker) -> None: +def test_forcesell_handle(default_conf, update, ticker, fee, + ticker_sell_up, markets, mocker) -> None: """ Test _forcesell() method """ @@ -781,7 +787,8 @@ def test_forcesell_handle(default_conf, update, ticker, fee, ticker_sell_up, moc 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) freqtradebot = FreqtradeBot(default_conf) @@ -808,7 +815,8 @@ def test_forcesell_handle(default_conf, update, ticker, fee, ticker_sell_up, moc assert '0.919 USD' in rpc_mock.call_args_list[-1][0][0] -def test_forcesell_down_handle(default_conf, update, ticker, fee, ticker_sell_down, mocker) -> None: +def test_forcesell_down_handle(default_conf, update, ticker, fee, + ticker_sell_down, markets, mocker) -> None: """ Test _forcesell() method """ @@ -821,7 +829,8 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, ticker_sell_do 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) freqtradebot = FreqtradeBot(default_conf) @@ -852,7 +861,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, ticker_sell_do assert '-0.824 USD' in rpc_mock.call_args_list[-1][0][0] -def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None: +def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker) -> None: """ Test _forcesell() method """ @@ -866,7 +875,8 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) freqtradebot = FreqtradeBot(default_conf) @@ -930,7 +940,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: def test_performance_handle(default_conf, update, ticker, fee, - limit_buy_order, limit_sell_order, mocker) -> None: + limit_buy_order, limit_sell_order, markets, mocker) -> None: """ Test _performance() method """ @@ -946,7 +956,8 @@ def test_performance_handle(default_conf, update, ticker, fee, 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) freqtradebot = FreqtradeBot(default_conf) @@ -994,7 +1005,7 @@ def test_performance_handle_invalid(default_conf, update, mocker) -> None: assert 'not running' in msg_mock.call_args_list[0][0][0] -def test_count_handle(default_conf, update, ticker, fee, mocker) -> None: +def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> None: """ Test _count() method """ @@ -1010,7 +1021,8 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None: 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - buy=MagicMock(return_value={'id': 'mocked_order_id'}) + buy=MagicMock(return_value={'id': 'mocked_order_id'}), + get_markets=markets ) mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee) freqtradebot = FreqtradeBot(default_conf) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 80b66735d..badc1fc3d 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -255,20 +255,12 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf, with pytest.raises(DependencyException, match=r'.*stake amount.*'): freqtrade._get_trade_stake_amount() - # test UNLIMITED_STAKE_AMOUNT - conf = deepcopy(default_conf) - conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT - conf['max_open_trades'] = 2 - freqtrade = FreqtradeBot(conf) - - with pytest.raises(DependencyException, match=r'.*is lower than minimal.*'): - freqtrade._get_trade_stake_amount() - def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, limit_buy_order, fee, + markets, mocker) -> None: """ Test get_trade_stake_amount() method @@ -283,6 +275,7 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_balance=MagicMock(return_value=default_conf['stake_amount']), get_fee=fee, + get_markets=markets ) conf = deepcopy(default_conf) @@ -314,28 +307,119 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, assert result is None -def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: +def test_get_min_pair_stake_amount(mocker, default_conf) -> None: """ - Test create_trade() method + Test get_trade_stake_amount() method """ - patch_get_signal(mocker) + patch_RPCManager(mocker) - patch_coinmarketcap(mocker) - mocker.patch.multiple( - 'freqtrade.freqtradebot.exchange', - validate_pairs=MagicMock(), - get_ticker=ticker, - buy=MagicMock(return_value={'id': limit_buy_order['id']}), - get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5), - get_fee=fee, - ) + mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock()) + mocker.patch('freqtrade.freqtradebot.Analyze.get_stoploss', MagicMock(return_value=-0.05)) freqtrade = FreqtradeBot(default_conf) - with pytest.raises(DependencyException, match=r'.*stake amount.*'): - freqtrade.create_trade() + # no pair found + mocker.patch( + 'freqtrade.freqtradebot.exchange.get_markets', + MagicMock(return_value=[{ + 'symbol': 'ETH/BTC' + }]) + ) + with pytest.raises(ValueError, match=r'.*get market information.*'): + freqtrade._get_min_pair_stake_amount('BNB/BTC', 1) + + # no 'limits' section + mocker.patch( + 'freqtrade.freqtradebot.exchange.get_markets', + MagicMock(return_value=[{ + 'symbol': 'ETH/BTC' + }]) + ) + result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1) + assert result is None + + # empty 'limits' section + mocker.patch( + 'freqtrade.freqtradebot.exchange.get_markets', + MagicMock(return_value=[{ + 'symbol': 'ETH/BTC', + 'limits': {} + }]) + ) + result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1) + assert result is None + + # empty 'cost'/'amount' section + mocker.patch( + 'freqtrade.freqtradebot.exchange.get_markets', + MagicMock(return_value=[{ + 'symbol': 'ETH/BTC', + 'limits': { + 'cost': {}, + 'amount': {} + } + }]) + ) + result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1) + assert result is None + + # min cost is set + mocker.patch( + 'freqtrade.freqtradebot.exchange.get_markets', + MagicMock(return_value=[{ + 'symbol': 'ETH/BTC', + 'limits': { + 'cost': {'min': 2}, + 'amount': {} + } + }]) + ) + result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1) + assert result == 2 / 0.9 + + # min amount is set + mocker.patch( + 'freqtrade.freqtradebot.exchange.get_markets', + MagicMock(return_value=[{ + 'symbol': 'ETH/BTC', + 'limits': { + 'cost': {}, + 'amount': {'min': 2} + } + }]) + ) + result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2) + assert result == 2 * 2 / 0.9 + + # min amount and cost are set (cost is minimal) + mocker.patch( + 'freqtrade.freqtradebot.exchange.get_markets', + MagicMock(return_value=[{ + 'symbol': 'ETH/BTC', + 'limits': { + 'cost': {'min': 2}, + 'amount': {'min': 2} + } + }]) + ) + result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2) + assert result == min(2, 2 * 2) / 0.9 + + # min amount and cost are set (amount is minial) + mocker.patch( + 'freqtrade.freqtradebot.exchange.get_markets', + MagicMock(return_value=[{ + 'symbol': 'ETH/BTC', + 'limits': { + 'cost': {'min': 8}, + 'amount': {'min': 2} + } + }]) + ) + result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2) + assert result == min(8, 2 * 2) / 0.9 -def test_create_trade(default_conf, ticker, limit_buy_order, fee, mocker) -> None: +def test_create_trade(default_conf, ticker, limit_buy_order, fee, markets, mocker) -> None: """ Test create_trade() method """ @@ -348,6 +432,7 @@ def test_create_trade(default_conf, ticker, limit_buy_order, fee, mocker) -> Non get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, + get_markets=markets ) # Save state of current whitelist @@ -371,7 +456,31 @@ def test_create_trade(default_conf, ticker, limit_buy_order, fee, mocker) -> Non assert whitelist == default_conf['exchange']['pair_whitelist'] -def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: +def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, + fee, markets, mocker) -> None: + """ + Test create_trade() method + """ + patch_get_signal(mocker) + patch_RPCManager(mocker) + patch_coinmarketcap(mocker) + mocker.patch.multiple( + 'freqtrade.freqtradebot.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker, + buy=MagicMock(return_value={'id': limit_buy_order['id']}), + get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5), + get_fee=fee, + get_markets=markets + ) + freqtrade = FreqtradeBot(default_conf) + + with pytest.raises(DependencyException, match=r'.*stake amount.*'): + freqtrade.create_trade() + + +def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order, + fee, markets, mocker) -> None: """ Test create_trade() method """ @@ -385,6 +494,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order, fee, get_ticker=ticker, buy=buy_mock, get_fee=fee, + get_markets=markets ) conf = deepcopy(default_conf) @@ -396,7 +506,34 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order, fee, assert rate * amount >= conf['stake_amount'] -def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order, fee, mocker) -> None: +def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_order, + fee, markets, mocker) -> None: + """ + Test create_trade() method + """ + patch_get_signal(mocker) + patch_RPCManager(mocker) + patch_coinmarketcap(mocker) + buy_mock = MagicMock(return_value={'id': limit_buy_order['id']}) + mocker.patch.multiple( + 'freqtrade.freqtradebot.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker, + buy=buy_mock, + get_fee=fee, + get_markets=markets + ) + + conf = deepcopy(default_conf) + conf['stake_amount'] = 0.000000005 + freqtrade = FreqtradeBot(conf) + + result = freqtrade.create_trade() + assert result is False + + +def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order, + fee, markets, mocker) -> None: """ Test create_trade() method """ @@ -410,6 +547,7 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order, fee, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_balance=MagicMock(return_value=default_conf['stake_amount']), get_fee=fee, + get_markets=markets ) conf = deepcopy(default_conf) conf['max_open_trades'] = 0 @@ -421,7 +559,7 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order, fee, assert freqtrade._get_trade_stake_amount() is None -def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, mocker) -> None: +def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, markets, mocker) -> None: """ Test create_trade() method """ @@ -434,6 +572,7 @@ def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, mocke get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, + get_markets=markets ) conf = deepcopy(default_conf) @@ -448,7 +587,7 @@ def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, mocke def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, - limit_buy_order, fee, mocker) -> None: + limit_buy_order, fee, markets, mocker) -> None: """ Test create_trade() method """ @@ -461,6 +600,7 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, + get_markets=markets ) conf = deepcopy(default_conf) @@ -736,7 +876,8 @@ def test_process_maybe_execute_sell_exception(mocker, default_conf, assert log_has('Unable to sell trade: ', caplog.record_tuples) -def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mocker) -> None: +def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, + fee, markets, mocker) -> None: """ Test check_handle() method """ @@ -752,7 +893,8 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mock }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), sell=MagicMock(return_value={'id': limit_sell_order['id']}), - get_fee=fee + get_fee=fee, + get_markets=markets ) patch_coinmarketcap(mocker, value={'price_usd': 15000.0}) @@ -780,7 +922,8 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mock assert trade.close_date is not None -def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, fee, mocker) -> None: +def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, + fee, markets, mocker) -> None: """ Test check_handle() method """ @@ -797,6 +940,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, fee, get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, + get_markets=markets ) freqtrade = FreqtradeBot(conf) @@ -838,7 +982,8 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, fee, assert freqtrade.handle_trade(trades[0]) is True -def test_handle_trade_roi(default_conf, ticker, limit_buy_order, fee, mocker, caplog) -> None: +def test_handle_trade_roi(default_conf, ticker, limit_buy_order, + fee, mocker, markets, caplog) -> None: """ Test check_handle() method """ @@ -855,6 +1000,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order, fee, mocker, ca get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, + get_markets=markets ) mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=True) @@ -875,7 +1021,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order, fee, mocker, ca def test_handle_trade_experimental( - default_conf, ticker, limit_buy_order, fee, mocker, caplog) -> None: + default_conf, ticker, limit_buy_order, fee, mocker, markets, caplog) -> None: """ Test check_handle() method """ @@ -892,6 +1038,7 @@ def test_handle_trade_experimental( get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, + get_markets=markets ) mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False) @@ -909,7 +1056,8 @@ def test_handle_trade_experimental( assert log_has('Sell signal received. Selling..', caplog.record_tuples) -def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, fee, mocker) -> None: +def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, + fee, markets, mocker) -> None: """ Test check_handle() method """ @@ -922,6 +1070,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, fe get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, + get_markets=markets ) freqtrade = FreqtradeBot(default_conf) @@ -1160,7 +1309,7 @@ def test_handle_timedout_limit_sell(mocker, default_conf) -> None: assert cancel_order_mock.call_count == 1 -def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None: +def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, mocker) -> None: """ Test execute_sell() method with a ticker going UP """ @@ -1171,7 +1320,8 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0) freqtrade = FreqtradeBot(default_conf) @@ -1201,7 +1351,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N assert '0.919 USD' in rpc_mock.call_args_list[-1][0][0] -def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) -> None: +def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets, mocker) -> None: """ Test execute_sell() method with a ticker going DOWN """ @@ -1213,7 +1363,8 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) freqtrade = FreqtradeBot(default_conf) @@ -1242,7 +1393,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee, - ticker_sell_up, mocker) -> None: + ticker_sell_up, markets, mocker) -> None: """ Test execute_sell() method with a ticker going DOWN and with a bot config empty """ @@ -1253,7 +1404,8 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee, 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) freqtrade = FreqtradeBot(default_conf) @@ -1283,7 +1435,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee, def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee, - ticker_sell_down, mocker) -> None: + ticker_sell_down, markets, mocker) -> None: """ Test execute_sell() method with a ticker going DOWN and with a bot config empty """ @@ -1294,7 +1446,8 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee, 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_fee=fee + get_fee=fee, + get_markets=markets ) freqtrade = FreqtradeBot(default_conf) @@ -1321,7 +1474,8 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee, assert 'loss: -5.48%, -0.00005492' in rpc_mock.call_args_list[-1][0][0] -def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, fee, mocker) -> None: +def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, + fee, markets, mocker) -> None: """ Test sell_profit_only feature when enabled """ @@ -1339,6 +1493,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, fee, mock }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, + get_markets=markets ) conf = deepcopy(default_conf) conf['experimental'] = { @@ -1354,7 +1509,8 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, fee, mock assert freqtrade.handle_trade(trade) is True -def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, fee, mocker) -> None: +def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, + fee, markets, mocker) -> None: """ Test sell_profit_only feature when disabled """ @@ -1372,6 +1528,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, fee, moc }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, + get_markets=markets ) conf = deepcopy(default_conf) conf['experimental'] = { @@ -1387,7 +1544,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, fee, moc assert freqtrade.handle_trade(trade) is True -def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, mocker) -> None: +def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, markets, mocker) -> None: """ Test sell_profit_only feature when enabled and we have a loss """ @@ -1405,6 +1562,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, mocker }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, + get_markets=markets ) conf = deepcopy(default_conf) conf['experimental'] = { @@ -1420,7 +1578,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, mocker assert freqtrade.handle_trade(trade) is False -def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, mocker) -> None: +def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, markets, mocker) -> None: """ Test sell_profit_only feature when enabled and we have a loss """ @@ -1438,6 +1596,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, mocke }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, + get_markets=markets ) conf = deepcopy(default_conf)