Merge branch 'develop' of https://github.com/kecheon/freqtrade into develop

This commit is contained in:
kecheon 2021-04-11 10:19:51 +09:00
commit 7c290baeae
11 changed files with 70 additions and 227 deletions

View File

@ -174,7 +174,7 @@ This filter removes pairs if the average volatility over a `lookback_days` days
This filter can be used to narrow down your pairs to a certain volatility or avoid very volatile pairs. This filter can be used to narrow down your pairs to a certain volatility or avoid very volatile pairs.
In the below example: In the below example:
If the volatility over the last 10 days is not in the range of 0.20-0.30, remove the pair from the whitelist. The filter is applied every 24h. If the volatility over the last 10 days is not in the range of 0.05-0.50, remove the pair from the whitelist. The filter is applied every 24h.
```json ```json
"pairlists": [ "pairlists": [
@ -190,7 +190,7 @@ If the volatility over the last 10 days is not in the range of 0.20-0.30, remove
### Full example of Pairlist Handlers ### Full example of Pairlist Handlers
The below example blacklists `BNB/BTC`, uses `VolumePairList` with `20` assets, sorting pairs by `quoteVolume` and applies both [`PrecisionFilter`](#precisionfilter) and [`PriceFilter`](#price-filter), filtering all assets where 1 price unit is > 1%. Then the `SpreadFilter` is applied and pairs are finally shuffled with the random seed set to some predefined value. The below example blacklists `BNB/BTC`, uses `VolumePairList` with `20` assets, sorting pairs by `quoteVolume` and applies [`PrecisionFilter`](#precisionfilter) and [`PriceFilter`](#price-filter), filtering all assets where 1 price unit is > 1%. Then the [`SpreadFilter`](#spreadfilter) and [`VolatilityFilter`](#volatilityfilter) is applied and pairs are finally shuffled with the random seed set to some predefined value.
```json ```json
"exchange": { "exchange": {

View File

@ -52,7 +52,7 @@ class Binance(Exchange):
'In stoploss limit order, stop price should be more than limit price') 'In stoploss limit order, stop price should be more than limit price')
if self._config['dry_run']: if self._config['dry_run']:
dry_order = self.dry_run_order( dry_order = self.create_dry_run_order(
pair, ordertype, "sell", amount, stop_price) pair, ordertype, "sell", amount, stop_price)
return dry_order return dry_order

View File

@ -543,8 +543,8 @@ class Exchange:
# See also #2575 at github. # See also #2575 at github.
return max(min_stake_amounts) * amount_reserve_percent return max(min_stake_amounts) * amount_reserve_percent
def dry_run_order(self, pair: str, ordertype: str, side: str, amount: float, def create_dry_run_order(self, pair: str, ordertype: str, side: str, amount: float,
rate: float, params: Dict = {}) -> Dict[str, Any]: rate: float, params: Dict = {}) -> Dict[str, Any]:
order_id = f'dry_run_{side}_{datetime.now().timestamp()}' order_id = f'dry_run_{side}_{datetime.now().timestamp()}'
_amount = self.amount_to_precision(pair, amount) _amount = self.amount_to_precision(pair, amount)
dry_order = { dry_order = {
@ -618,7 +618,7 @@ class Exchange:
rate: float, time_in_force: str) -> Dict: rate: float, time_in_force: str) -> Dict:
if self._config['dry_run']: if self._config['dry_run']:
dry_order = self.dry_run_order(pair, ordertype, "buy", amount, rate) dry_order = self.create_dry_run_order(pair, ordertype, "buy", amount, rate)
return dry_order return dry_order
params = self._params.copy() params = self._params.copy()
@ -631,7 +631,7 @@ class Exchange:
rate: float, time_in_force: str = 'gtc') -> Dict: rate: float, time_in_force: str = 'gtc') -> Dict:
if self._config['dry_run']: if self._config['dry_run']:
dry_order = self.dry_run_order(pair, ordertype, "sell", amount, rate) dry_order = self.create_dry_run_order(pair, ordertype, "sell", amount, rate)
return dry_order return dry_order
params = self._params.copy() params = self._params.copy()
@ -662,8 +662,6 @@ class Exchange:
@retrier @retrier
def get_balance(self, currency: str) -> float: def get_balance(self, currency: str) -> float:
if self._config['dry_run']:
return self._config['dry_run_wallet']
# ccxt exception is already handled by get_balances # ccxt exception is already handled by get_balances
balances = self.get_balances() balances = self.get_balances()
@ -675,8 +673,6 @@ class Exchange:
@retrier @retrier
def get_balances(self) -> dict: def get_balances(self) -> dict:
if self._config['dry_run']:
return {}
try: try:
balances = self._api.fetch_balance() balances = self._api.fetch_balance()

View File

@ -53,7 +53,7 @@ class Ftx(Exchange):
stop_price = self.price_to_precision(pair, stop_price) stop_price = self.price_to_precision(pair, stop_price)
if self._config['dry_run']: if self._config['dry_run']:
dry_order = self.dry_run_order( dry_order = self.create_dry_run_order(
pair, ordertype, "sell", amount, stop_price) pair, ordertype, "sell", amount, stop_price)
return dry_order return dry_order

View File

@ -92,7 +92,7 @@ class Kraken(Exchange):
stop_price = self.price_to_precision(pair, stop_price) stop_price = self.price_to_precision(pair, stop_price)
if self._config['dry_run']: if self._config['dry_run']:
dry_order = self.dry_run_order( dry_order = self.create_dry_run_order(
pair, ordertype, "sell", amount, stop_price) pair, ordertype, "sell", amount, stop_price)
return dry_order return dry_order

View File

@ -410,9 +410,7 @@ class FreqtradeBot(LoggingMixin):
bid_strategy = self.config.get('bid_strategy', {}) bid_strategy = self.config.get('bid_strategy', {})
if 'use_order_book' in bid_strategy and bid_strategy.get('use_order_book', False): if 'use_order_book' in bid_strategy and bid_strategy.get('use_order_book', False):
logger.info(
f"Getting price from order book {bid_strategy['price_side'].capitalize()} side."
)
order_book_top = bid_strategy.get('order_book_top', 1) order_book_top = bid_strategy.get('order_book_top', 1)
order_book = self.exchange.fetch_l2_order_book(pair, order_book_top) order_book = self.exchange.fetch_l2_order_book(pair, order_book_top)
logger.debug('order_book %s', order_book) logger.debug('order_book %s', order_book)
@ -425,7 +423,8 @@ class FreqtradeBot(LoggingMixin):
f"Orderbook: {order_book}" f"Orderbook: {order_book}"
) )
raise PricingError from e raise PricingError from e
logger.info(f'...top {order_book_top} order book buy rate {rate_from_l2:.8f}') logger.info(f"Buy price from orderbook {bid_strategy['price_side'].capitalize()} side "
f"- top {order_book_top} order book buy rate {rate_from_l2:.8f}")
used_rate = rate_from_l2 used_rate = rate_from_l2
else: else:
logger.info(f"Using Last {bid_strategy['price_side'].capitalize()} / Last Price") logger.info(f"Using Last {bid_strategy['price_side'].capitalize()} / Last Price")
@ -479,19 +478,17 @@ class FreqtradeBot(LoggingMixin):
logger.debug(f"Stake amount is 0, ignoring possible trade for {pair}.") logger.debug(f"Stake amount is 0, ignoring possible trade for {pair}.")
return False return False
logger.info(f"Buy signal found: about create a new trade with stake_amount: " logger.info(f"Buy signal found: about create a new trade for {pair} with stake_amount: "
f"{stake_amount} ...") f"{stake_amount} ...")
bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {}) bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {})
if ((bid_check_dom.get('enabled', False)) and if ((bid_check_dom.get('enabled', False)) and
(bid_check_dom.get('bids_to_ask_delta', 0) > 0)): (bid_check_dom.get('bids_to_ask_delta', 0) > 0)):
if self._check_depth_of_market_buy(pair, bid_check_dom): if self._check_depth_of_market_buy(pair, bid_check_dom):
logger.info(f'Executing Buy for {pair}.')
return self.execute_buy(pair, stake_amount) return self.execute_buy(pair, stake_amount)
else: else:
return False return False
logger.info(f'Executing Buy for {pair}')
return self.execute_buy(pair, stake_amount) return self.execute_buy(pair, stake_amount)
else: else:
return False return False

