From fff2b81c10435e192fbe018fd8f156e670e218ac Mon Sep 17 00:00:00 2001 From: Nullart Date: Wed, 27 Jun 2018 21:47:29 +0800 Subject: [PATCH] experimental depth of market check prior to buying --- README.md | 1 + config.json.example | 4 +++- config_full.json.example | 4 +++- docs/configuration.md | 5 +++++ freqtrade/analyze.py | 12 ++++-------- freqtrade/constants.py | 4 +++- freqtrade/freqtradebot.py | 16 ++++++++++++++-- 7 files changed, 33 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 8fe97f630..4039c2c00 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ strategy parameters with real exchange data. - [x] [separated unfilled orders timeout](docs/configuration.md) - [x] [option to disable buying](docs/configuration.md) - [x] [option to get a buy price based on %](docs/configuration.md) +- [x] [option to check depth of market before buying](docs/configuration.md) ### Drawbacks diff --git a/config.json.example b/config.json.example index 0ea0fc344..a7858e29b 100644 --- a/config.json.example +++ b/config.json.example @@ -47,7 +47,9 @@ "experimental": { "use_sell_signal": false, "sell_profit_only": false, - "sell_fullfilled_at_roi": false + "sell_fullfilled_at_roi": false, + "check_depth_of_market": true, + "dom_bids_asks_delta": 1.5 }, "telegram": { "enabled": true, diff --git a/config_full.json.example b/config_full.json.example index 41e14822c..2374f5bd1 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -52,7 +52,9 @@ "experimental": { "use_sell_signal": false, "sell_profit_only": false, - "sell_fullfilled_at_roi": false + "sell_fullfilled_at_roi": false, + "check_depth_of_market": true, + "dom_bids_asks_delta": 1.5 }, "telegram": { "enabled": true, diff --git a/docs/configuration.md b/docs/configuration.md index 925af823b..fde8368c2 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -40,6 +40,8 @@ The table below will list all configuration parameters. | `experimental.use_sell_signal` | false | No | Use your sell strategy in addition of the `minimal_roi`. | `experimental.sell_profit_only` | false | No | waits until you have made a positive profit before taking a sell decision. | `experimental.sell_fullfilled_at_roi` | false | No | automatically creates a sell order based on `minimal_roi` once a buy order has been fullfilled. +| `experimental.check_depth_of_market` | false | No | checks order book depth by comparing total size of bids and total size of asks. [More information below](docs/configuration.md#understanding-experimentalcheck_depth_of_market). +| `experimental.dom_bids_asks_delta` | 0 | No | the difference of total size bids vs total size asks to indicate a buy signal. [More information below](docs/configuration.md#understanding-experimentalcheck_depth_of_market). | `telegram.enabled` | true | Yes | Enable or not the usage of Telegram. | `telegram.token` | token | No | Your Telegram bot token. Only required if `telegram.enabled` is `true`. | `telegram.chat_id` | chat_id | No | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. @@ -91,6 +93,9 @@ Most of the strategy files already include the optimal `stoploss` value. This pa ### Understanding ask_strategy.use_book_order `ask_strategy.use_book_order` loads the exchange book order and sets the askng price based on the `book_order_top` value. If the `book_order_min` is set to 3 and `book_order_max` is set to 10, then the bot will search between top 3rd and 10th asking prices from the top of the book order will be selected as the bidding price for the trade. +### Understanding experimental.check_depth_of_market +`experimental.check_depth_of_market` loads the exchange book order of a pair and calculates the total size of bids and asks. If the difference of the total size of bids and asks reaches the `experimental.dom_bids_asks_delta` then a buy signal is triggered. Do note that `experimental.check_depth_of_market` will only be executed after the strategy triggers a buy signal. + ### What are the valid values for exchange.name? Freqtrade is based on [CCXT library](https://github.com/ccxt/ccxt) that supports 115+ cryptocurrency exchange markets and trading APIs. The complete up-to-date list can be found in the [CCXT repo homepage](https://github.com/ccxt/ccxt/tree/master/python). However, the bot was thoroughly tested with only Bittrex and Binance. diff --git a/freqtrade/analyze.py b/freqtrade/analyze.py index 5e774d373..a2238e7de 100644 --- a/freqtrade/analyze.py +++ b/freqtrade/analyze.py @@ -11,7 +11,7 @@ import pandas as pd from pandas import DataFrame, to_datetime from freqtrade import constants -from freqtrade.exchange import get_fee, get_ticker_history +from freqtrade.exchange import get_fee, get_ticker_history, get_order_book from freqtrade.persistence import Trade from freqtrade.strategy.resolver import StrategyResolver, IStrategy @@ -108,7 +108,7 @@ class Analyze(object): dataframe = self.parse_ticker_dataframe(ticker_history) # eliminate partials for known exchanges that sends partial candles if self.config['exchange']['name'] in ['binance']: - logger.info('eliminating partial candle') + logger.debug('eliminating partial candle') dataframe.drop(dataframe.tail(1).index, inplace=True) # eliminate partial candle dataframe = self.populate_indicators(dataframe, pair) dataframe = self.populate_buy_trend(dataframe, pair) @@ -122,6 +122,7 @@ class Analyze(object): :param interval: Interval to use (in min) :return: (Buy, Sell) A bool-tuple indicating buy/sell signal """ + logger.info('Checking signal for %s', pair) ticker_hist = get_ticker_history(pair, interval) if not ticker_hist: logger.warning('Empty ticker history for pair %s', pair) @@ -271,7 +272,7 @@ class Analyze(object): break return sell_rate - def order_book_to_dataframe(data: list) -> DataFrame: + def order_book_to_dataframe(self, data: list) -> DataFrame: """ Gets order book list, returns dataframe with below format ------------------------------------------------------------------- @@ -292,8 +293,3 @@ class Analyze(object): keys=['b_sum', 'b_size', 'bids', 'asks', 'a_size', 'a_sum']) return frame - - def order_book_dom() -> DataFrame: - # https://stackoverflow.com/questions/36835793/pandas-group-by-consecutive-ranges - return DataFrame - diff --git a/freqtrade/constants.py b/freqtrade/constants.py index e8ecafc2f..404fd322c 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -93,7 +93,9 @@ CONF_SCHEMA = { 'properties': { 'use_sell_signal': {'type': 'boolean'}, 'sell_profit_only': {'type': 'boolean'}, - 'sell_fullfilled_at_roi': {'type': 'boolean'} + 'sell_fullfilled_at_roi': {'type': 'boolean'}, + 'check_depth_of_market': {'type': 'boolean'}, + 'dom_bids_asks_delta': {'type': 'number', 'minimum': 0} } }, 'telegram': { diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 2a31b9a17..4fde1add0 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -195,7 +195,6 @@ class FreqtradeBot(object): :param key: sort key (defaults to 'quoteVolume') :return: List of pairs """ - if not exchange.exchange_has('fetchTickers'): raise OperationalException( 'Exchange does not support dynamic whitelist.' @@ -264,7 +263,7 @@ class FreqtradeBot(object): if 'use_book_order' in self.config['bid_strategy'] and self.config['bid_strategy'].get('use_book_order', False): logger.info('Getting price from Order Book') orderBook_top = self.config.get('bid_strategy', {}).get('book_order_top', 1) - orderBook = exchange.get_order_book(pair, orderBook_top) + orderBook = exchange.get_order_book(pair, orderBook_top) # top 1 = index 0 orderBook_rate = orderBook['bids'][orderBook_top - 1][0] orderBook_rate = orderBook_rate + 0.00000001 @@ -327,6 +326,19 @@ class FreqtradeBot(object): break else: return False + + # order book depth of market + if self.config.get('experimental', {}).get('check_depth_of_market', False) \ + and (self.config.get('experimental', {}).get('dom_bids_asks_delta', 0) > 0): + logger.info('depth of market check for %s', pair) + orderBook = exchange.get_order_book(pair, 1000) + orderBook_df = self.analyze.order_book_to_dataframe(orderBook) + orderBook_bids = orderBook_df['b_size'].sum() + orderBook_asks = orderBook_df['a_size'].sum() + logger.info('bids: %s, asks: %s, delta: %s', orderBook_bids, orderBook_asks, orderBook_bids / orderBook_asks) + if (orderBook_bids / orderBook_asks) < self.config.get('experimental', {}).get('dom_bids_asks_delta', 0): + return False + pair_s = pair.replace('_', '/') pair_url = exchange.get_pair_detail_url(pair) # Calculate amount