chore: update samples to use doublequotes

This commit is contained in:
Matthias 2024-08-18 08:44:37 +02:00
parent d754a2e295
commit 7952712c5e

View File

@ -43,10 +43,10 @@ class AwesomeStrategy(IStrategy):
Called only once after bot instantiation. Called only once after bot instantiation.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :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'): if self.config["runmode"].value in ("live", "dry_run"):
# Assign this to the class by using self.* # Assign this to the class by using self.*
# can then be used by populate_* methods # can then be used by populate_* methods
self.custom_remote_data = requests.get('https://some_remote_source.example.com') self.custom_remote_data = requests.get("https://some_remote_source.example.com")
``` ```
@ -59,6 +59,7 @@ seconds, unless configured differently) or once per candle in backtest/hyperopt
This can be used to perform calculations which are pair independent (apply to all pairs), loading of external data, etc. This can be used to perform calculations which are pair independent (apply to all pairs), loading of external data, etc.
``` python ``` python
# Default imports
import requests import requests
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@ -73,10 +74,10 @@ class AwesomeStrategy(IStrategy):
:param current_time: datetime object, containing the current datetime :param current_time: datetime object, containing the current datetime
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :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'): if self.config["runmode"].value in ("live", "dry_run"):
# Assign this to the class by using self.* # Assign this to the class by using self.*
# can then be used by populate_* methods # can then be used by populate_* methods
self.remote_data = requests.get('https://some_remote_source.example.com') self.remote_data = requests.get("https://some_remote_source.example.com")
``` ```
@ -85,6 +86,8 @@ class AwesomeStrategy(IStrategy):
Called before entering a trade, makes it possible to manage your position size when placing a new trade. Called before entering a trade, makes it possible to manage your position size when placing a new trade.
```python ```python
# Default imports
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: Optional[float], max_stake: float, proposed_stake: float, min_stake: Optional[float], max_stake: float,
@ -94,13 +97,13 @@ class AwesomeStrategy(IStrategy):
dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
current_candle = dataframe.iloc[-1].squeeze() current_candle = dataframe.iloc[-1].squeeze()
if current_candle['fastk_rsi_1h'] > current_candle['fastd_rsi_1h']: if current_candle["fastk_rsi_1h"] > current_candle["fastd_rsi_1h"]:
if self.config['stake_amount'] == 'unlimited': if self.config["stake_amount"] == "unlimited":
# Use entire available wallet during favorable conditions when in compounding mode. # Use entire available wallet during favorable conditions when in compounding mode.
return max_stake return max_stake
else: else:
# Compound profits during favorable conditions instead of using a static stake. # Compound profits during favorable conditions instead of using a static stake.
return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] return self.wallets.get_total_stake_amount() / self.config["max_open_trades"]
# Use default stake amount. # Use default stake amount.
return proposed_stake return proposed_stake
@ -131,25 +134,27 @@ Using `custom_exit()` signals in place of stoploss though *is not recommended*.
An example of how we can use different indicators depending on the current profit and also exit trades that were open longer than one day: An example of how we can use different indicators depending on the current profit and also exit trades that were open longer than one day:
``` python ``` python
# Default imports
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
current_profit: float, **kwargs): current_profit: float, **kwargs):
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze() last_candle = dataframe.iloc[-1].squeeze()
# Above 20% profit, sell when rsi < 80 # Above 20% profit, sell when rsi < 80
if current_profit > 0.2: if current_profit > 0.2:
if last_candle['rsi'] < 80: if last_candle["rsi"] < 80:
return 'rsi_below_80' return "rsi_below_80"
# Between 2% and 10%, sell if EMA-long above EMA-short # Between 2% and 10%, sell if EMA-long above EMA-short
if 0.02 < current_profit < 0.1: if 0.02 < current_profit < 0.1:
if last_candle['emalong'] > last_candle['emashort']: if last_candle["emalong"] > last_candle["emashort"]:
return 'ema_long_below_80' return "ema_long_below_80"
# Sell any positions at a loss if they are held for more than one day. # Sell any positions at a loss if they are held for more than one day.
if current_profit < 0.0 and (current_time - trade.open_date_utc).days >= 1: if current_profit < 0.0 and (current_time - trade.open_date_utc).days >= 1:
return 'unclog' return "unclog"
``` ```
See [Dataframe access](strategy-advanced.md#dataframe-access) for more information about dataframe use in strategy callbacks. See [Dataframe access](strategy-advanced.md#dataframe-access) for more information about dataframe use in strategy callbacks.
@ -170,7 +175,6 @@ The absolute value of the return value is used (the sign is ignored), so returni
Returning `None` will be interpreted as "no desire to change", and is the only safe way to return when you'd like to not modify the stoploss. Returning `None` will be interpreted as "no desire to change", and is the only safe way to return when you'd like to not modify the stoploss.
`NaN` and `inf` values are considered invalid and will be ignored (identical to `None`). `NaN` and `inf` values are considered invalid and will be ignored (identical to `None`).
Stoploss on exchange works similar to `trailing_stop`, and the stoploss on exchange is updated as configured in `stoploss_on_exchange_interval` ([More details about stoploss on exchange](stoploss.md#stop-loss-on-exchangefreqtrade)). Stoploss on exchange works similar to `trailing_stop`, and the stoploss on exchange is updated as configured in `stoploss_on_exchange_interval` ([More details about stoploss on exchange](stoploss.md#stop-loss-on-exchangefreqtrade)).
!!! Note "Use of dates" !!! Note "Use of dates"
@ -303,9 +307,9 @@ class AwesomeStrategy(IStrategy):
current_rate: float, current_profit: float, after_fill: bool, current_rate: float, current_profit: float, after_fill: bool,
**kwargs) -> Optional[float]: **kwargs) -> Optional[float]:
if pair in ('ETH/BTC', 'XRP/BTC'): if pair in ("ETH/BTC", "XRP/BTC"):
return -0.10 return -0.10
elif pair in ('LTC/BTC'): elif pair in ("LTC/BTC"):
return -0.05 return -0.05
return -0.15 return -0.15
``` ```
@ -384,7 +388,7 @@ class AwesomeStrategy(IStrategy):
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# <...> # <...>
dataframe['sar'] = ta.SAR(dataframe) dataframe["sar"] = ta.SAR(dataframe)
use_custom_stoploss = True use_custom_stoploss = True
@ -396,7 +400,7 @@ class AwesomeStrategy(IStrategy):
last_candle = dataframe.iloc[-1].squeeze() last_candle = dataframe.iloc[-1].squeeze()
# Use parabolic sar as absolute stoploss price # Use parabolic sar as absolute stoploss price
stoploss_price = last_candle['sar'] stoploss_price = last_candle["sar"]
# Convert absolute price to percentage relative to current_rate # Convert absolute price to percentage relative to current_rate
if stoploss_price < current_rate: if stoploss_price < current_rate:
@ -462,7 +466,7 @@ The helper function `stoploss_from_absolute()` can be used to convert from an ab
??? Example "Returning a stoploss using absolute price from the custom stoploss function" ??? Example "Returning a stoploss using absolute price from the custom stoploss function"
If we want to trail a stop price at 2xATR below current price we can call `stoploss_from_absolute(current_rate + (side * candle['atr'] * 2), current_rate=current_rate, is_short=trade.is_short, leverage=trade.leverage)`. If we want to trail a stop price at 2xATR below current price we can call `stoploss_from_absolute(current_rate + (side * candle["atr"] * 2), current_rate=current_rate, is_short=trade.is_short, leverage=trade.leverage)`.
For futures, we need to adjust the direction (up or down), as well as adjust for leverage, since the [`custom_stoploss`](strategy-callbacks.md#custom-stoploss) callback returns the ["risk for this trade"](stoploss.md#stoploss-and-leverage) - not the relative price movement. For futures, we need to adjust the direction (up or down), as well as adjust for leverage, since the [`custom_stoploss`](strategy-callbacks.md#custom-stoploss) callback returns the ["risk for this trade"](stoploss.md#stoploss-and-leverage) - not the relative price movement.
``` python ``` python
@ -472,8 +476,8 @@ The helper function `stoploss_from_absolute()` can be used to convert from an ab
use_custom_stoploss = True use_custom_stoploss = True
def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['atr'] = ta.ATR(dataframe, timeperiod=14) dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)
return dataframe return dataframe
def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
@ -483,7 +487,7 @@ The helper function `stoploss_from_absolute()` can be used to convert from an ab
trade_date = timeframe_to_prev_date(self.timeframe, trade.open_date_utc) trade_date = timeframe_to_prev_date(self.timeframe, trade.open_date_utc)
candle = dataframe.iloc[-1].squeeze() candle = dataframe.iloc[-1].squeeze()
side = 1 if trade.is_short else -1 side = 1 if trade.is_short else -1
return stoploss_from_absolute(current_rate + (side * candle['atr'] * 2), return stoploss_from_absolute(current_rate + (side * candle["atr"] * 2),
current_rate=current_rate, current_rate=current_rate,
is_short=trade.is_short, is_short=trade.is_short,
leverage=trade.leverage) leverage=trade.leverage)
@ -575,8 +579,8 @@ class AwesomeStrategy(IStrategy):
# Set unfilledtimeout to 25 hours, since the maximum timeout from below is 24 hours. # Set unfilledtimeout to 25 hours, since the maximum timeout from below is 24 hours.
unfilledtimeout = { unfilledtimeout = {
'entry': 60 * 25, "entry": 60 * 25,
'exit': 60 * 25 "exit": 60 * 25
} }
def check_entry_timeout(self, pair: str, trade: Trade, order: Order, def check_entry_timeout(self, pair: str, trade: Trade, order: Order,
@ -677,7 +681,7 @@ class AwesomeStrategy(IStrategy):
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
:param current_time: datetime object, containing the current datetime :param current_time: datetime object, containing the current datetime
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:param side: 'long' or 'short' - indicating the direction of the proposed trade :param side: "long" or "short" - indicating the direction of the proposed trade
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :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. :return bool: When True is returned, then the buy-order is placed on the exchange.
False aborts the process False aborts the process
@ -725,8 +729,8 @@ class AwesomeStrategy(IStrategy):
or current rate for market orders. or current rate for market orders.
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
:param exit_reason: Exit reason. :param exit_reason: Exit reason.
Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss', Can be any of ["roi", "stop_loss", "stoploss_on_exchange", "trailing_stop_loss",
'exit_signal', 'force_exit', 'emergency_exit'] "exit_signal", "force_exit", "emergency_exit"]
:param current_time: datetime object, containing the current datetime :param current_time: datetime object, containing the current datetime
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return bool: When True, then the exit-order is placed on the exchange. :return bool: When True, then the exit-order is placed on the exchange.
@ -758,7 +762,7 @@ This callback is **not** called when there is an open order (either buy or sell)
`adjust_trade_position()` is called very frequently for the duration of a trade, so you must keep your implementation as performant as possible. `adjust_trade_position()` is called very frequently for the duration of a trade, so you must keep your implementation as performant as possible.
Position adjustments will always be applied in the direction of the trade, so a positive value will always increase your position (negative values will decrease your position), no matter if it's a long or short trade. Position adjustments will always be applied in the direction of the trade, so a positive value will always increase your position (negative values will decrease your position), no matter if it's a long or short trade.
Adjustment orders can be assigned with a tag by returning a 2 element Tuple, with the first element being the adjustment amount, and the 2nd element the tag (e.g. `return 250, 'increase_favorable_conditions'`). Adjustment orders can be assigned with a tag by returning a 2 element Tuple, with the first element being the adjustment amount, and the 2nd element the tag (e.g. `return 250, "increase_favorable_conditions"`).
Modifications to leverage are not possible, and the stake-amount returned is assumed to be before applying leverage. Modifications to leverage are not possible, and the stake-amount returned is assumed to be before applying leverage.
@ -780,7 +784,7 @@ Returning a value more than the above (so remaining stake_amount would become ne
!!! Note "About stake size" !!! 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 fixed stake size means it will be the amount used for the first order, just like without position adjustment.
If you wish to buy additional orders with DCA, then make sure to leave enough funds in the wallet for that. If you wish to buy additional orders with DCA, then make sure to leave enough funds in the wallet for that.
Using 'unlimited' stake amount with DCA orders requires you to also implement the `custom_stake_amount()` callback to avoid allocating all funds to the initial order. Using `"unlimited"` stake amount with DCA orders requires you to also implement the `custom_stake_amount()` callback to avoid allocating all funds to the initial order.
!!! Warning "Stoploss calculation" !!! Warning "Stoploss calculation"
Stoploss is still calculated from the initial opening price, not averaged price. Stoploss is still calculated from the initial opening price, not averaged price.
@ -799,9 +803,6 @@ Returning a value more than the above (so remaining stake_amount would become ne
``` python ``` python
# Default imports # Default imports
from freqtrade.persistence import Trade
from typing import Optional, Tuple, Union
class DigDeeperStrategy(IStrategy): class DigDeeperStrategy(IStrategy):
@ -864,7 +865,7 @@ class DigDeeperStrategy(IStrategy):
if current_profit > 0.05 and trade.nr_of_successful_exits == 0: if current_profit > 0.05 and trade.nr_of_successful_exits == 0:
# Take half of the profit at +5% # Take half of the profit at +5%
return -(trade.stake_amount / 2), 'half_profit_5%' return -(trade.stake_amount / 2), "half_profit_5%"
if current_profit > -0.05: if current_profit > -0.05:
return None return None
@ -874,7 +875,7 @@ class DigDeeperStrategy(IStrategy):
# Only buy when not actively falling price. # Only buy when not actively falling price.
last_candle = dataframe.iloc[-1].squeeze() last_candle = dataframe.iloc[-1].squeeze()
previous_candle = dataframe.iloc[-2].squeeze() previous_candle = dataframe.iloc[-2].squeeze()
if last_candle['close'] < previous_candle['close']: if last_candle["close"] < previous_candle["close"]:
return None return None
filled_entries = trade.select_filled_orders(trade.entry_side) filled_entries = trade.select_filled_orders(trade.entry_side)
@ -892,7 +893,7 @@ class DigDeeperStrategy(IStrategy):
stake_amount = filled_entries[0].stake_amount stake_amount = filled_entries[0].stake_amount
# This then calculates current safety order size # This then calculates current safety order size
stake_amount = stake_amount * (1 + (count_of_entries * 0.25)) stake_amount = stake_amount * (1 + (count_of_entries * 0.25))
return stake_amount, '1/3rd_increase' return stake_amount, "1/3rd_increase"
except Exception as exception: except Exception as exception:
return None return None
@ -964,7 +965,7 @@ class AwesomeStrategy(IStrategy):
:param proposed_rate: Rate, calculated based on pricing settings in entry_pricing. :param proposed_rate: Rate, calculated based on pricing settings in entry_pricing.
:param current_order_rate: Rate of the existing order in place. :param current_order_rate: Rate of the existing order in place.
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:param side: 'long' or 'short' - indicating the direction of the proposed trade :param side: "long" or "short" - indicating the direction of the proposed trade
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return float: New entry price value if provided :return float: New entry price value if provided
@ -1013,7 +1014,7 @@ class AwesomeStrategy(IStrategy):
:param proposed_leverage: A leverage proposed by the bot. :param proposed_leverage: A leverage proposed by the bot.
:param max_leverage: Max leverage allowed on this pair :param max_leverage: Max leverage allowed on this pair
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:param side: 'long' or 'short' - indicating the direction of the proposed trade :param side: "long" or "short" - indicating the direction of the proposed trade
:return: A leverage amount, which is between 1.0 and max_leverage. :return: A leverage amount, which is between 1.0 and max_leverage.
""" """
return 1.0 return 1.0
@ -1048,7 +1049,7 @@ class AwesomeStrategy(IStrategy):
last_candle = dataframe.iloc[-1].squeeze() last_candle = dataframe.iloc[-1].squeeze()
if (trade.nr_of_successful_entries == 1) and (order.ft_order_side == trade.entry_side): if (trade.nr_of_successful_entries == 1) and (order.ft_order_side == trade.entry_side):
trade.set_custom_data(key='entry_candle_high', value=last_candle['high']) trade.set_custom_data(key="entry_candle_high", value=last_candle["high"])
return None return None