Merge remote-tracking branch 'origin/strategy_utils' into strategy_utils
This commit is contained in:
commit
f722823b0d
@ -84,6 +84,7 @@ Mandatory parameters are marked as **Required** and have to be set in one of the
|
||||
| `add_state_info` | Tell FreqAI to include state information in the feature set for training and inferencing. The current state variables include trade duration, current profit, trade position. This is only available in dry/live runs, and is automatically switched to false for backtesting. <br> **Datatype:** bool. <br> Default: `False`.
|
||||
| `net_arch` | Network architecture which is well described in [`stable_baselines3` doc](https://stable-baselines3.readthedocs.io/en/master/guide/custom_policy.html#examples). In summary: `[<shared layers>, dict(vf=[<non-shared value network layers>], pi=[<non-shared policy network layers>])]`. By default this is set to `[128, 128]`, which defines 2 shared hidden layers with 128 units each.
|
||||
| `randomize_starting_position` | Randomize the starting point of each episode to avoid overfitting. <br> **Datatype:** bool. <br> Default: `False`.
|
||||
| `drop_ohlc_from_features` | Do not include the normalized ohlc data in the feature set passed to the agent during training (ohlc will still be used for driving the environment in all cases) <br> **Datatype:** Boolean. <br> **Default:** `False`
|
||||
|
||||
### Additional parameters
|
||||
|
||||
|
@ -176,9 +176,11 @@ As you begin to modify the strategy and the prediction model, you will quickly r
|
||||
|
||||
factor = 100
|
||||
|
||||
pair = self.pair.replace(':', '')
|
||||
|
||||
# you can use feature values from dataframe
|
||||
# Assumes the shifted RSI indicator has been generated in the strategy.
|
||||
rsi_now = self.raw_features[f"%-rsi-period-10_shift-1_{self.pair}_"
|
||||
rsi_now = self.raw_features[f"%-rsi-period-10_shift-1_{pair}_"
|
||||
f"{self.config['timeframe']}"].iloc[self._current_tick]
|
||||
|
||||
# reward agent for entering trades
|
||||
|
@ -316,11 +316,11 @@ class AwesomeStrategy(IStrategy):
|
||||
|
||||
# evaluate highest to lowest, so that highest possible stop is used
|
||||
if current_profit > 0.40:
|
||||
return stoploss_from_open(0.25, current_profit, is_short=trade.is_short)
|
||||
return stoploss_from_open(0.25, current_profit, is_short=trade.is_short, leverage=trade.leverage)
|
||||
elif current_profit > 0.25:
|
||||
return stoploss_from_open(0.15, current_profit, is_short=trade.is_short)
|
||||
return stoploss_from_open(0.15, current_profit, is_short=trade.is_short, leverage=trade.leverage)
|
||||
elif current_profit > 0.20:
|
||||
return stoploss_from_open(0.07, current_profit, is_short=trade.is_short)
|
||||
return stoploss_from_open(0.07, current_profit, is_short=trade.is_short, leverage=trade.leverage)
|
||||
|
||||
# return maximum stoploss value, keeping current stoploss price unchanged
|
||||
return 1
|
||||
|
@ -881,7 +881,7 @@ All columns of the informative dataframe will be available on the returning data
|
||||
|
||||
### *stoploss_from_open()*
|
||||
|
||||
Stoploss values returned from `custom_stoploss` must specify a percentage relative to `current_rate`, but sometimes you may want to specify a stoploss relative to the open price instead. `stoploss_from_open()` is a helper function to calculate a stoploss value that can be returned from `custom_stoploss` which will be equivalent to the desired percentage above the open price.
|
||||
Stoploss values returned from `custom_stoploss` must specify a percentage relative to `current_rate`, but sometimes you may want to specify a stoploss relative to the entry point instead. `stoploss_from_open()` is a helper function to calculate a stoploss value that can be returned from `custom_stoploss` which will be equivalent to the desired trade profit above the entry point.
|
||||
|
||||
??? Example "Returning a stoploss relative to the open price from the custom stoploss function"
|
||||
|
||||
@ -889,6 +889,8 @@ Stoploss values returned from `custom_stoploss` must specify a percentage relati
|
||||
|
||||
If we want a stop price at 7% above the open price we can call `stoploss_from_open(0.07, current_profit, False)` which will return `0.1157024793`. 11.57% below $121 is $107, which is the same as 7% above $100.
|
||||
|
||||
This function will consider leverage - so at 10x leverage, the actual stoploss would be 0.7% above $100 (0.7% * 10x = 7%).
|
||||
|
||||
|
||||
``` python
|
||||
|
||||
@ -907,7 +909,7 @@ Stoploss values returned from `custom_stoploss` must specify a percentage relati
|
||||
|
||||
# once the profit has risen above 10%, keep the stoploss at 7% above the open price
|
||||
if current_profit > 0.10:
|
||||
return stoploss_from_open(0.07, current_profit, is_short=trade.is_short)
|
||||
return stoploss_from_open(0.07, current_profit, is_short=trade.is_short, leverage=trade.leverage)
|
||||
|
||||
return 1
|
||||
|
||||
|
@ -588,6 +588,7 @@ CONF_SCHEMA = {
|
||||
"rl_config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"drop_ohlc_from_features": {"type": "boolean", "default": False},
|
||||
"train_cycles": {"type": "integer"},
|
||||
"max_trade_duration_candles": {"type": "integer"},
|
||||
"add_state_info": {"type": "boolean", "default": False},
|
||||
|
@ -69,6 +69,7 @@ class Exchange:
|
||||
# Check https://github.com/ccxt/ccxt/issues/10767 for removal of ohlcv_volume_currency
|
||||
"ohlcv_volume_currency": "base", # "base" or "quote"
|
||||
"tickers_have_quoteVolume": True,
|
||||
"tickers_have_bid_ask": True, # bid / ask empty for fetch_tickers
|
||||
"tickers_have_price": True,
|
||||
"trades_pagination": "time", # Possible are "time" or "id"
|
||||
"trades_pagination_arg": "since",
|
||||
|
@ -32,6 +32,7 @@ class Gate(Exchange):
|
||||
|
||||
_ft_has_futures: Dict = {
|
||||
"needs_trading_fees": True,
|
||||
"tickers_have_bid_ask": False,
|
||||
"fee_cost_in_contracts": False, # Set explicitly to false for clarity
|
||||
"order_props_in_contracts": ['amount', 'filled', 'remaining'],
|
||||
"stop_price_type_field": "price_type",
|
||||
|
@ -114,6 +114,7 @@ class BaseReinforcementLearningModel(IFreqaiModel):
|
||||
|
||||
# normalize all data based on train_dataset only
|
||||
prices_train, prices_test = self.build_ohlc_price_dataframes(dk.data_dictionary, pair, dk)
|
||||
|
||||
data_dictionary = dk.normalize_data(data_dictionary)
|
||||
|
||||
# data cleaning/analysis
|
||||
@ -148,12 +149,8 @@ class BaseReinforcementLearningModel(IFreqaiModel):
|
||||
|
||||
env_info = self.pack_env_dict(dk.pair)
|
||||
|
||||
self.train_env = self.MyRLEnv(df=train_df,
|
||||
prices=prices_train,
|
||||
**env_info)
|
||||
self.eval_env = Monitor(self.MyRLEnv(df=test_df,
|
||||
prices=prices_test,
|
||||
**env_info))
|
||||
self.train_env = self.MyRLEnv(df=train_df, prices=prices_train, **env_info)
|
||||
self.eval_env = Monitor(self.MyRLEnv(df=test_df, prices=prices_test, **env_info))
|
||||
self.eval_callback = EvalCallback(self.eval_env, deterministic=True,
|
||||
render=False, eval_freq=len(train_df),
|
||||
best_model_save_path=str(dk.data_path))
|
||||
@ -238,6 +235,9 @@ class BaseReinforcementLearningModel(IFreqaiModel):
|
||||
filtered_dataframe, _ = dk.filter_features(
|
||||
unfiltered_df, dk.training_features_list, training_filter=False
|
||||
)
|
||||
|
||||
filtered_dataframe = self.drop_ohlc_from_df(filtered_dataframe, dk)
|
||||
|
||||
filtered_dataframe = dk.normalize_data_from_metadata(filtered_dataframe)
|
||||
dk.data_dictionary["prediction_features"] = filtered_dataframe
|
||||
|
||||
@ -285,7 +285,6 @@ class BaseReinforcementLearningModel(IFreqaiModel):
|
||||
train_df = data_dictionary["train_features"]
|
||||
test_df = data_dictionary["test_features"]
|
||||
|
||||
# %-raw_volume_gen_shift-2_ETH/USDT_1h
|
||||
# price data for model training and evaluation
|
||||
tf = self.config['timeframe']
|
||||
rename_dict = {'%-raw_open': 'open', '%-raw_low': 'low',
|
||||
@ -318,8 +317,24 @@ class BaseReinforcementLearningModel(IFreqaiModel):
|
||||
prices_test.rename(columns=rename_dict, inplace=True)
|
||||
prices_test.reset_index(drop=True)
|
||||
|
||||
train_df = self.drop_ohlc_from_df(train_df, dk)
|
||||
test_df = self.drop_ohlc_from_df(test_df, dk)
|
||||
|
||||
return prices_train, prices_test
|
||||
|
||||
def drop_ohlc_from_df(self, df: DataFrame, dk: FreqaiDataKitchen):
|
||||
"""
|
||||
Given a dataframe, drop the ohlc data
|
||||
"""
|
||||
drop_list = ['%-raw_open', '%-raw_low', '%-raw_high', '%-raw_close']
|
||||
|
||||
if self.rl_config["drop_ohlc_from_features"]:
|
||||
df.drop(drop_list, axis=1, inplace=True)
|
||||
feature_list = dk.training_features_list
|
||||
dk.training_features_list = [e for e in feature_list if e not in drop_list]
|
||||
|
||||
return df
|
||||
|
||||
def load_model_from_disk(self, dk: FreqaiDataKitchen) -> Any:
|
||||
"""
|
||||
Can be used by user if they are trying to limit_ram_usage *and*
|
||||
|
@ -5,6 +5,7 @@ import logging
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.exchange.types import Ticker
|
||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||
|
||||
@ -22,6 +23,12 @@ class SpreadFilter(IPairList):
|
||||
self._max_spread_ratio = pairlistconfig.get('max_spread_ratio', 0.005)
|
||||
self._enabled = self._max_spread_ratio != 0
|
||||
|
||||
if not self._exchange.get_option('tickers_have_bid_ask'):
|
||||
raise OperationalException(
|
||||
f"{self.name} requires exchange to have bid/ask data for tickers, "
|
||||
"which is not available for the selected exchange / trading mode."
|
||||
)
|
||||
|
||||
@property
|
||||
def needstickers(self) -> bool:
|
||||
"""
|
||||
|
@ -86,37 +86,41 @@ def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame,
|
||||
def stoploss_from_open(
|
||||
open_relative_stop: float,
|
||||
current_profit: float,
|
||||
is_short: bool = False
|
||||
is_short: bool = False,
|
||||
leverage: float = 1.0
|
||||
) -> float:
|
||||
"""
|
||||
|
||||
Given the current profit, and a desired stop loss value relative to the open price,
|
||||
Given the current profit, and a desired stop loss value relative to the trade entry price,
|
||||
return a stop loss value that is relative to the current price, and which can be
|
||||
returned from `custom_stoploss`.
|
||||
|
||||
The requested stop can be positive for a stop above the open price, or negative for
|
||||
a stop below the open price. The return value is always >= 0.
|
||||
`open_relative_stop` will be considered as adjusted for leverage if leverage is provided..
|
||||
|
||||
Returns 0 if the resulting stop price would be above/below (longs/shorts) the current price
|
||||
|
||||
:param open_relative_stop: Desired stop loss percentage relative to open price
|
||||
:param open_relative_stop: Desired stop loss percentage, relative to the open price,
|
||||
adjusted for leverage
|
||||
:param current_profit: The current profit percentage
|
||||
:param is_short: When true, perform the calculation for short instead of long
|
||||
:param leverage: Leverage to use for the calculation
|
||||
:return: Stop loss value relative to current price
|
||||
"""
|
||||
|
||||
# formula is undefined for current_profit -1 (longs) or 1 (shorts), return maximum value
|
||||
if (current_profit == -1 and not is_short) or (is_short and current_profit == 1):
|
||||
_current_profit = current_profit / leverage
|
||||
if (_current_profit == -1 and not is_short) or (is_short and _current_profit == 1):
|
||||
return 1
|
||||
|
||||
if is_short is True:
|
||||
stoploss = -1 + ((1 - open_relative_stop) / (1 - current_profit))
|
||||
stoploss = -1 + ((1 - open_relative_stop / leverage) / (1 - _current_profit))
|
||||
else:
|
||||
stoploss = 1 - ((1 + open_relative_stop) / (1 + current_profit))
|
||||
stoploss = 1 - ((1 + open_relative_stop / leverage) / (1 + _current_profit))
|
||||
|
||||
# negative stoploss values indicate the requested stop price is higher/lower
|
||||
# (long/short) than the current price
|
||||
return max(stoploss, 0.0)
|
||||
return max(stoploss * leverage, 0.0)
|
||||
|
||||
|
||||
def stoploss_from_absolute(stop_rate: float, current_rate: float, is_short: bool = False) -> float:
|
||||
|
@ -78,7 +78,9 @@ def make_rl_config(conf):
|
||||
"rr": 1,
|
||||
"profit_aim": 0.02,
|
||||
"win_reward_factor": 2
|
||||
}}
|
||||
},
|
||||
"drop_ohlc_from_features": False
|
||||
}
|
||||
|
||||
return conf
|
||||
|
||||
|
@ -68,13 +68,6 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca,
|
||||
freqai_conf['freqai']['feature_parameters'].update({"shuffle_after_split": shuffle})
|
||||
freqai_conf['freqai']['feature_parameters'].update({"buffer_train_data_candles": buffer})
|
||||
|
||||
if 'ReinforcementLearner' in model:
|
||||
model_save_ext = 'zip'
|
||||
freqai_conf = make_rl_config(freqai_conf)
|
||||
# test the RL guardrails
|
||||
freqai_conf['freqai']['feature_parameters'].update({"use_SVM_to_remove_outliers": True})
|
||||
freqai_conf['freqai']['data_split_parameters'].update({'shuffle': True})
|
||||
|
||||
if 'ReinforcementLearner' in model:
|
||||
model_save_ext = 'zip'
|
||||
freqai_conf = make_rl_config(freqai_conf)
|
||||
@ -84,6 +77,7 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca,
|
||||
|
||||
if 'test_3ac' in model or 'test_4ac' in model:
|
||||
freqai_conf["freqaimodel_path"] = str(Path(__file__).parents[1] / "freqai" / "test_models")
|
||||
freqai_conf["freqai"]["rl_config"]["drop_ohlc_from_features"] = True
|
||||
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
|
@ -924,7 +924,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data: BTContainer)
|
||||
mocker.patch(f"{EXMS}.get_fee", return_value=0.0)
|
||||
mocker.patch(f"{EXMS}.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch(f"{EXMS}.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.get_max_leverage', return_value=100)
|
||||
mocker.patch(f"{EXMS}.get_max_leverage", return_value=100)
|
||||
patch_exchange(mocker)
|
||||
frame = _build_backtest_dataframe(data.data)
|
||||
backtesting = Backtesting(default_conf)
|
||||
|
@ -828,6 +828,12 @@ def test_pair_whitelist_not_supported_Spread(mocker, default_conf, tickers) -> N
|
||||
match=r'Exchange does not support fetchTickers, .*'):
|
||||
get_patched_freqtradebot(mocker, default_conf)
|
||||
|
||||
mocker.patch(f'{EXMS}.exchange_has', MagicMock(return_value=True))
|
||||
mocker.patch(f'{EXMS}.get_option', MagicMock(return_value=False))
|
||||
with pytest.raises(OperationalException,
|
||||
match=r'.*requires exchange to have bid/ask data'):
|
||||
get_patched_freqtradebot(mocker, default_conf)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pairlist", TESTABLE_PAIRLISTS)
|
||||
def test_pairlist_class(mocker, whitelist_conf, markets, pairlist):
|
||||
|
@ -177,26 +177,30 @@ def test_stoploss_from_open(side, profitrange):
|
||||
("long", 0.1, 0.2, 1, 0.08333333),
|
||||
("long", 0.1, 0.5, 1, 0.266666666),
|
||||
("long", 0.1, 5, 1, 0.816666666), # 500% profit, set stoploss to 10% above open price
|
||||
("long", 0, 5, 10, 3.3333333), # 500% profit, set stoploss break even
|
||||
("long", 0.1, 5, 10, 3.26666666), # 500% profit, set stoploss to 10% above open price
|
||||
("long", -0.1, 5, 10, 3.3999999), # 500% profit, set stoploss to 10% belowopen price
|
||||
|
||||
("short", 0, 0.1, 1, 0.1111111),
|
||||
("short", -0.1, 0.1, 1, 0.2222222),
|
||||
("short", 0.1, 0.2, 1, 0.125),
|
||||
("short", 0.1, 1, 1, 1),
|
||||
("short", -0.01, 5, 10, 10.01999999), # 500% profit at 10x
|
||||
])
|
||||
def test_stoploss_from_open_leverage(side, rel_stop, curr_profit, leverage, expected):
|
||||
|
||||
stoploss = stoploss_from_open(rel_stop, curr_profit, side == 'short')
|
||||
stoploss = stoploss_from_open(rel_stop, curr_profit, side == 'short', leverage)
|
||||
assert pytest.approx(stoploss) == expected
|
||||
open_rate = 100
|
||||
if stoploss != 1:
|
||||
if side == 'long':
|
||||
current_rate = open_rate * (1 + curr_profit)
|
||||
stop = current_rate * (1 - stoploss)
|
||||
assert pytest.approx(stop) == open_rate * (1 + rel_stop)
|
||||
current_rate = open_rate * (1 + curr_profit / leverage)
|
||||
stop = current_rate * (1 - stoploss / leverage)
|
||||
assert pytest.approx(stop) == open_rate * (1 + rel_stop / leverage)
|
||||
else:
|
||||
current_rate = open_rate * (1 - curr_profit)
|
||||
stop = current_rate * (1 + stoploss)
|
||||
assert pytest.approx(stop) == open_rate * (1 - rel_stop)
|
||||
current_rate = open_rate * (1 - curr_profit / leverage)
|
||||
stop = current_rate * (1 + stoploss / leverage)
|
||||
assert pytest.approx(stop) == open_rate * (1 - rel_stop / leverage)
|
||||
|
||||
|
||||
def test_stoploss_from_absolute():
|
||||
|
@ -1068,7 +1068,7 @@ def test_add_stoploss_on_exchange(mocker, default_conf_usdt, limit_order, is_sho
|
||||
mocker.patch(f'{EXMS}.get_trades_for_order', return_value=[])
|
||||
|
||||
stoploss = MagicMock(return_value={'id': 13434334})
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.create_stoploss', stoploss)
|
||||
mocker.patch(f'{EXMS}.create_stoploss', stoploss)
|
||||
|
||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||
@ -1263,7 +1263,7 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf_usdt, fee, caplog,
|
||||
get_fee=fee,
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.binance.Binance',
|
||||
EXMS,
|
||||
fetch_stoploss_order=MagicMock(return_value={'status': 'canceled', 'id': 100}),
|
||||
create_stoploss=MagicMock(side_effect=ExchangeError()),
|
||||
)
|
||||
@ -1307,7 +1307,7 @@ def test_create_stoploss_order_invalid_order(
|
||||
get_fee=fee,
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.binance.Binance',
|
||||
EXMS,
|
||||
fetch_order=MagicMock(return_value={'status': 'canceled'}),
|
||||
create_stoploss=MagicMock(side_effect=InvalidOrderException()),
|
||||
)
|
||||
@ -1360,7 +1360,7 @@ def test_create_stoploss_order_insufficient_funds(
|
||||
fetch_order=MagicMock(return_value={'status': 'canceled'}),
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.binance.Binance',
|
||||
EXMS,
|
||||
create_stoploss=MagicMock(side_effect=InsufficientFundsError()),
|
||||
)
|
||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
||||
@ -1410,7 +1410,7 @@ def test_handle_stoploss_on_exchange_trailing(
|
||||
get_fee=fee,
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.binance.Binance',
|
||||
EXMS,
|
||||
create_stoploss=stoploss,
|
||||
stoploss_adjust=MagicMock(return_value=True),
|
||||
)
|
||||
@ -1453,7 +1453,7 @@ def test_handle_stoploss_on_exchange_trailing(
|
||||
}
|
||||
})
|
||||
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.fetch_stoploss_order', stoploss_order_hanging)
|
||||
mocker.patch(f'{EXMS}.fetch_stoploss_order', stoploss_order_hanging)
|
||||
|
||||
# stoploss initially at 5%
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
@ -1471,8 +1471,8 @@ def test_handle_stoploss_on_exchange_trailing(
|
||||
|
||||
cancel_order_mock = MagicMock()
|
||||
stoploss_order_mock = MagicMock(return_value={'id': 'so1'})
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.cancel_stoploss_order', cancel_order_mock)
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.create_stoploss', stoploss_order_mock)
|
||||
mocker.patch(f'{EXMS}.cancel_stoploss_order', cancel_order_mock)
|
||||
mocker.patch(f'{EXMS}.create_stoploss', stoploss_order_mock)
|
||||
|
||||
# stoploss should not be updated as the interval is 60 seconds
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
@ -1535,7 +1535,7 @@ def test_handle_stoploss_on_exchange_trailing_error(
|
||||
get_fee=fee,
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.binance.Binance',
|
||||
EXMS,
|
||||
create_stoploss=stoploss,
|
||||
stoploss_adjust=MagicMock(return_value=True),
|
||||
)
|
||||
@ -1573,9 +1573,9 @@ def test_handle_stoploss_on_exchange_trailing_error(
|
||||
'stopPrice': '0.1'
|
||||
}
|
||||
}
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.cancel_stoploss_order',
|
||||
mocker.patch(f'{EXMS}.cancel_stoploss_order',
|
||||
side_effect=InvalidOrderException())
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.fetch_stoploss_order',
|
||||
mocker.patch(f'{EXMS}.fetch_stoploss_order',
|
||||
return_value=stoploss_order_hanging)
|
||||
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
|
||||
assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/USDT.*", caplog)
|
||||
@ -1586,8 +1586,8 @@ def test_handle_stoploss_on_exchange_trailing_error(
|
||||
# Fail creating stoploss order
|
||||
trade.stoploss_last_update = arrow.utcnow().shift(minutes=-601).datetime
|
||||
caplog.clear()
|
||||
cancel_mock = mocker.patch('freqtrade.exchange.binance.Binance.cancel_stoploss_order')
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.create_stoploss', side_effect=ExchangeError())
|
||||
cancel_mock = mocker.patch(f'{EXMS}.cancel_stoploss_order')
|
||||
mocker.patch(f'{EXMS}.create_stoploss', side_effect=ExchangeError())
|
||||
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
|
||||
assert cancel_mock.call_count == 1
|
||||
assert log_has_re(r"Could not create trailing stoploss order for pair ETH/USDT\..*", caplog)
|
||||
@ -1604,7 +1604,7 @@ def test_stoploss_on_exchange_price_rounding(
|
||||
stoploss_mock = MagicMock(return_value={'id': '13434334'})
|
||||
adjust_mock = MagicMock(return_value=False)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.binance.Binance',
|
||||
EXMS,
|
||||
create_stoploss=stoploss_mock,
|
||||
stoploss_adjust=adjust_mock,
|
||||
price_to_precision=price_mock,
|
||||
@ -1643,7 +1643,7 @@ def test_handle_stoploss_on_exchange_custom_stop(
|
||||
get_fee=fee,
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.binance.Binance',
|
||||
EXMS,
|
||||
create_stoploss=stoploss,
|
||||
stoploss_adjust=MagicMock(return_value=True),
|
||||
)
|
||||
@ -1686,7 +1686,7 @@ def test_handle_stoploss_on_exchange_custom_stop(
|
||||
}
|
||||
})
|
||||
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.fetch_stoploss_order', stoploss_order_hanging)
|
||||
mocker.patch(f'{EXMS}.fetch_stoploss_order', stoploss_order_hanging)
|
||||
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||
@ -1703,8 +1703,8 @@ def test_handle_stoploss_on_exchange_custom_stop(
|
||||
|
||||
cancel_order_mock = MagicMock()
|
||||
stoploss_order_mock = MagicMock(return_value={'id': 'so1'})
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.cancel_stoploss_order', cancel_order_mock)
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.create_stoploss', stoploss_order_mock)
|
||||
mocker.patch(f'{EXMS}.cancel_stoploss_order', cancel_order_mock)
|
||||
mocker.patch(f'{EXMS}.create_stoploss', stoploss_order_mock)
|
||||
|
||||
# stoploss should not be updated as the interval is 60 seconds
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
@ -1821,7 +1821,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, limit_orde
|
||||
cancel_order_mock = MagicMock()
|
||||
stoploss_order_mock = MagicMock()
|
||||
mocker.patch(f'{EXMS}.cancel_stoploss_order', cancel_order_mock)
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.create_stoploss', stoploss_order_mock)
|
||||
mocker.patch(f'{EXMS}.create_stoploss', stoploss_order_mock)
|
||||
|
||||
# price goes down 5%
|
||||
mocker.patch(f'{EXMS}.fetch_ticker', MagicMock(return_value={
|
||||
@ -3660,7 +3660,7 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(
|
||||
}
|
||||
})
|
||||
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.create_stoploss', stoploss)
|
||||
mocker.patch(f'{EXMS}.create_stoploss', stoploss)
|
||||
|
||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||
|
@ -56,9 +56,9 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
||||
[ExitCheckTuple(exit_type=ExitType.EXIT_SIGNAL)]]
|
||||
)
|
||||
cancel_order_mock = MagicMock()
|
||||
mocker.patch('freqtrade.exchange.binance.Binance.create_stoploss', stoploss)
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
create_stoploss=stoploss,
|
||||
fetch_ticker=ticker,
|
||||
get_fee=fee,
|
||||
amount_to_precision=lambda s, x, y: y,
|
||||
|
Loading…
Reference in New Issue
Block a user