View File

@ -4,9 +4,9 @@ e.g BTC to USD
""" """
import logging import logging
import time from typing import Dict
from typing import Dict, List
from cachetools.ttl import TTLCache
from pycoingecko import CoinGeckoAPI from pycoingecko import CoinGeckoAPI
from freqtrade.constants import SUPPORTED_FIAT from freqtrade.constants import SUPPORTED_FIAT
@ -15,51 +15,6 @@ from freqtrade.constants import SUPPORTED_FIAT
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class CryptoFiat:
"""
Object to describe what is the price of Crypto-currency in a FIAT
"""
# Constants
CACHE_DURATION = 6 * 60 * 60 # 6 hours
def __init__(self, crypto_symbol: str, fiat_symbol: str, price: float) -> None:
"""
Create an object that will contains the price for a crypto-currency in fiat
:param crypto_symbol: Crypto-currency you want to convert (e.g BTC)
:param fiat_symbol: FIAT currency you want to convert to (e.g USD)
:param price: Price in FIAT
"""
# Public attributes
self.crypto_symbol = None
self.fiat_symbol = None
self.price = 0.0
# Private attributes
self._expiration = 0.0
self.crypto_symbol = crypto_symbol.lower()
self.fiat_symbol = fiat_symbol.lower()
self.set_price(price=price)
def set_price(self, price: float) -> None:
"""
Set the price of the Crypto-currency in FIAT and set the expiration time
:param price: Price of the current Crypto currency in the fiat
:return: None
"""
self.price = price
self._expiration = time.time() + self.CACHE_DURATION
def is_expired(self) -> bool:
"""
Return if the current price is still valid or needs to be refreshed
:return: bool, true the price is expired and needs to be refreshed, false the price is
still valid
"""
return self._expiration - time.time() <= 0
class CryptoToFiatConverter: class CryptoToFiatConverter:
""" """
Main class to initiate Crypto to FIAT. Main class to initiate Crypto to FIAT.
@ -84,7 +39,9 @@ class CryptoToFiatConverter:
return CryptoToFiatConverter.__instance return CryptoToFiatConverter.__instance
def __init__(self) -> None: def __init__(self) -> None:
self._pairs: List[CryptoFiat] = [] # Timeout: 6h
self._pair_price: TTLCache = TTLCache(maxsize=500, ttl=6 * 60 * 60)
self._load_cryptomap() self._load_cryptomap()
def _load_cryptomap(self) -> None: def _load_cryptomap(self) -> None:
@ -118,49 +75,31 @@ class CryptoToFiatConverter:
""" """
crypto_symbol = crypto_symbol.lower() crypto_symbol = crypto_symbol.lower()
fiat_symbol = fiat_symbol.lower() fiat_symbol = fiat_symbol.lower()
inverse = False
if crypto_symbol == 'usd':
# usd corresponds to "uniswap-state-dollar" for coingecko.
# We'll therefore need to "swap" the currencies
logger.info(f"reversing Rates {crypto_symbol}, {fiat_symbol}")
crypto_symbol = fiat_symbol
fiat_symbol = 'usd'
inverse = True
symbol = f"{crypto_symbol}/{fiat_symbol}"
# Check if the fiat convertion you want is supported # Check if the fiat convertion you want is supported
if not self._is_supported_fiat(fiat=fiat_symbol): if not self._is_supported_fiat(fiat=fiat_symbol):
raise ValueError(f'The fiat {fiat_symbol} is not supported.') raise ValueError(f'The fiat {fiat_symbol} is not supported.')
# Get the pair that interest us and return the price in fiat price = self._pair_price.get(symbol, None)
for pair in self._pairs:
if pair.crypto_symbol == crypto_symbol and pair.fiat_symbol == fiat_symbol:
# If the price is expired we refresh it, avoid to call the API all the time
if pair.is_expired():
pair.set_price(
price=self._find_price(
crypto_symbol=pair.crypto_symbol,
fiat_symbol=pair.fiat_symbol
)
)
# return the last price we have for this pair if not price:
return pair.price price = self._find_price(
# The pair does not exist, so we create it and return the price
return self._add_pair(
crypto_symbol=crypto_symbol,
fiat_symbol=fiat_symbol,
price=self._find_price(
crypto_symbol=crypto_symbol, crypto_symbol=crypto_symbol,
fiat_symbol=fiat_symbol fiat_symbol=fiat_symbol
) )
) if inverse and price != 0.0:
price = 1 / price
def _add_pair(self, crypto_symbol: str, fiat_symbol: str, price: float) -> float: self._pair_price[symbol] = price
"""
:param crypto_symbol: Crypto-currency you want to convert (e.g BTC)
:param fiat_symbol: FIAT currency you want to convert to (e.g USD)
:return: price in FIAT
"""
self._pairs.append(
CryptoFiat(
crypto_symbol=crypto_symbol,
fiat_symbol=fiat_symbol,
price=price
)
)
return price return price

