Initial steps to change bid/ask pricing to enter/exit

This commit is contained in:
Matthias 2022-03-27 18:03:49 +02:00
parent d1f61c4cf9
commit bcf326a035
30 changed files with 193 additions and 131 deletions

View File

@ -13,7 +13,8 @@
"exit_timeout_count": 0,
"unit": "minutes"
},
"bid_strategy": {
"entry_pricing": {
"price_side": "same",
"ask_last_balance": 0.0,
"use_order_book": true,
"order_book_top": 1,
@ -22,7 +23,8 @@
"bids_to_ask_delta": 1
}
},
"ask_strategy": {
"exit_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1
},

View File

@ -13,7 +13,8 @@
"exit_timeout_count": 0,
"unit": "minutes"
},
"bid_strategy": {
"entry_pricing": {
"price_side": "same",
"use_order_book": true,
"ask_last_balance": 0.0,
"order_book_top": 1,
@ -22,7 +23,8 @@
"bids_to_ask_delta": 1
}
},
"ask_strategy":{
"exit_pricing":{
"price_side": "same",
"use_order_book": true,
"order_book_top": 1
},

View File

@ -13,7 +13,8 @@
"exit_timeout_count": 0,
"unit": "minutes"
},
"bid_strategy": {
"entry_pricing": {
"price_side": "same",
"ask_last_balance": 0.0,
"use_order_book": true,
"order_book_top": 1,
@ -22,7 +23,8 @@
"bids_to_ask_delta": 1
}
},
"ask_strategy": {
"exit_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1
},

View File

