stable/docs/strategy-advanced.md

119 lines
4.3 KiB
Markdown
Raw Normal View History

# 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 first.
## 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
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
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
```
2020-02-22 10:28:13 +00:00
!!! 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
2020-05-12 10:54:13 +00:00
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.
2020-04-25 15:25:18 +00:00
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.
2020-04-25 15:25:18 +00:00
if current_price < order['price'] * 0.98:
return True
return False
```
2020-06-14 05:00:55 +00:00
## Bot loop start callback
A simple callback which is called at the start of every bot iteration.
This can be used to perform calculations which are pair independent.
``` 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 ressource 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')
```