2020-02-22 07:14:08 +00:00
# Advanced Strategies
This page explains some advanced concepts available for strategies.
2020-06-14 09:38:56 +00:00
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.
2020-07-04 07:43:49 +00:00
[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.
2020-02-22 07:14:08 +00:00
2020-06-14 08:08:19 +00:00
!!! Note
2020-07-04 14:41:19 +00:00
All callback methods described below should only be implemented in a strategy if they are actually used.
2020-06-14 08:08:19 +00:00
2020-12-09 06:45:41 +00:00
## Custom stoploss logic
// TODO: Complete this section
2020-02-22 07:14:08 +00:00
## 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.
2020-02-22 10:28:13 +00:00
!!! Note
2020-02-22 07:14:08 +00:00
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
2020-05-12 11:42:33 +00:00
from datetime import datetime, timedelta
2020-02-22 07:14:08 +00:00
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
}
2020-03-01 08:43:20 +00:00
def check_buy_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool:
2020-02-22 07:14:08 +00:00
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
2020-02-25 13:48:46 +00:00
return False
2020-02-22 07:14:08 +00:00
2020-03-01 08:43:20 +00:00
def check_sell_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool:
2020-02-22 07:14:08 +00:00
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
2020-02-25 13:48:46 +00:00
return False
2020-02-22 07:14:08 +00:00
```
2020-02-22 10:28:13 +00:00
!!! Note
2020-02-22 07:14:08 +00:00
For the above example, `unfilledtimeout` must be set to something bigger than 24h, otherwise that type of timeout will apply first.
2020-02-25 13:48:46 +00:00
### Custom order timeout example (using additional data)
``` python
2020-05-12 10:54:13 +00:00
from datetime import datetime
2020-02-25 13:48:46 +00:00
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.
2020-04-25 15:25:18 +00:00
if current_price > order['price'] * 1.02:
2020-02-25 13:48:46 +00:00
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.
2020-04-25 15:25:18 +00:00
if current_price < order [ ' price ' ] * 0 . 98:
2020-02-25 13:48:46 +00:00
return True
return False
```
2020-06-14 05:00:55 +00:00
## Bot loop start callback
2020-07-04 07:43:49 +00:00
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.
2020-06-14 05:00:55 +00:00
``` 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
2020-07-04 07:43:49 +00:00
(e.g. gather some remote resource for comparison)
2020-06-14 05:00:55 +00:00
: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')
```
2020-06-14 08:08:19 +00:00
## Bot order confirmation
### Trade entry (buy order) confirmation
2020-07-04 07:43:49 +00:00
`confirm_trade_entry()` can be used to abort a trade entry at the latest second (maybe because the price is not what we expect).
2020-06-14 08:08:19 +00:00
``` 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
2020-06-14 08:49:15 +00:00
network requests in this method.
2020-06-14 08:08:19 +00:00
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
2020-07-04 07:43:49 +00:00
`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).
2020-06-14 08:08:19 +00:00
``` python
from freqtrade.persistence import Trade
class Awesomestrategy(IStrategy):
# ... populate_* methods
2020-06-18 17:46:03 +00:00
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:
2020-06-14 08:08:19 +00:00
"""
Called right before placing a regular sell order.
Timing for this function is critical, so avoid doing heavy computations or
2020-06-14 08:49:15 +00:00
network requests in this method.
2020-06-14 08:08:19 +00:00
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
```
2020-08-08 15:08:38 +00:00
## Derived strategies
The strategies can be derived from other strategies. This avoids duplication of your custom strategy code. You can use this technique to override small parts of your main strategy, leaving the rest untouched:
``` python
class MyAwesomeStrategy(IStrategy):
...
stoploss = 0.13
trailing_stop = False
# All other attributes and methods are here as they
# should be in any custom strategy...
...
class MyAwesomeStrategy2(MyAwesomeStrategy):
# Override something
stoploss = 0.08
trailing_stop = True
```
Both attributes and methods may be overriden, altering behavior of the original strategy in a way you need.