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
|
||||
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.
|
||||
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
|
||||
|
||||
```
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user