bring this PR closer to reality, with better naming

This commit is contained in:
Yazeed Al Oyoun 2020-02-04 11:24:57 +01:00
parent f209ef6ad9
commit 29a8674496
9 changed files with 112 additions and 85 deletions

View File

@ -7,13 +7,17 @@
"ticker_interval" : "5m", "ticker_interval" : "5m",
"dry_run": false, "dry_run": false,
"trailing_stop": false, "trailing_stop": false,
"unfilledtimeout": { "unfilled_timeout": {
"buy": 10, "buy": {
"sell": 30 "after": 10,
"if_buy_signal_still_valid": false
},
"sell": {
"after": 30
}
}, },
"bid_strategy": { "bid_strategy": {
"ask_last_balance": 0.0, "ask_last_balance": 0.0,
"timeout_even_if_buy_signal_valid": true,
"use_order_book": false, "use_order_book": false,
"order_book_top": 1, "order_book_top": 1,
"check_depth_of_market": { "check_depth_of_market": {

View File

@ -7,13 +7,17 @@
"ticker_interval" : "5m", "ticker_interval" : "5m",
"dry_run": true, "dry_run": true,
"trailing_stop": false, "trailing_stop": false,
"unfilledtimeout": { "unfilled_timeout": {
"buy": 10, "buy": {
"sell": 30 "after": 10,
"if_buy_signal_still_valid": false
},
"sell": {
"after": 30
}
}, },
"bid_strategy": { "bid_strategy": {
"use_order_book": false, "use_order_book": false,
"timeout_even_if_buy_signal_valid": true,
"ask_last_balance": 0.0, "ask_last_balance": 0.0,
"order_book_top": 1, "order_book_top": 1,
"check_depth_of_market": { "check_depth_of_market": {

View File

@ -20,13 +20,17 @@
"0": 0.04 "0": 0.04
}, },
"stoploss": -0.10, "stoploss": -0.10,
"unfilledtimeout": { "unfilled_timeout": {
"buy": 10, "buy": {
"sell": 30 "after": 10,
"if_buy_signal_still_valid": false
},
"sell": {
"after": 30
}
}, },
"bid_strategy": { "bid_strategy": {
"use_order_book": false, "use_order_book": false,
"timeout_even_if_buy_signal_valid": true,
"ask_last_balance": 0.0, "ask_last_balance": 0.0,
"order_book_top": 1, "order_book_top": 1,
"check_depth_of_market": { "check_depth_of_market": {

View File

@ -7,13 +7,17 @@
"ticker_interval" : "5m", "ticker_interval" : "5m",
"dry_run": true, "dry_run": true,
"trailing_stop": false, "trailing_stop": false,
"unfilledtimeout": { "unfilled_timeout": {
"buy": 10, "buy": {
"sell": 30 "after": 10,
"if_buy_signal_still_valid": false
},
"sell": {
"after": 30
}
}, },
"bid_strategy": { "bid_strategy": {
"use_order_book": false, "use_order_book": false,
"timeout_even_if_buy_signal_valid": true,
"ask_last_balance": 0.0, "ask_last_balance": 0.0,
"order_book_top": 1, "order_book_top": 1,
"check_depth_of_market": { "check_depth_of_market": {

View File

@ -58,10 +58,10 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `trailing_stop_positive` | Changes stoploss once profit has been reached. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br> ***Datatype:*** *Float* | `trailing_stop_positive` | Changes stoploss once profit has been reached. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br> ***Datatype:*** *Float*
| `trailing_stop_positive_offset` | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `0.0` (no offset).* <br> ***Datatype:*** *Float* | `trailing_stop_positive_offset` | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `0.0` (no offset).* <br> ***Datatype:*** *Float*
| `trailing_only_offset_is_reached` | Only apply trailing stoploss when the offset is reached. [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> ***Datatype:*** *Boolean* | `trailing_only_offset_is_reached` | Only apply trailing stoploss when the offset is reached. [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> ***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).<br> ***Datatype:*** *Integer* | `unfilled_timeout.buy.after` | **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).<br> ***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).<br> ***Datatype:*** *Integer* | `unfilled_timeout.buy.if_buy_signal_still_valid` | **Required.** Choose whether you would prefer unfilled buy orders to [timeout if buy signal was still valid](#timeout-even-if-buy-signal-valid). If false, your unfilled buy orders won't get timed out, so use this option wisely. <br>*Defaults to `true`.* <br> ***Datatype:*** *Boolean*
| `unfilled_timeout.sell.after` | **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).<br> ***Datatype:*** *Integer*
| `bid_strategy.ask_last_balance` | **Required.** Set the bidding price. More information [below](#buy-price-without-orderbook). | `bid_strategy.ask_last_balance` | **Required.** Set the bidding price. More information [below](#buy-price-without-orderbook).
| `bid_strategy.timeout_even_if_buy_signal_valid` | **Required.** Choose whether you would prefer buy orders to [timeout even if buy signal was still valid](#timeout-even-if-buy-signal-valid). If false, your buy orders will not timeout and will ignore your unfilledtimeout for buy orders, so use this option wisely. <br>*Defaults to `true`.* <br> ***Datatype:*** *Boolean*
| `bid_strategy.use_order_book` | Enable buying using the rates in [Order Book Bids](#buy-price-with-orderbook-enabled). <br> ***Datatype:*** *Boolean* | `bid_strategy.use_order_book` | Enable buying using the rates in [Order Book Bids](#buy-price-with-orderbook-enabled). <br> ***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). <br>*Defaults to `1`.* <br> ***Datatype:*** *Positive Integer* | `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). <br>*Defaults to `1`.* <br> ***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). <br>*Defaults to `false`.* <br> ***Datatype:*** *Boolean* | `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). <br>*Defaults to `false`.* <br> ***Datatype:*** *Boolean*
@ -128,7 +128,7 @@ Values set in the configuration file always overwrite values set in the strategy
* `order_time_in_force` * `order_time_in_force`
* `stake_currency` * `stake_currency`
* `stake_amount` * `stake_amount`
* `unfilledtimeout` * `unfilled_timeout`
* `use_sell_signal` (ask_strategy) * `use_sell_signal` (ask_strategy)
* `sell_profit_only` (ask_strategy) * `sell_profit_only` (ask_strategy)
* `ignore_roi_if_buy_signal` (ask_strategy) * `ignore_roi_if_buy_signal` (ask_strategy)

View File

@ -94,11 +94,22 @@ CONF_SCHEMA = {
'trailing_stop_positive': {'type': 'number', 'minimum': 0, 'maximum': 1}, 'trailing_stop_positive': {'type': 'number', 'minimum': 0, 'maximum': 1},
'trailing_stop_positive_offset': {'type': 'number', 'minimum': 0, 'maximum': 1}, 'trailing_stop_positive_offset': {'type': 'number', 'minimum': 0, 'maximum': 1},
'trailing_only_offset_is_reached': {'type': 'boolean'}, 'trailing_only_offset_is_reached': {'type': 'boolean'},
'unfilledtimeout': { 'unfilled_timeout': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'buy': {'type': 'number', 'minimum': 1}, 'buy': {
'sell': {'type': 'number', 'minimum': 1} 'type': 'object',
'properties': {
'after': {'type': 'number', 'minimum': 1},
'if_buy_signal_still_valid': {'type': 'boolean'},
},
},
'sell': {
'type': 'object',
'properties': {
'after': {'type': 'number', 'minimum': 1},
}
}
} }
}, },
'bid_strategy': { 'bid_strategy': {
@ -110,7 +121,6 @@ CONF_SCHEMA = {
'maximum': 1, 'maximum': 1,
'exclusiveMaximum': False, 'exclusiveMaximum': False,
}, },
'timeout_even_if_buy_signal_valid': {'type': 'boolean'},
'use_order_book': {'type': 'boolean'}, 'use_order_book': {'type': 'boolean'},
'order_book_top': {'type': 'integer', 'maximum': 20, 'minimum': 1}, 'order_book_top': {'type': 'integer', 'maximum': 20, 'minimum': 1},
'check_depth_of_market': { 'check_depth_of_market': {
@ -286,7 +296,7 @@ SCHEMA_TRADE_REQUIRED = [
'dry_run', 'dry_run',
'dry_run_wallet', 'dry_run_wallet',
'bid_strategy', 'bid_strategy',
'unfilledtimeout', 'unfilled_timeout',
'stoploss', 'stoploss',
'minimal_roi', 'minimal_roi',
] ]

View File

@ -785,7 +785,7 @@ class FreqtradeBot:
""" """
Check if timeout is active, and if the order is still open and timed out Check if timeout is active, and if the order is still open and timed out
""" """
timeout = self.config.get('unfilledtimeout', {}).get(side) timeout = self.config.get('unfilled_timeout', {}).get(side).get('after')
ordertime = arrow.get(order['datetime']).datetime ordertime = arrow.get(order['datetime']).datetime
if timeout is not None: if timeout is not None:
timeout_threshold = arrow.utcnow().shift(minutes=-timeout).datetime timeout_threshold = arrow.utcnow().shift(minutes=-timeout).datetime
@ -821,8 +821,20 @@ class FreqtradeBot:
if ((order['side'] == 'buy' and order['status'] == 'canceled') if ((order['side'] == 'buy' and order['status'] == 'canceled')
or (self._check_timed_out('buy', order))): or (self._check_timed_out('buy', order))):
self.handle_timedout_limit_buy(trade, order) # running get_signal on historical data fetched
self.wallets.update() (buy, sell) = self.strategy.get_signal(
trade.pair, self.strategy.ticker_interval,
self.dataprovider.ohlcv(trade.pair, self.strategy.ticker_interval))
# proceed to cancel buy order by timeout if configuration
# unfilled_timeout.if_buy_signal_still_valid is true (original behaviour) -OR-
# cancel buy order only if buying condition is no longer valid OR if there's
# a sell signal present
config_buy_signal_still_valid = self.config.get('unfilled_timeout', {}) \
.get('buy').get('if_buy_signal_still_valid')
if (config_buy_signal_still_valid) or (not buy or sell):
self.handle_timedout_limit_buy(trade, order)
self.wallets.update()
elif ((order['side'] == 'sell' and order['status'] == 'canceled') elif ((order['side'] == 'sell' and order['status'] == 'canceled')
or (self._check_timed_out('sell', order))): or (self._check_timed_out('sell', order))):
@ -846,59 +858,44 @@ class FreqtradeBot:
""" """
reason = "cancelled due to timeout" reason = "cancelled due to timeout"
# running get_signal on historical data fetched if order['status'] != 'canceled':
(buy, sell) = self.strategy.get_signal( corder = self.exchange.cancel_order(trade.open_order_id, trade.pair)
trade.pair, self.strategy.ticker_interval,
self.dataprovider.ohlcv(trade.pair, self.strategy.ticker_interval))
# get config for bid strategy
config_bid_strategy = self.config.get('bid_strategy', {})
# proceed to cancel buy order by timeout if timeout_even_if_buy_signal_valid
# is true (original behaviour) -OR-
# cancel buy order only if buying condition is no longer valid OR if there's
# a sell signal present
if config_bid_strategy.get('timeout_even_if_buy_signal_valid', True) or (not buy or sell):
if order['status'] != 'canceled':
corder = self.exchange.cancel_order(trade.open_order_id, trade.pair)
else:
# Order was cancelled already, so we can reuse the existing dict
corder = order
reason = "canceled on exchange"
if corder.get('remaining', order['remaining']) == order['amount']:
# if trade is not partially completed, just delete the trade
self.handle_buy_order_full_cancel(trade, reason)
return True
# if trade is partially complete, edit the stake details for the trade
# and close the order
# cancel_order may not contain the full order dict, so we need to fallback
# to the order dict aquired before cancelling.
# we need to fall back to the values from order if corder does not contain these keys.
trade.amount = order['amount'] - corder.get('remaining', order['remaining'])
trade.stake_amount = trade.amount * trade.open_rate
# verify if fees were taken from amount to avoid problems during selling
try:
new_amount = self.get_real_amount(trade, corder if 'fee' in corder else order,
trade.amount)
if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC):
trade.amount = new_amount
# Fee was applied, so set to 0
trade.fee_open = 0
trade.recalc_open_trade_price()
except DependencyException as e:
logger.warning("Could not update trade amount: %s", e)
trade.open_order_id = None
logger.info(f"Partial buy order timeout for {trade.pair}")
self.rpc.send_msg({
'type': RPCMessageType.STATUS_NOTIFICATION,
'status': f"Remaining buy order for {trade.pair} cancelled due to timeout"
})
return False
else: else:
return False # Order was cancelled already, so we can reuse the existing dict
corder = order
reason = "canceled on exchange"
if corder.get('remaining', order['remaining']) == order['amount']:
# if trade is not partially completed, just delete the trade
self.handle_buy_order_full_cancel(trade, reason)
return True
# if trade is partially complete, edit the stake details for the trade
# and close the order
# cancel_order may not contain the full order dict, so we need to fallback
# to the order dict aquired before cancelling.
# we need to fall back to the values from order if corder does not contain these keys.
trade.amount = order['amount'] - corder.get('remaining', order['remaining'])
trade.stake_amount = trade.amount * trade.open_rate
# verify if fees were taken from amount to avoid problems during selling
try:
new_amount = self.get_real_amount(trade, corder if 'fee' in corder else order,
trade.amount)
if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC):
trade.amount = new_amount
# Fee was applied, so set to 0
trade.fee_open = 0
trade.recalc_open_trade_price()
except DependencyException as e:
logger.warning("Could not update trade amount: %s", e)
trade.open_order_id = None
logger.info(f"Partial buy order timeout for {trade.pair}")
self.rpc.send_msg({
'type': RPCMessageType.STATUS_NOTIFICATION,
'status': f"Remaining buy order for {trade.pair} cancelled due to timeout"
})
return False
def handle_timedout_limit_sell(self, trade: Trade, order: Dict) -> bool: def handle_timedout_limit_sell(self, trade: Trade, order: Dict) -> bool:
""" """

View File

@ -66,7 +66,7 @@ class StrategyResolver(IResolver):
("stake_currency", None, False), ("stake_currency", None, False),
("stake_amount", None, False), ("stake_amount", None, False),
("startup_candle_count", None, False), ("startup_candle_count", None, False),
("unfilledtimeout", None, False), ("unfilled_timeout", None, False),
("use_sell_signal", True, True), ("use_sell_signal", True, True),
("sell_profit_only", False, True), ("sell_profit_only", False, True),
("ignore_roi_if_buy_signal", False, True), ("ignore_roi_if_buy_signal", False, True),

View File

@ -210,13 +210,17 @@ def default_conf(testdatadir):
"0": 0.04 "0": 0.04
}, },
"stoploss": -0.10, "stoploss": -0.10,
"unfilledtimeout": { "unfilled_timeout": {
"buy": 10, "buy": {
"sell": 30 "after": 10,
"if_buy_signal_still_valid": False
},
"sell": {
"after": 30
}
}, },
"bid_strategy": { "bid_strategy": {
"ask_last_balance": 0.0, "ask_last_balance": 0.0,
"timeout_even_if_buy_signal_valid": True,
"use_order_book": False, "use_order_book": False,
"order_book_top": 1, "order_book_top": 1,
"check_depth_of_market": { "check_depth_of_market": {