75318525a9
Co-authored-by: hroff-1902 <47309513+hroff-1902@users.noreply.github.com>
202 lines
8.1 KiB
Markdown
202 lines
8.1 KiB
Markdown
# Advanced Strategies
|
|
|
|
This page explains some advanced concepts available for strategies.
|
|
If you're just getting started, please be familiar with the methods described in the [Strategy Customization](strategy-customization.md) documentation and with the [Freqtrade basics](bot-basics.md) first.
|
|
|
|
[Freqtrade basics](bot-basics.md) describes in which sequence each method described below is called, which can be helpful to understand which method to use for your custom needs.
|
|
|
|
!!! Note
|
|
All callback methods described below should only be implemented in a strategy if they are actually used.
|
|
|
|
## Custom order timeout rules
|
|
|
|
Simple, timebased order-timeouts can be configured either via strategy or in the configuration in the `unfilledtimeout` section.
|
|
|
|
However, freqtrade also offers a custom callback for both ordertypes, which allows you to decide based on custom criteria if a order did time out or not.
|
|
|
|
!!! Note
|
|
Unfilled order timeouts are not relevant during backtesting or hyperopt, and are only relevant during real (live) trading. Therefore these methods are only called in these circumstances.
|
|
|
|
### Custom order timeout example
|
|
|
|
A simple example, which applies different unfilled-timeouts depending on the price of the asset can be seen below.
|
|
It applies a tight timeout for higher priced assets, while allowing more time to fill on cheap coins.
|
|
|
|
The function must return either `True` (cancel order) or `False` (keep order alive).
|
|
|
|
``` python
|
|
from datetime import datetime, timedelta
|
|
from freqtrade.persistence import Trade
|
|
|
|
class Awesomestrategy(IStrategy):
|
|
|
|
# ... populate_* methods
|
|
|
|
# Set unfilledtimeout to 25 hours, since our maximum timeout from below is 24 hours.
|
|
unfilledtimeout = {
|
|
'buy': 60 * 25,
|
|
'sell': 60 * 25
|
|
}
|
|
|
|
def check_buy_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool:
|
|
if trade.open_rate > 100 and trade.open_date < datetime.utcnow() - timedelta(minutes=5):
|
|
return True
|
|
elif trade.open_rate > 10 and trade.open_date < datetime.utcnow() - timedelta(minutes=3):
|
|
return True
|
|
elif trade.open_rate < 1 and trade.open_date < datetime.utcnow() - timedelta(hours=24):
|
|
return True
|
|
return False
|
|
|
|
|
|
def check_sell_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool:
|
|
if trade.open_rate > 100 and trade.open_date < datetime.utcnow() - timedelta(minutes=5):
|
|
return True
|
|
elif trade.open_rate > 10 and trade.open_date < datetime.utcnow() - timedelta(minutes=3):
|
|
return True
|
|
elif trade.open_rate < 1 and trade.open_date < datetime.utcnow() - timedelta(hours=24):
|
|
return True
|
|
return False
|
|
```
|
|
|
|
!!! Note
|
|
For the above example, `unfilledtimeout` must be set to something bigger than 24h, otherwise that type of timeout will apply first.
|
|
|
|
### Custom order timeout example (using additional data)
|
|
|
|
``` python
|
|
from datetime import datetime
|
|
from freqtrade.persistence import Trade
|
|
|
|
class Awesomestrategy(IStrategy):
|
|
|
|
# ... populate_* methods
|
|
|
|
# Set unfilledtimeout to 25 hours, since our maximum timeout from below is 24 hours.
|
|
unfilledtimeout = {
|
|
'buy': 60 * 25,
|
|
'sell': 60 * 25
|
|
}
|
|
|
|
def check_buy_timeout(self, pair: str, trade: Trade, order: dict, **kwargs) -> bool:
|
|
ob = self.dp.orderbook(pair, 1)
|
|
current_price = ob['bids'][0][0]
|
|
# Cancel buy order if price is more than 2% above the order.
|
|
if current_price > order['price'] * 1.02:
|
|
return True
|
|
return False
|
|
|
|
|
|
def check_sell_timeout(self, pair: str, trade: Trade, order: dict, **kwargs) -> bool:
|
|
ob = self.dp.orderbook(pair, 1)
|
|
current_price = ob['asks'][0][0]
|
|
# Cancel sell order if price is more than 2% below the order.
|
|
if current_price < order['price'] * 0.98:
|
|
return True
|
|
return False
|
|
```
|
|
|
|
## Bot loop start callback
|
|
|
|
A simple callback which is called once at the start of every bot throttling iteration.
|
|
This can be used to perform calculations which are pair independent (apply to all pairs), loading of external data, etc.
|
|
|
|
``` python
|
|
import requests
|
|
|
|
class Awesomestrategy(IStrategy):
|
|
|
|
# ... populate_* methods
|
|
|
|
def bot_loop_start(self, **kwargs) -> None:
|
|
"""
|
|
Called at the start of the bot iteration (one loop).
|
|
Might be used to perform pair-independent tasks
|
|
(e.g. gather some remote resource for comparison)
|
|
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
|
"""
|
|
if self.config['runmode'].value in ('live', 'dry_run'):
|
|
# Assign this to the class by using self.*
|
|
# can then be used by populate_* methods
|
|
self.remote_data = requests.get('https://some_remote_source.example.com')
|
|
|
|
```
|
|
|
|
## Bot order confirmation
|
|
|
|
### Trade entry (buy order) confirmation
|
|
|
|
`confirm_trade_entry()` can be used to abort a trade entry at the latest second (maybe because the price is not what we expect).
|
|
|
|
``` python
|
|
class Awesomestrategy(IStrategy):
|
|
|
|
# ... populate_* methods
|
|
|
|
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
|
|
time_in_force: str, **kwargs) -> bool:
|
|
"""
|
|
Called right before placing a buy order.
|
|
Timing for this function is critical, so avoid doing heavy computations or
|
|
network requests in this method.
|
|
|
|
For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/
|
|
|
|
When not implemented by a strategy, returns True (always confirming).
|
|
|
|
:param pair: Pair that's about to be bought.
|
|
:param order_type: Order type (as configured in order_types). usually limit or market.
|
|
:param amount: Amount in target (quote) currency that's going to be traded.
|
|
:param rate: Rate that's going to be used when using limit orders
|
|
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
|
|
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
|
:return bool: When True is returned, then the buy-order is placed on the exchange.
|
|
False aborts the process
|
|
"""
|
|
return True
|
|
|
|
```
|
|
|
|
### Trade exit (sell order) confirmation
|
|
|
|
`confirm_trade_exit()` can be used to abort a trade exit (sell) at the latest second (maybe because the price is not what we expect).
|
|
|
|
``` python
|
|
from freqtrade.persistence import Trade
|
|
|
|
|
|
class Awesomestrategy(IStrategy):
|
|
|
|
# ... populate_* methods
|
|
|
|
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
|
|
rate: float, time_in_force: str, sell_reason: str, **kwargs) -> bool:
|
|
"""
|
|
Called right before placing a regular sell order.
|
|
Timing for this function is critical, so avoid doing heavy computations or
|
|
network requests in this method.
|
|
|
|
For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/
|
|
|
|
When not implemented by a strategy, returns True (always confirming).
|
|
|
|
:param pair: Pair that's about to be sold.
|
|
:param order_type: Order type (as configured in order_types). usually limit or market.
|
|
:param amount: Amount in quote currency.
|
|
:param rate: Rate that's going to be used when using limit orders
|
|
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
|
|
:param sell_reason: Sell reason.
|
|
Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss',
|
|
'sell_signal', 'force_sell', 'emergency_sell']
|
|
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
|
:return bool: When True is returned, then the sell-order is placed on the exchange.
|
|
False aborts the process
|
|
"""
|
|
if sell_reason == 'force_sell' and trade.calc_profit_ratio(rate) < 0:
|
|
# Reject force-sells with negative profit
|
|
# This is just a sample, please adjust to your needs
|
|
# (this does not necessarily make sense, assuming you know when you're force-selling)
|
|
return False
|
|
return True
|
|
|
|
```
|