View File

@ -931,11 +931,11 @@ def test_exchange_has(default_conf, mocker):
("sell") ("sell")
]) ])
@pytest.mark.parametrize("exchange_name", EXCHANGES) @pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_dry_run_order(default_conf, mocker, side, exchange_name): def test_create_dry_run_order(default_conf, mocker, side, exchange_name):
default_conf['dry_run'] = True default_conf['dry_run'] = True
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
order = exchange.dry_run_order( order = exchange.create_dry_run_order(
pair='ETH/BTC', ordertype='limit', side=side, amount=1, rate=200) pair='ETH/BTC', ordertype='limit', side=side, amount=1, rate=200)
assert 'id' in order assert 'id' in order
assert f'dry_run_{side}_' in order["id"] assert f'dry_run_{side}_' in order["id"]
@ -1245,14 +1245,6 @@ def test_sell_considers_time_in_force(default_conf, mocker, exchange_name):
assert "timeInForce" not in api_mock.create_order.call_args[0][5] assert "timeInForce" not in api_mock.create_order.call_args[0][5]
def test_get_balance_dry_run(default_conf, mocker):
default_conf['dry_run'] = True
default_conf['dry_run_wallet'] = 999.9
exchange = get_patched_exchange(mocker, default_conf)
assert exchange.get_balance(currency='BTC') == 999.9
@pytest.mark.parametrize("exchange_name", EXCHANGES) @pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_get_balance_prod(default_conf, mocker, exchange_name): def test_get_balance_prod(default_conf, mocker, exchange_name):
api_mock = MagicMock() api_mock = MagicMock()
@ -1276,13 +1268,6 @@ def test_get_balance_prod(default_conf, mocker, exchange_name):
exchange.get_balance(currency='BTC') exchange.get_balance(currency='BTC')
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_get_balances_dry_run(default_conf, mocker, exchange_name):
default_conf['dry_run'] = True
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
assert exchange.get_balances() == {}
@pytest.mark.parametrize("exchange_name", EXCHANGES) @pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_get_balances_prod(default_conf, mocker, exchange_name): def test_get_balances_prod(default_conf, mocker, exchange_name):
balance_item = { balance_item = {

View File

@ -1,44 +1,15 @@
# pragma pylint: disable=missing-docstring, too-many-arguments, too-many-ancestors, # pragma pylint: disable=missing-docstring, too-many-arguments, too-many-ancestors,
# pragma pylint: disable=protected-access, C0103 # pragma pylint: disable=protected-access, C0103
import time
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest import pytest
from requests.exceptions import RequestException from requests.exceptions import RequestException
from freqtrade.rpc.fiat_convert import CryptoFiat, CryptoToFiatConverter from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
from tests.conftest import log_has, log_has_re from tests.conftest import log_has, log_has_re
def test_pair_convertion_object():
pair_convertion = CryptoFiat(
crypto_symbol='btc',
fiat_symbol='usd',
price=12345.0
)
# Check the cache duration is 6 hours
assert pair_convertion.CACHE_DURATION == 6 * 60 * 60
# Check a regular usage
assert pair_convertion.crypto_symbol == 'btc'
assert pair_convertion.fiat_symbol == 'usd'
assert pair_convertion.price == 12345.0
assert pair_convertion.is_expired() is False
# Update the expiration time (- 2 hours) and check the behavior
pair_convertion._expiration = time.time() - 2 * 60 * 60
assert pair_convertion.is_expired() is True
# Check set price behaviour
time_reference = time.time() + pair_convertion.CACHE_DURATION
pair_convertion.set_price(price=30000.123)
assert pair_convertion.is_expired() is False
assert pair_convertion._expiration >= time_reference
assert pair_convertion.price == 30000.123
def test_fiat_convert_is_supported(mocker): def test_fiat_convert_is_supported(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
@ -47,28 +18,6 @@ def test_fiat_convert_is_supported(mocker):
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(mocker):
fiat_convert = CryptoToFiatConverter()
pair_len = len(fiat_convert._pairs)
assert pair_len == 0
fiat_convert._add_pair(crypto_symbol='btc', fiat_symbol='usd', price=12345.0)
pair_len = len(fiat_convert._pairs)
assert pair_len == 1
assert fiat_convert._pairs[0].crypto_symbol == 'btc'
assert fiat_convert._pairs[0].fiat_symbol == 'usd'
assert fiat_convert._pairs[0].price == 12345.0
fiat_convert._add_pair(crypto_symbol='btc', fiat_symbol='Eur', price=13000.2)
pair_len = len(fiat_convert._pairs)
assert pair_len == 2
assert fiat_convert._pairs[1].crypto_symbol == 'btc'
assert fiat_convert._pairs[1].fiat_symbol == 'eur'
assert fiat_convert._pairs[1].price == 13000.2
def test_fiat_convert_find_price(mocker): def test_fiat_convert_find_price(mocker):
fiat_convert = CryptoToFiatConverter() fiat_convert = CryptoToFiatConverter()
@ -95,8 +44,8 @@ def test_fiat_convert_unsupported_crypto(mocker, caplog):
def test_fiat_convert_get_price(mocker): def test_fiat_convert_get_price(mocker):
mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price', find_price = mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
return_value=28000.0) return_value=28000.0)
fiat_convert = CryptoToFiatConverter() fiat_convert = CryptoToFiatConverter()
@ -104,26 +53,17 @@ def test_fiat_convert_get_price(mocker):
fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='US Dollar') fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='US Dollar')
# Check the value return by the method # Check the value return by the method
pair_len = len(fiat_convert._pairs) pair_len = len(fiat_convert._pair_price)
assert pair_len == 0 assert pair_len == 0
assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 28000.0 assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 28000.0
assert fiat_convert._pairs[0].crypto_symbol == 'btc' assert fiat_convert._pair_price['btc/usd'] == 28000.0
assert fiat_convert._pairs[0].fiat_symbol == 'usd' assert len(fiat_convert._pair_price) == 1
assert fiat_convert._pairs[0].price == 28000.0 assert find_price.call_count == 1
assert fiat_convert._pairs[0]._expiration != 0
assert len(fiat_convert._pairs) == 1
# Verify the cached is used # Verify the cached is used
fiat_convert._pairs[0].price = 9867.543 fiat_convert._pair_price['btc/usd'] = 9867.543
expiration = fiat_convert._pairs[0]._expiration
assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 9867.543 assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 9867.543
assert fiat_convert._pairs[0]._expiration == expiration assert find_price.call_count == 1
# Verify the cache expiration
expiration = time.time() - 2 * 60 * 60
fiat_convert._pairs[0]._expiration = expiration
assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 28000.0
assert fiat_convert._pairs[0]._expiration is not expiration
def test_fiat_convert_same_currencies(mocker): def test_fiat_convert_same_currencies(mocker):

