From ee77ae3ada56e31697520248a55f55148fd7cad4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 3 Mar 2022 20:28:36 +0100 Subject: [PATCH 01/15] Add strategy-migration doc page --- docs/deprecated.md | 13 ++++--------- docs/strategy_migration.md | 26 ++++++++++++++++++++++++++ mkdocs.yml | 1 + 3 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 docs/strategy_migration.md diff --git a/docs/deprecated.md b/docs/deprecated.md index 5d4797d07..ddd71dc7e 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -48,17 +48,12 @@ Please switch to the new [Parametrized Strategies](hyperopt.md) to benefit from // TODO-lev: update version here -## Strategy changes +## Strategy changes between V2 and V3 -As strategies now have to support multiple different signal types, some things had to change. +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 are no changes necessary. +While we may drop support for the current interface sometime in the future, we will announce this separately and have an appropriate transition period. -Dataframe columns: - -* `buy` -> `enter_long` -* `sell` -> `exit_long` -* `buy_tag` -> `enter_tag` - -New columns are `enter_short` and `exit_short`, which will initiate short trades (requires additional configuration!) +Please follow the [Strategy migration](strategy_migration.md) guide to migrate your strategy to the new format to start using the new functionalities. ### webhooks - `buy_tag` has been renamed to `enter_tag` diff --git a/docs/strategy_migration.md b/docs/strategy_migration.md new file mode 100644 index 000000000..d2614444a --- /dev/null +++ b/docs/strategy_migration.md @@ -0,0 +1,26 @@ +# Strategy Migration between V2 and V3 + +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. + +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. + +## Quick summary / checklist + +* Dataframe columns: + * `buy` -> `enter_long` + * `sell` -> `exit_long` + * `buy_tag` -> `enter_tag` (used for both long and short trades) + * New column `enter_short` and corresponding new column `exit_short` +* trade-object now has the following new properties: `is_short`, `enter_side`, `exit_side` and `trade_direction`. +* New `side` argument to callbacks without trade object + * `custom_stake_amount` + * `confirm_trade_entry` +* Renamed `trade.nr_of_successful_buys` to `trade.nr_of_successful_entries`. +* Introduced new `leverage` callback +* `@informative` decorator now takes an optional `candle_type` argument +* helper methods `stoploss_from_open` and `stoploss_from_absolute` now take `is_short` as additional argument. + +## Extensive explanation + + diff --git a/mkdocs.yml b/mkdocs.yml index 5e1eb0c87..c95d53b86 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -23,6 +23,7 @@ nav: - Backtesting: backtesting.md - Hyperopt: hyperopt.md - Leverage: leverage.md + - Strategy migration: strategy_migration.md - Utility Sub-commands: utils.md - Plotting: plotting.md - Exchange-specific Notes: exchanges.md From 72fd937a74d109c54cf7dc01f61848456198b754 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Mar 2022 14:26:18 +0100 Subject: [PATCH 02/15] INTERFACE_VERSION to 3 --- docs/strategy-customization.md | 2 +- docs/strategy_migration.md | 1 + freqtrade/strategy/interface.py | 3 ++- freqtrade/templates/base_strategy.py.j2 | 2 +- freqtrade/templates/sample_short_strategy.py | 2 +- freqtrade/templates/sample_strategy.py | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index e344c1b7d..3a8425b4e 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -35,7 +35,7 @@ The bot also include a sample strategy called `SampleStrategy` you can update: ` You can test it with the parameter: `--strategy SampleStrategy` Additionally, there is an attribute called `INTERFACE_VERSION`, which defines the version of the strategy interface the bot should use. -The current version is 2 - which is also the default when it's not set explicitly in the strategy. +The current version is 3 - which is also the default when it's not set explicitly in the strategy. Future versions will require this to be set. diff --git a/docs/strategy_migration.md b/docs/strategy_migration.md index d2614444a..9f0300fe3 100644 --- a/docs/strategy_migration.md +++ b/docs/strategy_migration.md @@ -20,6 +20,7 @@ If you intend on using markets other than spot markets, please migrate your stra * Introduced new `leverage` callback * `@informative` decorator now takes an optional `candle_type` argument * helper methods `stoploss_from_open` and `stoploss_from_absolute` now take `is_short` as additional argument. +* `INTERFACE_VERSION` should be set to 3. ## Extensive explanation diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 3fddc98df..c759d50de 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -62,7 +62,8 @@ class IStrategy(ABC, HyperStrategyMixin): # Default to version 2 # Version 1 is the initial interface without metadata dict # Version 2 populate_* include metadata dict - INTERFACE_VERSION: int = 2 + # Version 3 - First version with short and leverage support + INTERFACE_VERSION: int = 3 _populate_fun_len: int = 0 _buy_fun_len: int = 0 diff --git a/freqtrade/templates/base_strategy.py.j2 b/freqtrade/templates/base_strategy.py.j2 index ef8f46f5c..78ee8572e 100644 --- a/freqtrade/templates/base_strategy.py.j2 +++ b/freqtrade/templates/base_strategy.py.j2 @@ -35,7 +35,7 @@ class {{ strategy }}(IStrategy): """ # Strategy interface version - allow new iterations of the strategy interface. # Check the documentation or the Sample strategy to get the latest version. - INTERFACE_VERSION = 2 + INTERFACE_VERSION = 3 # Optimal timeframe for the strategy. timeframe = '5m' diff --git a/freqtrade/templates/sample_short_strategy.py b/freqtrade/templates/sample_short_strategy.py index 1dfd1df0d..6be46430b 100644 --- a/freqtrade/templates/sample_short_strategy.py +++ b/freqtrade/templates/sample_short_strategy.py @@ -36,7 +36,7 @@ class SampleShortStrategy(IStrategy): """ # Strategy interface version - allow new iterations of the strategy interface. # Check the documentation or the Sample strategy to get the latest version. - INTERFACE_VERSION = 2 + INTERFACE_VERSION = 3 # Can this strategy go short? can_short: bool = True diff --git a/freqtrade/templates/sample_strategy.py b/freqtrade/templates/sample_strategy.py index fe1bd22fb..08a690ab0 100644 --- a/freqtrade/templates/sample_strategy.py +++ b/freqtrade/templates/sample_strategy.py @@ -35,7 +35,7 @@ class SampleStrategy(IStrategy): """ # Strategy interface version - allow new iterations of the strategy interface. # Check the documentation or the Sample strategy to get the latest version. - INTERFACE_VERSION = 2 + INTERFACE_VERSION = 3 # Can this strategy go short? can_short: bool = False From 23b98fbb730006545b2e9c17b12973161e15f456 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Mar 2022 14:53:00 +0100 Subject: [PATCH 03/15] Update some documentation for short trading --- docs/configuration.md | 2 +- docs/hyperopt.md | 14 ++--- docs/strategy-advanced.md | 4 +- docs/strategy-callbacks.md | 4 +- docs/strategy-customization.md | 95 +++++++++++++++++++++++++++------- 5 files changed, 89 insertions(+), 30 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 9610b5866..147f0b672 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -86,7 +86,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `amend_last_stake_amount` | Use reduced last stake amount if necessary. [More information below](#configuring-amount-per-trade).
*Defaults to `false`.*
**Datatype:** Boolean | `last_stake_amount_min_ratio` | Defines minimum stake amount that has to be left and executed. Applies only to the last stake amount when it's amended to a reduced value (i.e. if `amend_last_stake_amount` is set to `true`). [More information below](#configuring-amount-per-trade).
*Defaults to `0.5`.*
**Datatype:** Float (as ratio) | `amount_reserve_percent` | Reserve some amount in min pair stake amount. The bot will reserve `amount_reserve_percent` + stoploss value when calculating min pair stake amount in order to avoid possible trade refusals.
*Defaults to `0.05` (5%).*
**Datatype:** Positive Float as ratio. -| `timeframe` | The timeframe (former ticker interval) to use (e.g `1m`, `5m`, `15m`, `30m`, `1h` ...). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** String +| `timeframe` | The timeframe to use (e.g `1m`, `5m`, `15m`, `30m`, `1h` ...). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** String | `fiat_display_currency` | Fiat currency used to show your profits. [More information below](#what-values-can-be-used-for-fiat_display_currency).
**Datatype:** String | `dry_run` | **Required.** Define if the bot must be in Dry Run or production mode.
*Defaults to `true`.*
**Datatype:** Boolean | `dry_run_wallet` | Define the starting amount in stake currency for the simulated wallet used by the bot running in Dry Run mode.
*Defaults to `1000`.*
**Datatype:** Float diff --git a/docs/hyperopt.md b/docs/hyperopt.md index f2ec4f875..bab81fb31 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -153,8 +153,8 @@ Checklist on all tasks / possibilities in hyperopt Depending on the space you want to optimize, only some of the below are required: -* define parameters with `space='buy'` - for buy signal optimization -* define parameters with `space='sell'` - for sell signal optimization +* define parameters with `space='buy'` - for entry signal optimization +* define parameters with `space='sell'` - for exit signal optimization !!! Note `populate_indicators` needs to create all indicators any of the spaces may use, otherwise hyperopt will not work. @@ -204,7 +204,7 @@ There you have two different types of indicators: 1. `guards` and 2. `triggers`. Hyper-optimization will, for each epoch round, pick one trigger and possibly multiple guards. -#### Sell optimization +#### Exit signal optimization Similar to the entry-signal above, exit-signals can also be optimized. Place the corresponding settings into the following methods @@ -216,7 +216,7 @@ The configuration and rules are the same than for buy signals. ## Solving a Mystery -Let's say you are curious: should you use MACD crossings or lower Bollinger Bands to trigger your buys. +Let's say you are curious: should you use MACD crossings or lower Bollinger Bands to trigger your buys. And you also wonder should you use RSI or ADX to help with those buy decisions. If you decide to use RSI or ADX, which values should I use for them? @@ -296,7 +296,7 @@ So let's write the buy strategy using these values: if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), - 'buy'] = 1 + 'enter_long'] = 1 return dataframe ``` @@ -376,7 +376,7 @@ class MyAwesomeStrategy(IStrategy): if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), - 'buy'] = 1 + 'enter_long'] = 1 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -391,7 +391,7 @@ class MyAwesomeStrategy(IStrategy): if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), - 'sell'] = 1 + 'exit_long'] = 1 return dataframe ``` diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index ec91c91a7..b5d0ef8b9 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -89,7 +89,7 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: (dataframe['rsi'] < 35) & (dataframe['volume'] > 0) ), - ['buy', 'enter_tag']] = (1, 'buy_signal_rsi') + ['enter_long', 'enter_tag']] = (1, 'buy_signal_rsi') return dataframe @@ -117,7 +117,7 @@ def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame (dataframe['rsi'] > 70) & (dataframe['volume'] > 0) ), - ['sell', 'exit_tag']] = (1, 'exit_rsi') + ['exit_long', 'exit_tag']] = (1, 'exit_rsi') return dataframe ``` diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index fb7bea5f3..38c1e7fa5 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -664,7 +664,7 @@ class DigDeeperStrategy(IStrategy): if last_candle['close'] < previous_candle['close']: return None - filled_buys = trade.select_filled_orders('buy') + filled_entries = trade.select_filled_orders(trade.enter_side) count_of_entries = trade.nr_of_successful_entries # Allow up to 3 additional increasingly larger buys (4 in total) # Initial buy is 1x @@ -676,7 +676,7 @@ class DigDeeperStrategy(IStrategy): # Hope you have a deep wallet! try: # This returns first order stake size - stake_amount = filled_buys[0].cost + stake_amount = filled_entries[0].cost # This then calculates current safety order size stake_amount = stake_amount * (1 + (count_of_entries * 0.25)) return stake_amount diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 3a8425b4e..fb598282e 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -26,8 +26,8 @@ This will create a new strategy file from a template, which will be located unde A strategy file contains all the information needed to build a good strategy: - Indicators -- Buy strategy rules -- Sell strategy rules +- Entry strategy rules +- Exit strategy rules - Minimal ROI recommended - Stoploss strongly recommended @@ -82,7 +82,7 @@ As a dataframe is a table, simple python comparisons like the following will not ``` python if dataframe['rsi'] > 30: - dataframe['buy'] = 1 + dataframe['enter_long'] = 1 ``` The above section will fail with `The truth value of a Series is ambiguous. [...]`. @@ -92,7 +92,7 @@ This must instead be written in a pandas-compatible way, so the operation is per ``` python dataframe.loc[ (dataframe['rsi'] > 30) - , 'buy'] = 1 + , 'enter_long'] = 1 ``` With this section, you have a new column in your dataframe, which has `1` assigned whenever RSI is above 30. @@ -199,13 +199,13 @@ If this data is available, indicators will be calculated with this extended time !!! Note If data for the startup period is not available, then the timerange will be adjusted to account for this startup period - so Backtesting would start at 2019-01-01 08:30:00. -### Buy signal rules +### Entry signal rules -Edit the method `populate_buy_trend()` in your strategy file to update your buy strategy. +Edit the method `populate_buy_trend()` in your strategy file to update your entry strategy. It's important to always return the dataframe without removing/modifying the columns `"open", "high", "low", "close", "volume"`, otherwise these fields would contain something unexpected. -This method will also define a new column, `"buy"`, which needs to contain 1 for buys, and 0 for "no action". +This method will also define a new column, `"enter_long"`, which needs to contain 1 for entries, and 0 for "no action". Sample from `user_data/strategies/sample_strategy.py`: @@ -224,22 +224,50 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: (dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard (dataframe['volume'] > 0) # Make sure Volume is not 0 ), - 'buy'] = 1 + ['enter_long', 'enter_tag']] = (1, 'rsi_cross') return dataframe ``` +??? Note "Enter short trades" + Short-entries can be created by setting `enter_short` (corresponds to `enter_long` for long trades). + The `enter_tag` column remains identical. + Short-trades need to be supported by your exchange and market configuration! + + ```python + 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 + ), + ['enter_long', 'enter_tag']] = (1, 'rsi_cross') + + dataframe.loc[ + ( + (qtpylib.crossed_below(dataframe['rsi'], 70)) & # Signal: RSI crosses below 70 + (dataframe['tema'] > dataframe['bb_middleband']) & # Guard + (dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + ['enter_short', 'enter_tag']] = (1, 'rsi_cross') + + return dataframe + ``` + !!! Note Buying requires sellers to buy from - therefore volume needs to be > 0 (`dataframe['volume'] > 0`) to make sure that the bot does not buy/sell in no-activity periods. -### Sell signal rules +### Exit signal rules Edit the method `populate_sell_trend()` into your strategy file to update your sell strategy. Please note that the sell-signal is only used if `use_sell_signal` is set to true in the configuration. It's important to always return the dataframe without removing/modifying the columns `"open", "high", "low", "close", "volume"`, otherwise these fields would contain something unexpected. -This method will also define a new column, `"sell"`, which needs to contain 1 for sells, and 0 for "no action". +This method will also define a new column, `"exit_long"`, which needs to contain 1 for sells, and 0 for "no action". Sample from `user_data/strategies/sample_strategy.py`: @@ -258,10 +286,36 @@ def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame (dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard (dataframe['volume'] > 0) # Make sure Volume is not 0 ), - 'sell'] = 1 + ['exit_long', 'exit_tag']] = (1, 'rsi_too_high') return dataframe ``` +??? Note "Exit short trades" + Short-exits can be created by setting `exit_short` (corresponds to `exit_long`). + The `exit_tag` column remains identical. + Short-trades need to be supported by your exchange and market configuration! + + ```python + 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 + ), + ['exit_long', 'exit_tag']] = (1, 'rsi_too_high') + dataframe.loc[ + ( + (qtpylib.crossed_below(dataframe['rsi'], 30)) & # Signal: RSI crosses below 30 + (dataframe['tema'] < dataframe['bb_middleband']) & # Guard + (dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + ['exit_short', 'exit_tag']] = (1, 'rsi_too_low') + return dataframe + ``` + ### Minimal ROI This dict defines the minimal Return On Investment (ROI) a trade should reach before selling, independent from the sell signal. @@ -325,7 +379,7 @@ stoploss = -0.10 For the full documentation on stoploss features, look at the dedicated [stoploss page](stoploss.md). -### Timeframe (formerly ticker interval) +### Timeframe This is the set of candles the bot should download and use for the analysis. Common values are `"1m"`, `"5m"`, `"15m"`, `"1h"`, however all values supported by your exchange should work. @@ -454,7 +508,7 @@ for more information. # Define BTC/STAKE informative pair. Available in populate_indicators and other methods as # 'btc_rsi_1h'. Current stake currency should be specified as {stake} format variable - # instead of hardcoding actual stake currency. Available in populate_indicators and other + # instead of hard-coding actual stake currency. Available in populate_indicators and other # methods as 'btc_usdt_rsi_1h' (when stake currency is USDT). @informative('1h', 'BTC/{stake}') def populate_indicators_btc_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -501,7 +555,7 @@ for more information. & (dataframe['volume'] > 0) ), - ['buy', 'enter_tag']] = (1, 'buy_signal_rsi') + ['enter_long', 'enter_tag']] = (1, 'buy_signal_rsi') return dataframe ``` @@ -716,7 +770,7 @@ class SampleStrategy(IStrategy): (dataframe['rsi_1d'] < 30) & # Ensure daily RSI is < 30 (dataframe['volume'] > 0) # Ensure this candle had volume (important for backtesting) ), - 'buy'] = 1 + ['enter_long', 'enter_tag']] = (1, 'rsi_cross') ``` @@ -922,7 +976,7 @@ if self.config['runmode'].value in ('live', 'dry_run'): Sample return value: ETH/BTC had 5 trades, with a total profit of 1.5% (ratio of 0.015). ``` json -{'pair': "ETH/BTC", 'profit': 0.015, 'count': 5} +{"pair": "ETH/BTC", "profit": 0.015, "count": 5} ``` !!! Warning @@ -985,7 +1039,7 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: ( #>> whatever condition<<< ), - 'buy'] = 1 + ['enter_long', 'enter_tag']] = (1, 'somestring') # Print the Analyzed pair print(f"result for {metadata['pair']}") @@ -1014,7 +1068,12 @@ The following lists some common patterns which should be avoided to prevent frus ### Colliding signals -When buy and sell signals collide (both `'buy'` and `'sell'` are 1), freqtrade will do nothing and ignore the entry (buy) signal. This will avoid trades that buy, and sell immediately. Obviously, this can potentially lead to missed entries. +When conflicting signals collide (e.g. both `'enter_long'` and `'exit_long'` are 1), freqtrade will do nothing and ignore the entry signal. This will avoid trades that buy, and sell immediately. Obviously, this can potentially lead to missed entries. + +The following rules apply, and entry signals will be ignored if more than one of the 3 signals is set: + +- `enter_long` -> `exit_long`, `exit_short` +- `enter_short` -> `exit_short`, `enter_long` ## Further strategy ideas From 36287a84cba29b738c0cbe6f8ea94f65d3a85919 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Mar 2022 15:05:03 +0100 Subject: [PATCH 04/15] enhance migration documentation --- docs/strategy-callbacks.md | 1 + docs/strategy-customization.md | 2 +- docs/strategy_analysis_example.md | 4 +- docs/strategy_migration.md | 145 +++++++++++++++++- .../templates/strategy_analysis_example.ipynb | 4 +- 5 files changed, 149 insertions(+), 7 deletions(-) diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 38c1e7fa5..af4033c8d 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -713,3 +713,4 @@ class AwesomeStrategy(IStrategy): :return: A leverage amount, which is between 1.0 and max_leverage. """ return 1.0 +``` diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index fb598282e..c6d347dc8 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -908,7 +908,7 @@ In some situations it may be confusing to deal with stops relative to current ra current_rate: float, current_profit: float, **kwargs) -> float: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) candle = dataframe.iloc[-1].squeeze() - return stoploss_from_absolute(current_rate - (candle['atr'] * 2, is_short=trade.is_short), current_rate) + return stoploss_from_absolute(current_rate - (candle['atr'] * 2), current_rate, is_short=trade.is_short) ``` diff --git a/docs/strategy_analysis_example.md b/docs/strategy_analysis_example.md index 90d8d8800..f2ee9c4e4 100644 --- a/docs/strategy_analysis_example.md +++ b/docs/strategy_analysis_example.md @@ -73,7 +73,7 @@ df.tail() ```python # Report results -print(f"Generated {df['buy'].sum()} buy signals") +print(f"Generated {df['enter_long'].sum()} entry signals") data = df.set_index('date', drop=False) data.tail() ``` @@ -244,7 +244,7 @@ import plotly.figure_factory as ff hist_data = [trades.profit_ratio] group_labels = ['profit_ratio'] # name of the dataset -fig = ff.create_distplot(hist_data, group_labels,bin_size=0.01) +fig = ff.create_distplot(hist_data, group_labels, bin_size=0.01) fig.show() ``` diff --git a/docs/strategy_migration.md b/docs/strategy_migration.md index 9f0300fe3..71225b84f 100644 --- a/docs/strategy_migration.md +++ b/docs/strategy_migration.md @@ -5,7 +5,7 @@ We have put a great effort into keeping compatibility with existing strategies, 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. -## Quick summary / checklist +## Quick summary / migration checklist * Dataframe columns: * `buy` -> `enter_long` @@ -17,11 +17,152 @@ If you intend on using markets other than spot markets, please migrate your stra * `custom_stake_amount` * `confirm_trade_entry` * Renamed `trade.nr_of_successful_buys` to `trade.nr_of_successful_entries`. -* Introduced new `leverage` callback +* Introduced new [`leverage` callback](strategy-callbacks.md#leverage-callback) +* Informative pairs can now pass a 3rd element in the Tuple, defining the candle type. * `@informative` decorator now takes an optional `candle_type` argument * helper methods `stoploss_from_open` and `stoploss_from_absolute` now take `is_short` as additional argument. * `INTERFACE_VERSION` should be set to 3. ## Extensive explanation +### `populate_buy_trend` + +In `populate_buy_trend()` - you will want to change the columns you assign from `'buy`' to `'enter_long` + +```python hl_lines="9" +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: + +```python hl_lines="9" +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 + ), + ['enter_long', 'enter_tag']] = (1, 'rsi_cross') + + return dataframe +``` + +### `populate_sell_trend` + +``` python hl_lines="9" +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 + +``` python hl_lines="9" +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 + ), + ['exit_long', 'exit_tag']] = (1, 'some_exit_tag') + return dataframe +``` + +### Custom-stake-amount + +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, + proposed_stake: float, min_stake: float, max_stake: float, + 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, + proposed_stake: float, min_stake: float, max_stake: float, + 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"`. + +``` python hl_lines="5" +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 +``` +After: + +``` python hl_lines="5" +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 +``` + +### 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 + +``` + +``` 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) + + +``` diff --git a/freqtrade/templates/strategy_analysis_example.ipynb b/freqtrade/templates/strategy_analysis_example.ipynb index 3b937d1c5..dc20d71b8 100644 --- a/freqtrade/templates/strategy_analysis_example.ipynb +++ b/freqtrade/templates/strategy_analysis_example.ipynb @@ -110,7 +110,7 @@ "outputs": [], "source": [ "# Report results\n", - "print(f\"Generated {df['buy'].sum()} buy signals\")\n", + "print(f\"Generated {df['enter_long'].sum()} entry signals\")\n", "data = df.set_index('date', drop=False)\n", "data.tail()" ] @@ -348,7 +348,7 @@ "hist_data = [trades.profit_ratio]\n", "group_labels = ['profit_ratio'] # name of the dataset\n", "\n", - "fig = ff.create_distplot(hist_data, group_labels,bin_size=0.01)\n", + "fig = ff.create_distplot(hist_data, group_labels, bin_size=0.01)\n", "fig.show()\n" ] }, From c89a68c1ab84c70367d87efdc57157aea62bcd4a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Mar 2022 15:11:02 +0100 Subject: [PATCH 05/15] Alternative candle types --- docs/strategy-customization.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index c6d347dc8..44d3ed62b 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -436,6 +436,19 @@ A full sample can be found [in the DataProvider section](#complete-data-provider It is however better to use resampling to longer timeframes whenever possible to avoid hammering the exchange with too many requests and risk being blocked. +??? Note "Alternative candle types" + Informative_pairs can also provide a 3rd tuple element defining the candle type explicitly. + Availability of alternative candle-types will depend on the trading-mode and the exchange. Details about this can be found in the exchange documentation. + + ``` python + def informative_pairs(self): + return [ + ("ETH/USDT", "5m", ""), # Uses default candletype, depends on trading_mode + ("ETH/USDT", "5m", "spot"), # Forces usage of spot candles + ("BTC/TUSD", "15m", "futures"), # Uses futures candles + ("BTC/TUSD", "15m", "mark"), # Uses mark candles + ] + ``` *** ### Informative pairs decorator (`@informative()`) From 7c7b0d1fcce15a4b622c01b9bb7bacb86cb2c1c9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 7 Mar 2022 20:10:54 +0100 Subject: [PATCH 06/15] Update documentation for time_in_force migration --- docs/strategy_migration.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/strategy_migration.md b/docs/strategy_migration.md index 71225b84f..422dceff5 100644 --- a/docs/strategy_migration.md +++ b/docs/strategy_migration.md @@ -22,6 +22,8 @@ If you intend on using markets other than spot markets, please migrate your stra * `@informative` decorator now takes an optional `candle_type` argument * helper methods `stoploss_from_open` and `stoploss_from_absolute` now take `is_short` as additional argument. * `INTERFACE_VERSION` should be set to 3. +* Strategy/Configuration settings + * `time_in_force` buy -> entry, sell -> exit ## Extensive explanation @@ -166,3 +168,22 @@ This should be given the value of `trade.is_short`. ``` +### 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", + } +``` + +``` python hl_lines="2 3" + order_time_in_force: Dict = { + "entry": "gtc", + "exit": "gtc", + } +``` From 6a80c0f0307dc4d954137ff615864fd81686bfca Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 9 Mar 2022 06:46:31 +0100 Subject: [PATCH 07/15] Add order_types migration docs --- docs/strategy_migration.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/strategy_migration.md b/docs/strategy_migration.md index 422dceff5..4a5adc55a 100644 --- a/docs/strategy_migration.md +++ b/docs/strategy_migration.md @@ -23,7 +23,8 @@ If you intend on using markets other than spot markets, please migrate your stra * helper methods `stoploss_from_open` and `stoploss_from_absolute` now take `is_short` as additional argument. * `INTERFACE_VERSION` should be set to 3. * Strategy/Configuration settings - * `time_in_force` buy -> entry, sell -> exit + * `order_time_in_force` buy -> entry, sell -> exit + * `order_types` buy -> entry, sell -> exit ## Extensive explanation @@ -187,3 +188,31 @@ This should be given the value of `trade.is_short`. "exit": "gtc", } ``` + +#### `order_types` + +`order_types` have changed all wordings from `buy` to `entry` - and `sell` to `exit`. + +``` 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 +``` + +``` python hl_lines="2-6" + order_types = { + "entry": "limit", + "exit": "limit", + "emergencyexit": "market", + "forceexit": "market", + "forceentry": "market", + "stoploss": "market", + "stoploss_on_exchange": false, + "stoploss_on_exchange_interval": 60 +``` From e51a1e1b200f31026191504a7567f72be7e5f88c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 07:16:31 +0100 Subject: [PATCH 08/15] Improve documentation, add "can_short" --- docs/leverage.md | 16 +++++++++------- docs/strategy-customization.md | 7 +++++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/leverage.md b/docs/leverage.md index 55f644462..f1954d880 100644 --- a/docs/leverage.md +++ b/docs/leverage.md @@ -32,11 +32,12 @@ With leverage, a trader borrows capital from the exchange. The capital must be r Because the capital must always be repayed, exchanges will **liquidate** a trade (forcefully sell the traders assets) made using borrowed capital when the total value of assets in a leverage account drops to a certain point(a point where the total value of losses is less than the value of the collateral that the trader actually owns in the leverage account), in order to ensure that the trader has enough capital to pay back the borrowed assets to the exchange. The exchange will also charge a **liquidation fee**, adding to the traders losses. For this reason, **DO NOT TRADE WITH LEVERAGE IF YOU DON'T KNOW EXACTLY WHAT YOUR DOING. LEVERAGE TRADING IS HIGH RISK, AND CAN RESULT IN THE VALUE OF YOUR ASSETS DROPPING TO 0 VERY QUICKLY, WITH NO CHANCE OF INCREASING IN VALUE AGAIN** -#### MARGIN +#### Margin + *Currently unavailable* Trading occurs on the spot market, but the exchange lends currency to you in an amount equal to the chosen leverage. You pay the amount lent to you back to the exchange with interest, and your profits/losses are multiplied by the leverage specified - -#### FUTURES + +#### Futures Perpetual swaps (also known as Perpetual Futures) are contracts traded at a price that is closely tied to the underlying asset they are based off of(ex. ). You are not trading the actual asset but instead are trading a derivative contract. Perpetual swap contracts can last indefinately, in contrast to futures or option contracts. @@ -52,7 +53,7 @@ In addition to the gains/losses from the change in price of the futures contract The possible values are: `isolated`, or `cross`(*currently unavailable*) -#### ISOLATED +#### Isolated margin mode Each market(trading pair), keeps collateral in a separate account @@ -60,7 +61,7 @@ Each market(trading pair), keeps collateral in a separate account "margin_mode": "isolated" ``` -#### CROSS +#### Cross margin mode *currently unavailable* One account is used to share collateral between markets (trading pairs). Margin is taken from total account balance to avoid liquidation when needed. @@ -70,6 +71,7 @@ One account is used to share collateral between markets (trading pairs). Margin ``` ## Understand `liquidation_buffer` + *Defaults to `0.05`* A ratio specifying how large of a safety net to place between the liquidation price and the stoploss to prevent a position from reaching the liquidation price. @@ -84,7 +86,7 @@ Possible values are any floats between 0.0 and 0.99 **ex:** If a trade is entered at a price of 10 coin/USDT, and the liquidation price of this trade is 8 coin/USDT, then with `liquidation_buffer` set to `0.05` the minimum stoploss for this trade would be 8 + ((10 - 8) * 0.05) = 8 + 0.1 = 8.1 !!! Danger "A `liquidation_buffer` of 0.0, or a low `liquidation_buffer` is likely to result in liquidations, and liquidation fees" -Currently Freqtrade is able to calculate liquidation prices, but does not calculate liquidation fees. Setting your `liquidation_buffer` to 0.0, or using a low `liquidation_buffer` could result in your positions being liquidated. Freqtrade does not track liquidation fees, so liquidations will result in inaccurate profit/loss results for your bot. If you use a low `liquidation_buffer`, it is recommended to use `stoploss_on_exchange` if your exchange supports this. + Currently Freqtrade is able to calculate liquidation prices, but does not calculate liquidation fees. Setting your `liquidation_buffer` to 0.0, or using a low `liquidation_buffer` could result in your positions being liquidated. Freqtrade does not track liquidation fees, so liquidations will result in inaccurate profit/loss results for your bot. If you use a low `liquidation_buffer`, it is recommended to use `stoploss_on_exchange` if your exchange supports this. ### Developer @@ -96,7 +98,7 @@ For longs, the currency which pays the interest fee for the `borrowed` will alre All Fees are included in `current_profit` calculations during the trade. -#### FUTURES MODE +#### Futures mode Funding fees are either added or subtracted from the total amount of a trade diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 44d3ed62b..4261eeda9 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -233,6 +233,7 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: Short-entries can be created by setting `enter_short` (corresponds to `enter_long` for long trades). The `enter_tag` column remains identical. Short-trades need to be supported by your exchange and market configuration! + Please make sure to set [`can_short`]() appropriately on your strategy if you intend to short. ```python def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -388,6 +389,12 @@ Please note that the same buy/sell signals may work well with one timeframe, but This setting is accessible within the strategy methods as the `self.timeframe` attribute. +### Can short + +To use short signals in futures markets, you will have to let us know to do so by setting `can_short=True`. +Strategies which enable this will fail to load on spot markets. +Disabling of this will have short signals ignored (also in futures markets). + ### Metadata dict The metadata-dict (available for `populate_buy_trend`, `populate_sell_trend`, `populate_indicators`) contains additional information. From 25aba9c422656b648e775831ddbad88927a72210 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 07:33:43 +0100 Subject: [PATCH 09/15] reformat leverage docs --- docs/leverage.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/leverage.md b/docs/leverage.md index f1954d880..78d93dbed 100644 --- a/docs/leverage.md +++ b/docs/leverage.md @@ -28,25 +28,28 @@ Regular trading mode (low risk) ### Leverage trading modes -With leverage, a trader borrows capital from the exchange. The capital must be repayed fully to the exchange(potentially with interest), and the trader keeps any profits, or pays any losses, from any trades made using the borrowed capital. +With leverage, a trader borrows capital from the exchange. The capital must be re-payed fully to the exchange (potentially with interest), and the trader keeps any profits, or pays any losses, from any trades made using the borrowed capital. -Because the capital must always be repayed, exchanges will **liquidate** a trade (forcefully sell the traders assets) made using borrowed capital when the total value of assets in a leverage account drops to a certain point(a point where the total value of losses is less than the value of the collateral that the trader actually owns in the leverage account), in order to ensure that the trader has enough capital to pay back the borrowed assets to the exchange. The exchange will also charge a **liquidation fee**, adding to the traders losses. For this reason, **DO NOT TRADE WITH LEVERAGE IF YOU DON'T KNOW EXACTLY WHAT YOUR DOING. LEVERAGE TRADING IS HIGH RISK, AND CAN RESULT IN THE VALUE OF YOUR ASSETS DROPPING TO 0 VERY QUICKLY, WITH NO CHANCE OF INCREASING IN VALUE AGAIN** +Because the capital must always be re-payed, exchanges will **liquidate** (forcefully sell the traders assets) a trade made using borrowed capital when the total value of assets in the leverage account drops to a certain point (a point where the total value of losses is less than the value of the collateral that the trader actually owns in the leverage account), in order to ensure that the trader has enough capital to pay the borrowed assets back to the exchange. The exchange will also charge a **liquidation fee**, adding to the traders losses. -#### Margin +For this reason, **DO NOT TRADE WITH LEVERAGE IF YOU DON'T KNOW EXACTLY WHAT YOUR DOING. LEVERAGE TRADING IS HIGH RISK, AND CAN RESULT IN THE VALUE OF YOUR ASSETS DROPPING TO 0 VERY QUICKLY, WITH NO CHANCE OF INCREASING IN VALUE AGAIN.** -*Currently unavailable* - Trading occurs on the spot market, but the exchange lends currency to you in an amount equal to the chosen leverage. You pay the amount lent to you back to the exchange with interest, and your profits/losses are multiplied by the leverage specified +#### Margin (currently unavailable) + +Trading occurs on the spot market, but the exchange lends currency to you in an amount equal to the chosen leverage. You pay the amount lent to you back to the exchange with interest, and your profits/losses are multiplied by the leverage specified. #### Futures -Perpetual swaps (also known as Perpetual Futures) are contracts traded at a price that is closely tied to the underlying asset they are based off of(ex. ). You are not trading the actual asset but instead are trading a derivative contract. Perpetual swap contracts can last indefinately, in contrast to futures or option contracts. +Perpetual swaps (also known as Perpetual Futures) are contracts traded at a price that is closely tied to the underlying asset they are based off of (ex.). You are not trading the actual asset but instead are trading a derivative contract. Perpetual swap contracts can last indefinitely, in contrast to futures or option contracts. -In addition to the gains/losses from the change in price of the contract, traders also exchange funding fees, which are gains/losses worth an amount that is derived from the difference in price between the contract and the underlying asset. The difference in price between a contract and the underlying asset varies between exchanges. +In addition to the gains/losses from the change in price of the futures contract, traders also exchange _funding fees_, which are gains/losses worth an amount that is derived from the difference in price between the futures contract and the underlying asset. The difference in price between a futures contract and the underlying asset varies between exchanges. -In addition to the gains/losses from the change in price of the futures contract, traders also exchange funding fees, which are gains/losses worth an amount that is derived from the difference in price between the futures contract and the underlying asset. The difference in price between a futures contract and the underlying asset varies between exchanges. +To trade in futures markets, you'll have to set `trading_mode` to "futures". +You will also have to pick a "margin mode" (explanation below) - with freqtrade currently only supporting isolated margin. ``` json -"trading_mode": "futures" +"trading_mode": "futures", +"margin_mode": "isolated" ``` ### Margin mode @@ -61,9 +64,8 @@ Each market(trading pair), keeps collateral in a separate account "margin_mode": "isolated" ``` -#### Cross margin mode +#### Cross margin mode (currently unavailable) -*currently unavailable* One account is used to share collateral between markets (trading pairs). Margin is taken from total account balance to avoid liquidation when needed. ``` json @@ -75,15 +77,16 @@ One account is used to share collateral between markets (trading pairs). Margin *Defaults to `0.05`* A ratio specifying how large of a safety net to place between the liquidation price and the stoploss to prevent a position from reaching the liquidation price. -This artificial liquidation price is calculated as +This artificial liquidation price is calculated as: `freqtrade_liquidation_price = liquidation_price ± (abs(open_rate - liquidation_price) * liquidation_buffer)` + - `±` = `+` for long trades - `±` = `-` for short trades Possible values are any floats between 0.0 and 0.99 -**ex:** If a trade is entered at a price of 10 coin/USDT, and the liquidation price of this trade is 8 coin/USDT, then with `liquidation_buffer` set to `0.05` the minimum stoploss for this trade would be 8 + ((10 - 8) * 0.05) = 8 + 0.1 = 8.1 +**ex:** If a trade is entered at a price of 10 coin/USDT, and the liquidation price of this trade is 8 coin/USDT, then with `liquidation_buffer` set to `0.05` the minimum stoploss for this trade would be $8 + ((10 - 8) * 0.05) = 8 + 0.1 = 8.1$ !!! Danger "A `liquidation_buffer` of 0.0, or a low `liquidation_buffer` is likely to result in liquidations, and liquidation fees" Currently Freqtrade is able to calculate liquidation prices, but does not calculate liquidation fees. Setting your `liquidation_buffer` to 0.0, or using a low `liquidation_buffer` could result in your positions being liquidated. Freqtrade does not track liquidation fees, so liquidations will result in inaccurate profit/loss results for your bot. If you use a low `liquidation_buffer`, it is recommended to use `stoploss_on_exchange` if your exchange supports this. From cd3ae7ebdfb8d7ef863eb6c302013c891b1242e6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 09:39:40 +0100 Subject: [PATCH 10/15] Update migration docs to include buy/entry trend migration --- docs/strategy_migration.md | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/docs/strategy_migration.md b/docs/strategy_migration.md index 4a5adc55a..6bd283808 100644 --- a/docs/strategy_migration.md +++ b/docs/strategy_migration.md @@ -7,6 +7,9 @@ If you intend on using markets other than spot markets, please migrate your stra ## Quick summary / migration checklist +* Strategy methods: + * `populate_buy_trend()` -> `populate_entry_trend()` + * `populate_sell_trend()` -> `populate_exit_trend()` * Dataframe columns: * `buy` -> `enter_long` * `sell` -> `exit_long` @@ -16,23 +19,23 @@ If you intend on using markets other than spot markets, please migrate your stra * New `side` argument to callbacks without trade object * `custom_stake_amount` * `confirm_trade_entry` -* Renamed `trade.nr_of_successful_buys` to `trade.nr_of_successful_entries`. -* Introduced new [`leverage` callback](strategy-callbacks.md#leverage-callback) +* Renamed `trade.nr_of_successful_buys` to `trade.nr_of_successful_entries` (mostly relevant for `adjust_trade_position()`). +* Introduced new [`leverage` callback](strategy-callbacks.md#leverage-callback). * Informative pairs can now pass a 3rd element in the Tuple, defining the candle type. -* `@informative` decorator now takes an optional `candle_type` argument +* `@informative` decorator now takes an optional `candle_type` argument. * helper methods `stoploss_from_open` and `stoploss_from_absolute` now take `is_short` as additional argument. * `INTERFACE_VERSION` should be set to 3. -* Strategy/Configuration settings - * `order_time_in_force` buy -> entry, sell -> exit - * `order_types` buy -> entry, sell -> exit +* Strategy/Configuration settings. + * `order_time_in_force` buy -> entry, sell -> exit. + * `order_types` buy -> entry, sell -> exit. ## Extensive explanation ### `populate_buy_trend` -In `populate_buy_trend()` - you will want to change the columns you assign from `'buy`' to `'enter_long` +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`. -```python hl_lines="9" +```python hl_lines="1 9" def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( @@ -48,8 +51,8 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: After: -```python hl_lines="9" -def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: +```python hl_lines="1 9" +def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( (qtpylib.crossed_above(dataframe['rsi'], 30)) & # Signal: RSI crosses above 30 @@ -62,9 +65,14 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: return dataframe ``` +Please refer to the [Strategy documentation](strategy-customization.md#entry-signal-rules) on how to enter and exit short trades. + ### `populate_sell_trend` -``` python hl_lines="9" +Similar to `populate_buy_trend`, `populate_sell_trend()` will be renamed to `populate_exit_trend()`. +We'll also change the column from `"sell"` to `"exit_long"`. + +``` python hl_lines="1 9" def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( @@ -79,8 +87,8 @@ def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame After -``` python hl_lines="9" -def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: +``` python hl_lines="1 9" +def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( (qtpylib.crossed_above(dataframe['rsi'], 70)) & # Signal: RSI crosses above 70 @@ -92,6 +100,8 @@ def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame return dataframe ``` +Please refer to the [Strategy documentation](strategy-customization.md#exit-signal-rules) on how to enter and exit short trades. + ### Custom-stake-amount New string argument `side` - which can be either `"long"` or `"short"`. From 9d6d8043ee27a1099702abcc2606fa53648db007 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 10:07:48 +0100 Subject: [PATCH 11/15] update FAQ to reflect new reality --- docs/faq.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 147e850ac..73a2646ae 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -6,13 +6,14 @@ Freqtrade supports spot trading only. ### Can I open short positions? -No, Freqtrade does not support trading with margin / leverage, and cannot open short positions. +Freqtrade can open short positions in futures markets. +This requires the strategy to be made for this - and `"trading_mode": "futures"` in the configuration. -In some cases, your exchange may provide leveraged spot tokens which can be traded with Freqtrade eg. BTCUP/USD, BTCDOWN/USD, ETHBULL/USD, ETHBEAR/USD, etc... +In spot markets, you can in some cases use leveraged spot tokens, which reflect an inverted pair (eg. BTCUP/USD, BTCDOWN/USD, ETHBULL/USD, ETHBEAR/USD,...) which can be traded with Freqtrade. ### Can I trade options or futures? -No, options and futures trading are not supported. +Futures trading is supported for selected exchanges. ## Beginner Tips & Tricks From 59791b06599297adf755c551570a6ed5df12b193 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 10:49:00 +0100 Subject: [PATCH 12/15] Update populate_buy_trend to populate_entry_trend --- docs/bot-basics.md | 4 ++-- docs/hyperopt.md | 10 +++++----- docs/strategy-advanced.md | 2 +- docs/strategy-customization.md | 18 +++++++++--------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/bot-basics.md b/docs/bot-basics.md index 9f4ef8277..3f16cbc4c 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -29,7 +29,7 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and * Call `bot_loop_start()` strategy callback. * Analyze strategy per pair. * Call `populate_indicators()` - * Call `populate_buy_trend()` + * Call `populate_entry_trend()` * Call `populate_sell_trend()` * Check timeouts for open orders. * Calls `check_buy_timeout()` strategy callback for open buy orders. @@ -55,7 +55,7 @@ This loop will be repeated again and again until the bot is stopped. * Load historic data for configured pairlist. * Calls `bot_loop_start()` once. * Calculate indicators (calls `populate_indicators()` once per pair). -* Calculate buy / sell signals (calls `populate_buy_trend()` and `populate_sell_trend()` once per pair). +* Calculate buy / sell signals (calls `populate_entry_trend()` and `populate_sell_trend()` once per pair). * Loops per candle simulating entry and exit points. * Confirm trade buy / sell (calls `confirm_trade_entry()` and `confirm_trade_exit()` if implemented in the strategy). * Call `custom_entry_price()` (if implemented in the strategy) to determine entry price (Prices are moved to be within the opening candle). diff --git a/docs/hyperopt.md b/docs/hyperopt.md index bab81fb31..f67616a02 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -180,7 +180,7 @@ Hyperopt will first load your data into memory and will then run `populate_indic Hyperopt will then spawn into different processes (number of processors, or `-j `), and run backtesting over and over again, changing the parameters that are part of the `--spaces` defined. -For every new set of parameters, freqtrade will run first `populate_buy_trend()` followed by `populate_sell_trend()`, and then run the regular backtesting process to simulate trades. +For every new set of parameters, freqtrade will run first `populate_entry_trend()` followed by `populate_sell_trend()`, and then run the regular backtesting process to simulate trades. After backtesting, the results are passed into the [loss function](#loss-functions), which will evaluate if this result was better or worse than previous results. Based on the loss function result, hyperopt will determine the next set of parameters to try in the next round of backtesting. @@ -190,7 +190,7 @@ Based on the loss function result, hyperopt will determine the next set of param There are two places you need to change in your strategy file to add a new buy hyperopt for testing: * Define the parameters at the class level hyperopt shall be optimizing. -* Within `populate_buy_trend()` - use defined parameter values instead of raw constants. +* Within `populate_entry_trend()` - use defined parameter values instead of raw constants. There you have two different types of indicators: 1. `guards` and 2. `triggers`. @@ -274,7 +274,7 @@ The last one we call `trigger` and use it to decide which buy trigger we want to So let's write the buy strategy using these values: ```python - def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] # GUARDS AND TRENDS if self.buy_adx_enabled.value: @@ -301,7 +301,7 @@ So let's write the buy strategy using these values: return dataframe ``` -Hyperopt will now call `populate_buy_trend()` many times (`epochs`) with different value combinations. +Hyperopt will now call `populate_entry_trend()` many times (`epochs`) with different value combinations. It will use the given historical data and simulate buys based on the buy signals generated with the above function. Based on the results, hyperopt will tell you which parameter combination produced the best results (based on the configured [loss function](#loss-functions)). @@ -364,7 +364,7 @@ class MyAwesomeStrategy(IStrategy): return dataframe - def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] conditions.append(qtpylib.crossed_above( dataframe[f'ema_short_{self.buy_ema_short.value}'], dataframe[f'ema_long_{self.buy_ema_long.value}'] diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index b5d0ef8b9..35faa9a9c 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -83,7 +83,7 @@ When your strategy has multiple buy signals, you can name the signal that trigge Then you can access you buy signal on `custom_sell` ```python -def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: +def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( (dataframe['rsi'] < 35) & diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 4261eeda9..38da3e6b1 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -101,7 +101,7 @@ With this section, you have a new column in your dataframe, which has `1` assign Buy and sell strategies need indicators. You can add more indicators by extending the list contained in the method `populate_indicators()` from your strategy file. -You should only add the indicators used in either `populate_buy_trend()`, `populate_sell_trend()`, or to populate another indicator, otherwise performance may suffer. +You should only add the indicators used in either `populate_entry_trend()`, `populate_sell_trend()`, or to populate another indicator, otherwise performance may suffer. It's important to always return the dataframe without removing/modifying the columns `"open", "high", "low", "close", "volume"`, otherwise these fields would contain something unexpected. @@ -201,7 +201,7 @@ If this data is available, indicators will be calculated with this extended time ### Entry signal rules -Edit the method `populate_buy_trend()` in your strategy file to update your entry strategy. +Edit the method `populate_entry_trend()` in your strategy file to update your entry strategy. It's important to always return the dataframe without removing/modifying the columns `"open", "high", "low", "close", "volume"`, otherwise these fields would contain something unexpected. @@ -210,7 +210,7 @@ This method will also define a new column, `"enter_long"`, which needs to contai Sample from `user_data/strategies/sample_strategy.py`: ```python -def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: +def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Based on TA indicators, populates the buy signal for the given dataframe :param dataframe: DataFrame populated with indicators @@ -236,7 +236,7 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: Please make sure to set [`can_short`]() appropriately on your strategy if you intend to short. ```python - def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( (qtpylib.crossed_above(dataframe['rsi'], 30)) & # Signal: RSI crosses above 30 @@ -397,7 +397,7 @@ Disabling of this will have short signals ignored (also in futures markets). ### Metadata dict -The metadata-dict (available for `populate_buy_trend`, `populate_sell_trend`, `populate_indicators`) contains additional information. +The metadata-dict (available for `populate_entry_trend`, `populate_sell_trend`, `populate_indicators`) contains additional information. Currently this is `pair`, which can be accessed using `metadata['pair']` - and will return a pair in the format `XRP/BTC`. The Metadata-dict should not be modified and does not persist information across multiple calls. @@ -567,7 +567,7 @@ for more information. Use string formatting when accessing informative dataframes of other pairs. This will allow easily changing stake currency in config without having to adjust strategy code. ``` python - def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: stake = self.config['stake_currency'] dataframe.loc[ ( @@ -782,7 +782,7 @@ class SampleStrategy(IStrategy): return dataframe - def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( @@ -1050,11 +1050,11 @@ if self.config['runmode'].value in ('live', 'dry_run'): ## Print created dataframe -To inspect the created dataframe, you can issue a print-statement in either `populate_buy_trend()` or `populate_sell_trend()`. +To inspect the created dataframe, you can issue a print-statement in either `populate_entry_trend()` or `populate_sell_trend()`. You may also want to print the pair so it's clear what data is currently shown. ``` python -def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: +def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( #>> whatever condition<<< From d27a37be0d08948cadf49086669703823e18e9db Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 10:50:01 +0100 Subject: [PATCH 13/15] Update docs for populate_exit_trend --- docs/bot-basics.md | 4 ++-- docs/hyperopt.md | 6 +++--- docs/strategy-advanced.md | 2 +- docs/strategy-callbacks.md | 2 +- docs/strategy-customization.md | 12 ++++++------ 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/bot-basics.md b/docs/bot-basics.md index 3f16cbc4c..5294009cc 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -30,7 +30,7 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and * Analyze strategy per pair. * Call `populate_indicators()` * Call `populate_entry_trend()` - * Call `populate_sell_trend()` + * Call `populate_exit_trend()` * Check timeouts for open orders. * Calls `check_buy_timeout()` strategy callback for open buy orders. * Calls `check_sell_timeout()` strategy callback for open sell orders. @@ -55,7 +55,7 @@ This loop will be repeated again and again until the bot is stopped. * Load historic data for configured pairlist. * Calls `bot_loop_start()` once. * Calculate indicators (calls `populate_indicators()` once per pair). -* Calculate buy / sell signals (calls `populate_entry_trend()` and `populate_sell_trend()` once per pair). +* Calculate buy / sell signals (calls `populate_entry_trend()` and `populate_exit_trend()` once per pair). * Loops per candle simulating entry and exit points. * Confirm trade buy / sell (calls `confirm_trade_entry()` and `confirm_trade_exit()` if implemented in the strategy). * Call `custom_entry_price()` (if implemented in the strategy) to determine entry price (Prices are moved to be within the opening candle). diff --git a/docs/hyperopt.md b/docs/hyperopt.md index f67616a02..685050800 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -180,7 +180,7 @@ Hyperopt will first load your data into memory and will then run `populate_indic Hyperopt will then spawn into different processes (number of processors, or `-j `), and run backtesting over and over again, changing the parameters that are part of the `--spaces` defined. -For every new set of parameters, freqtrade will run first `populate_entry_trend()` followed by `populate_sell_trend()`, and then run the regular backtesting process to simulate trades. +For every new set of parameters, freqtrade will run first `populate_entry_trend()` followed by `populate_exit_trend()`, and then run the regular backtesting process to simulate trades. After backtesting, the results are passed into the [loss function](#loss-functions), which will evaluate if this result was better or worse than previous results. Based on the loss function result, hyperopt will determine the next set of parameters to try in the next round of backtesting. @@ -210,7 +210,7 @@ Similar to the entry-signal above, exit-signals can also be optimized. Place the corresponding settings into the following methods * Define the parameters at the class level hyperopt shall be optimizing, either naming them `sell_*`, or by explicitly defining `space='sell'`. -* Within `populate_sell_trend()` - use defined parameter values instead of raw constants. +* Within `populate_exit_trend()` - use defined parameter values instead of raw constants. The configuration and rules are the same than for buy signals. @@ -379,7 +379,7 @@ class MyAwesomeStrategy(IStrategy): 'enter_long'] = 1 return dataframe - def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] conditions.append(qtpylib.crossed_above( dataframe[f'ema_long_{self.buy_ema_long.value}'], dataframe[f'ema_short_{self.buy_ema_short.value}'] diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 35faa9a9c..623e6cdfe 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -111,7 +111,7 @@ def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_r Similar to [Buy Tagging](#buy-tag), you can also specify a sell tag. ``` python -def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: +def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( (dataframe['rsi'] > 70) & diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index af4033c8d..54920b69f 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -1,6 +1,6 @@ # Strategy Callbacks -While the main strategy functions (`populate_indicators()`, `populate_buy_trend()`, `populate_sell_trend()`) should be used in a vectorized way, and are only called [once during backtesting](bot-basics.md#backtesting-hyperopt-execution-logic), callbacks are called "whenever needed". +While the main strategy functions (`populate_indicators()`, `populate_entry_trend()`, `populate_exit_trend()`) should be used in a vectorized way, and are only called [once during backtesting](bot-basics.md#backtesting-hyperopt-execution-logic), callbacks are called "whenever needed". As such, you should avoid doing heavy calculations in callbacks to avoid delays during operations. Depending on the callback used, they may be called when entering / exiting a trade, or throughout the duration of a trade. diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 38da3e6b1..4b505d400 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -101,7 +101,7 @@ With this section, you have a new column in your dataframe, which has `1` assign Buy and sell strategies need indicators. You can add more indicators by extending the list contained in the method `populate_indicators()` from your strategy file. -You should only add the indicators used in either `populate_entry_trend()`, `populate_sell_trend()`, or to populate another indicator, otherwise performance may suffer. +You should only add the indicators used in either `populate_entry_trend()`, `populate_exit_trend()`, or to populate another indicator, otherwise performance may suffer. It's important to always return the dataframe without removing/modifying the columns `"open", "high", "low", "close", "volume"`, otherwise these fields would contain something unexpected. @@ -263,7 +263,7 @@ def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFram ### Exit signal rules -Edit the method `populate_sell_trend()` into your strategy file to update your sell strategy. +Edit the method `populate_exit_trend()` into your strategy file to update your sell strategy. Please note that the sell-signal is only used if `use_sell_signal` is set to true in the configuration. It's important to always return the dataframe without removing/modifying the columns `"open", "high", "low", "close", "volume"`, otherwise these fields would contain something unexpected. @@ -273,7 +273,7 @@ This method will also define a new column, `"exit_long"`, which needs to contain Sample from `user_data/strategies/sample_strategy.py`: ```python -def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: +def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Based on TA indicators, populates the sell signal for the given dataframe :param dataframe: DataFrame populated with indicators @@ -297,7 +297,7 @@ def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame Short-trades need to be supported by your exchange and market configuration! ```python - def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( (qtpylib.crossed_above(dataframe['rsi'], 70)) & # Signal: RSI crosses above 70 @@ -397,7 +397,7 @@ Disabling of this will have short signals ignored (also in futures markets). ### Metadata dict -The metadata-dict (available for `populate_entry_trend`, `populate_sell_trend`, `populate_indicators`) contains additional information. +The metadata-dict (available for `populate_entry_trend`, `populate_exit_trend`, `populate_indicators`) contains additional information. Currently this is `pair`, which can be accessed using `metadata['pair']` - and will return a pair in the format `XRP/BTC`. The Metadata-dict should not be modified and does not persist information across multiple calls. @@ -1050,7 +1050,7 @@ if self.config['runmode'].value in ('live', 'dry_run'): ## Print created dataframe -To inspect the created dataframe, you can issue a print-statement in either `populate_entry_trend()` or `populate_sell_trend()`. +To inspect the created dataframe, you can issue a print-statement in either `populate_entry_trend()` or `populate_exit_trend()`. You may also want to print the pair so it's clear what data is currently shown. ``` python From c38f8a0e6969ac1c1a719598101a9af9f22bc0d2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 11:07:31 +0100 Subject: [PATCH 14/15] Update custom_sell() documentation --- docs/backtesting.md | 2 +- docs/bot-basics.md | 4 ++-- docs/leverage.md | 1 - docs/strategy-advanced.md | 6 +++--- docs/strategy-callbacks.md | 12 ++++++------ docs/strategy_migration.md | 21 +++++++++++++++++++++ 6 files changed, 33 insertions(+), 13 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 95722f506..a5d8726b4 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -533,7 +533,7 @@ freqtrade backtesting --strategy AwesomeStrategy --timeframe 1h --timeframe-deta ``` This will load 1h data as well as 5m data for the timeframe. The strategy will be analyzed with the 1h timeframe - and for every "open trade candle" (candles where a trade is open) the 5m data will be used to simulate intra-candle movements. -All callback functions (`custom_sell()`, `custom_stoploss()`, ... ) will be running for each 5m candle once the trade is opened (so 12 times in the above example of 1h timeframe, and 5m detailed timeframe). +All callback functions (`custom_exit()`, `custom_stoploss()`, ... ) will be running for each 5m candle once the trade is opened (so 12 times in the above example of 1h timeframe, and 5m detailed timeframe). `--timeframe-detail` must be smaller than the original timeframe, otherwise backtesting will fail to start. diff --git a/docs/bot-basics.md b/docs/bot-basics.md index 5294009cc..49dd8f5c7 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -35,7 +35,7 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and * Calls `check_buy_timeout()` strategy callback for open buy orders. * Calls `check_sell_timeout()` strategy callback for open sell orders. * Verifies existing positions and eventually places sell orders. - * Considers stoploss, ROI and sell-signal, `custom_sell()` and `custom_stoploss()`. + * Considers stoploss, ROI and sell-signal, `custom_exit()` and `custom_stoploss()`. * Determine sell-price based on `ask_strategy` configuration setting or by using the `custom_exit_price()` callback. * Before a sell 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. @@ -62,7 +62,7 @@ This loop will be repeated again and again until the bot is stopped. * Determine stake size by calling the `custom_stake_amount()` callback. * In Margin and Futures mode, `leverage()` strategy callback is called to determine the desired leverage. * Check position adjustments for open trades if enabled and call `adjust_trade_position()` to determine if an additional order is requested. - * Call `custom_stoploss()` and `custom_sell()` to find custom exit points. + * Call `custom_stoploss()` and `custom_exit()` to find custom exit points. * For sells based on sell-signal and custom-sell: Call `custom_exit_price()` to determine exit price (Prices are moved to be within the closing candle). * Check for Order timeouts, either via the `unfilledtimeout` configuration, or via `check_buy_timeout()` / `check_sell_timeout()` strategy callbacks. * Generate backtest report output diff --git a/docs/leverage.md b/docs/leverage.md index 78d93dbed..852d4a8c4 100644 --- a/docs/leverage.md +++ b/docs/leverage.md @@ -104,4 +104,3 @@ All Fees are included in `current_profit` calculations during the trade. #### Futures mode Funding fees are either added or subtracted from the total amount of a trade - diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 623e6cdfe..8fa5c1612 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -80,7 +80,7 @@ class AwesomeStrategy(IStrategy): ## Enter Tag When your strategy has multiple buy signals, you can name the signal that triggered. -Then you can access you buy signal on `custom_sell` +Then you can access you buy signal on `custom_exit` ```python def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -93,8 +93,8 @@ def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFram return dataframe -def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, - current_profit: float, **kwargs): +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() if trade.enter_tag == 'buy_signal_rsi' and last_candle['rsi'] > 80: diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 54920b69f..d7aa7dfc7 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -9,10 +9,10 @@ Currently available callbacks: * [`bot_loop_start()`](#bot-loop-start) * [`custom_stake_amount()`](#custom-stake-size) -* [`custom_sell()`](#custom-sell-signal) +* [`custom_exit()`](#custom-exit-signal) * [`custom_stoploss()`](#custom-stoploss) * [`custom_entry_price()` and `custom_exit_price()`](#custom-order-price-rules) -* [`check_buy_timeout()` and `check_sell_timeout()](#custom-order-timeout-rules) +* [`check_buy_timeout()` and `check_sell_timeout()`](#custom-order-timeout-rules) * [`confirm_trade_entry()`](#trade-entry-buy-order-confirmation) * [`confirm_trade_exit()`](#trade-exit-sell-order-confirmation) * [`adjust_trade_position()`](#adjust-trade-position) @@ -79,15 +79,15 @@ Freqtrade will fall back to the `proposed_stake` value should your code raise an !!! Tip Returning `0` or `None` will prevent trades from being placed. -## Custom sell signal +## Custom exit signal Called for open trade every throttling iteration (roughly every 5 seconds) until a trade is closed. Allows to define custom sell signals, indicating that specified position should be sold. This is very useful when we need to customize sell conditions for each individual trade, or if you need trade data to make an exit decision. -For example you could implement a 1:2 risk-reward ROI with `custom_sell()`. +For example you could implement a 1:2 risk-reward ROI with `custom_exit()`. -Using custom_sell() signals in place of stoploss though *is not recommended*. It is a inferior method to using `custom_stoploss()` in this regard - which also allows you to keep the stoploss on exchange. +Using custom_exit() signals in place of stoploss though *is not recommended*. It is a inferior method to using `custom_stoploss()` in this regard - which also allows you to keep the stoploss on exchange. !!! Note Returning a (none-empty) `string` or `True` from this method is equal to setting sell signal on a candle at specified time. This method is not called when sell signal is set already, or if sell signals are disabled (`use_sell_signal=False` or `sell_profit_only=True` while profit is below `sell_profit_offset`). `string` max length is 64 characters. Exceeding this limit will cause the message to be truncated to 64 characters. @@ -96,7 +96,7 @@ An example of how we can use different indicators depending on the current profi ``` python class AwesomeStrategy(IStrategy): - def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + 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() diff --git a/docs/strategy_migration.md b/docs/strategy_migration.md index 6bd283808..4d6de440f 100644 --- a/docs/strategy_migration.md +++ b/docs/strategy_migration.md @@ -10,6 +10,7 @@ If you intend on using markets other than spot markets, please migrate your stra * Strategy methods: * `populate_buy_trend()` -> `populate_entry_trend()` * `populate_sell_trend()` -> `populate_exit_trend()` + * `custom_sell()` -> `custom_exit()` * Dataframe columns: * `buy` -> `enter_long` * `sell` -> `exit_long` @@ -102,6 +103,26 @@ def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame Please refer to the [Strategy documentation](strategy-customization.md#exit-signal-rules) on how to enter and exit short trades. +### `custom_sell` + +``` 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() + # ... +``` + ### Custom-stake-amount New string argument `side` - which can be either `"long"` or `"short"`. From de8e8690387f6f52eed7f54972df0a311a03fdc1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 20 Mar 2022 09:16:34 +0100 Subject: [PATCH 15/15] update missing "side" argument --- docs/strategy-callbacks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index d7aa7dfc7..4ee36e73d 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -630,7 +630,7 @@ class DigDeeperStrategy(IStrategy): # This is called when placing the initial order (opening trade) def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: float, max_stake: float, - entry_tag: Optional[str], **kwargs) -> float: + entry_tag: Optional[str], side: str, **kwargs) -> float: # We need to leave most of the funds for possible further DCA orders # This also applies to fixed stakes