From d2a163e2cf6e63088b90e1251bfe0be6244b7c3a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Mar 2022 19:05:20 +0100 Subject: [PATCH 1/3] rename column to liquidation_price --- freqtrade/freqtradebot.py | 2 +- freqtrade/persistence/migrations.py | 14 ++++++------- freqtrade/persistence/models.py | 26 +++++++++++------------ tests/optimize/test_backtesting.py | 4 ++-- tests/rpc/test_rpc.py | 4 ++-- tests/test_freqtradebot.py | 2 +- tests/test_persistence.py | 32 ++++++++++++++--------------- 7 files changed, 42 insertions(+), 42 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 45c18378d..341693982 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -725,7 +725,7 @@ class FreqtradeBot(LoggingMixin): leverage=leverage, is_short=is_short, interest_rate=interest_rate, - isolated_liq=isolated_liq, + liquidation_price=isolated_liq, trading_mode=self.trading_mode, funding_fees=funding_fees ) diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 08eb9563b..112538570 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -82,7 +82,8 @@ def migrate_trades_and_orders_table( # Leverage Properties leverage = get_column_def(cols, 'leverage', '1.0') - isolated_liq = get_column_def(cols, 'isolated_liq', 'null') + liquidation_price = get_column_def(cols, 'liquidation_price', + get_column_def(cols, 'isolated_liq', 'null')) # sqlite does not support literals for booleans is_short = get_column_def(cols, 'is_short', '0') @@ -137,7 +138,7 @@ def migrate_trades_and_orders_table( stoploss_order_id, stoploss_last_update, max_rate, min_rate, sell_reason, sell_order_status, strategy, enter_tag, timeframe, open_trade_value, close_profit_abs, - trading_mode, leverage, isolated_liq, is_short, + trading_mode, leverage, liquidation_price, is_short, interest_rate, funding_fees ) select id, lower(exchange), pair, @@ -155,7 +156,7 @@ def migrate_trades_and_orders_table( {sell_order_status} sell_order_status, {strategy} strategy, {enter_tag} enter_tag, {timeframe} timeframe, {open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs, - {trading_mode} trading_mode, {leverage} leverage, {isolated_liq} isolated_liq, + {trading_mode} trading_mode, {leverage} leverage, {liquidation_price} liquidation_price, {is_short} is_short, {interest_rate} interest_rate, {funding_fees} funding_fees from {trade_back_name} @@ -233,10 +234,9 @@ def check_migrate(engine, decl_base, previous_tables) -> None: # Check if migration necessary # Migrates both trades and orders table! - # if not has_column(cols, 'buy_tag'): - if ('orders' not in previous_tables - or not has_column(cols_orders, 'ft_fee_base') - or not has_column(cols_orders, 'leverage')): + # if ('orders' not in previous_tables + # or not has_column(cols_orders, 'leverage')): + if not has_column(cols, 'liquidation_price'): logger.info(f"Running database migration for trades - " f"backup: {table_back_name}, {order_table_bak_name}") migrate_trades_and_orders_table( diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 565ece5a4..b80d75dc0 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -329,7 +329,7 @@ class LocalTrade(): trading_mode: TradingMode = TradingMode.SPOT # Leverage trading properties - isolated_liq: Optional[float] = None + liquidation_price: Optional[float] = None is_short: bool = False leverage: float = 1.0 @@ -483,7 +483,7 @@ class LocalTrade(): 'leverage': self.leverage, 'interest_rate': self.interest_rate, - 'isolated_liq': self.isolated_liq, + 'liquidation_price': self.liquidation_price, 'is_short': self.is_short, 'trading_mode': self.trading_mode, 'funding_fees': self.funding_fees, @@ -507,25 +507,25 @@ class LocalTrade(): self.max_rate = max(current_price, self.max_rate or self.open_rate) self.min_rate = min(current_price_low, self.min_rate or self.open_rate) - def set_isolated_liq(self, isolated_liq: Optional[float]): + def set_isolated_liq(self, liquidation_price: Optional[float]): """ Method you should use to set self.liquidation price. Assures stop_loss is not passed the liquidation price """ - if not isolated_liq: + if not liquidation_price: return - self.isolated_liq = isolated_liq + self.liquidation_price = liquidation_price def _set_stop_loss(self, stop_loss: float, percent: float): """ Method you should use to set self.stop_loss. Assures stop_loss is not passed the liquidation price """ - if self.isolated_liq is not None: + if self.liquidation_price is not None: if self.is_short: - sl = min(stop_loss, self.isolated_liq) + sl = min(stop_loss, self.liquidation_price) else: - sl = max(stop_loss, self.isolated_liq) + sl = max(stop_loss, self.liquidation_price) else: sl = stop_loss @@ -553,13 +553,13 @@ class LocalTrade(): if self.is_short: new_loss = float(current_price * (1 + abs(stoploss / leverage))) # If trading with leverage, don't set the stoploss below the liquidation price - if self.isolated_liq: - new_loss = min(self.isolated_liq, new_loss) + if self.liquidation_price: + new_loss = min(self.liquidation_price, new_loss) else: new_loss = float(current_price * (1 - abs(stoploss / leverage))) # If trading with leverage, don't set the stoploss below the liquidation price - if self.isolated_liq: - new_loss = max(self.isolated_liq, new_loss) + if self.liquidation_price: + new_loss = max(self.liquidation_price, new_loss) # no stop loss assigned yet if self.initial_stop_loss_pct is None: @@ -1093,7 +1093,7 @@ class Trade(_DECL_BASE, LocalTrade): # Leverage trading properties leverage = Column(Float, nullable=True, default=1.0) is_short = Column(Boolean, nullable=False, default=False) - isolated_liq = Column(Float, nullable=True) + liquidation_price = Column(Float, nullable=True) # Margin Trading Properties interest_rate = Column(Float, nullable=False, default=0.0) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index da8751566..1f0735907 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -613,7 +613,7 @@ def test_backtest__enter_trade_futures(default_conf_usdt, fee, mocker) -> None: # = 0.0008176703703703704 trade = backtesting._enter_trade(pair, row=row, direction='long') - assert pytest.approx(trade.isolated_liq) == 0.00081767037 + assert pytest.approx(trade.liquidation_price) == 0.00081767037 # Binance, Short # liquidation_price @@ -625,7 +625,7 @@ def test_backtest__enter_trade_futures(default_conf_usdt, fee, mocker) -> None: # = 0.0011787191419141915 trade = backtesting._enter_trade(pair, row=row, direction='short') - assert pytest.approx(trade.isolated_liq) == 0.0011787191 + assert pytest.approx(trade.liquidation_price) == 0.0011787191 # Stake-amount too high! mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=600.0) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index ce881bcf1..7e34506d6 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -112,7 +112,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'exchange': 'binance', 'leverage': 1.0, 'interest_rate': 0.0, - 'isolated_liq': None, + 'liquidation_price': None, 'is_short': False, 'funding_fees': 0.0, 'trading_mode': TradingMode.SPOT, @@ -194,7 +194,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'exchange': 'binance', 'leverage': 1.0, 'interest_rate': 0.0, - 'isolated_liq': None, + 'liquidation_price': None, 'is_short': False, 'funding_fees': 0.0, 'trading_mode': TradingMode.SPOT, diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index add6c586d..b51637143 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -944,7 +944,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order, trade.is_short = is_short assert trade assert trade.open_rate_requested == 10 - assert trade.isolated_liq == liq_price + assert trade.liquidation_price == liq_price # In case of too high stake amount diff --git a/tests/test_persistence.py b/tests/test_persistence.py index c11987027..313f32685 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -116,38 +116,38 @@ def test_set_stop_loss_isolated_liq(fee): trading_mode=margin ) trade.set_isolated_liq(0.09) - assert trade.isolated_liq == 0.09 + assert trade.liquidation_price == 0.09 assert trade.stop_loss is None assert trade.initial_stop_loss is None trade._set_stop_loss(0.1, (1.0/9.0)) - assert trade.isolated_liq == 0.09 + assert trade.liquidation_price == 0.09 assert trade.stop_loss == 0.1 assert trade.initial_stop_loss == 0.1 trade.set_isolated_liq(0.08) - assert trade.isolated_liq == 0.08 + assert trade.liquidation_price == 0.08 assert trade.stop_loss == 0.1 assert trade.initial_stop_loss == 0.1 trade.set_isolated_liq(0.11) trade._set_stop_loss(0.1, 0) - assert trade.isolated_liq == 0.11 + assert trade.liquidation_price == 0.11 assert trade.stop_loss == 0.11 assert trade.initial_stop_loss == 0.1 # lower stop doesn't move stoploss trade._set_stop_loss(0.1, 0) - assert trade.isolated_liq == 0.11 + assert trade.liquidation_price == 0.11 assert trade.stop_loss == 0.11 assert trade.initial_stop_loss == 0.1 trade.stop_loss = None - trade.isolated_liq = None + trade.liquidation_price = None trade.initial_stop_loss = None trade._set_stop_loss(0.07, 0) - assert trade.isolated_liq is None + assert trade.liquidation_price is None assert trade.stop_loss == 0.07 assert trade.initial_stop_loss == 0.07 @@ -157,29 +157,29 @@ def test_set_stop_loss_isolated_liq(fee): trade.initial_stop_loss = None trade.set_isolated_liq(0.09) - assert trade.isolated_liq == 0.09 + assert trade.liquidation_price == 0.09 assert trade.stop_loss is None assert trade.initial_stop_loss is None trade._set_stop_loss(0.08, (1.0/9.0)) - assert trade.isolated_liq == 0.09 + assert trade.liquidation_price == 0.09 assert trade.stop_loss == 0.08 assert trade.initial_stop_loss == 0.08 trade.set_isolated_liq(0.1) - assert trade.isolated_liq == 0.1 + assert trade.liquidation_price == 0.1 assert trade.stop_loss == 0.08 assert trade.initial_stop_loss == 0.08 trade.set_isolated_liq(0.07) trade._set_stop_loss(0.1, (1.0/8.0)) - assert trade.isolated_liq == 0.07 + assert trade.liquidation_price == 0.07 assert trade.stop_loss == 0.07 assert trade.initial_stop_loss == 0.08 # Stop doesn't move stop higher trade._set_stop_loss(0.1, (1.0/9.0)) - assert trade.isolated_liq == 0.07 + assert trade.liquidation_price == 0.07 assert trade.stop_loss == 0.07 assert trade.initial_stop_loss == 0.08 @@ -1474,7 +1474,7 @@ def test_adjust_stop_loss_short(fee): trade.set_isolated_liq(0.63) trade.adjust_stop_loss(0.59, -0.1) assert trade.stop_loss == 0.63 - assert trade.isolated_liq == 0.63 + assert trade.liquidation_price == 0.63 def test_adjust_min_max_rates(fee): @@ -1539,7 +1539,7 @@ def test_get_open_lev(fee, use_db): @pytest.mark.usefixtures("init_persistence") -def test_to_json(default_conf, fee): +def test_to_json(fee): # Simulate dry_run entries trade = Trade( @@ -1608,7 +1608,7 @@ def test_to_json(default_conf, fee): 'exchange': 'binance', 'leverage': None, 'interest_rate': None, - 'isolated_liq': None, + 'liquidation_price': None, 'is_short': None, 'trading_mode': None, 'funding_fees': None, @@ -1683,7 +1683,7 @@ def test_to_json(default_conf, fee): 'exchange': 'binance', 'leverage': None, 'interest_rate': None, - 'isolated_liq': None, + 'liquidation_price': None, 'is_short': None, 'trading_mode': None, 'funding_fees': None, From 3ff261e22cbd4a61a0388ff85acaab1c3c2f2116 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 7 Mar 2022 07:09:01 +0100 Subject: [PATCH 2/3] Update order time in force to use entry/exit wording --- config_examples/config_full.example.json | 4 ++-- docs/configuration.md | 6 ++--- freqtrade/configuration/config_validation.py | 24 ++++++++++++++++++- freqtrade/constants.py | 8 +++---- freqtrade/freqtradebot.py | 16 ++++++------- freqtrade/optimize/backtesting.py | 4 ++-- freqtrade/resolvers/strategy_resolver.py | 5 +++- freqtrade/strategy/interface.py | 4 ++-- freqtrade/templates/base_strategy.py.j2 | 4 ++-- freqtrade/templates/sample_short_strategy.py | 4 ++-- freqtrade/templates/sample_strategy.py | 4 ++-- tests/optimize/test_backtesting.py | 1 + .../strategy/strats/hyperoptable_strategy.py | 4 ++-- tests/strategy/strats/strategy_test_v2.py | 4 ++-- tests/strategy/strats/strategy_test_v3.py | 4 ++-- tests/strategy/test_strategy_loading.py | 10 ++++---- tests/test_configuration.py | 22 +++++++++++++++++ 17 files changed, 88 insertions(+), 40 deletions(-) diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json index 85c1bde5b..1fb2817b8 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -61,8 +61,8 @@ "stoploss_on_exchange_interval": 60 }, "order_time_in_force": { - "buy": "gtc", - "sell": "gtc" + "entry": "gtc", + "exit": "gtc" }, "pairlists": [ {"method": "StaticPairList"}, diff --git a/docs/configuration.md b/docs/configuration.md index 7a42966b0..99c13ca5a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -122,7 +122,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `ignore_roi_if_buy_signal` | Do not sell if the buy signal is still active. This setting takes preference over `minimal_roi` and `use_sell_signal`. [Strategy Override](#parameters-in-the-strategy).
*Defaults to `false`.*
**Datatype:** Boolean | `ignore_buying_expired_candle_after` | Specifies the number of seconds until a buy signal is no longer used.
**Datatype:** Integer | `order_types` | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Dict -| `order_time_in_force` | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Dict +| `order_time_in_force` | Configure time in force for entry and exit orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Dict | `custom_price_max_distance_ratio` | Configure maximum distance ratio between current and custom entry or exit price.
*Defaults to `0.02` 2%).*
**Datatype:** Positive float | `exchange.name` | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename).
**Datatype:** String | `exchange.sandbox` | Use the 'sandbox' version of the exchange, where the exchange provides a sandbox for risk-free integration. See [here](sandbox-testing.md) in more details.
**Datatype:** Boolean @@ -465,8 +465,8 @@ The possible values are: `gtc` (default), `fok` or `ioc`. ``` python "order_time_in_force": { - "buy": "gtc", - "sell": "gtc" + "entry": "gtc", + "exit": "gtc" }, ``` diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 85ff4408f..87a309f12 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -6,7 +6,7 @@ from jsonschema import Draft4Validator, validators from jsonschema.exceptions import ValidationError, best_match from freqtrade import constants -from freqtrade.enums import RunMode +from freqtrade.enums import RunMode, TradingMode from freqtrade.exceptions import OperationalException @@ -80,6 +80,7 @@ def validate_config_consistency(conf: Dict[str, Any]) -> None: _validate_protections(conf) _validate_unlimited_amount(conf) _validate_ask_orderbook(conf) + validate_migrated_strategy_settings(conf) # validate configuration before returning logger.info('Validating configuration ...') @@ -207,3 +208,24 @@ def _validate_ask_orderbook(conf: Dict[str, Any]) -> None: "Please use `order_book_top` instead of `order_book_min` and `order_book_max` " "for your `ask_strategy` configuration." ) + + +def validate_migrated_strategy_settings(conf: Dict[str, Any]) -> None: + + _validate_time_in_force(conf) + + +def _validate_time_in_force(conf: Dict[str, Any]) -> None: + + time_in_force = conf.get('order_time_in_force', {}) + if 'buy' in time_in_force or 'sell' in time_in_force: + if conf.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT: + raise OperationalException( + "Please migrate your time_in_force settings to use 'entry' and 'exit'.") + else: + logger.warning( + "DEPRECATED: Using 'buy' and 'sell' for time_in_force is deprecated." + "Please migrate your time_in_force settings to use 'entry' and 'exit'." + ) + time_in_force['entry'] = time_in_force.pop('buy') + time_in_force['exit'] = time_in_force.pop('sell') diff --git a/freqtrade/constants.py b/freqtrade/constants.py index cc4a14a2b..bafda93db 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -19,7 +19,7 @@ DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite' DEFAULT_DB_DRYRUN_URL = 'sqlite:///tradesv3.dryrun.sqlite' UNLIMITED_STAKE_AMOUNT = 'unlimited' DEFAULT_AMOUNT_RESERVE_PERCENT = 0.05 -REQUIRED_ORDERTIF = ['buy', 'sell'] +REQUIRED_ORDERTIF = ['entry', 'exit'] REQUIRED_ORDERTYPES = ['buy', 'sell', 'stoploss', 'stoploss_on_exchange'] ORDERBOOK_SIDES = ['ask', 'bid'] ORDERTYPE_POSSIBILITIES = ['limit', 'market'] @@ -233,10 +233,10 @@ CONF_SCHEMA = { 'order_time_in_force': { 'type': 'object', 'properties': { - 'buy': {'type': 'string', 'enum': ORDERTIF_POSSIBILITIES}, - 'sell': {'type': 'string', 'enum': ORDERTIF_POSSIBILITIES} + 'entry': {'type': 'string', 'enum': ORDERTIF_POSSIBILITIES}, + 'exit': {'type': 'string', 'enum': ORDERTIF_POSSIBILITIES} }, - 'required': ['buy', 'sell'] + 'required': REQUIRED_ORDERTIF }, 'exchange': {'$ref': '#/definitions/exchange'}, 'edge': {'$ref': '#/definitions/edge'}, diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 341693982..4f3f723c0 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -595,7 +595,7 @@ class FreqtradeBot(LoggingMixin): :param leverage: amount of leverage applied to this trade :return: True if a buy order is created, false if it fails. """ - time_in_force = self.strategy.order_time_in_force['buy'] + time_in_force = self.strategy.order_time_in_force['entry'] [side, name] = ['sell', 'Short'] if is_short else ['buy', 'Long'] trade_side = 'short' if is_short else 'long' @@ -659,13 +659,12 @@ class FreqtradeBot(LoggingMixin): amount_requested = amount if order_status == 'expired' or order_status == 'rejected': - order_tif = self.strategy.order_time_in_force['buy'] # return false if the order is not filled if float(order['filled']) == 0: - logger.warning('%s %s order with time in force %s for %s is %s by %s.' - ' zero amount is fulfilled.', - name, order_tif, order_type, pair, order_status, self.exchange.name) + logger.warning(f'{name} {time_in_force} order with time in force {order_type} ' + f'for {pair} is {order_status} by {self.exchange.name}.' + ' zero amount is fulfilled.') return False else: # the order is partially fulfilled @@ -673,8 +672,9 @@ class FreqtradeBot(LoggingMixin): # if the order is fulfilled fully or partially logger.warning('%s %s order with time in force %s for %s is %s by %s.' ' %s amount fulfilled out of %s (%s remaining which is canceled).', - name, order_tif, order_type, pair, order_status, self.exchange.name, - order['filled'], order['amount'], order['remaining'] + name, time_in_force, order_type, pair, order_status, + self.exchange.name, order['filled'], order['amount'], + order['remaining'] ) stake_amount = order['cost'] amount = safe_value_fallback(order, 'filled', 'amount') @@ -1382,7 +1382,7 @@ class FreqtradeBot(LoggingMixin): order_type = self.strategy.order_types.get("emergencysell", "market") amount = self._safe_exit_amount(trade.pair, trade.amount) - time_in_force = self.strategy.order_time_in_force['sell'] + time_in_force = self.strategy.order_time_in_force['exit'] if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( pair=trade.pair, trade=trade, order_type=order_type, amount=amount, rate=limit, diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index fa3deb86f..744c77844 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -504,7 +504,7 @@ class Backtesting: # freqtrade does not support this in live, and the order would fill immediately closerate = max(closerate, sell_row[LOW_IDX]) # Confirm trade exit: - time_in_force = self.strategy.order_time_in_force['sell'] + time_in_force = self.strategy.order_time_in_force['exit'] if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( pair=trade.pair, trade=trade, order_type='limit', amount=trade.amount, @@ -640,7 +640,7 @@ class Backtesting: # If not pos adjust, trade is None return trade order_type = self.strategy.order_types['buy'] - time_in_force = self.strategy.order_time_in_force['buy'] + time_in_force = self.strategy.order_time_in_force['entry'] if not pos_adjust: max_leverage = self.exchange.get_max_leverage(pair, stake_amount) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index fc4b71f1a..8dee459ba 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -10,6 +10,7 @@ from inspect import getfullargspec from pathlib import Path from typing import Any, Dict, Optional +from freqtrade.configuration.config_validation import validate_migrated_strategy_settings from freqtrade.constants import REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES, USERPATH_STRATEGIES from freqtrade.exceptions import OperationalException from freqtrade.resolvers import IResolver @@ -160,10 +161,12 @@ class StrategyResolver(IResolver): @staticmethod def _strategy_sanity_validations(strategy): + # Ensure necessary migrations are performed first. + validate_migrated_strategy_settings(strategy.config) + if not all(k in strategy.order_types for k in REQUIRED_ORDERTYPES): raise ImportError(f"Impossible to load Strategy '{strategy.__class__.__name__}'. " f"Order-types mapping is incomplete.") - if not all(k in strategy.order_time_in_force for k in REQUIRED_ORDERTIF): raise ImportError(f"Impossible to load Strategy '{strategy.__class__.__name__}'. " f"Order-time-in-force mapping is incomplete.") diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 92ea3daba..e5b583a9e 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -96,8 +96,8 @@ class IStrategy(ABC, HyperStrategyMixin): # Optional time in force order_time_in_force: Dict = { - 'buy': 'gtc', - 'sell': 'gtc', + 'entry': 'gtc', + 'exit': 'gtc', } # run "populate_indicators" only for new candle diff --git a/freqtrade/templates/base_strategy.py.j2 b/freqtrade/templates/base_strategy.py.j2 index 06abecc42..701909bf6 100644 --- a/freqtrade/templates/base_strategy.py.j2 +++ b/freqtrade/templates/base_strategy.py.j2 @@ -83,8 +83,8 @@ class {{ strategy }}(IStrategy): # Optional order time in force. order_time_in_force = { - 'buy': 'gtc', - 'sell': 'gtc' + 'entry': 'gtc', + 'exit': 'gtc' } {{ plot_config | indent(4) }} diff --git a/freqtrade/templates/sample_short_strategy.py b/freqtrade/templates/sample_short_strategy.py index bcb6c921e..c33327715 100644 --- a/freqtrade/templates/sample_short_strategy.py +++ b/freqtrade/templates/sample_short_strategy.py @@ -84,8 +84,8 @@ class SampleShortStrategy(IStrategy): # Optional order time in force. order_time_in_force = { - 'buy': 'gtc', - 'sell': 'gtc' + 'entry': 'gtc', + 'exit': 'gtc' } plot_config = { diff --git a/freqtrade/templates/sample_strategy.py b/freqtrade/templates/sample_strategy.py index 13df9c2a8..b3f1ae1c8 100644 --- a/freqtrade/templates/sample_strategy.py +++ b/freqtrade/templates/sample_strategy.py @@ -85,8 +85,8 @@ class SampleStrategy(IStrategy): # Optional order time in force. order_time_in_force = { - 'buy': 'gtc', - 'sell': 'gtc' + 'entry': 'gtc', + 'exit': 'gtc' } plot_config = { diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 1f0735907..ec77d1cbf 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -319,6 +319,7 @@ def test_backtesting_init_no_timeframe(mocker, default_conf, caplog) -> None: del default_conf['timeframe'] default_conf['strategy_list'] = [CURRENT_TEST_STRATEGY, 'SampleStrategy'] + # TODO: This refers to the sampleStrategy in user_data if it exists... mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5)) with pytest.raises(OperationalException): diff --git a/tests/strategy/strats/hyperoptable_strategy.py b/tests/strategy/strats/hyperoptable_strategy.py index 1126bd6cf..e843f6b58 100644 --- a/tests/strategy/strats/hyperoptable_strategy.py +++ b/tests/strategy/strats/hyperoptable_strategy.py @@ -45,8 +45,8 @@ class HyperoptableStrategy(IStrategy): # Optional time in force for orders order_time_in_force = { - 'buy': 'gtc', - 'sell': 'gtc', + 'entry': 'gtc', + 'exit': 'gtc', } buy_params = { diff --git a/tests/strategy/strats/strategy_test_v2.py b/tests/strategy/strats/strategy_test_v2.py index 59f1f569e..fd70cf346 100644 --- a/tests/strategy/strats/strategy_test_v2.py +++ b/tests/strategy/strats/strategy_test_v2.py @@ -47,8 +47,8 @@ class StrategyTestV2(IStrategy): # Optional time in force for orders order_time_in_force = { - 'buy': 'gtc', - 'sell': 'gtc', + 'entry': 'gtc', + 'exit': 'gtc', } # By default this strategy does not use Position Adjustments diff --git a/tests/strategy/strats/strategy_test_v3.py b/tests/strategy/strats/strategy_test_v3.py index 0b73c1271..962fd02e9 100644 --- a/tests/strategy/strats/strategy_test_v3.py +++ b/tests/strategy/strats/strategy_test_v3.py @@ -48,8 +48,8 @@ class StrategyTestV3(IStrategy): # Optional time in force for orders order_time_in_force = { - 'buy': 'gtc', - 'sell': 'gtc', + 'entry': 'gtc', + 'exit': 'gtc', } buy_params = { diff --git a/tests/strategy/test_strategy_loading.py b/tests/strategy/test_strategy_loading.py index da4f8fb78..8f407396c 100644 --- a/tests/strategy/test_strategy_loading.py +++ b/tests/strategy/test_strategy_loading.py @@ -257,8 +257,8 @@ def test_strategy_override_order_tif(caplog, default_conf): caplog.set_level(logging.INFO) order_time_in_force = { - 'buy': 'fok', - 'sell': 'gtc', + 'entry': 'fok', + 'exit': 'gtc', } default_conf.update({ @@ -268,15 +268,15 @@ def test_strategy_override_order_tif(caplog, default_conf): strategy = StrategyResolver.load_strategy(default_conf) assert strategy.order_time_in_force - for method in ['buy', 'sell']: + for method in ['entry', 'exit']: assert strategy.order_time_in_force[method] == order_time_in_force[method] assert log_has("Override strategy 'order_time_in_force' with value in config file:" - " {'buy': 'fok', 'sell': 'gtc'}.", caplog) + " {'entry': 'fok', 'exit': 'gtc'}.", caplog) default_conf.update({ 'strategy': CURRENT_TEST_STRATEGY, - 'order_time_in_force': {'buy': 'fok'} + 'order_time_in_force': {'entry': 'fok'} }) # Raise error for invalid configuration with pytest.raises(ImportError, diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 935421409..e2ab3c9b5 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -941,6 +941,28 @@ def test_validate_ask_orderbook(default_conf, caplog) -> None: validate_config_consistency(conf) +def test_validate_time_in_force(default_conf, caplog) -> None: + conf = deepcopy(default_conf) + conf['order_time_in_force'] = { + 'buy': 'gtc', + 'sell': 'gtc', + } + validate_config_consistency(conf) + assert log_has_re(r"DEPRECATED: Using 'buy' and 'sell' for time_in_force is.*", caplog) + assert conf['order_time_in_force']['entry'] == 'gtc' + assert conf['order_time_in_force']['exit'] == 'gtc' + + conf = deepcopy(default_conf) + conf['order_time_in_force'] = { + 'buy': 'gtc', + 'sell': 'gtc', + } + conf['trading_mode'] = 'futures' + with pytest.raises(OperationalException, + match=r"Please migrate your time_in_force settings .* 'entry' and 'exit'\."): + validate_config_consistency(conf) + + def test_load_config_test_comments() -> None: """ Load config with comments From 1ce55e88b49286934bf01b810e41e45f87175662 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 8 Mar 2022 07:10:59 +0100 Subject: [PATCH 3/3] Try to revert sequence in test --- tests/rpc/test_rpc_apiserver.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index b634ec2f7..4050dcbdb 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -633,9 +633,6 @@ def test_api_delete_trade(botclient, mocker, fee, markets, is_short): cancel_order=cancel_mock, cancel_stoploss_order=stoploss_mock, ) - rc = client_delete(client, f"{BASE_URI}/trades/1") - # Error - trade won't exist yet. - assert_response(rc, 502) create_mock_trades(fee, is_short=is_short) @@ -664,6 +661,10 @@ def test_api_delete_trade(botclient, mocker, fee, markets, is_short): assert len(trades) - 2 == len(Trade.query.all()) assert stoploss_mock.call_count == 1 + rc = client_delete(client, f"{BASE_URI}/trades/502") + # Error - trade won't exist. + assert_response(rc, 502) + def test_api_logs(botclient): ftbot, client = botclient