View File

@ -416,10 +416,10 @@ def test_api_count(botclient, mocker, ticker, fee, markets):
assert rc.json()["max"] == 1 assert rc.json()["max"] == 1
# Create some test data # Create some test data
ftbot.enter_positions() create_mock_trades(fee)
rc = client_get(client, f"{BASE_URI}/count") rc = client_get(client, f"{BASE_URI}/count")
assert_response(rc) assert_response(rc)
assert rc.json()["current"] == 1 assert rc.json()["current"] == 4
assert rc.json()["max"] == 1 assert rc.json()["max"] == 1
ftbot.config['max_open_trades'] = float('inf') ftbot.config['max_open_trades'] = float('inf')
@ -612,7 +612,7 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_api_profit(botclient, mocker, ticker, fee, markets, limit_buy_order, limit_sell_order): def test_api_profit(botclient, mocker, ticker, fee, markets):
ftbot, client = botclient ftbot, client = botclient
patch_get_signal(ftbot, (True, False)) patch_get_signal(ftbot, (True, False))
mocker.patch.multiple( mocker.patch.multiple(
@ -627,48 +627,33 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, limit_buy_order, li
assert_response(rc, 200) assert_response(rc, 200)
assert rc.json()['trade_count'] == 0 assert rc.json()['trade_count'] == 0
ftbot.enter_positions() create_mock_trades(fee)
trade = Trade.query.first()
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order)
rc = client_get(client, f"{BASE_URI}/profit")
assert_response(rc, 200)
# One open trade
assert rc.json()['trade_count'] == 1
assert rc.json()['best_pair'] == ''
assert rc.json()['best_rate'] == 0
trade = Trade.query.first()
trade.update(limit_sell_order)
trade.close_date = datetime.utcnow()
trade.is_open = False
rc = client_get(client, f"{BASE_URI}/profit") rc = client_get(client, f"{BASE_URI}/profit")
assert_response(rc) assert_response(rc)
assert rc.json() == {'avg_duration': ANY, assert rc.json() == {'avg_duration': ANY,
'best_pair': 'ETH/BTC', 'best_pair': 'XRP/BTC',
'best_rate': 6.2, 'best_rate': 1.0,
'first_trade_date': 'just now', 'first_trade_date': ANY,
'first_trade_timestamp': ANY, 'first_trade_timestamp': ANY,
'latest_trade_date': 'just now', 'latest_trade_date': '5 minutes ago',
'latest_trade_timestamp': ANY, 'latest_trade_timestamp': ANY,
'profit_all_coin': 6.217e-05, 'profit_all_coin': -44.0631579,
'profit_all_fiat': 0.76748865, 'profit_all_fiat': -543959.6842755,
'profit_all_percent_mean': 6.2, 'profit_all_percent_mean': -66.41,
'profit_all_ratio_mean': 0.06201058, 'profit_all_ratio_mean': -0.6641100666666667,
'profit_all_percent_sum': 6.2, 'profit_all_percent_sum': -398.47,
'profit_all_ratio_sum': 0.06201058, 'profit_all_ratio_sum': -3.9846604,
'profit_closed_coin': 6.217e-05, 'profit_closed_coin': 0.00073913,
'profit_closed_fiat': 0.76748865, 'profit_closed_fiat': 9.124559849999999,
'profit_closed_ratio_mean': 0.06201058, 'profit_closed_ratio_mean': 0.0075,
'profit_closed_percent_mean': 6.2, 'profit_closed_percent_mean': 0.75,
'profit_closed_ratio_sum': 0.06201058, 'profit_closed_ratio_sum': 0.015,
'profit_closed_percent_sum': 6.2, 'profit_closed_percent_sum': 1.5,
'trade_count': 1, 'trade_count': 6,
'closed_trade_count': 1, 'closed_trade_count': 2,
'winning_trades': 1, 'winning_trades': 2,
'losing_trades': 0, 'losing_trades': 0,
} }

View File

@ -685,7 +685,8 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order, limit_buy
assert trade.amount == 91.07468123 assert trade.amount == 91.07468123
assert log_has( assert log_has(
'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog 'Buy signal found: about create a new trade for ETH/BTC with stake_amount: 0.001 ...',
caplog
) )