Improve documentation on adjust_trade_position and position_adjustment_enable
This commit is contained in:
parent
f97662e816
commit
f11a40f144
@ -38,6 +38,8 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and
|
|||||||
* Considers stoploss, ROI and sell-signal, `custom_sell()` and `custom_stoploss()`.
|
* Considers stoploss, ROI and sell-signal, `custom_sell()` and `custom_stoploss()`.
|
||||||
* Determine sell-price based on `ask_strategy` configuration setting or by using the `custom_exit_price()` callback.
|
* 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.
|
* Before a sell order is placed, `confirm_trade_exit()` strategy callback is called.
|
||||||
|
* Check position adjustments for open trades if enabled.
|
||||||
|
* Call `adjust_trade_position()` strategy callback and place additional order if required.
|
||||||
* Check if trade-slots are still available (if `max_open_trades` is reached).
|
* Check if trade-slots are still available (if `max_open_trades` is reached).
|
||||||
* Verifies buy signal trying to enter new positions.
|
* Verifies buy signal trying to enter new positions.
|
||||||
* Determine buy-price based on `bid_strategy` configuration setting, or by using the `custom_entry_price()` callback.
|
* Determine buy-price based on `bid_strategy` configuration setting, or by using the `custom_entry_price()` callback.
|
||||||
@ -60,7 +62,8 @@ This loop will be repeated again and again until the bot is stopped.
|
|||||||
* Determine stake size by calling the `custom_stake_amount()` callback.
|
* Determine stake size by calling the `custom_stake_amount()` callback.
|
||||||
* Call `custom_stoploss()` and `custom_sell()` to find custom exit points.
|
* Call `custom_stoploss()` and `custom_sell()` 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).
|
* 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 position adjustments for open trades if enabled and call `adjust_trade_position()` determine additional order is required.
|
||||||
|
|
||||||
* Generate backtest report output
|
* Generate backtest report output
|
||||||
|
|
||||||
!!! Note
|
!!! Note
|
||||||
|
@ -171,6 +171,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
|
|||||||
| `user_data_dir` | Directory containing user data. <br> *Defaults to `./user_data/`*. <br> **Datatype:** String
|
| `user_data_dir` | Directory containing user data. <br> *Defaults to `./user_data/`*. <br> **Datatype:** String
|
||||||
| `dataformat_ohlcv` | Data format to use to store historical candle (OHLCV) data. <br> *Defaults to `json`*. <br> **Datatype:** String
|
| `dataformat_ohlcv` | Data format to use to store historical candle (OHLCV) data. <br> *Defaults to `json`*. <br> **Datatype:** String
|
||||||
| `dataformat_trades` | Data format to use to store historical trades data. <br> *Defaults to `jsongz`*. <br> **Datatype:** String
|
| `dataformat_trades` | Data format to use to store historical trades data. <br> *Defaults to `jsongz`*. <br> **Datatype:** String
|
||||||
|
| `position_adjustment_enable` | Enables the strategy to use position adjustments (additional buys or sells). More information below. <br> **Datatype:** Boolean
|
||||||
|
|
||||||
### Parameters in the strategy
|
### Parameters in the strategy
|
||||||
|
|
||||||
@ -583,6 +584,15 @@ export HTTPS_PROXY="http://addr:port"
|
|||||||
freqtrade
|
freqtrade
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Understand position_adjustment_enable
|
||||||
|
|
||||||
|
The `position_adjustment_enable` configuration parameter enables the usage of `adjust_trade_position()` callback in strategy.
|
||||||
|
For performance reasons, it's disabled by default, and freqtrade will show a warning message on startup if enabled.
|
||||||
|
This can be dangerous with some strategies, so use with care.
|
||||||
|
|
||||||
|
See [the strategy callbacks](strategy-callbacks.md) for details on usage.
|
||||||
|
|
||||||
|
|
||||||
## Next step
|
## Next step
|
||||||
|
|
||||||
Now you have configured your config.json, the next step is to [start your bot](bot-usage.md).
|
Now you have configured your config.json, the next step is to [start your bot](bot-usage.md).
|
||||||
|
@ -273,6 +273,9 @@ def plot_config(self):
|
|||||||
!!! Warning
|
!!! Warning
|
||||||
`plotly` arguments are only supported with plotly library and will not work with freq-ui.
|
`plotly` arguments are only supported with plotly library and will not work with freq-ui.
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
If `position_adjustment_enable` / `adjust_trade_position()` is used, the trade initial buy price is averaged over multiple orders and the trade start price will most likely appear outside the candle range.
|
||||||
|
|
||||||
## Plot profit
|
## Plot profit
|
||||||
|
|
||||||
![plot-profit](assets/plot-profit.png)
|
![plot-profit](assets/plot-profit.png)
|
||||||
|
@ -229,3 +229,77 @@ for val in self.buy_ema_short.range:
|
|||||||
# Append columns to existing dataframe
|
# Append columns to existing dataframe
|
||||||
merged_frame = pd.concat(frames, axis=1)
|
merged_frame = pd.concat(frames, axis=1)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Adjust trade position
|
||||||
|
|
||||||
|
`adjust_trade_position()` can be used to perform additional orders to manage risk with DCA (Dollar Cost Averaging) for example.
|
||||||
|
|
||||||
|
!!! Tip: The `position_adjustment_enable` configuration parameter must be enabled to use adjust_trade_position callback in strategy.
|
||||||
|
|
||||||
|
!!! Warning: Additional orders also mean additional fees.
|
||||||
|
|
||||||
|
!!! Warning: Stoploss is still calculated from the initial opening price, not averaged price.
|
||||||
|
|
||||||
|
``` python
|
||||||
|
from freqtrade.persistence import Trade
|
||||||
|
|
||||||
|
|
||||||
|
class DigDeeperStrategy(IStrategy):
|
||||||
|
|
||||||
|
# Attempts to handle large drops with DCA. High stoploss is required.
|
||||||
|
stoploss = -0.30
|
||||||
|
|
||||||
|
# ... populate_* methods
|
||||||
|
|
||||||
|
def adjust_trade_position(self, pair: str, trade: Trade,
|
||||||
|
current_time: datetime, current_rate: float, current_profit: float,
|
||||||
|
**kwargs) -> Optional[float]:
|
||||||
|
"""
|
||||||
|
Custom trade adjustment logic, returning the stake amount that a trade should be increased.
|
||||||
|
This means extra buy orders with additional fees.
|
||||||
|
|
||||||
|
:param pair: Pair that's currently analyzed
|
||||||
|
:param trade: trade object.
|
||||||
|
:param current_time: datetime object, containing the current datetime
|
||||||
|
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
|
||||||
|
:param current_profit: Current profit (as ratio), calculated based on current_rate.
|
||||||
|
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
||||||
|
:return float: Stake amount to adjust your trade
|
||||||
|
"""
|
||||||
|
|
||||||
|
if current_profit > -0.05:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Obtain pair dataframe.
|
||||||
|
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||||
|
|
||||||
|
# Only buy when not actively falling price.
|
||||||
|
if dataframe['close'] < dataframe['close'].shift(1):
|
||||||
|
return None
|
||||||
|
|
||||||
|
count_of_buys = 0
|
||||||
|
for order in trade.orders:
|
||||||
|
# Instantly stop when there's an open order
|
||||||
|
if order.ft_is_open:
|
||||||
|
return None
|
||||||
|
if order.ft_order_side == 'buy' and order.status == "closed":
|
||||||
|
count_of_buys += 1
|
||||||
|
|
||||||
|
# Allow up to 3 additional increasingly larger buys (4 in total)
|
||||||
|
# Initial buy is 1x
|
||||||
|
# If that falls to -5% profit, we buy 1.25x more, average profit should increase to roughly -2.2%
|
||||||
|
# If that falles down to -5% again, we buy 1.5x more
|
||||||
|
# If that falles once again down to -5%, we buy 1.75x more
|
||||||
|
# Total stake for this trade would be 1 + 1.25 + 1.5 + 1.75 = 5.5x of the initial allowed stake.
|
||||||
|
# Hope you have a deep wallet!
|
||||||
|
if 0 < count_of_buys <= 3:
|
||||||
|
try:
|
||||||
|
stake_amount = self.wallets.get_trade_stake_amount(pair, None)
|
||||||
|
stake_amount = stake_amount * (1 + (count_of_buys * 0.25))
|
||||||
|
return stake_amount
|
||||||
|
except Exception as exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
```
|
||||||
|
@ -15,6 +15,7 @@ Currently available callbacks:
|
|||||||
* [`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_entry()`](#trade-entry-buy-order-confirmation)
|
||||||
* [`confirm_trade_exit()`](#trade-exit-sell-order-confirmation)
|
* [`confirm_trade_exit()`](#trade-exit-sell-order-confirmation)
|
||||||
|
* [`adjust_trade_position()`](#adjust-trade-position)
|
||||||
|
|
||||||
!!! Tip "Callback calling sequence"
|
!!! Tip "Callback calling sequence"
|
||||||
You can find the callback calling sequence in [bot-basics](bot-basics.md#bot-execution-logic)
|
You can find the callback calling sequence in [bot-basics](bot-basics.md#bot-execution-logic)
|
||||||
@ -568,3 +569,49 @@ class AwesomeStrategy(IStrategy):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Adjust trade position
|
||||||
|
|
||||||
|
`adjust_trade_position()` can be used to perform additional orders to manage risk with DCA (Dollar Cost Averaging).
|
||||||
|
|
||||||
|
!!! Tip: The `position_adjustment_enable` configuration parameter must be enabled to use adjust_trade_position callback in strategy.
|
||||||
|
|
||||||
|
!!! Warning: Additional orders also mean additional fees.
|
||||||
|
|
||||||
|
!!! Warning: Stoploss is still calculated from the initial opening price, not averaged price.
|
||||||
|
|
||||||
|
``` python
|
||||||
|
from freqtrade.persistence import Trade
|
||||||
|
|
||||||
|
|
||||||
|
class AwesomeStrategy(IStrategy):
|
||||||
|
|
||||||
|
# ... populate_* methods
|
||||||
|
|
||||||
|
def adjust_trade_position(self, pair: str, trade: Trade,
|
||||||
|
current_time: datetime, current_rate: float, current_profit: float,
|
||||||
|
**kwargs) -> Optional[float]:
|
||||||
|
"""
|
||||||
|
Custom trade adjustment logic, returning the stake amount that a trade should be increased.
|
||||||
|
This means extra buy orders with additional fees.
|
||||||
|
|
||||||
|
For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/
|
||||||
|
|
||||||
|
When not implemented by a strategy, returns None
|
||||||
|
|
||||||
|
:param pair: Pair that's currently analyzed
|
||||||
|
:param trade: trade object.
|
||||||
|
:param current_time: datetime object, containing the current datetime
|
||||||
|
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
|
||||||
|
:param current_profit: Current profit (as ratio), calculated based on current_rate.
|
||||||
|
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
||||||
|
:return float: Stake amount to adjust your trade
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Example: If 10% loss / -10% profit then buy more the same amount we had before.
|
||||||
|
if current_profit < -0.10:
|
||||||
|
return trade.stake_amount
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
```
|
||||||
|
@ -102,6 +102,9 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
self._exit_lock = Lock()
|
self._exit_lock = Lock()
|
||||||
LoggingMixin.__init__(self, logger, timeframe_to_seconds(self.strategy.timeframe))
|
LoggingMixin.__init__(self, logger, timeframe_to_seconds(self.strategy.timeframe))
|
||||||
|
|
||||||
|
# Is Position Adjustment enabled?
|
||||||
|
self.position_adjustment = bool(self.config.get('position_adjustment_enable', False))
|
||||||
|
|
||||||
def notify_status(self, msg: str) -> None:
|
def notify_status(self, msg: str) -> None:
|
||||||
"""
|
"""
|
||||||
Public method for users of this class (worker, etc.) to send notifications
|
Public method for users of this class (worker, etc.) to send notifications
|
||||||
@ -179,7 +182,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
self.exit_positions(trades)
|
self.exit_positions(trades)
|
||||||
|
|
||||||
# Check if we need to adjust our current positions before attempting to buy new trades.
|
# Check if we need to adjust our current positions before attempting to buy new trades.
|
||||||
if self.config.get('position_adjustment_enable', False):
|
if self.position_adjustment:
|
||||||
self.process_open_trade_positions()
|
self.process_open_trade_positions()
|
||||||
|
|
||||||
# Then looking for buy opportunities
|
# Then looking for buy opportunities
|
||||||
|
@ -118,6 +118,7 @@ class Backtesting:
|
|||||||
# Add maximum startup candle count to configuration for informative pairs support
|
# Add maximum startup candle count to configuration for informative pairs support
|
||||||
self.config['startup_candle_count'] = self.required_startup
|
self.config['startup_candle_count'] = self.required_startup
|
||||||
self.exchange.validate_required_startup_candles(self.required_startup, self.timeframe)
|
self.exchange.validate_required_startup_candles(self.required_startup, self.timeframe)
|
||||||
|
self.position_adjustment = bool(self.config.get('position_adjustment_enable', False))
|
||||||
self.init_backtest()
|
self.init_backtest()
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
@ -353,7 +354,7 @@ class Backtesting:
|
|||||||
def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple) -> Optional[LocalTrade]:
|
def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple) -> Optional[LocalTrade]:
|
||||||
|
|
||||||
current_profit = trade.calc_profit_ratio(row[OPEN_IDX])
|
current_profit = trade.calc_profit_ratio(row[OPEN_IDX])
|
||||||
|
|
||||||
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position, default_retval=None)(
|
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position, default_retval=None)(
|
||||||
pair=trade.pair, trade=trade, current_time=row[DATE_IDX].to_pydatetime(),
|
pair=trade.pair, trade=trade, current_time=row[DATE_IDX].to_pydatetime(),
|
||||||
current_rate=row[OPEN_IDX], current_profit=current_profit)
|
current_rate=row[OPEN_IDX], current_profit=current_profit)
|
||||||
@ -403,9 +404,9 @@ class Backtesting:
|
|||||||
|
|
||||||
def _get_sell_trade_entry_for_candle(self, trade: LocalTrade,
|
def _get_sell_trade_entry_for_candle(self, trade: LocalTrade,
|
||||||
sell_row: Tuple) -> Optional[LocalTrade]:
|
sell_row: Tuple) -> Optional[LocalTrade]:
|
||||||
|
|
||||||
# Check if we need to adjust our current positions
|
# Check if we need to adjust our current positions
|
||||||
if self.config.get('position_adjustment_enable', False):
|
if self.position_adjustment:
|
||||||
trade = self._get_adjust_trade_entry_for_candle(trade, sell_row)
|
trade = self._get_adjust_trade_entry_for_candle(trade, sell_row)
|
||||||
|
|
||||||
sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
|
sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
|
||||||
|
@ -386,11 +386,12 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
current_time: datetime, current_rate: float, current_profit: float,
|
current_time: datetime, current_rate: float, current_profit: float,
|
||||||
**kwargs) -> Optional[float]:
|
**kwargs) -> Optional[float]:
|
||||||
"""
|
"""
|
||||||
Custom trade adjustment logic, returning the stake amount that a trade shold be either increased or decreased.
|
Custom trade adjustment logic, returning the stake amount that a trade should be increased.
|
||||||
|
This means extra buy orders with additional fees.
|
||||||
|
|
||||||
For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/
|
For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/
|
||||||
|
|
||||||
When not implemented by a strategy, returns 0.0
|
When not implemented by a strategy, returns None
|
||||||
|
|
||||||
:param pair: Pair that's currently analyzed
|
:param pair: Pair that's currently analyzed
|
||||||
:param trade: trade object.
|
:param trade: trade object.
|
||||||
|
Loading…
Reference in New Issue
Block a user