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..9c48dcc23 100644 --- a/config.json.example +++ b/config.json.example @@ -47,7 +47,10 @@ "experimental": { "use_sell_signal": false, "sell_profit_only": false, - "sell_fullfilled_at_roi": false + "sell_fullfilled_at_roi": false, + "check_depth_of_market": false, + "dom_bids_asks_delta": 1.5, + "buy_price_below_24h_h_l": false }, "telegram": { "enabled": true, diff --git a/config_full.json.example b/config_full.json.example index 41e14822c..7ee22fe55 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -52,7 +52,10 @@ "experimental": { "use_sell_signal": false, "sell_profit_only": false, - "sell_fullfilled_at_roi": false + "sell_fullfilled_at_roi": false, + "check_depth_of_market": false, + "dom_bids_asks_delta": 1.5 + "buy_price_below_24h_h_l": false }, "telegram": { "enabled": true, diff --git a/docs/configuration.md b/docs/configuration.md index 925af823b..4a217991c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -18,20 +18,20 @@ The table below will list all configuration parameters. | `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. | `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](docs/configuration.md#what-are-the-valid-values-for-fiat_display_currency). -| `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode. [More information below](docs/configuration.md#switch-to-dry-run--paper-trading-mode) -| `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file. [More information below](docs/configuration.md#understanding-minimal_roi). +| `fiat_display_currency` | USD | Yes | Fiat currency used to show your profits. [More information below](#what-are-the-valid-values-for-fiat_display_currency). +| `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode. [More information below](#switch-to-dry-run--paper-trading-mode) +| `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file. [More information below](#understanding-minimal_roi). | `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file. | `disable_buy` | false | No | Disables buying of crypto-currency. Bot will continue to sell. | `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. | `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. -| `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. [More information below](docs/configuration.md#understanding-bid_strategyask_last_balance). -| `bid_strategy.use_book_order` | false | No | Use book order to set the bidding price. [More information below](docs/configuration.md#understanding-bid_strategyuse_book_order). -| `bid_strategy.book_order_top` | 1 | No | Selects the top n bidding price in book order. [More information below](docs/configuration.md#understanding-bid_strategyuse_book_order). -| `bid_strategy.percent_from_top` | 0 | No | Set the percent to deduct from the buy rate from book order (if enabled) or from ask/last price. [More information below](docs/configuration.md#understanding-bid_strategypercent_from_top). +| `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. [More information below](#understanding-bid_strategyask_last_balance). +| `bid_strategy.use_book_order` | false | No | Use book order to set the bidding price. [More information below](#understanding-bid_strategyuse_book_order). +| `bid_strategy.book_order_top` | 1 | No | Selects the top n bidding price in book order. [More information below](#understanding-bid_strategyuse_book_order). +| `bid_strategy.percent_from_top` | 0 | No | Set the percent to deduct from the buy rate from book order (if enabled) or from ask/last price. [More information below](#understanding-bid_strategypercent_from_top). | `ask_strategy.use_book_order` | false | No | Use book order to set the asking price. More information below. -| `ask_strategy.book_order_min` | 1 | No | The minimum index from the top to search for profitable asking price from book order. [More information below](docs/configuration.md#understanding-ask_strategyuse_book_order). -| `ask_strategy.book_order_max` | 1 | No | The maximum index from the top to search for profitable asking price from book order. [More information below](docs/configuration.md#understanding-ask_strategyuse_book_order). +| `ask_strategy.book_order_min` | 1 | No | The minimum index from the top to search for profitable asking price from book order. [More information below](#understanding-ask_strategyuse_book_order). +| `ask_strategy.book_order_max` | 1 | No | The maximum index from the top to search for profitable asking price from book order. [More information below](#understanding-ask_strategyuse_book_order). | `exchange.name` | bittrex | Yes | Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). | `exchange.key` | key | No | API key to use for the exchange. Only required when you are in production mode. | `exchange.secret` | secret | No | API secret to use for the exchange. Only required when you are in production mode. @@ -40,11 +40,14 @@ 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](#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](#understanding-experimentalcheck_depth_of_market). +| `experimental.buy_price_below_24h_h_l` | false | No | allows buy if the buying price is below average of 24 hour high and low. | `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`. | `db_url` | `sqlite:///tradesv3.sqlite` | No | Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`. -| `initial_state` | running | No | Defines the initial application state. [More information below](docs/configuration.md#understanding-initial_state). +| `initial_state` | running | No | Defines the initial application state. [More information below](#understanding-initial_state). | `strategy` | DefaultStrategy | No | Defines Strategy class to use. | `strategy_path` | null | No | Adds an additional strategy lookup path (must be a folder). | `internals.process_throttle_secs` | 5 | Yes | Set the process throttle. Value in second. @@ -91,6 +94,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..87db72367 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -93,7 +93,10 @@ 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}, + 'buy_price_below_24h_h_l': {'type': 'boolean'}, } }, 'telegram': { diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 2a31b9a17..b290244a2 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 @@ -323,10 +322,31 @@ class FreqtradeBot(object): for _pair in whitelist: (buy, sell) = self.analyze.get_signal(_pair, interval) if buy and not sell: - pair = _pair - break + # 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): + # check if price is below average of 24h high low + if self.config.get('experimental', {}).get('buy_price_below_24h_h_l', False): + pair_ticker = exchange.get_ticker(_pair) + logger.info('checking ask price if below 24h high %s and low %s average...', pair_ticker['high'], pair_ticker['low']) + if pair_ticker['ask'] > ((pair_ticker['high']+pair_ticker['low'])/2): + pair = _pair + break + pair = _pair + break + else: + pair = _pair + break else: return False + pair_s = pair.replace('_', '/') pair_url = exchange.get_pair_detail_url(pair) # Calculate amount