bring this PR closer to reality, with better naming
This commit is contained in:
parent
f209ef6ad9
commit
29a8674496
@ -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": {
|
||||||
|
@ -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": {
|
||||||
|
@ -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": {
|
||||||
|
@ -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": {
|
||||||
|
@ -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)
|
||||||
|
@ -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',
|
||||||
]
|
]
|
||||||
|
@ -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:
|
||||||
"""
|
"""
|
||||||
|
@ -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),
|
||||||
|
@ -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": {
|
||||||
|
Loading…
Reference in New Issue
Block a user