Improve documentation. Fix bug.

This commit is contained in:
Reigo Reinmets 2021-12-27 19:41:33 +02:00
parent 53ef37d5fc
commit 2a728c676e
3 changed files with 96 additions and 107 deletions

View File

@ -229,99 +229,3 @@ for val in self.buy_ema_short.range:
# Append columns to existing dataframe
merged_frame = pd.concat(frames, axis=1)
```
## Adjust trade position
The `position_adjustment_enable` strategy property 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.
`adjust_trade_position()` can be used to perform additional orders to manage risk with DCA (Dollar Cost Averaging) for example.
The strategy is expected to return a stake_amount if and when an additional buy order should be made (position is increased).
If there is not enough funds in the wallet then nothing will happen.
Additional orders also mean additional fees and those orders don't count towards `max_open_trades`.
Using unlimited stake amount with DCA orders requires you to also implement `custom_stake_amount` callback to avoid allocating all funds to initial order.
!!! 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
max_dca_orders = 3
# This number is explained a bit further down
max_dca_multiplier = 5.5
# ... populate_* methods
# Let unlimited stakes leave funds open for DCA orders
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: float, max_stake: float,
**kwargs) -> float:
if self.config['stake_amount'] == 'unlimited':
return proposed_stake / self.max_dca_multiplier
# Use default stake amount.
return proposed_stake
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.
last_candle = dataframe.iloc[-1].squeeze()
previous_candle = dataframe.iloc[-2].squeeze()
if last_candle['close'] < previous_candle['close']:
return None
count_of_buys = 0
for order in trade.orders:
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 falls down to -5% again, we buy 1.5x more
# If that falls 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.
# That is why max_dca_multiplier is 5.5
# Hope you have a deep wallet!
if 0 < count_of_buys <= self.max_dca_orders:
try:
# This returns max stakes for one trade
stake_amount = self.wallets.get_trade_stake_amount(pair, None)
# This calculates base order size
stake_amount = stake_amount / self.max_dca_multiplier
# This then calculates current safety order size
stake_amount = stake_amount * (1 + (count_of_buys * 0.25))
return stake_amount
except Exception as exception:
return None
return None
```

View File

@ -574,8 +574,95 @@ class AwesomeStrategy(IStrategy):
The `position_adjustment_enable` strategy property 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.
Enabling this does nothing unless the strategy also implements `adjust_trade_position()` callback.
`adjust_trade_position()` can be used to perform additional orders, for example to manage risk with DCA (Dollar Cost Averaging).
The strategy is expected to return a stake_amount if and when an additional buy order should be made (position is increased).
If there is not enough funds in the wallet then nothing will happen.
Additional orders also mean additional fees and those orders don't count towards `max_open_trades`.
[See Advanced Strategies for an example](strategy-advanced.md#adjust-trade-position)
!!! Note About stake size
Using fixed stake size means it will be the amount used for the first order just like without position adjustment.
Using 'unlimited' stake amount with DCA orders requires you to also implement custom_stake_amount callback to avoid allocating all funds to initial order.
!!! 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
max_dca_orders = 3
# This number is explained a bit further down
max_dca_multiplier = 5.5
# ... populate_* methods
# 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,
**kwargs) -> float:
# We need to leave most of the funds for possible further DCA orders
# This also applies to fixed stakes
return proposed_stake / self.max_dca_multiplier
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 (just to show how to access it)
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
# Only buy when not actively falling price.
last_candle = dataframe.iloc[-1].squeeze()
previous_candle = dataframe.iloc[-2].squeeze()
if last_candle['close'] < previous_candle['close']:
return None
count_of_buys = 0
initial_order_stake =
for order in trade.orders:
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 falls down to -5% again, we buy 1.5x more
# If that falls 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.
# That is why max_dca_multiplier is 5.5
# Hope you have a deep wallet!
if 0 < count_of_buys <= self.max_dca_orders:
try:
# This returns max stakes for one trade
stake_amount = self.wallets.get_trade_stake_amount(pair, None)
# This calculates base order size
stake_amount = stake_amount / self.max_dca_multiplier
# This then calculates current safety order size
stake_amount = stake_amount * (1 + (count_of_buys * 0.25))
return stake_amount
except Exception as exception:
return None
return None
```

View File

@ -458,14 +458,12 @@ class FreqtradeBot(LoggingMixin):
# Walk through each pair and check if it needs changes
for trade in Trade.get_open_trades():
# If there is any open orders, wait for them to finish.
for order in trade.orders:
if order.ft_is_open:
break
try:
self.check_and_call_adjust_trade_position(trade)
except DependencyException as exception:
logger.warning('Unable to adjust position of trade for %s: %s',
trade.pair, exception)
if len([o for o in trade.orders if o.ft_is_open]) == 0:
try:
self.check_and_call_adjust_trade_position(trade)
except DependencyException as exception:
logger.warning('Unable to adjust position of trade for %s: %s',
trade.pair, exception)
def check_and_call_adjust_trade_position(self, trade: Trade):
"""
@ -1385,7 +1383,7 @@ class FreqtradeBot(LoggingMixin):
if order['status'] in constants.NON_OPEN_EXCHANGE_STATES:
# If a buy order was closed, force update on stoploss on exchange
if order['side'] == 'buy':
if order.get('side', None) == 'buy':
trade = self.cancel_stoploss_on_exchange(trade)
# Updating wallets when order is closed
self.wallets.update()