2022-03-03 19:28:36 +00:00
# Strategy Migration between V2 and V3
To support new markets and trade-types (namely short trades / trades with leverage), some things had to change in the interface.
If you intend on using markets other than spot markets, please migrate your strategy to the new format.
2022-03-31 04:53:33 +00:00
We have put a great effort into keeping compatibility with existing strategies, so if you just want to continue using freqtrade in __spot markets__ , there should be no changes necessary for now.
You can use the quick summary as checklist. Please refer to the detailed sections below for full migration details.
2022-03-05 14:05:03 +00:00
## Quick summary / migration checklist
2022-03-03 19:28:36 +00:00
2022-04-09 17:58:58 +00:00
Note : `forcesell` , `forcebuy` , `emergencysell` are changed to `force_exit` , `force_enter` , `emergency_exit` respectively.
2022-04-06 01:35:43 +00:00
2022-03-12 08:39:40 +00:00
* Strategy methods:
2022-03-31 04:53:33 +00:00
* [`populate_buy_trend()` -> `populate_entry_trend()` ](#populate_buy_trend )
* [`populate_sell_trend()` -> `populate_exit_trend()` ](#populate_sell_trend )
* [`custom_sell()` -> `custom_exit()` ](#custom_sell )
* [`check_buy_timeout()` -> `check_entry_timeout()` ](#custom_entry_timeout )
* [`check_sell_timeout()` -> `check_exit_timeout()` ](#custom_entry_timeout )
* New `side` argument to callbacks without trade object
2022-08-05 02:36:26 +00:00
* [`custom_stake_amount` ](#custom_stake_amount )
2022-03-31 04:53:33 +00:00
* [`confirm_trade_entry` ](#confirm_trade_entry )
2022-04-04 14:48:27 +00:00
* [`custom_entry_price` ](#custom_entry_price )
2022-03-31 04:53:33 +00:00
* [Changed argument name in `confirm_trade_exit` ](#confirm_trade_exit )
2022-03-03 19:28:36 +00:00
* Dataframe columns:
2022-03-31 04:53:33 +00:00
* [`buy` -> `enter_long` ](#populate_buy_trend )
* [`sell` -> `exit_long` ](#populate_sell_trend )
* [`buy_tag` -> `enter_tag` (used for both long and short trades) ](#populate_buy_trend )
* [New column `enter_short` and corresponding new column `exit_short` ](#populate_sell_trend )
2022-03-24 19:53:22 +00:00
* trade-object now has the following new properties:
* `is_short`
2022-04-06 01:02:13 +00:00
* `entry_side`
2022-03-24 19:53:22 +00:00
* `exit_side`
* `trade_direction`
* renamed: `sell_reason` -> `exit_reason`
2022-03-31 04:53:33 +00:00
* [Renamed `trade.nr_of_successful_buys` to `trade.nr_of_successful_entries` (mostly relevant for `adjust_trade_position()`) ](#adjust-trade-position-changes )
2022-03-12 08:39:40 +00:00
* Introduced new [`leverage` callback ](strategy-callbacks.md#leverage-callback ).
2022-03-05 14:05:03 +00:00
* Informative pairs can now pass a 3rd element in the Tuple, defining the candle type.
2022-03-12 08:39:40 +00:00
* `@informative` decorator now takes an optional `candle_type` argument.
2022-03-31 04:53:33 +00:00
* [helper methods ](#helper-methods ) `stoploss_from_open` and `stoploss_from_absolute` now take `is_short` as additional argument.
2022-03-05 13:26:18 +00:00
* `INTERFACE_VERSION` should be set to 3.
2022-03-31 04:53:33 +00:00
* [Strategy/Configuration settings ](#strategyconfiguration-settings ).
2022-03-12 08:39:40 +00:00
* `order_time_in_force` buy -> entry, sell -> exit.
* `order_types` buy -> entry, sell -> exit.
2022-03-26 10:55:11 +00:00
* `unfilledtimeout` buy -> entry, sell -> exit.
2022-04-04 17:05:36 +00:00
* Terminology changes
2022-04-04 17:53:56 +00:00
* Sell reasons changed to reflect the new naming of "exit" instead of sells. Be careful in your strategy if you're using `exit_reason` checks and eventually update your strategy.
2022-04-04 17:05:36 +00:00
* `sell_signal` -> `exit_signal`
* `custom_sell` -> `custom_exit`
* `force_sell` -> `force_exit`
* `emergency_sell` -> `emergency_exit`
2022-04-04 17:45:39 +00:00
* Webhook terminology changed from "sell" to "exit", and from "buy" to entry
2022-04-04 17:32:27 +00:00
* `webhookbuy` -> `webhookentry`
* `webhookbuyfill` -> `webhookentryfill`
* `webhookbuycancel` -> `webhookentrycancel`
2022-04-04 17:05:36 +00:00
* `webhooksell` -> `webhookexit`
* `webhooksellfill` -> `webhookexitfill`
* `webhooksellcancel` -> `webhookexitcancel`
2022-04-04 17:10:44 +00:00
* Telegram notification settings
2022-04-04 17:29:15 +00:00
* `buy` -> `entry`
* `buy_fill` -> `entry_fill`
* `buy_cancel` -> `entry_cancel`
2022-04-04 17:10:44 +00:00
* `sell` -> `exit`
* `sell_fill` -> `exit_fill`
* `sell_cancel` -> `exit_cancel`
2022-04-05 18:25:31 +00:00
* Strategy/config settings:
* `use_sell_signal` -> `use_exit_signal`
* `sell_profit_only` -> `exit_profit_only`
* `sell_profit_offset` -> `exit_profit_offset`
* `ignore_roi_if_buy_signal` -> `ignore_roi_if_entry_signal`
2022-04-08 11:39:41 +00:00
* `forcebuy_enable` -> `force_entry_enable`
2022-03-03 19:28:36 +00:00
## Extensive explanation
2022-03-05 14:05:03 +00:00
### `populate_buy_trend`
2022-04-01 10:28:42 +00:00
In `populate_buy_trend()` - you will want to change the columns you assign from `'buy` ' to `'enter_long'` , as well as the method name from `populate_buy_trend` to `populate_entry_trend` .
2022-03-05 14:05:03 +00:00
2022-03-12 08:39:40 +00:00
```python hl_lines="1 9"
2022-03-05 14:05:03 +00:00
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(qtpylib.crossed_above(dataframe['rsi'], 30)) & # Signal: RSI crosses above 30
(dataframe['tema'] < = dataframe['bb_middleband']) & # Guard
(dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard
(dataframe['volume'] > 0) # Make sure Volume is not 0
),
['buy', 'buy_tag']] = (1, 'rsi_cross')
return dataframe
```
After:
2022-03-12 08:39:40 +00:00
```python hl_lines="1 9"
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
2022-03-05 14:05:03 +00:00
dataframe.loc[
(
(qtpylib.crossed_above(dataframe['rsi'], 30)) & # Signal: RSI crosses above 30
(dataframe['tema'] < = dataframe['bb_middleband']) & # Guard
(dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard
(dataframe['volume'] > 0) # Make sure Volume is not 0
),
['enter_long', 'enter_tag']] = (1, 'rsi_cross')
return dataframe
```
2022-03-12 08:39:40 +00:00
Please refer to the [Strategy documentation ](strategy-customization.md#entry-signal-rules ) on how to enter and exit short trades.
2022-03-05 14:05:03 +00:00
### `populate_sell_trend`
2022-03-12 08:39:40 +00:00
Similar to `populate_buy_trend` , `populate_sell_trend()` will be renamed to `populate_exit_trend()` .
2022-04-01 10:28:42 +00:00
We'll also change the column from `'sell'` to `'exit_long'` .
2022-03-12 08:39:40 +00:00
``` python hl_lines="1 9"
2022-03-05 14:05:03 +00:00
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(qtpylib.crossed_above(dataframe['rsi'], 70)) & # Signal: RSI crosses above 70
(dataframe['tema'] > dataframe['bb_middleband']) & # Guard
(dataframe['tema'] < dataframe [ ' tema ' ] . shift ( 1 ) ) & # Guard
(dataframe['volume'] > 0) # Make sure Volume is not 0
),
['sell', 'exit_tag']] = (1, 'some_exit_tag')
return dataframe
```
After
2022-03-12 08:39:40 +00:00
``` python hl_lines="1 9"
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
2022-03-05 14:05:03 +00:00
dataframe.loc[
(
(qtpylib.crossed_above(dataframe['rsi'], 70)) & # Signal: RSI crosses above 70
(dataframe['tema'] > dataframe['bb_middleband']) & # Guard
(dataframe['tema'] < dataframe [ ' tema ' ] . shift ( 1 ) ) & # Guard
(dataframe['volume'] > 0) # Make sure Volume is not 0
),
['exit_long', 'exit_tag']] = (1, 'some_exit_tag')
return dataframe
```
2022-03-12 08:39:40 +00:00
Please refer to the [Strategy documentation ](strategy-customization.md#exit-signal-rules ) on how to enter and exit short trades.
2022-03-12 10:07:31 +00:00
### `custom_sell`
2022-04-09 14:50:38 +00:00
`custom_sell` has been renamed to `custom_exit` .
It's now also being called for every iteration, independent of current profit and `exit_profit_only` settings.
2022-03-12 10:07:31 +00:00
``` python hl_lines="2"
class AwesomeStrategy(IStrategy):
def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
current_profit: float, **kwargs):
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze()
# ...
```
``` python hl_lines="2"
class AwesomeStrategy(IStrategy):
def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
current_profit: float, **kwargs):
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze()
# ...
```
2022-03-25 18:46:56 +00:00
### `custom_entry_timeout`
`check_buy_timeout()` has been renamed to `check_entry_timeout()` , and `check_sell_timeout()` has been renamed to `check_exit_timeout()` .
``` python hl_lines="2 6"
class AwesomeStrategy(IStrategy):
def check_buy_timeout(self, pair: str, trade: 'Trade', order: dict,
current_time: datetime, **kwargs) -> bool:
return False
def check_sell_timeout(self, pair: str, trade: 'Trade', order: dict,
current_time: datetime, **kwargs) -> bool:
return False
```
``` python hl_lines="2 6"
class AwesomeStrategy(IStrategy):
2022-04-16 04:47:56 +00:00
def check_entry_timeout(self, pair: str, trade: 'Trade', order: 'Order',
2022-03-25 18:46:56 +00:00
current_time: datetime, **kwargs) -> bool:
return False
2022-04-16 04:47:56 +00:00
def check_exit_timeout(self, pair: str, trade: 'Trade', order: 'Order',
2022-03-25 18:46:56 +00:00
current_time: datetime, **kwargs) -> bool:
return False
```
2022-08-04 17:28:28 +00:00
### `custom_stake_amount`
2022-03-05 14:05:03 +00:00
New string argument `side` - which can be either `"long"` or `"short"` .
``` python hl_lines="4"
class AwesomeStrategy(IStrategy):
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
2022-05-07 09:55:49 +00:00
proposed_stake: float, min_stake: Optional[float], max_stake: float,
2022-03-05 14:05:03 +00:00
entry_tag: Optional[str], **kwargs) -> float:
# ...
return proposed_stake
```
``` python hl_lines="4"
class AwesomeStrategy(IStrategy):
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
2022-05-07 09:55:49 +00:00
proposed_stake: float, min_stake: Optional[float], max_stake: float,
2022-03-05 14:05:03 +00:00
entry_tag: Optional[str], side: str, **kwargs) -> float:
# ...
return proposed_stake
```
### `confirm_trade_entry`
New string argument `side` - which can be either `"long"` or `"short"` .
2022-03-25 05:37:40 +00:00
``` python hl_lines="4"
2022-03-05 14:05:03 +00:00
class AwesomeStrategy(IStrategy):
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: datetime, entry_tag: Optional[str],
**kwargs) -> bool:
return True
```
2022-03-25 05:37:40 +00:00
2022-03-05 14:05:03 +00:00
After:
2022-03-25 05:37:40 +00:00
``` python hl_lines="4"
2022-03-05 14:05:03 +00:00
class AwesomeStrategy(IStrategy):
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: datetime, entry_tag: Optional[str],
side: str, **kwargs) -> bool:
return True
```
2022-03-25 05:37:40 +00:00
### `confirm_trade_exit`
Changed argument `sell_reason` to `exit_reason` .
For compatibility, `sell_reason` will still be provided for a limited time.
``` python hl_lines="3"
class AwesomeStrategy(IStrategy):
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
rate: float, time_in_force: str, sell_reason: str,
current_time: datetime, **kwargs) -> bool:
return True
```
After:
``` python hl_lines="3"
class AwesomeStrategy(IStrategy):
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
rate: float, time_in_force: str, exit_reason: str,
current_time: datetime, **kwargs) -> bool:
return True
```
2022-04-04 14:48:27 +00:00
### `custom_entry_price`
New string argument `side` - which can be either `"long"` or `"short"` .
``` python hl_lines="3"
class AwesomeStrategy(IStrategy):
def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float,
entry_tag: Optional[str], **kwargs) -> float:
return proposed_rate
```
After:
``` python hl_lines="3"
class AwesomeStrategy(IStrategy):
def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float,
entry_tag: Optional[str], side: str, **kwargs) -> float:
return proposed_rate
```
2022-03-05 14:05:03 +00:00
### Adjust trade position changes
While adjust-trade-position itself did not change, you should no longer use `trade.nr_of_successful_buys` - and instead use `trade.nr_of_successful_entries` , which will also include short entries.
### Helper methods
Added argument "is_short" to `stoploss_from_open` and `stoploss_from_absolute` .
This should be given the value of `trade.is_short` .
``` python hl_lines="5 7"
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
current_rate: float, current_profit: float, **kwargs) -> float:
# once the profit has risen above 10%, keep the stoploss at 7% above the open price
if current_profit > 0.10:
return stoploss_from_open(0.07, current_profit)
return stoploss_from_absolute(current_rate - (candle['atr'] * 2), current_rate)
return 1
```
2022-03-27 16:03:49 +00:00
After:
2022-03-05 14:05:03 +00:00
``` python hl_lines="5 7"
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
current_rate: float, current_profit: float, **kwargs) -> float:
# once the profit has risen above 10%, keep the stoploss at 7% above the open price
if current_profit > 0.10:
return stoploss_from_open(0.07, current_profit, is_short=trade.is_short)
return stoploss_from_absolute(current_rate - (candle['atr'] * 2), current_rate, is_short=trade.is_short)
```
2022-03-03 19:28:36 +00:00
2022-03-07 19:10:54 +00:00
### Strategy/Configuration settings
#### `order_time_in_force`
`order_time_in_force` attributes changed from `"buy"` to `"entry"` and `"sell"` to `"exit"` .
``` python
order_time_in_force: Dict = {
"buy": "gtc",
"sell": "gtc",
}
```
2022-03-27 16:03:49 +00:00
After:
2022-03-07 19:10:54 +00:00
``` python hl_lines="2 3"
order_time_in_force: Dict = {
2022-08-27 08:24:56 +00:00
"entry": "GTC",
"exit": "GTC",
2022-03-07 19:10:54 +00:00
}
```
2022-03-09 05:46:31 +00:00
#### `order_types`
`order_types` have changed all wordings from `buy` to `entry` - and `sell` to `exit` .
2022-04-06 01:35:43 +00:00
And two words are joined with `_` .
2022-03-09 05:46:31 +00:00
``` python hl_lines="2-6"
order_types = {
"buy": "limit",
"sell": "limit",
"emergencysell": "market",
"forcesell": "market",
"forcebuy": "market",
"stoploss": "market",
"stoploss_on_exchange": false,
"stoploss_on_exchange_interval": 60
2022-03-26 10:55:11 +00:00
}
2022-03-09 05:46:31 +00:00
```
2022-03-27 16:03:49 +00:00
After:
2022-03-09 05:46:31 +00:00
``` python hl_lines="2-6"
order_types = {
"entry": "limit",
"exit": "limit",
2022-04-05 10:31:53 +00:00
"emergency_exit": "market",
"force_exit": "market",
"force_entry": "market",
2022-03-09 05:46:31 +00:00
"stoploss": "market",
"stoploss_on_exchange": false,
"stoploss_on_exchange_interval": 60
2022-03-26 10:55:11 +00:00
}
```
2022-04-05 18:25:31 +00:00
#### Strategy level settings
* `use_sell_signal` -> `use_exit_signal`
* `sell_profit_only` -> `exit_profit_only`
* `sell_profit_offset` -> `exit_profit_offset`
* `ignore_roi_if_buy_signal` -> `ignore_roi_if_entry_signal`
``` python hl_lines="2-5"
# These values can be overridden in the config.
use_sell_signal = True
sell_profit_only = True
sell_profit_offset: 0.01
ignore_roi_if_buy_signal = False
```
After:
``` python hl_lines="2-5"
# These values can be overridden in the config.
use_exit_signal = True
exit_profit_only = True
exit_profit_offset: 0.01
ignore_roi_if_entry_signal = False
```
2022-03-26 10:55:11 +00:00
#### `unfilledtimeout`
`unfilledtimeout` have changed all wordings from `buy` to `entry` - and `sell` to `exit` .
``` python hl_lines="2-3"
unfilledtimeout = {
"buy": 10,
"sell": 10,
"exit_timeout_count": 0,
"unit": "minutes"
}
```
2022-03-27 16:03:49 +00:00
After:
2022-03-26 10:55:11 +00:00
``` python hl_lines="2-3"
unfilledtimeout = {
"entry": 10,
"exit": 10,
"exit_timeout_count": 0,
"unit": "minutes"
}
2022-03-09 05:46:31 +00:00
```
2022-03-27 16:03:49 +00:00
#### `order pricing`
2022-03-31 17:14:11 +00:00
Order pricing changed in 2 ways. `bid_strategy` was renamed to `entry_pricing` and `ask_strategy` was renamed to `exit_pricing` .
2022-03-28 17:48:45 +00:00
The attributes `ask_last_balance` -> `price_last_balance` and `bid_last_balance` -> `price_last_balance` were renamed as well.
2022-03-27 16:03:49 +00:00
Also, price-side can now be defined as `ask` , `bid` , `same` or `other` .
2022-03-31 04:53:33 +00:00
Please refer to the [pricing documentation ](configuration.md#prices-used-for-orders ) for more information.
2022-03-27 16:03:49 +00:00
2022-03-28 17:48:45 +00:00
``` json hl_lines="2-3 6 12-13 16"
2022-03-27 16:03:49 +00:00
{
"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,
2022-03-28 17:48:45 +00:00
"order_book_top": 1,
"bid_last_balance": 0.0
2022-03-27 16:03:49 +00:00
}
}
```
after:
2022-03-28 17:48:45 +00:00
``` json hl_lines="2-3 6 12-13 16"
2022-03-27 16:03:49 +00:00
{
"entry_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1,
2022-03-28 17:48:45 +00:00
"price_last_balance": 0.0,
2022-03-27 16:03:49 +00:00
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"exit_pricing":{
"price_side": "same",
"use_order_book": true,
2022-03-28 17:48:45 +00:00
"order_book_top": 1,
"price_last_balance": 0.0
2022-03-27 16:03:49 +00:00
}
}
```