@ -35,18 +35,18 @@
"exit_timeout_count": 0,
"unit": "minutes"
},
"bid_strategy": {
"price_side": "bid",
"entry_pricing": {
"price_side": "same",
"use_order_book": true,
"ask_last_balance": 0.0,
"order_book_top": 1,
"ask_last_balance": 0.0,
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"ask_strategy":{
"price_side": "ask",
"exit_pricing":{
"price_side": "same",
"use_order_book": true,
"order_book_top": 1
},

View File

@ -13,7 +13,8 @@
"exit_timeout_count": 0,
"unit": "minutes"
},
"bid_strategy": {
"entry_pricing": {
"price_side": "same",
"use_order_book": true,
"ask_last_balance": 0.0,
"order_book_top": 1,
@ -22,7 +23,8 @@
"bids_to_ask_delta": 1
}
},
"ask_strategy":{
"exit_pricing":{
"price_side": "same",
"use_order_book": true,
"order_book_top": 1
},

View File

@ -36,12 +36,12 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and
* Calls `check_exit_timeout()` strategy callback for open exit orders.
* Verifies existing positions and eventually places exit orders.
* Considers stoploss, ROI and exit-signal, `custom_exit()` and `custom_stoploss()`.
* Determine exit-price based on `ask_strategy` configuration setting or by using the `custom_exit_price()` callback.
* Determine exit-price based on `exit_pricing` configuration setting or by using the `custom_exit_price()` callback.
* Before a exit order is placed, `confirm_trade_exit()` strategy callback is called.
* Check position adjustments for open trades if enabled by calling `adjust_trade_position()` and place additional order if required.
* Check if trade-slots are still available (if `max_open_trades` is reached).
* Verifies entry signal trying to enter new positions.
* Determine entry-price based on `bid_strategy` configuration setting, or by using the `custom_entry_price()` callback.
* Determine entry-price based on `entry_pricing` configuration setting, or by using the `custom_entry_price()` callback.
* In Margin and Futures mode, `leverage()` strategy callback is called to determine the desired leverage.
* Determine stake size by calling the `custom_stake_amount()` callback.
* Before an entry order is placed, `confirm_trade_entry()` strategy callback is called.

View File

@ -106,16 +106,16 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `unfilledtimeout.exit` | **Required.** How long (in minutes or seconds) the bot will wait for an unfilled exit order to complete, after which the order will be cancelled and repeated at current (new) price, as long as there is a signal. [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Integer
| `unfilledtimeout.unit` | Unit to use in unfilledtimeout setting. Note: If you set unfilledtimeout.unit to "seconds", "internals.process_throttle_secs" must be inferior or equal to timeout [Strategy Override](#parameters-in-the-strategy). <br> *Defaults to `minutes`.* <br> **Datatype:** String
| `unfilledtimeout.exit_timeout_count` | How many times can exit orders time out. Once this number of timeouts is reached, an emergency sell is triggered. 0 to disable and allow unlimited order cancels. [Strategy Override](#parameters-in-the-strategy).<br>*Defaults to `0`.* <br> **Datatype:** Integer
| `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).<br> *Defaults to `bid`.* <br> **Datatype:** String (either `ask` or `bid`).
| `bid_strategy.ask_last_balance` | **Required.** Interpolate 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). <br> **Datatype:** Boolean
| `bid_strategy.order_book_top` | Bot will use the top N rate in Order Book "price_side" 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.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) <br> *Defaults to `0`.* <br> **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).<br> *Defaults to `ask`.* <br> **Datatype:** String (either `ask` or `bid`).
| `ask_strategy.bid_last_balance` | Interpolate the selling price. More information [below](#sell-price-without-orderbook-enabled).
| `ask_strategy.use_order_book` | Enable selling of open trades using [Order Book Asks](#sell-price-with-orderbook-enabled). <br> **Datatype:** Boolean
| `ask_strategy.order_book_top` | Bot will use the top N rate in Order Book "price_side" to sell. I.e. a value of 2 will allow the bot to pick the 2nd ask rate in [Order Book Asks](#sell-price-with-orderbook-enabled)<br>*Defaults to `1`.* <br> **Datatype:** Positive Integer
| `enter_pricing.price_side` | Select the side of the spread the bot should look at to get the entry rate. [More information below](#buy-price-side).<br> *Defaults to `same`.* <br> **Datatype:** String (either `ask`, `bid`, `same` or `other`).
| `enter_pricing.ask_last_balance` | **Required.** Interpolate the bidding price. More information [below](#buy-price-without-orderbook-enabled).
| `enter_pricing.use_order_book` | Enable entering using the rates in [Order Book Bids](#buy-price-with-orderbook-enabled). <br> *Defaults to `True`.*<br> **Datatype:** Boolean
| `enter_pricing.order_book_top` | Bot will use the top N rate in Order Book "price_side" to enter a trade. I.e. a value of 2 will allow the bot to pick the 2nd entry in [Order Book Bids](#buy-price-with-orderbook-enabled). <br>*Defaults to `1`.* <br> **Datatype:** Positive Integer
| `enter_pricing. check_depth_of_market.enabled` | Do not enter 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
| `enter_pricing. 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) <br> *Defaults to `0`.* <br> **Datatype:** Float (as ratio)
| `exit_pricing.price_side` | Select the side of the spread the bot should look at to get the exit rate. [More information below](#sell-price-side).<br> *Defaults to `same`.* <br> **Datatype:** String (either `ask`, `bid`, `same` or `other`).
| `exit_pricing.bid_last_balance` | Interpolate the exiting price. More information [below](#sell-price-without-orderbook-enabled).
| `exit_pricing.use_order_book` | Enable exiting of open trades using [Order Book Asks](#sell-price-with-orderbook-enabled). <br> *Defaults to `True`.*<br> **Datatype:** Boolean
| `exit_pricing.order_book_top` | Bot will use the top N rate in Order Book "price_side" to sell. I.e. a value of 2 will allow the bot to pick the 2nd ask rate in [Order Book Asks](#sell-price-with-orderbook-enabled)<br>*Defaults to `1`.* <br> **Datatype:** Positive Integer
| `use_sell_signal` | Use sell signals produced by the strategy in addition to the `minimal_roi`. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `true`.* <br> **Datatype:** Boolean
| `sell_profit_only` | Wait until the bot reaches `sell_profit_offset` before taking a sell decision. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
| `sell_profit_offset` | Sell-signal is only active above this value. Only active in combination with `sell_profit_only=True`. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `0.0`.* <br> **Datatype:** Float (as ratio)

View File

@ -108,12 +108,12 @@ To mitigate this, you can try to match the first order on the opposite orderbook
"exit": "limit"
// ...
},
"bid_strategy": {
"price_side": "ask",
"entry_pricing": {
"price_side": "other",
// ...
},
"ask_strategy":{
"price_side": "bid",
"exit_pricing":{
"price_side": "other",
// ...
},
```

View File

@ -160,7 +160,7 @@ class AwesomeStrategy(IStrategy):
:param pair: Pair that's currently analyzed
:param trade: trade object.
:param current_time: datetime object, containing the current datetime
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
:param current_rate: Rate, calculated based on pricing settings in exit_pricing.
:param current_profit: Current profit (as ratio), calculated based on current_rate.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return float: New stoploss value, relative to the current rate
@ -707,7 +707,7 @@ class AwesomeStrategy(IStrategy):
:param pair: Pair that's currently analyzed
:param current_time: datetime object, containing the current datetime
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
:param current_rate: Rate, calculated based on pricing settings in exit_pricing.
:param proposed_leverage: A leverage proposed by the bot.
:param max_leverage: Max leverage allowed on this pair
:param side: 'long' or 'short' - indicating the direction of the proposed trade

View File

@ -242,6 +242,8 @@ This should be given the value of `trade.is_short`.
```
After:
``` python hl_lines="5 7"
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
current_rate: float, current_profit: float, **kwargs) -> float:
@ -267,6 +269,8 @@ This should be given the value of `trade.is_short`.
}
```
After:
``` python hl_lines="2 3"
order_time_in_force: Dict = {
"entry": "gtc",
@ -291,6 +295,8 @@ This should be given the value of `trade.is_short`.
}
```
After:
``` python hl_lines="2-6"
order_types = {
"entry": "limit",
@ -317,6 +323,8 @@ unfilledtimeout = {
}
```
After:
``` python hl_lines="2-3"
unfilledtimeout = {
"entry": 10,
@ -325,3 +333,51 @@ unfilledtimeout = {
"unit": "minutes"
}
```
#### `order pricing`
Order pricing changed in 2 ways. `bid_strategy` was renamed to `entry_strategy` and `ask_strategy` was renamed to `exit_strategy`.
Also, price-side can now be defined as `ask`, `bid`, `same` or `other`.
Please refer to the [pricing documentation](configuration.md) for more information.
``` json hl_lines="2-3 12-13"
{
"bid_strategy": {
"price_side": "bid",
"use_order_book": true,
"order_book_top": 1,
"ask_last_balance": 0.0,
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"ask_strategy":{
"price_side": "ask",
"use_order_book": true,
"order_book_top": 1
}
}
```
after:
``` json hl_lines="2-3 12-13"
{
"entry_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1,
"ask_last_balance": 0.0,
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"exit_pricing":{
"price_side": "same",
"use_order_book": true,
"order_book_top": 1
}
}
```

View File

@ -105,8 +105,8 @@ def _validate_price_config(conf: Dict[str, Any]) -> None:
"""
# TODO-lev: check this again when determining how to migrate pricing strategies!
if (conf.get('order_types', {}).get('entry') == 'market'
and conf.get('bid_strategy', {}).get('price_side') != 'ask'):
raise OperationalException('Market buy orders require bid_strategy.price_side = "ask".')
and conf.get('entry_pricing', {}).get('price_side') != 'ask'):
raise OperationalException('Market buy orders require entry_pricing.price_side = "ask".')
if (conf.get('order_types', {}).get('exit') == 'market'
and conf.get('ask_strategy', {}).get('price_side') != 'bid'):

View File

@ -81,8 +81,6 @@ class Configuration:
# Normalize config
if 'internals' not in config:
config['internals'] = {}
if 'ask_strategy' not in config:
config['ask_strategy'] = {}
if 'pairlists' not in config:
config['pairlists'] = []

View File

@ -21,7 +21,7 @@ UNLIMITED_STAKE_AMOUNT = 'unlimited'
DEFAULT_AMOUNT_RESERVE_PERCENT = 0.05
REQUIRED_ORDERTIF = ['entry', 'exit']
REQUIRED_ORDERTYPES = ['entry', 'exit', 'stoploss', 'stoploss_on_exchange']
ORDERBOOK_SIDES = ['ask', 'bid']
PRICING_SIDES = ['ask', 'bid', 'same', 'other']
ORDERTYPE_POSSIBILITIES = ['limit', 'market']
ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc']
HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss',
@ -171,7 +171,7 @@ CONF_SCHEMA = {
'unit': {'type': 'string', 'enum': TIMEOUT_UNITS, 'default': 'minutes'}
}
},
'bid_strategy': {
'entry_pricing': {
'type': 'object',
'properties': {
'ask_last_balance': {
@ -180,7 +180,7 @@ CONF_SCHEMA = {
'maximum': 1,
'exclusiveMaximum': False,
},
'price_side': {'type': 'string', 'enum': ORDERBOOK_SIDES, 'default': 'bid'},
'price_side': {'type': 'string', 'enum': PRICING_SIDES, 'default': 'bid'},
'use_order_book': {'type': 'boolean'},
'order_book_top': {'type': 'integer', 'minimum': 1, 'maximum': 50, },
'check_depth_of_market': {
@ -193,10 +193,10 @@ CONF_SCHEMA = {
},
'required': ['price_side']
},
'ask_strategy': {
'exit_pricing': {
'type': 'object',
'properties': {
'price_side': {'type': 'string', 'enum': ORDERBOOK_SIDES, 'default': 'ask'},
'price_side': {'type': 'string', 'enum': PRICING_SIDES, 'default': 'ask'},
'bid_last_balance': {
'type': 'number',
'minimum': 0,
@ -445,8 +445,8 @@ SCHEMA_TRADE_REQUIRED = [
'last_stake_amount_min_ratio',
'dry_run',
'dry_run_wallet',
'ask_strategy',
'bid_strategy',
'exit_pricing',
'entry_pricing',
'stoploss',
'minimal_roi',
'internals',

View File

@ -184,8 +184,8 @@ class Exchange:
self.required_candle_call_count = self.validate_required_startup_candles(
config.get('startup_candle_count', 0), config.get('timeframe', ''))
self.validate_trading_mode_and_margin_mode(self.trading_mode, self.margin_mode)
self.validate_pricing(config['ask_strategy'])
self.validate_pricing(config['bid_strategy'])
self.validate_pricing(config['exit_pricing'])
self.validate_pricing(config['entry_pricing'])
# Converts the interval provided in minutes in config to seconds
self.markets_refresh_interval: int = exchange_config.get(

View File

@ -459,7 +459,7 @@ class FreqtradeBot(LoggingMixin):
if signal:
stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge)
bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {})
bid_check_dom = self.config.get('entry_pricing', {}).get('check_depth_of_market', {})
if ((bid_check_dom.get('enabled', False)) and
(bid_check_dom.get('bids_to_ask_delta', 0) > 0)):
if self._check_depth_of_market(pair, bid_check_dom, side=signal):

View File

@ -115,9 +115,9 @@ class Hyperopt:
if HyperoptTools.has_space(self.config, 'sell'):
# Make sure use_sell_signal is enabled
if 'ask_strategy' not in self.config:
self.config['ask_strategy'] = {}
self.config['ask_strategy']['use_sell_signal'] = True
if 'exit_pricing' not in self.config:
self.config['exit_pricing'] = {}
self.config['exit_pricing']['use_sell_signal'] = True
self.print_all = self.config.get('print_all', False)
self.hyperopt_table_header = 0

View File

@ -175,8 +175,8 @@ class ShowConfig(BaseModel):
exchange: str
strategy: Optional[str]
forcebuy_enabled: bool
ask_strategy: Dict[str, Any]
bid_strategy: Dict[str, Any]
exit_pricing: Dict[str, Any]
entry_pricing: Dict[str, Any]
bot_name: str
state: str
runmode: str

View File

@ -136,8 +136,8 @@ class RPC:
'exchange': config['exchange']['name'],
'strategy': config['strategy'],
'forcebuy_enabled': config.get('forcebuy_enable', False),
'ask_strategy': config.get('ask_strategy', {}),
'bid_strategy': config.get('bid_strategy', {}),
'exit_pricing': config.get('exit_pricing', {}),
'entry_pricing': config.get('entry_pricing', {}),
'state': str(botstate),
'runmode': config['runmode'].value,
'position_adjustment_enable': config.get('position_adjustment_enable', False),

View File

@ -1490,8 +1490,8 @@ class Telegram(RPCHandler):
f"*Stake per trade:* `{val['stake_amount']} {val['stake_currency']}`\n"
f"*Max open Trades:* `{val['max_open_trades']}`\n"
f"*Minimum ROI:* `{val['minimal_roi']}`\n"
f"*Ask strategy:* ```\n{json.dumps(val['ask_strategy'])}```\n"
f"*Bid strategy:* ```\n{json.dumps(val['bid_strategy'])}```\n"
f"*Entry strategy:* ```\n{json.dumps(val['entry_pricing'])}```\n"
f"*Exit strategy:* ```\n{json.dumps(val['exit_strategy'])}```\n"
f"{sl_info}"
f"{pa_info}"
f"*Timeframe:* `{val['timeframe']}`\n"

View File

@ -331,7 +331,7 @@ class IStrategy(ABC, HyperStrategyMixin):
:param pair: Pair that's currently analyzed
:param trade: trade object.
:param current_time: datetime object, containing the current datetime
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
:param current_rate: Rate, calculated based on pricing settings in exit_pricing.
:param current_profit: Current profit (as ratio), calculated based on current_rate.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return float: New stoploss value, relative to the current_rate
@ -349,7 +349,7 @@ class IStrategy(ABC, HyperStrategyMixin):
:param pair: Pair that's currently analyzed
:param current_time: datetime object, containing the current datetime
:param proposed_rate: Rate, calculated based on pricing settings in ask_strategy.
:param proposed_rate: Rate, calculated based on pricing settings in exit_pricing.
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return float: New entry price value if provided
@ -369,7 +369,7 @@ class IStrategy(ABC, HyperStrategyMixin):
:param pair: Pair that's currently analyzed
:param trade: trade object.
:param current_time: datetime object, containing the current datetime
:param proposed_rate: Rate, calculated based on pricing settings in ask_strategy.
:param proposed_rate: Rate, calculated based on pricing settings in exit_pricing.
:param current_profit: Current profit (as ratio), calculated based on current_rate.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return float: New exit price value if provided
@ -393,7 +393,7 @@ class IStrategy(ABC, HyperStrategyMixin):
:param pair: Pair that's currently analyzed
:param trade: trade object.
:param current_time: datetime object, containing the current datetime
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
:param current_rate: Rate, calculated based on pricing settings in exit_pricing.
:param current_profit: Current profit (as ratio), calculated based on current_rate.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return: To execute exit, return a string with custom sell reason or True. Otherwise return
@ -417,7 +417,7 @@ class IStrategy(ABC, HyperStrategyMixin):
:param pair: Pair that's currently analyzed
:param trade: trade object.
:param current_time: datetime object, containing the current datetime
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
:param current_rate: Rate, calculated based on pricing settings in exit_pricing.
:param current_profit: Current profit (as ratio), calculated based on current_rate.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return: To execute exit, return a string with custom sell reason or True. Otherwise return
@ -433,7 +433,7 @@ class IStrategy(ABC, HyperStrategyMixin):
:param pair: Pair that's currently analyzed
:param current_time: datetime object, containing the current datetime
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
:param current_rate: Rate, calculated based on pricing settings in exit_pricing.
:param proposed_stake: A stake amount proposed by the bot.
:param min_stake: Minimal stake size allowed by exchange.
:param max_stake: Balance available for trading.
@ -474,7 +474,7 @@ class IStrategy(ABC, HyperStrategyMixin):
:param pair: Pair that's currently analyzed
:param current_time: datetime object, containing the current datetime
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
:param current_rate: Rate, calculated based on pricing settings in exit_pricing.
:param proposed_leverage: A leverage proposed by the bot.
:param max_leverage: Max leverage allowed on this pair
:param side: 'long' or 'short' - indicating the direction of the proposed trade

View File

@ -21,8 +21,8 @@
"exit_timeout_count": 0,
"unit": "minutes"
},
"bid_strategy": {
"price_side": "bid",
"entry_pricing": {
"price_side": "same",
"ask_last_balance": 0.0,
"use_order_book": true,
"order_book_top": 1,
@ -31,8 +31,8 @@
"bids_to_ask_delta": 1
}
},
"ask_strategy": {
"price_side": "ask",
"exit_pricing":{
"price_side": "same",
"use_order_book": true,
"order_book_top": 1
},

View File

@ -64,7 +64,7 @@ class {{ strategy }}(IStrategy):
# Run "populate_indicators()" only for new candle.
process_only_new_candles = False
# These values can be overridden in the "ask_strategy" section in the config.
# These values can be overridden in the config.
use_sell_signal = True
sell_profit_only = False
ignore_roi_if_buy_signal = False

View File

@ -64,7 +64,7 @@ class SampleStrategy(IStrategy):
# Run "populate_indicators()" only for new candle.
process_only_new_candles = False
# These values can be overridden in the "ask_strategy" section in the config.
# These values can be overridden in the config.
use_sell_signal = True
sell_profit_only = False
ignore_roi_if_buy_signal = False

View File

@ -23,7 +23,7 @@ def custom_entry_price(self, pair: str, current_time: 'datetime', proposed_rate:
:param pair: Pair that's currently analyzed
:param current_time: datetime object, containing the current datetime
:param proposed_rate: Rate, calculated based on pricing settings in ask_strategy.
:param proposed_rate: Rate, calculated based on pricing settings in exit_pricing.
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return float: New entry price value if provided
@ -43,7 +43,7 @@ def custom_exit_price(self, pair: str, trade: 'Trade',
:param pair: Pair that's currently analyzed
:param trade: trade object.
:param current_time: datetime object, containing the current datetime
:param proposed_rate: Rate, calculated based on pricing settings in ask_strategy.
:param proposed_rate: Rate, calculated based on pricing settings in exit_pricing.
:param current_profit: Current profit (as ratio), calculated based on current_rate.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return float: New exit price value if provided
@ -58,7 +58,7 @@ def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate:
:param pair: Pair that's currently analyzed
:param current_time: datetime object, containing the current datetime
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
:param current_rate: Rate, calculated based on pricing settings in exit_pricing.
:param proposed_stake: A stake amount proposed by the bot.
:param min_stake: Minimal stake size allowed by exchange.
:param max_stake: Balance available for trading.
@ -85,7 +85,7 @@ def custom_stoploss(self, pair: str, trade: 'Trade', current_time: 'datetime',
:param pair: Pair that's currently analyzed
:param trade: trade object.
:param current_time: datetime object, containing the current datetime
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
:param current_rate: Rate, calculated based on pricing settings in exit_pricing.
:param current_profit: Current profit (as ratio), calculated based on current_rate.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return float: New stoploss value, relative to the current_rate
@ -108,7 +108,7 @@ def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', curre
:param pair: Pair that's currently analyzed
:param trade: trade object.
:param current_time: datetime object, containing the current datetime
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
:param current_rate: Rate, calculated based on pricing settings in exit_pricing.
:param current_profit: Current profit (as ratio), calculated based on current_rate.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return: To execute sell, return a string with custom sell reason or True. Otherwise return
@ -241,7 +241,7 @@ def leverage(self, pair: str, current_time: datetime, current_rate: float,
:param pair: Pair that's currently analyzed
:param current_time: datetime object, containing the current datetime
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
:param current_rate: Rate, calculated based on pricing settings in exit_pricing.
:param proposed_leverage: A leverage proposed by the bot.
:param max_leverage: Max leverage allowed on this pair
:param side: 'long' or 'short' - indicating the direction of the proposed trade

View File

@ -419,7 +419,7 @@ def get_default_conf(testdatadir):
"entry": 10,
"exit": 30
},
"bid_strategy": {
"entry_pricing": {
"ask_last_balance": 0.0,
"use_order_book": False,
"order_book_top": 1,
@ -428,7 +428,7 @@ def get_default_conf(testdatadir):
"bids_to_ask_delta": 1
}
},
"ask_strategy": {
"exit_pricing": {
"use_order_book": False,
"order_book_top": 1,
},

View File

@ -107,8 +107,8 @@ def exchange_conf():
config['exchange']['key'] = ''
config['exchange']['secret'] = ''
config['dry_run'] = False
config['bid_strategy']['use_order_book'] = True
config['ask_strategy']['use_order_book'] = True
config['entry_pricing']['use_order_book'] = True
config['exit_pricing']['use_order_book'] = True
return config

View File

@ -971,7 +971,7 @@ def test_validate_pricing(default_conf, mocker):
has.update({'fetchTicker': True})
default_conf['ask_strategy']['use_order_book'] = True
default_conf['exit_pricing']['use_order_book'] = True
ExchangeResolver.load_exchange('binance', default_conf)
has.update({'fetchL2OrderBook': False})
@ -2305,10 +2305,10 @@ def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid,
last, last_ab, expected) -> None:
caplog.set_level(logging.DEBUG)
if last_ab is None:
del default_conf['bid_strategy']['ask_last_balance']
del default_conf['entry_pricing']['ask_last_balance']
else:
default_conf['bid_strategy']['ask_last_balance'] = last_ab
default_conf['bid_strategy']['price_side'] = side
default_conf['entry_pricing']['ask_last_balance'] = last_ab
default_conf['entry_pricing']['price_side'] = side
exchange = get_patched_exchange(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
return_value={'ask': ask, 'last': last, 'bid': bid})
@ -2349,9 +2349,9 @@ def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask,
last, last_ab, expected) -> None:
caplog.set_level(logging.DEBUG)
default_conf['ask_strategy']['price_side'] = side
default_conf['exit_pricing']['price_side'] = side
if last_ab is not None:
default_conf['ask_strategy']['bid_last_balance'] = last_ab
default_conf['exit_pricing']['bid_last_balance'] = last_ab
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
return_value={'ask': ask, 'bid': bid, 'last': last})
pair = "ETH/BTC"
@ -2381,10 +2381,10 @@ def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask,
def test_get_ticker_rate_error(mocker, entry, default_conf, caplog, side, ask, bid,
last, last_ab, expected) -> None:
caplog.set_level(logging.DEBUG)
default_conf['bid_strategy']['ask_last_balance'] = last_ab
default_conf['bid_strategy']['price_side'] = side
default_conf['ask_strategy']['price_side'] = side
default_conf['ask_strategy']['ask_last_balance'] = last_ab
default_conf['entry_pricing']['ask_last_balance'] = last_ab
default_conf['entry_pricing']['price_side'] = side
default_conf['exit_pricing']['price_side'] = side
default_conf['exit_pricing']['ask_last_balance'] = last_ab
exchange = get_patched_exchange(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
return_value={'ask': ask, 'last': last, 'bid': bid})
@ -2400,9 +2400,9 @@ def test_get_ticker_rate_error(mocker, entry, default_conf, caplog, side, ask, b
def test_get_sell_rate_orderbook(default_conf, mocker, caplog, side, expected, order_book_l2):
caplog.set_level(logging.DEBUG)
# Test orderbook mode
default_conf['ask_strategy']['price_side'] = side
default_conf['ask_strategy']['use_order_book'] = True
default_conf['ask_strategy']['order_book_top'] = 1
default_conf['exit_pricing']['price_side'] = side
default_conf['exit_pricing']['use_order_book'] = True
default_conf['exit_pricing']['order_book_top'] = 1
pair = "ETH/BTC"
mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2)
exchange = get_patched_exchange(mocker, default_conf)
@ -2417,9 +2417,9 @@ def test_get_sell_rate_orderbook(default_conf, mocker, caplog, side, expected, o
def test_get_sell_rate_orderbook_exception(default_conf, mocker, caplog):
# Test orderbook mode
default_conf['ask_strategy']['price_side'] = 'ask'
default_conf['ask_strategy']['use_order_book'] = True
default_conf['ask_strategy']['order_book_top'] = 1
default_conf['exit_pricing']['price_side'] = 'ask'
default_conf['exit_pricing']['use_order_book'] = True
default_conf['exit_pricing']['order_book_top'] = 1
pair = "ETH/BTC"
# Test What happens if the exchange returns an empty orderbook.
mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book',
@ -2433,7 +2433,7 @@ def test_get_sell_rate_orderbook_exception(default_conf, mocker, caplog):
def test_get_sell_rate_exception(default_conf, mocker, caplog):
# Ticker on one side can be empty in certain circumstances.
default_conf['ask_strategy']['price_side'] = 'ask'
default_conf['exit_pricing']['price_side'] = 'ask'
pair = "ETH/BTC"
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
return_value={'ask': None, 'bid': 0.12, 'last': None})
@ -2441,7 +2441,7 @@ def test_get_sell_rate_exception(default_conf, mocker, caplog):
with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."):
exchange.get_rate(pair, refresh=True, side="sell")
exchange._config['ask_strategy']['price_side'] = 'bid'
exchange._config['exit_pricing']['price_side'] = 'bid'
assert exchange.get_rate(pair, refresh=True, side="sell") == 0.12
# Reverse sides
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
@ -2449,7 +2449,7 @@ def test_get_sell_rate_exception(default_conf, mocker, caplog):
with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."):
exchange.get_rate(pair, refresh=True, side="sell")
exchange._config['ask_strategy']['price_side'] = 'ask'
exchange._config['exit_pricing']['price_side'] = 'ask'
assert exchange.get_rate(pair, refresh=True, side="sell") == 0.13

View File

@ -543,8 +543,8 @@ def test_api_show_config(botclient):
assert response['trading_mode'] == 'spot'
assert response['strategy_version'] is None
assert not response['trailing_stop']
assert 'bid_strategy' in response
assert 'ask_strategy' in response
assert 'entry_pricing' in response
assert 'exit_pricing' in response
assert 'unfilledtimeout' in response
assert 'version' in response
assert 'api_version' in response

View File

@ -809,21 +809,21 @@ def test_validate_price_side(default_conf):
conf = deepcopy(default_conf)
conf['order_types']['entry'] = 'market'
with pytest.raises(OperationalException,
match='Market buy orders require bid_strategy.price_side = "ask".'):
match='Market buy orders require entry_pricing.price_side = "ask".'):
validate_config_consistency(conf)
conf = deepcopy(default_conf)
conf['order_types']['exit'] = 'market'
with pytest.raises(OperationalException,
match='Market sell orders require ask_strategy.price_side = "bid".'):
match='Market sell orders require exit_pricing.price_side = "bid".'):
validate_config_consistency(conf)
# Validate inversed case
conf = deepcopy(default_conf)
conf['order_types']['exit'] = 'market'
conf['order_types']['entry'] = 'market'
conf['ask_strategy']['price_side'] = 'bid'
conf['bid_strategy']['price_side'] = 'ask'
conf['exit_pricing']['price_side'] = 'bid'
conf['entry_pricing']['price_side'] = 'ask'
validate_config_consistency(conf)
@ -926,18 +926,18 @@ def test_validate_protections(default_conf, protconf, expected):
def test_validate_ask_orderbook(default_conf, caplog) -> None:
conf = deepcopy(default_conf)
conf['ask_strategy']['use_order_book'] = True
conf['ask_strategy']['order_book_min'] = 2
conf['ask_strategy']['order_book_max'] = 2
conf['exit_pricing']['use_order_book'] = True
conf['exit_pricing']['order_book_min'] = 2
conf['exit_pricing']['order_book_max'] = 2
validate_config_consistency(conf)
assert log_has_re(r"DEPRECATED: Please use `order_book_top` instead of.*", caplog)
assert conf['ask_strategy']['order_book_top'] == 2
assert conf['exit_pricing']['order_book_top'] == 2
conf['ask_strategy']['order_book_max'] = 5
conf['exit_pricing']['order_book_max'] = 5
with pytest.raises(OperationalException,
match=r"Using order_book_max != order_book_min in ask_strategy.*"):
match=r"Using order_book_max != order_book_min in exit_pricing.*"):
validate_config_consistency(conf)
@ -1200,15 +1200,15 @@ def test_pairlist_resolving_fallback(mocker):
@pytest.mark.parametrize("setting", [
("ask_strategy", "use_sell_signal", True,
("exit_pricing", "use_sell_signal", True,
None, "use_sell_signal", False),
("ask_strategy", "sell_profit_only", True,
("exit_pricing", "sell_profit_only", True,
None, "sell_profit_only", False),
("ask_strategy", "sell_profit_offset", 0.1,
("exit_pricing", "sell_profit_offset", 0.1,
None, "sell_profit_offset", 0.01),
("ask_strategy", "ignore_roi_if_buy_signal", True,
("exit_pricing", "ignore_roi_if_buy_signal", True,
None, "ignore_roi_if_buy_signal", False),
("ask_strategy", "ignore_buying_expired_candle_after", 5,
("exit_pricing", "ignore_buying_expired_candle_after", 5,
None, "ignore_buying_expired_candle_after", 6),
])
def test_process_temporary_deprecated_settings(mocker, default_conf, setting, caplog):
@ -1434,15 +1434,15 @@ def test_flat_vars_to_nested_dict(caplog):
'FREQTRADE__EXCHANGE__SOME_SETTING': 'true',
'FREQTRADE__EXCHANGE__SOME_FALSE_SETTING': 'false',
'FREQTRADE__EXCHANGE__CONFIG__whatever': 'sometime',
'FREQTRADE__ASK_STRATEGY__PRICE_SIDE': 'bid',
'FREQTRADE__ASK_STRATEGY__cccc': '500',
'FREQTRADE__EXIT_PRICING__PRICE_SIDE': 'bid',
'FREQTRADE__EXIT_PRICING__cccc': '500',
'FREQTRADE__STAKE_AMOUNT': '200.05',
'FREQTRADE__TELEGRAM__CHAT_ID': '2151',
'NOT_RELEVANT': '200.0', # Will be ignored
}
expected = {
'stake_amount': 200.05,
'ask_strategy': {
'exit_pricing': {
'price_side': 'bid',
'cccc': 500,
},

View File

@ -96,7 +96,7 @@ def test_order_dict(default_conf_usdt, mocker, runmode, caplog) -> None:
'stoploss': 'limit',
'stoploss_on_exchange': True,
}
conf['bid_strategy']['price_side'] = 'ask'
conf['entry_pricing']['price_side'] = 'ask'
freqtrade = FreqtradeBot(conf)
if runmode == RunMode.LIVE:
@ -4052,7 +4052,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_
get_fee=fee,
_is_dry_limit_order_filled=MagicMock(return_value=False),
)
default_conf_usdt['ask_strategy'] = {
default_conf_usdt['exit_pricing'] = {
'ignore_roi_if_buy_signal': False
}
freqtrade = FreqtradeBot(default_conf_usdt)
@ -4432,8 +4432,8 @@ def test_order_book_depth_of_market(
):
ticker_side = 'ask' if is_short else 'bid'
default_conf_usdt['bid_strategy']['check_depth_of_market']['enabled'] = True
default_conf_usdt['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = delta
default_conf_usdt['entry_pricing']['check_depth_of_market']['enabled'] = True
default_conf_usdt['entry_pricing']['check_depth_of_market']['bids_to_ask_delta'] = delta
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2)
@ -4476,7 +4476,7 @@ def test_order_book_depth_of_market(
(False, 0.045, 0.046, 2, None),
(True, 0.042, 0.046, 1, {'bids': [[]], 'asks': [[]]})
])
def test_order_book_bid_strategy1(mocker, default_conf_usdt, order_book_l2, exception_thrown,
def test_order_book_entry_pricing1(mocker, default_conf_usdt, order_book_l2, exception_thrown,
ask, last, order_book_top, order_book, caplog) -> None:
"""
test if function get_rate will return the order book price instead of the ask rate
@ -4489,9 +4489,9 @@ def test_order_book_bid_strategy1(mocker, default_conf_usdt, order_book_l2, exce
fetch_ticker=ticker_usdt_mock,
)
default_conf_usdt['exchange']['name'] = 'binance'
default_conf_usdt['bid_strategy']['use_order_book'] = True
default_conf_usdt['bid_strategy']['order_book_top'] = order_book_top
default_conf_usdt['bid_strategy']['ask_last_balance'] = 0
default_conf_usdt['entry_pricing']['use_order_book'] = True
default_conf_usdt['entry_pricing']['order_book_top'] = order_book_top
default_conf_usdt['entry_pricing']['ask_last_balance'] = 0
default_conf_usdt['telegram']['enabled'] = False
freqtrade = FreqtradeBot(default_conf_usdt)
@ -4516,17 +4516,17 @@ def test_check_depth_of_market(default_conf_usdt, mocker, order_book_l2) -> None
)
default_conf_usdt['telegram']['enabled'] = False
default_conf_usdt['exchange']['name'] = 'binance'
default_conf_usdt['bid_strategy']['check_depth_of_market']['enabled'] = True
default_conf_usdt['entry_pricing']['check_depth_of_market']['enabled'] = True
# delta is 100 which is impossible to reach. hence function will return false
default_conf_usdt['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = 100
default_conf_usdt['entry_pricing']['check_depth_of_market']['bids_to_ask_delta'] = 100
freqtrade = FreqtradeBot(default_conf_usdt)
conf = default_conf_usdt['bid_strategy']['check_depth_of_market']
conf = default_conf_usdt['entry_pricing']['check_depth_of_market']
assert freqtrade._check_depth_of_market('ETH/BTC', conf, side=SignalDirection.LONG) is False
@pytest.mark.parametrize('is_short', [False, True])
def test_order_book_ask_strategy(
def test_order_book_exit_pricing(
default_conf_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, is_short,
limit_sell_order_usdt_open, mocker, order_book_l2, caplog) -> None:
"""
@ -4534,8 +4534,8 @@ def test_order_book_ask_strategy(
"""
mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2)
default_conf_usdt['exchange']['name'] = 'binance'
default_conf_usdt['ask_strategy']['use_order_book'] = True
default_conf_usdt['ask_strategy']['order_book_top'] = 1
default_conf_usdt['exit_pricing']['use_order_book'] = True
default_conf_usdt['exit_pricing']['order_book_top'] = 1
default_conf_usdt['telegram']['enabled'] = False
patch_RPCManager(mocker)
patch_exchange(mocker)