From f91d7beaa16edea12c6837489ae2f6d55e376a87 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 23 Feb 2020 13:51:16 +0100 Subject: [PATCH 01/14] Fix constants wrong parenteses --- freqtrade/constants.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 1504d1f1c..743070196 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -15,6 +15,7 @@ UNLIMITED_STAKE_AMOUNT = 'unlimited' DEFAULT_AMOUNT_RESERVE_PERCENT = 0.05 REQUIRED_ORDERTIF = ['buy', 'sell'] REQUIRED_ORDERTYPES = ['buy', 'sell', 'stoploss', 'stoploss_on_exchange'] +ORDERBOOK_SIDES = ['ask', 'bid'] ORDERTYPE_POSSIBILITIES = ['limit', 'market'] ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc'] AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', @@ -113,15 +114,15 @@ CONF_SCHEMA = { 'minimum': 0, 'maximum': 1, 'exclusiveMaximum': False, - 'use_order_book': {'type': 'boolean'}, - 'order_book_top': {'type': 'integer', 'maximum': 20, 'minimum': 1}, - 'check_depth_of_market': { - 'type': 'object', - 'properties': { - 'enabled': {'type': 'boolean'}, - 'bids_to_ask_delta': {'type': 'number', 'minimum': 0}, - } - }, + }, + 'use_order_book': {'type': 'boolean'}, + 'order_book_top': {'type': 'integer', 'maximum': 20, 'minimum': 1}, + 'check_depth_of_market': { + 'type': 'object', + 'properties': { + 'enabled': {'type': 'boolean'}, + 'bids_to_ask_delta': {'type': 'number', 'minimum': 0}, + } }, }, 'required': ['ask_last_balance'] From de48a697b0a5abdee5b012f2b5b919d0fe06cb9a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 23 Feb 2020 14:06:42 +0100 Subject: [PATCH 02/14] Use price_side for get_sell_rate --- freqtrade/constants.py | 3 +++ freqtrade/freqtradebot.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 743070196..ac1a8a6a9 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -115,6 +115,7 @@ CONF_SCHEMA = { 'maximum': 1, 'exclusiveMaximum': False, }, + 'price_side': {'type': 'string', 'enum': ORDERBOOK_SIDES, 'default': 'bid'}, 'use_order_book': {'type': 'boolean'}, 'order_book_top': {'type': 'integer', 'maximum': 20, 'minimum': 1}, 'check_depth_of_market': { @@ -130,6 +131,7 @@ CONF_SCHEMA = { 'ask_strategy': { 'type': 'object', 'properties': { + 'price_side': {'type': 'string', 'enum': ORDERBOOK_SIDES, 'default': 'ask'}, 'use_order_book': {'type': 'boolean'}, 'order_book_min': {'type': 'integer', 'minimum': 1}, 'order_book_max': {'type': 'integer', 'minimum': 1, 'maximum': 50}, @@ -300,6 +302,7 @@ SCHEMA_TRADE_REQUIRED = [ 'last_stake_amount_min_ratio', 'dry_run', 'dry_run_wallet', + 'ask_strategy', 'bid_strategy', 'unfilledtimeout', 'stoploss', diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index dffec940c..5e8f1cc98 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -639,10 +639,10 @@ class FreqtradeBot: logger.debug('Using order book to get sell rate') order_book = self.exchange.get_order_book(pair, 1) - rate = order_book['bids'][0][0] + rate = order_book[f"{config_ask_strategy['price_side']}s"][0][0] else: - rate = self.exchange.fetch_ticker(pair)['bid'] + rate = self.exchange.fetch_ticker(pair)[config_ask_strategy['price_side']] self._sell_rate_cache[pair] = rate return rate From 5f712320380824f2140b82e5d6a328ff90f53beb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 23 Feb 2020 14:08:16 +0100 Subject: [PATCH 03/14] Refactor get_buy_rate to use rate variable --- freqtrade/freqtradebot.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 5e8f1cc98..aaa66fe81 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -242,11 +242,10 @@ class FreqtradeBot: logger.info(f"Using cached buy rate for {pair}.") return rate - config_bid_strategy = self.config.get('bid_strategy', {}) - if 'use_order_book' in config_bid_strategy and\ - config_bid_strategy.get('use_order_book', False): + bid_strategy = self.config.get('bid_strategy', {}) + if 'use_order_book' in bid_strategy and bid_strategy.get('use_order_book', False): logger.info('Getting price from order book') - order_book_top = config_bid_strategy.get('order_book_top', 1) + order_book_top = bid_strategy.get('order_book_top', 1) order_book = self.exchange.get_order_book(pair, order_book_top) logger.debug('order_book %s', order_book) # top 1 = index 0 @@ -256,11 +255,12 @@ class FreqtradeBot: else: logger.info('Using Last Ask / Last Price') ticker = self.exchange.fetch_ticker(pair) - if ticker['ask'] < ticker['last']: - ticker_rate = ticker['ask'] + rate = ticker['ask'] + if rate < ticker['last']: + ticker_rate = rate else: balance = self.config['bid_strategy']['ask_last_balance'] - ticker_rate = ticker['ask'] + balance * (ticker['last'] - ticker['ask']) + ticker_rate = rate + balance * (ticker['last'] - rate) used_rate = ticker_rate self._buy_rate_cache[pair] = used_rate From e4b29491888c05444ec47599238926597ecac83a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 23 Feb 2020 14:13:43 +0100 Subject: [PATCH 04/14] Change buy_rate calculation to use price_side --- freqtrade/freqtradebot.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index aaa66fe81..2c9a960a4 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -244,18 +244,20 @@ class FreqtradeBot: bid_strategy = self.config.get('bid_strategy', {}) if 'use_order_book' in bid_strategy and bid_strategy.get('use_order_book', False): - logger.info('Getting price from order book') + 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 = self.exchange.get_order_book(pair, order_book_top) logger.debug('order_book %s', order_book) # top 1 = index 0 - order_book_rate = order_book['bids'][order_book_top - 1][0] - logger.info('...top %s order book buy rate %0.8f', order_book_top, order_book_rate) + order_book_rate = order_book[f"{bid_strategy['price_side']}s"][order_book_top - 1][0] + logger.info(f'...top {order_book_top} order book buy rate {order_book_rate:.8f}') used_rate = order_book_rate else: - logger.info('Using Last Ask / Last Price') + logger.info(f"Using Last {bid_strategy['price_side'].capitalize()} / Last Price") ticker = self.exchange.fetch_ticker(pair) - rate = ticker['ask'] + rate = ticker[bid_strategy['price_side']] if rate < ticker['last']: ticker_rate = rate else: From e7b9891335ba13abdcf1445b7cc354db8a51792e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 23 Feb 2020 14:27:03 +0100 Subject: [PATCH 05/14] Adapt rpc tests to corrected price side --- tests/rpc/test_rpc.py | 24 +++++++++++----------- tests/rpc/test_rpc_apiserver.py | 8 ++++---- tests/rpc/test_rpc_telegram.py | 36 ++++++++++++++++----------------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 93b6f6058..6319ab9e6 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -51,13 +51,13 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'open_date_hum': ANY, 'close_date': None, 'close_date_hum': None, - 'open_rate': 1.099e-05, + 'open_rate': 1.098e-05, 'close_rate': None, - 'current_rate': 1.098e-05, - 'amount': 90.99181074, + 'current_rate': 1.099e-05, + 'amount': 91.07468124, 'stake_amount': 0.001, 'close_profit': None, - 'current_profit': -0.59, + 'current_profit': -0.41, 'stop_loss': 0.0, 'initial_stop_loss': 0.0, 'initial_stop_loss_pct': None, @@ -78,10 +78,10 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'open_date_hum': ANY, 'close_date': None, 'close_date_hum': None, - 'open_rate': 1.099e-05, + 'open_rate': 1.098e-05, 'close_rate': None, 'current_rate': ANY, - 'amount': 90.99181074, + 'amount': 91.07468124, 'stake_amount': 0.001, 'close_profit': None, 'current_profit': ANY, @@ -121,7 +121,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: assert "Pair" in headers assert 'instantly' == result[0][2] assert 'ETH/BTC' in result[0][1] - assert '-0.59%' == result[0][3] + assert '-0.41%' == result[0][3] # Test with fiatconvert rpc._fiat_converter = CryptoToFiatConverter() @@ -130,7 +130,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: assert "Pair" in headers assert 'instantly' == result[0][2] assert 'ETH/BTC' in result[0][1] - assert '-0.59% (-0.09)' == result[0][3] + assert '-0.41% (-0.06)' == result[0][3] mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_sell_rate', MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available"))) @@ -245,9 +245,9 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, assert prec_satoshi(stats['profit_closed_coin'], 6.217e-05) assert prec_satoshi(stats['profit_closed_percent'], 6.2) assert prec_satoshi(stats['profit_closed_fiat'], 0.93255) - assert prec_satoshi(stats['profit_all_coin'], 5.632e-05) - assert prec_satoshi(stats['profit_all_percent'], 2.81) - assert prec_satoshi(stats['profit_all_fiat'], 0.8448) + assert prec_satoshi(stats['profit_all_coin'], 5.802e-05) + assert prec_satoshi(stats['profit_all_percent'], 2.89) + assert prec_satoshi(stats['profit_all_fiat'], 0.8703) assert stats['trade_count'] == 2 assert stats['first_trade_date'] == 'just now' assert stats['latest_trade_date'] == 'just now' @@ -668,7 +668,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order) -> None trade = rpc._rpc_forcebuy(pair, None) assert isinstance(trade, Trade) assert trade.pair == pair - assert trade.open_rate == ticker()['ask'] + assert trade.open_rate == ticker()['bid'] # Test buy duplicate with pytest.raises(RPCException, match=r'position for ETH/BTC already open - id: 1'): diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 25c971bf7..e0abd886d 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -426,20 +426,20 @@ def test_api_status(botclient, mocker, ticker, fee, markets): rc = client_get(client, f"{BASE_URI}/status") assert_response(rc) assert len(rc.json) == 1 - assert rc.json == [{'amount': 90.99181074, + assert rc.json == [{'amount': 91.07468124, 'base_currency': 'BTC', 'close_date': None, 'close_date_hum': None, 'close_profit': None, 'close_rate': None, - 'current_profit': -0.59, - 'current_rate': 1.098e-05, + 'current_profit': -0.41, + 'current_rate': 1.099e-05, 'initial_stop_loss': 0.0, 'initial_stop_loss_pct': None, 'open_date': ANY, 'open_date_hum': 'just now', 'open_order': '(limit buy rem=0.00000000)', - 'open_rate': 1.099e-05, + 'open_rate': 1.098e-05, 'pair': 'ETH/BTC', 'stake_amount': 0.001, 'stop_loss': 0.0, diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index a8b8e0c5a..fd3e4039a 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -720,13 +720,13 @@ def test_forcesell_handle(default_conf, update, ticker, fee, 'exchange': 'Bittrex', 'pair': 'ETH/BTC', 'gain': 'profit', - 'limit': 1.172e-05, - 'amount': 90.99181073703367, + 'limit': 1.173e-05, + 'amount': 91.07468123861567, 'order_type': 'limit', - 'open_rate': 1.099e-05, - 'current_rate': 1.172e-05, - 'profit_amount': 6.126e-05, - 'profit_percent': 0.0611052, + 'open_rate': 1.098e-05, + 'current_rate': 1.173e-05, + 'profit_amount': 6.314e-05, + 'profit_percent': 0.0629778, 'stake_currency': 'BTC', 'fiat_currency': 'USD', 'sell_reason': SellType.FORCE_SELL.value, @@ -779,13 +779,13 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, 'exchange': 'Bittrex', 'pair': 'ETH/BTC', 'gain': 'loss', - 'limit': 1.044e-05, - 'amount': 90.99181073703367, + 'limit': 1.043e-05, + 'amount': 91.07468123861567, 'order_type': 'limit', - 'open_rate': 1.099e-05, - 'current_rate': 1.044e-05, - 'profit_amount': -5.492e-05, - 'profit_percent': -0.05478342, + 'open_rate': 1.098e-05, + 'current_rate': 1.043e-05, + 'profit_amount': -5.497e-05, + 'profit_percent': -0.05482878, 'stake_currency': 'BTC', 'fiat_currency': 'USD', 'sell_reason': SellType.FORCE_SELL.value, @@ -827,13 +827,13 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None 'exchange': 'Bittrex', 'pair': 'ETH/BTC', 'gain': 'loss', - 'limit': 1.098e-05, - 'amount': 90.99181073703367, + 'limit': 1.099e-05, + 'amount': 91.07468123861567, 'order_type': 'limit', - 'open_rate': 1.099e-05, - 'current_rate': 1.098e-05, - 'profit_amount': -5.91e-06, - 'profit_percent': -0.00589291, + 'open_rate': 1.098e-05, + 'current_rate': 1.099e-05, + 'profit_amount': -4.09e-06, + 'profit_percent': -0.00408133, 'stake_currency': 'BTC', 'fiat_currency': 'USD', 'sell_reason': SellType.FORCE_SELL.value, From e1cb6f4ae3cd1de66df0eb0eb8128b520e170d5c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 23 Feb 2020 15:06:37 +0100 Subject: [PATCH 06/14] fix and improve tests in test_freqtradebot --- tests/test_freqtradebot.py | 125 ++++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 51 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 852b6b990..655fe4684 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -761,8 +761,8 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order, assert trade.is_open assert trade.open_date is not None assert trade.exchange == 'bittrex' - assert trade.open_rate == 0.00001099 - assert trade.amount == 90.99181073703367 + assert trade.open_rate == 0.00001098 + assert trade.amount == 91.07468123861567 assert log_has( 'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog @@ -906,20 +906,28 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: assert ("ETH/BTC", default_conf["ticker_interval"]) in refresh_mock.call_args[0][0] -@pytest.mark.parametrize("ask,last,last_ab,expected", [ - (20, 10, 0.0, 20), # Full ask side - (20, 10, 1.0, 10), # Full last side - (20, 10, 0.5, 15), # Between ask and last - (20, 10, 0.7, 13), # Between ask and last - (20, 10, 0.3, 17), # Between ask and last - (5, 10, 1.0, 5), # last bigger than ask - (5, 10, 0.5, 5), # last bigger than ask +@pytest.mark.parametrize("side,ask,bid,last,last_ab,expected", [ + ('ask', 20, 19, 10, 0.0, 20), # Full ask side + ('ask', 20, 19, 10, 1.0, 10), # Full last side + ('ask', 20, 19, 10, 0.5, 15), # Between ask and last + ('ask', 20, 19, 10, 0.7, 13), # Between ask and last + ('ask', 20, 19, 10, 0.3, 17), # Between ask and last + ('ask', 5, 6, 10, 1.0, 5), # last bigger than ask + ('ask', 5, 6, 10, 0.5, 5), # last bigger than ask + ('bid', 10, 20, 10, 0.0, 20), # Full bid side + ('bid', 10, 20, 10, 1.0, 10), # Full last side + ('bid', 10, 20, 10, 0.5, 15), # Between bid and last + ('bid', 10, 20, 10, 0.7, 13), # Between bid and last + ('bid', 10, 20, 10, 0.3, 17), # Between bid and last + ('bid', 4, 5, 10, 1.0, 5), # last bigger than bid + ('bid', 4, 5, 10, 0.5, 5), # last bigger than bid ]) -def test_get_buy_rate(mocker, default_conf, caplog, ask, last, last_ab, expected) -> None: +def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid, last, last_ab, expected) -> None: default_conf['bid_strategy']['ask_last_balance'] = last_ab + default_conf['bid_strategy']['price_side'] = side freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - MagicMock(return_value={'ask': ask, 'last': last})) + MagicMock(return_value={'ask': ask, 'last': last, 'bid': bid})) assert freqtrade.get_buy_rate('ETH/BTC', True) == expected assert not log_has("Using cached buy rate for ETH/BTC.", caplog) @@ -1317,7 +1325,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, stoploss_order_mock.assert_not_called() assert freqtrade.handle_trade(trade) is False - assert trade.stop_loss == 0.00002344 * 0.95 + assert trade.stop_loss == 0.00002346 * 0.95 # setting stoploss_on_exchange_interval to 0 seconds freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0 @@ -1325,10 +1333,10 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, assert freqtrade.handle_stoploss_on_exchange(trade) is False cancel_order_mock.assert_called_once_with(100, 'ETH/BTC') - stoploss_order_mock.assert_called_once_with(amount=85.25149190110828, + stoploss_order_mock.assert_called_once_with(amount=85.32423208191126, pair='ETH/BTC', order_types=freqtrade.strategy.order_types, - stop_price=0.00002344 * 0.95) + stop_price=0.00002346 * 0.95) # price fell below stoploss, so dry-run sells trade. mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ @@ -1510,12 +1518,12 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, assert freqtrade.handle_stoploss_on_exchange(trade) is False # stoploss should be set to 1% as trailing is on - assert trade.stop_loss == 0.00002344 * 0.99 + assert trade.stop_loss == 0.00002346 * 0.99 cancel_order_mock.assert_called_once_with(100, 'NEO/BTC') - stoploss_order_mock.assert_called_once_with(amount=2131074.168797954, + stoploss_order_mock.assert_called_once_with(amount=2132892.491467577, pair='NEO/BTC', order_types=freqtrade.strategy.order_types, - stop_price=0.00002344 * 0.99) + stop_price=0.00002346 * 0.99) def test_enter_positions(mocker, default_conf, caplog) -> None: @@ -2292,12 +2300,12 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N 'pair': 'ETH/BTC', 'gain': 'profit', 'limit': 1.172e-05, - 'amount': 90.99181073703367, + 'amount': 91.07468123861567, 'order_type': 'limit', - 'open_rate': 1.099e-05, - 'current_rate': 1.172e-05, - 'profit_amount': 6.126e-05, - 'profit_percent': 0.0611052, + 'open_rate': 1.098e-05, + 'current_rate': 1.173e-05, + 'profit_amount': 6.223e-05, + 'profit_percent': 0.0620716, 'stake_currency': 'BTC', 'fiat_currency': 'USD', 'sell_reason': SellType.ROI.value, @@ -2341,12 +2349,12 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) 'pair': 'ETH/BTC', 'gain': 'loss', 'limit': 1.044e-05, - 'amount': 90.99181073703367, + 'amount': 91.07468123861567, 'order_type': 'limit', - 'open_rate': 1.099e-05, - 'current_rate': 1.044e-05, - 'profit_amount': -5.492e-05, - 'profit_percent': -0.05478342, + 'open_rate': 1.098e-05, + 'current_rate': 1.043e-05, + 'profit_amount': -5.406e-05, + 'profit_percent': -0.05392257, 'stake_currency': 'BTC', 'fiat_currency': 'USD', 'sell_reason': SellType.STOP_LOSS.value, @@ -2397,12 +2405,12 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe 'pair': 'ETH/BTC', 'gain': 'loss', 'limit': 1.08801e-05, - 'amount': 90.99181073703367, + 'amount': 91.07468123861567, 'order_type': 'limit', - 'open_rate': 1.099e-05, - 'current_rate': 1.044e-05, - 'profit_amount': -1.498e-05, - 'profit_percent': -0.01493766, + 'open_rate': 1.098e-05, + 'current_rate': 1.043e-05, + 'profit_amount': -1.408e-05, + 'profit_percent': -0.01404051, 'stake_currency': 'BTC', 'fiat_currency': 'USD', 'sell_reason': SellType.STOP_LOSS.value, @@ -2587,7 +2595,7 @@ def test_execute_sell_market_order(default_conf, ticker, fee, freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], sell_reason=SellType.ROI) assert not trade.is_open - assert trade.close_profit == 0.0611052 + assert trade.close_profit == 0.0620716 assert rpc_mock.call_count == 2 last_msg = rpc_mock.call_args_list[-1][0][0] @@ -2597,12 +2605,12 @@ def test_execute_sell_market_order(default_conf, ticker, fee, 'pair': 'ETH/BTC', 'gain': 'profit', 'limit': 1.172e-05, - 'amount': 90.99181073703367, + 'amount': 91.07468123861567, 'order_type': 'market', - 'open_rate': 1.099e-05, - 'current_rate': 1.172e-05, - 'profit_amount': 6.126e-05, - 'profit_percent': 0.0611052, + 'open_rate': 1.098e-05, + 'current_rate': 1.173e-05, + 'profit_amount': 6.223e-05, + 'profit_percent': 0.0620716, 'stake_currency': 'BTC', 'fiat_currency': 'USD', 'sell_reason': SellType.ROI.value, @@ -3624,13 +3632,20 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order assert freqtrade.handle_trade(trade) is True -def test_get_sell_rate(default_conf, mocker, caplog, ticker, order_book_l2) -> None: - - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - get_order_book=order_book_l2, - fetch_ticker=ticker, - ) +@pytest.mark.parametrize('side,ask,bid,expected', [ + ('bid', 10.0, 11.0, 11.0), + ('bid', 10.0, 11.2, 11.2), + ('bid', 10.0, 11.0, 11.0), + ('bid', 9.8, 11.0, 11.0), + ('bid', 0.0001, 0.002, 0.002), + ('ask', 10.0, 11.0, 10.0), + ('ask', 10.11, 11.2, 10.11), + ('ask', 0.001, 0.002, 0.001), + ('ask', 0.006, 1.0, 0.006), +]) +def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask, expected) -> None: + default_conf['ask_strategy']['price_side'] = side + mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'ask': ask, 'bid': bid}) pair = "ETH/BTC" # Test regular mode @@ -3638,25 +3653,33 @@ def test_get_sell_rate(default_conf, mocker, caplog, ticker, order_book_l2) -> N rate = ft.get_sell_rate(pair, True) assert not log_has("Using cached sell rate for ETH/BTC.", caplog) assert isinstance(rate, float) - assert rate == 0.00001098 + assert rate == expected # Use caching rate = ft.get_sell_rate(pair, False) - assert rate == 0.00001098 + assert rate == expected assert log_has("Using cached sell rate for ETH/BTC.", caplog) - caplog.clear() +@pytest.mark.parametrize('side,expected', [ + ('bid', 0.043936), # Value from order_book_l2 fiture - bids side + ('ask', 0.043949), # Value from order_book_l2 fiture - asks side +]) +def test_get_sell_rate_orderbook(default_conf, mocker, caplog, side, expected, order_book_l2): # Test orderbook mode + default_conf['ask_strategy']['price_side'] = side default_conf['ask_strategy']['use_order_book'] = True default_conf['ask_strategy']['order_book_min'] = 1 default_conf['ask_strategy']['order_book_max'] = 2 + # TODO: min/max is irrelevant for this test until refactoring + pair = "ETH/BTC" + mocker.patch('freqtrade.exchange.Exchange.get_order_book', order_book_l2) ft = get_patched_freqtradebot(mocker, default_conf) rate = ft.get_sell_rate(pair, True) assert not log_has("Using cached sell rate for ETH/BTC.", caplog) assert isinstance(rate, float) - assert rate == 0.043936 + assert rate == expected rate = ft.get_sell_rate(pair, False) - assert rate == 0.043936 + assert rate == expected assert log_has("Using cached sell rate for ETH/BTC.", caplog) From 8edc3eb5fb8a6c4dc62f455f12d237c0d670b3d9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 23 Feb 2020 15:39:12 +0100 Subject: [PATCH 07/14] Use generator to generate sell price scaffold testing --- freqtrade/freqtradebot.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 2c9a960a4..22a73a273 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -619,6 +619,15 @@ class FreqtradeBot: return trades_closed + def _order_book_gen(self, pair: str, side: str, order_book_max: int = 1, + order_book_min: int = 1): + """ + Helper generator to query orderbook in loop (used for early sell-order placing) + """ + order_book = self.exchange.get_order_book(pair, order_book_max) + for i in range(order_book_min, order_book_max + 1): + yield order_book[side][i - 1][0] + def get_sell_rate(self, pair: str, refresh: bool) -> float: """ Get sell rate - either using get-ticker bid or first bid based on orderbook @@ -639,9 +648,10 @@ class FreqtradeBot: config_ask_strategy = self.config.get('ask_strategy', {}) if config_ask_strategy.get('use_order_book', False): logger.debug('Using order book to get sell rate') + rate = next(self._order_book_gen(pair, f"{config_ask_strategy['price_side']}s")) - order_book = self.exchange.get_order_book(pair, 1) - rate = order_book[f"{config_ask_strategy['price_side']}s"][0][0] + # order_book = self.exchange.get_order_book(pair, 1) + # rate = order_book[f"{config_ask_strategy['price_side']}s"][0][0] else: rate = self.exchange.fetch_ticker(pair)[config_ask_strategy['price_side']] @@ -674,12 +684,12 @@ class FreqtradeBot: order_book_min = config_ask_strategy.get('order_book_min', 1) order_book_max = config_ask_strategy.get('order_book_max', 1) - order_book = self.exchange.get_order_book(trade.pair, order_book_max) - + order_book = self._order_book_gen(trade.pair, f"{config_ask_strategy['price_side']}s", + order_book_min=order_book_min, + order_book_max=order_book_max) for i in range(order_book_min, order_book_max + 1): - order_book_rate = order_book['asks'][i - 1][0] - logger.debug(' order book asks top %s: %0.8f', i, order_book_rate) - sell_rate = order_book_rate + sell_rate = next(order_book) + logger.debug(' order book asks top %s: %0.8f', i, sell_rate) if self._check_and_execute_sell(trade, sell_rate, buy, sell): return True From 3c5e716d8f5b3c527ae7683877fb10149c03ebe2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 23 Feb 2020 16:00:34 +0100 Subject: [PATCH 08/14] Update some documentation regarding price_side --- docs/configuration.md | 64 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index b05dab7c9..54470f278 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -60,11 +60,13 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `trailing_only_offset_is_reached` | Only apply trailing stoploss when the offset is reached. [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy).
*Defaults to `false`.*
**Datatype:** Boolean | `unfilledtimeout.buy` | **Required.** How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Integer | `unfilledtimeout.sell` | **Required.** How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Integer -| `bid_strategy.ask_last_balance` | **Required.** Set the bidding price. More information [below](#buy-price-without-orderbook). +| `bid_strategy.price_side` | Select the side of the spread the bot should look at to get the buy rate. [More information below](#buy-price-side).
*Defaults to `bid`.*
**Datatype:** String (either `ask` or `bid`). +| `bid_strategy.ask_last_balance` | **Required.** Set the bidding price. More information [below](#buy-price-without-orderbook-enabled). | `bid_strategy.use_order_book` | Enable buying using the rates in [Order Book Bids](#buy-price-with-orderbook-enabled).
**Datatype:** Boolean | `bid_strategy.order_book_top` | Bot will use the top N rate in Order Book Bids to buy. I.e. a value of 2 will allow the bot to pick the 2nd bid rate in [Order Book Bids](#buy-price-with-orderbook-enabled).
*Defaults to `1`.*
**Datatype:** Positive Integer | `bid_strategy. check_depth_of_market.enabled` | Do not buy if the difference of buy orders and sell orders is met in Order Book. [Check market depth](#check-depth-of-market).
*Defaults to `false`.*
**Datatype:** Boolean | `bid_strategy. check_depth_of_market.bids_to_ask_delta` | The difference ratio of buy orders and sell orders found in Order Book. A value below 1 means sell order size is greater, while value greater than 1 means buy order size is higher. [Check market depth](#check-depth-of-market)
*Defaults to `0`.*
**Datatype:** Float (as ratio) +| `ask_strategy.price_side` | Select the side of the spread the bot should look at to get the sell rate. [More information below](#sell-price-side).
*Defaults to `ask`.*
**Datatype:** String (either `ask` or `bid`). | `ask_strategy.use_order_book` | Enable selling of open trades using [Order Book Asks](#sell-price-with-orderbook-enabled).
**Datatype:** Boolean | `ask_strategy.order_book_min` | Bot will scan from the top min to max Order Book Asks searching for a profitable rate.
*Defaults to `1`.*
**Datatype:** Positive Integer | `ask_strategy.order_book_max` | Bot will scan from the top min to max Order Book Asks searching for a profitable rate.
*Defaults to `1`.*
**Datatype:** Positive Integer @@ -461,23 +463,71 @@ Orderbook `bid` (buy) side depth is then divided by the orderbook `ask` (sell) s !!! Note A delta value below 1 means that `ask` (sell) orderbook side depth is greater than the depth of the `bid` (buy) orderbook side, while a value greater than 1 means opposite (depth of the buy side is higher than the depth of the sell side). +#### Buy price side + +The configuration option `bid_strategy.price_side` defines the side of the spread the bot looks for when buying. + +The following displays an orderbook. + +``` explanation +... +103 +102 +101 # ask +-------------Current spread +99 # bid +98 +97 +... +``` + +If `bid_strategy.price_side` is set to `"bid"`, then the bot will use 99 as buying price. +In line with that, if `bid_strategy.price_side` is set to `"ask"`, then the bot will use 101 as buying price. + +Using `ask` price often guarantees quicker filled orders, but the bot can also end up paying more than what would have been necessary. + + #### Buy price with Orderbook enabled -When buying with the orderbook enabled (`bid_strategy.use_order_book=True`), Freqtrade fetches the `bid_strategy.order_book_top` entries from the orderbook and then uses the entry specified as `bid_strategy.order_book_top` on the `bid` (buy) side of the orderbook. 1 specifies the topmost entry in the orderbook, while 2 would use the 2nd entry in the orderbook, and so on. +When buying with the orderbook enabled (`bid_strategy.use_order_book=True`), Freqtrade fetches the `bid_strategy.order_book_top` entries from the orderbook and then uses the entry specified as `bid_strategy.order_book_top` on the configured side (`bid_strategy.price_side`) of the orderbook. 1 specifies the topmost entry in the orderbook, while 2 would use the 2nd entry in the orderbook, and so on. #### Buy price without Orderbook enabled -When not using orderbook (`bid_strategy.use_order_book=False`), Freqtrade uses the best `ask` (sell) price from the ticker if it's below the `last` traded price from the ticker. Otherwise (when the `ask` price is not below the `last` price), it calculates a rate between `ask` and `last` price. +The following section uses `side` as the configured `bid_strategy.price_side`. -The `bid_strategy.ask_last_balance` configuration parameter controls this. A value of `0.0` will use `ask` price, while `1.0` will use the `last` price and values between those interpolate between ask and last price. +When not using orderbook (`bid_strategy.use_order_book=False`), Freqtrade uses the best `side` price from the ticker if it's below the `last` traded price from the ticker. Otherwise (when the `side` price is above the `last` price), it calculates a rate between `side` and `last` price. -Using `ask` price often guarantees quicker success in the bid, but the bot can also end up paying more than what would have been necessary. +The `bid_strategy.ask_last_balance` configuration parameter controls this. A value of `0.0` will use `side` price, while `1.0` will use the `last` price and values between those interpolate between ask and last price. ### Sell price +#### Sell price side + +The configuration option `ask_strategy.price_side` defines the side of the spread the bot looks for when selling. + +The following displays an orderbook: + +``` explanation +... +103 +102 +101 # ask +-------------Current spread +99 # bid +98 +97 +... +``` + +If `ask_strategy.price_side` is set to `"ask"`, then the bot will use 101 as selling price. +In line with that, if `ask_strategy.price_side` is set to `"bid"`, then the bot will use 99 as selling price. + #### Sell price with Orderbook enabled -When selling with the orderbook enabled (`ask_strategy.use_order_book=True`), Freqtrade fetches the `ask_strategy.order_book_max` entries in the orderbook. Then each of the orderbook steps between `ask_strategy.order_book_min` and `ask_strategy.order_book_max` on the `ask` orderbook side are validated for a profitable sell-possibility based on the strategy configuration and the sell order is placed at the first profitable spot. +When selling with the orderbook enabled (`ask_strategy.use_order_book=True`), Freqtrade fetches the `ask_strategy.order_book_max` entries in the orderbook. Then each of the orderbook steps between `ask_strategy.order_book_min` and `ask_strategy.order_book_max` on the configured orderbook side are validated for a profitable sell-possibility based on the strategy configuration and the sell order is placed at the first profitable spot. + +!!! Note: + Using `order_book_max` higher than `order_book_min` only makes sense when ask_strategy.price_side is set to `"ask"`. The idea here is to place the sell order early, to be ahead in the queue. @@ -488,7 +538,7 @@ A fixed slot (mirroring `bid_strategy.order_book_top`) can be defined by setting #### Sell price without Orderbook enabled -When not using orderbook (`ask_strategy.use_order_book=False`), the `bid` price from the ticker will be used as the sell price. +When not using orderbook (`ask_strategy.use_order_book=False`), the price at the `ask_strategy.price_side` side (defaults to `"ask"`) from the ticker will be used as the sell price. ## Pairlists From 0fea3a7ea74451243a9518d3059599d15ea7a9aa Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 26 Feb 2020 19:49:54 +0100 Subject: [PATCH 09/14] Some final polish to configurable_side --- docs/configuration.md | 2 +- freqtrade/freqtradebot.py | 4 +--- tests/test_freqtradebot.py | 3 ++- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 54470f278..c70f3425c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -526,7 +526,7 @@ In line with that, if `ask_strategy.price_side` is set to `"bid"`, then the bot When selling with the orderbook enabled (`ask_strategy.use_order_book=True`), Freqtrade fetches the `ask_strategy.order_book_max` entries in the orderbook. Then each of the orderbook steps between `ask_strategy.order_book_min` and `ask_strategy.order_book_max` on the configured orderbook side are validated for a profitable sell-possibility based on the strategy configuration and the sell order is placed at the first profitable spot. -!!! Note: +!!! Note Using `order_book_max` higher than `order_book_min` only makes sense when ask_strategy.price_side is set to `"ask"`. The idea here is to place the sell order early, to be ahead in the queue. diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 22a73a273..53493ad4c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -647,12 +647,10 @@ class FreqtradeBot: config_ask_strategy = self.config.get('ask_strategy', {}) if config_ask_strategy.get('use_order_book', False): + # This code is only used for notifications, selling uses the generator directly logger.debug('Using order book to get sell rate') rate = next(self._order_book_gen(pair, f"{config_ask_strategy['price_side']}s")) - # order_book = self.exchange.get_order_book(pair, 1) - # rate = order_book[f"{config_ask_strategy['price_side']}s"][0][0] - else: rate = self.exchange.fetch_ticker(pair)[config_ask_strategy['price_side']] self._sell_rate_cache[pair] = rate diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 655fe4684..49000382f 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -922,7 +922,8 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: ('bid', 4, 5, 10, 1.0, 5), # last bigger than bid ('bid', 4, 5, 10, 0.5, 5), # last bigger than bid ]) -def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid, last, last_ab, expected) -> None: +def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid, + last, last_ab, expected) -> None: default_conf['bid_strategy']['ask_last_balance'] = last_ab default_conf['bid_strategy']['price_side'] = side freqtrade = get_patched_freqtradebot(mocker, default_conf) From b6839289ec2cba080c3bfeb2d956e1e70660770e Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 26 Feb 2020 20:03:03 +0100 Subject: [PATCH 10/14] Add price_side to sample config files --- config_full.json.example | 2 ++ freqtrade/templates/base_config.json.j2 | 2 ++ 2 files changed, 4 insertions(+) diff --git a/config_full.json.example b/config_full.json.example index cdb7e841e..f0414bd0d 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -25,6 +25,7 @@ "sell": 30 }, "bid_strategy": { + "price_side": "bid", "use_order_book": false, "ask_last_balance": 0.0, "order_book_top": 1, @@ -34,6 +35,7 @@ } }, "ask_strategy":{ + "price_side": "ask", "use_order_book": false, "order_book_min": 1, "order_book_max": 9, diff --git a/freqtrade/templates/base_config.json.j2 b/freqtrade/templates/base_config.json.j2 index 88edeb1e8..0049d59a0 100644 --- a/freqtrade/templates/base_config.json.j2 +++ b/freqtrade/templates/base_config.json.j2 @@ -11,6 +11,7 @@ "sell": 30 }, "bid_strategy": { + "price_side": "bid", "ask_last_balance": 0.0, "use_order_book": false, "order_book_top": 1, @@ -20,6 +21,7 @@ } }, "ask_strategy": { + "price_side": "ask", "use_order_book": false, "order_book_min": 1, "order_book_max": 9, From 6e2290c4f0d142de4e924f7ee78b209d90ec3643 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 2 Mar 2020 20:05:54 +0100 Subject: [PATCH 11/14] Allow last to be empty - closes #3005 --- freqtrade/freqtradebot.py | 8 +++----- tests/test_freqtradebot.py | 8 ++++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 53493ad4c..04e3dd72f 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -257,12 +257,10 @@ class FreqtradeBot: else: logger.info(f"Using Last {bid_strategy['price_side'].capitalize()} / Last Price") ticker = self.exchange.fetch_ticker(pair) - rate = ticker[bid_strategy['price_side']] - if rate < ticker['last']: - ticker_rate = rate - else: + ticker_rate = ticker[bid_strategy['price_side']] + if ticker['last'] and ticker_rate > ticker['last']: balance = self.config['bid_strategy']['ask_last_balance'] - ticker_rate = rate + balance * (ticker['last'] - rate) + ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate) used_rate = ticker_rate self._buy_rate_cache[pair] = used_rate diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 49000382f..a5506017c 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -914,6 +914,10 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: ('ask', 20, 19, 10, 0.3, 17), # Between ask and last ('ask', 5, 6, 10, 1.0, 5), # last bigger than ask ('ask', 5, 6, 10, 0.5, 5), # last bigger than ask + ('ask', 10, 20, None, 0.5, 10), # last not available - uses ask + ('ask', 4, 5, None, 0.5, 4), # last not available - uses ask + ('ask', 4, 5, None, 1, 4), # last not available - uses ask + ('ask', 4, 5, None, 0, 4), # last not available - uses ask ('bid', 10, 20, 10, 0.0, 20), # Full bid side ('bid', 10, 20, 10, 1.0, 10), # Full last side ('bid', 10, 20, 10, 0.5, 15), # Between bid and last @@ -921,6 +925,10 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: ('bid', 10, 20, 10, 0.3, 17), # Between bid and last ('bid', 4, 5, 10, 1.0, 5), # last bigger than bid ('bid', 4, 5, 10, 0.5, 5), # last bigger than bid + ('bid', 10, 20, None, 0.5, 20), # last not available - uses bid + ('bid', 4, 5, None, 0.5, 5), # last not available - uses bid + ('bid', 4, 5, None, 1, 5), # last not available - uses bid + ('bid', 4, 5, None, 0, 5), # last not available - uses bid ]) def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid, last, last_ab, expected) -> None: From 53dcb5d5ed1dd1dc741e9cf3f6d0b49f90e55029 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 3 Mar 2020 19:36:03 +0100 Subject: [PATCH 12/14] Fix logging expression --- freqtrade/freqtradebot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 04e3dd72f..920c9203e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -685,7 +685,8 @@ class FreqtradeBot: order_book_max=order_book_max) for i in range(order_book_min, order_book_max + 1): sell_rate = next(order_book) - logger.debug(' order book asks top %s: %0.8f', i, sell_rate) + logger.debug(f" order book {config_ask_strategy['price_side']} top {i}: " + f"{sell_rate:0.8f}") if self._check_and_execute_sell(trade, sell_rate, buy, sell): return True From 8de35e1c83becae0f226deb1f55fd7277510cf41 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Mar 2020 06:40:19 +0100 Subject: [PATCH 13/14] Documentation suggestions from Review Co-Authored-By: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> --- docs/configuration.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index c70f3425c..c2c144541 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -465,7 +465,7 @@ Orderbook `bid` (buy) side depth is then divided by the orderbook `ask` (sell) s #### Buy price side -The configuration option `bid_strategy.price_side` defines the side of the spread the bot looks for when buying. +The configuration setting `bid_strategy.price_side` defines the side of the spread the bot looks for when buying. The following displays an orderbook. @@ -503,7 +503,7 @@ The `bid_strategy.ask_last_balance` configuration parameter controls this. A val #### Sell price side -The configuration option `ask_strategy.price_side` defines the side of the spread the bot looks for when selling. +The configuration setting `ask_strategy.price_side` defines the side of the spread the bot looks for when selling. The following displays an orderbook: @@ -524,7 +524,7 @@ In line with that, if `ask_strategy.price_side` is set to `"bid"`, then the bot #### Sell price with Orderbook enabled -When selling with the orderbook enabled (`ask_strategy.use_order_book=True`), Freqtrade fetches the `ask_strategy.order_book_max` entries in the orderbook. Then each of the orderbook steps between `ask_strategy.order_book_min` and `ask_strategy.order_book_max` on the configured orderbook side are validated for a profitable sell-possibility based on the strategy configuration and the sell order is placed at the first profitable spot. +When selling with the orderbook enabled (`ask_strategy.use_order_book=True`), Freqtrade fetches the `ask_strategy.order_book_max` entries in the orderbook. Then each of the orderbook steps between `ask_strategy.order_book_min` and `ask_strategy.order_book_max` on the configured orderbook side are validated for a profitable sell-possibility based on the strategy configuration (`minimal_roi` conditions) and the sell order is placed at the first profitable spot. !!! Note Using `order_book_max` higher than `order_book_min` only makes sense when ask_strategy.price_side is set to `"ask"`. From 2f54aff0ce25d04edf25f9663c62c9138cabf622 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Mar 2020 06:44:47 +0100 Subject: [PATCH 14/14] Improve documentation wording for price sides --- docs/configuration.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index c2c144541..7e33f1528 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -485,7 +485,8 @@ If `bid_strategy.price_side` is set to `"bid"`, then the bot will use 99 as buyi In line with that, if `bid_strategy.price_side` is set to `"ask"`, then the bot will use 101 as buying price. Using `ask` price often guarantees quicker filled orders, but the bot can also end up paying more than what would have been necessary. - +Taker fees instead of maker fees will most likely apply even when using limit buy orders. +Also, prices at the "ask" side of the spread are higher than prices at the "bid" side in the orderbook, so the order behaves similar to a market order (however with a maximum price). #### Buy price with Orderbook enabled