Improve documentation. Fix bug.
This commit is contained in:
parent
53ef37d5fc
commit
2a728c676e
@ -229,99 +229,3 @@ 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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
```
|
|
||||||
|
@ -574,8 +574,95 @@ class AwesomeStrategy(IStrategy):
|
|||||||
|
|
||||||
The `position_adjustment_enable` strategy property enables the usage of `adjust_trade_position()` callback in strategy.
|
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.
|
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).
|
`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).
|
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
|
||||||
|
|
||||||
|
```
|
||||||
|
@ -458,14 +458,12 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
# Walk through each pair and check if it needs changes
|
# Walk through each pair and check if it needs changes
|
||||||
for trade in Trade.get_open_trades():
|
for trade in Trade.get_open_trades():
|
||||||
# If there is any open orders, wait for them to finish.
|
# If there is any open orders, wait for them to finish.
|
||||||
for order in trade.orders:
|
if len([o for o in trade.orders if o.ft_is_open]) == 0:
|
||||||
if order.ft_is_open:
|
try:
|
||||||
break
|
self.check_and_call_adjust_trade_position(trade)
|
||||||
try:
|
except DependencyException as exception:
|
||||||
self.check_and_call_adjust_trade_position(trade)
|
logger.warning('Unable to adjust position of trade for %s: %s',
|
||||||
except DependencyException as exception:
|
trade.pair, 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):
|
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 order['status'] in constants.NON_OPEN_EXCHANGE_STATES:
|
||||||
# If a buy order was closed, force update on stoploss on exchange
|
# 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)
|
trade = self.cancel_stoploss_on_exchange(trade)
|
||||||
# Updating wallets when order is closed
|
# Updating wallets when order is closed
|
||||||
self.wallets.update()
|
self.wallets.update()
|
||||||
|
Loading…
Reference in New Issue
Block a user