ruff format: Update a few test files

This commit is contained in:
Matthias 2024-05-12 15:29:14 +02:00
parent baa15f6ed6
commit 7090950db6
13 changed files with 1629 additions and 1283 deletions

View File

@ -32,13 +32,12 @@ def is_arm() -> bool:
@pytest.fixture(autouse=True)
def patch_torch_initlogs(mocker) -> None:
if is_mac():
# Mock torch import completely
import sys
import types
module_name = 'torch'
module_name = "torch"
mocked_module = types.ModuleType(module_name)
sys.modules[module_name] = mocked_module
else:
@ -80,25 +79,23 @@ def freqai_conf(default_conf, tmp_path):
"stratify_training_data": 0,
"indicator_periods_candles": [10],
"shuffle_after_split": False,
"buffer_train_data_candles": 0
"buffer_train_data_candles": 0,
},
"data_split_parameters": {"test_size": 0.33, "shuffle": False},
"model_training_parameters": {"n_estimators": 100},
},
"config_files": [Path('config_examples', 'config_freqai.example.json')]
"config_files": [Path("config_examples", "config_freqai.example.json")],
}
)
freqaiconf['exchange'].update({'pair_whitelist': ['ADA/BTC', 'DASH/BTC', 'ETH/BTC', 'LTC/BTC']})
freqaiconf["exchange"].update({"pair_whitelist": ["ADA/BTC", "DASH/BTC", "ETH/BTC", "LTC/BTC"]})
return freqaiconf
def make_rl_config(conf):
conf.update({"strategy": "freqai_rl_test_strat"})
conf["freqai"].update({"model_training_parameters": {
"learning_rate": 0.00025,
"gamma": 0.9,
"verbose": 1
}})
conf["freqai"].update(
{"model_training_parameters": {"learning_rate": 0.00025, "gamma": 0.9, "verbose": 1}}
)
conf["freqai"]["rl_config"] = {
"train_cycles": 1,
"thread_count": 2,
@ -107,31 +104,27 @@ def make_rl_config(conf):
"policy_type": "MlpPolicy",
"max_training_drawdown_pct": 0.5,
"net_arch": [32, 32],
"model_reward_parameters": {
"rr": 1,
"profit_aim": 0.02,
"win_reward_factor": 2
},
"drop_ohlc_from_features": False
}
"model_reward_parameters": {"rr": 1, "profit_aim": 0.02, "win_reward_factor": 2},
"drop_ohlc_from_features": False,
}
return conf
def mock_pytorch_mlp_model_training_parameters() -> Dict[str, Any]:
return {
"learning_rate": 3e-4,
"trainer_kwargs": {
"n_steps": None,
"batch_size": 64,
"n_epochs": 1,
},
"model_kwargs": {
"hidden_dim": 32,
"dropout_percent": 0.2,
"n_layer": 1,
}
}
"learning_rate": 3e-4,
"trainer_kwargs": {
"n_steps": None,
"batch_size": 64,
"n_epochs": 1,
},
"model_kwargs": {
"hidden_dim": 32,
"dropout_percent": 0.2,
"n_layer": 1,
},
}
def get_patched_data_kitchen(mocker, freqaiconf):
@ -178,14 +171,14 @@ def make_unfiltered_dataframe(mocker, freqai_conf):
new_timerange = TimeRange.parse_timerange("20180120-20180130")
corr_dataframes, base_dataframes = freqai.dd.get_base_and_corr_dataframes(
data_load_timerange, freqai.dk.pair, freqai.dk
)
data_load_timerange, freqai.dk.pair, freqai.dk
)
unfiltered_dataframe = freqai.dk.use_strategy_to_populate_indicators(
strategy, corr_dataframes, base_dataframes, freqai.dk.pair
)
strategy, corr_dataframes, base_dataframes, freqai.dk.pair
)
for i in range(5):
unfiltered_dataframe[f'constant_{i}'] = i
unfiltered_dataframe[f"constant_{i}"] = i
unfiltered_dataframe = freqai.dk.slice_dataframe(new_timerange, unfiltered_dataframe)
@ -212,23 +205,23 @@ def make_data_dictionary(mocker, freqai_conf):
new_timerange = TimeRange.parse_timerange("20180120-20180130")
corr_dataframes, base_dataframes = freqai.dd.get_base_and_corr_dataframes(
data_load_timerange, freqai.dk.pair, freqai.dk
)
data_load_timerange, freqai.dk.pair, freqai.dk
)
unfiltered_dataframe = freqai.dk.use_strategy_to_populate_indicators(
strategy, corr_dataframes, base_dataframes, freqai.dk.pair
)
strategy, corr_dataframes, base_dataframes, freqai.dk.pair
)
unfiltered_dataframe = freqai.dk.slice_dataframe(new_timerange, unfiltered_dataframe)
freqai.dk.find_features(unfiltered_dataframe)
features_filtered, labels_filtered = freqai.dk.filter_features(
unfiltered_dataframe,
freqai.dk.training_features_list,
freqai.dk.label_list,
training_filter=True,
)
unfiltered_dataframe,
freqai.dk.training_features_list,
freqai.dk.label_list,
training_filter=True,
)
data_dictionary = freqai.dk.make_train_test_datasets(features_filtered, labels_filtered)
@ -247,8 +240,8 @@ def get_freqai_live_analyzed_dataframe(mocker, freqaiconf):
timerange = TimeRange.parse_timerange("20180110-20180114")
freqai.dk.load_all_pair_histories(timerange)
strategy.analyze_pair('ADA/BTC', '5m')
return strategy.dp.get_analyzed_dataframe('ADA/BTC', '5m')
strategy.analyze_pair("ADA/BTC", "5m")
return strategy.dp.get_analyzed_dataframe("ADA/BTC", "5m")
def get_freqai_analyzed_dataframe(mocker, freqaiconf):
@ -264,7 +257,7 @@ def get_freqai_analyzed_dataframe(mocker, freqaiconf):
sub_timerange = TimeRange.parse_timerange("20180111-20180114")
corr_df, base_df = freqai.dk.get_base_and_corr_dataframes(sub_timerange, "LTC/BTC")
return freqai.dk.use_strategy_to_populate_indicators(strategy, corr_df, base_df, 'LTC/BTC')
return freqai.dk.use_strategy_to_populate_indicators(strategy, corr_df, base_df, "LTC/BTC")
def get_ready_to_train(mocker, freqaiconf):

View File

@ -26,24 +26,25 @@ class ReinforcementLearner_test_3ac(ReinforcementLearner):
"""
def calculate_reward(self, action: int) -> float:
# first, penalize if the action is not valid
if not self._is_valid(action):
return -2
pnl = self.get_unrealized_profit()
rew = np.sign(pnl) * (pnl + 1)
factor = 100.
factor = 100.0
# reward agent for entering trades
if (action in (Actions.Buy.value, Actions.Sell.value)
and self._position == Positions.Neutral):
if (
action in (Actions.Buy.value, Actions.Sell.value)
and self._position == Positions.Neutral
):
return 25
# discourage agent from not entering trades
if action == Actions.Neutral.value and self._position == Positions.Neutral:
return -1
max_trade_duration = self.rl_config.get('max_trade_duration_candles', 300)
max_trade_duration = self.rl_config.get("max_trade_duration_candles", 300)
trade_duration = self._current_tick - self._last_trade_tick # type: ignore
if trade_duration <= max_trade_duration:
@ -67,4 +68,4 @@ class ReinforcementLearner_test_3ac(ReinforcementLearner):
factor *= self.rl_config["model_reward_parameters"].get("win_reward_factor", 2)
return float(rew * factor)
return 0.
return 0.0

View File

@ -26,24 +26,25 @@ class ReinforcementLearner_test_4ac(ReinforcementLearner):
"""
def calculate_reward(self, action: int) -> float:
# first, penalize if the action is not valid
if not self._is_valid(action):
return -2
pnl = self.get_unrealized_profit()
rew = np.sign(pnl) * (pnl + 1)
factor = 100.
factor = 100.0
# reward agent for entering trades
if (action in (Actions.Long_enter.value, Actions.Short_enter.value)
and self._position == Positions.Neutral):
if (
action in (Actions.Long_enter.value, Actions.Short_enter.value)
and self._position == Positions.Neutral
):
return 25
# discourage agent from not entering trades
if action == Actions.Neutral.value and self._position == Positions.Neutral:
return -1
max_trade_duration = self.rl_config.get('max_trade_duration_candles', 300)
max_trade_duration = self.rl_config.get("max_trade_duration_candles", 300)
trade_duration = self._current_tick - self._last_trade_tick # type: ignore
if trade_duration <= max_trade_duration:
@ -52,20 +53,22 @@ class ReinforcementLearner_test_4ac(ReinforcementLearner):
factor *= 0.5
# discourage sitting in position
if (self._position in (Positions.Short, Positions.Long) and
action == Actions.Neutral.value):
if (
self._position in (Positions.Short, Positions.Long)
and action == Actions.Neutral.value
):
return -1 * trade_duration / max_trade_duration
# close long
if action == Actions.Exit.value and self._position == Positions.Long:
if pnl > self.profit_aim * self.rr:
factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2)
factor *= self.rl_config["model_reward_parameters"].get("win_reward_factor", 2)
return float(rew * factor)
# close short
if action == Actions.Exit.value and self._position == Positions.Short:
if pnl > self.profit_aim * self.rr:
factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2)
factor *= self.rl_config["model_reward_parameters"].get("win_reward_factor", 2)
return float(rew * factor)
return 0.
return 0.0

View File

@ -10,33 +10,40 @@ five_hours = FtPrecise(5.0)
twentyfive_hours = FtPrecise(25.0)
@pytest.mark.parametrize('exchange,interest_rate,hours,expected', [
('binance', 0.0005, ten_mins, 0.00125),
('binance', 0.00025, ten_mins, 0.000625),
('binance', 0.00025, five_hours, 0.003125),
('binance', 0.00025, twentyfive_hours, 0.015625),
# Kraken
('kraken', 0.0005, ten_mins, 0.06),
('kraken', 0.00025, ten_mins, 0.03),
('kraken', 0.00025, five_hours, 0.045),
('kraken', 0.00025, twentyfive_hours, 0.12),
])
@pytest.mark.parametrize(
"exchange,interest_rate,hours,expected",
[
("binance", 0.0005, ten_mins, 0.00125),
("binance", 0.00025, ten_mins, 0.000625),
("binance", 0.00025, five_hours, 0.003125),
("binance", 0.00025, twentyfive_hours, 0.015625),
# Kraken
("kraken", 0.0005, ten_mins, 0.06),
("kraken", 0.00025, ten_mins, 0.03),
("kraken", 0.00025, five_hours, 0.045),
("kraken", 0.00025, twentyfive_hours, 0.12),
],
)
def test_interest(exchange, interest_rate, hours, expected):
borrowed = FtPrecise(60.0)
assert pytest.approx(float(interest(
exchange_name=exchange,
borrowed=borrowed,
rate=FtPrecise(interest_rate),
hours=hours
))) == expected
assert (
pytest.approx(
float(
interest(
exchange_name=exchange,
borrowed=borrowed,
rate=FtPrecise(interest_rate),
hours=hours,
)
)
)
== expected
)
def test_interest_exception():
with pytest.raises(OperationalException, match=r"Leverage not available on .* with freqtrade"):
interest(
exchange_name='bitmex',
borrowed=FtPrecise(60.0),
rate=FtPrecise(0.0005),
hours=ten_mins
exchange_name="bitmex", borrowed=FtPrecise(60.0), rate=FtPrecise(0.0005), hours=ten_mins
)

View File

@ -9,13 +9,14 @@ from freqtrade.util.datetime_helpers import dt_utc
tests_start_time = dt_utc(2018, 10, 3)
tests_timeframe = '1h'
tests_timeframe = "1h"
class BTrade(NamedTuple):
"""
Minimalistic Trade result used for functional backtesting
"""
exit_reason: ExitType
open_tick: int
close_tick: int
@ -27,6 +28,7 @@ class BTContainer(NamedTuple):
"""
Minimal BacktestContainer defining Backtest inputs and results.
"""
data: List[List[float]]
stop_loss: float
roi: Dict[str, float]
@ -51,22 +53,32 @@ def _get_frame_time_from_offset(offset):
def _build_backtest_dataframe(data):
columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'enter_long', 'exit_long',
'enter_short', 'exit_short']
columns = [
"date",
"open",
"high",
"low",
"close",
"volume",
"enter_long",
"exit_long",
"enter_short",
"exit_short",
]
if len(data[0]) == 8:
# No short columns
data = [d + [0, 0] for d in data]
columns = columns + ['enter_tag'] if len(data[0]) == 11 else columns
columns = columns + ["enter_tag"] if len(data[0]) == 11 else columns
frame = DataFrame.from_records(data, columns=columns)
frame['date'] = frame['date'].apply(_get_frame_time_from_offset)
frame["date"] = frame["date"].apply(_get_frame_time_from_offset)
# Ensure floats are in place
for column in ['open', 'high', 'low', 'close', 'volume']:
frame[column] = frame[column].astype('float64')
for column in ["open", "high", "low", "close", "volume"]:
frame[column] = frame[column].astype("float64")
# Ensure all candles make kindof sense
assert all(frame['low'] <= frame['close'])
assert all(frame['low'] <= frame['open'])
assert all(frame['high'] >= frame['close'])
assert all(frame['high'] >= frame['open'])
assert all(frame["low"] <= frame["close"])
assert all(frame["low"] <= frame["open"])
assert all(frame["high"] >= frame["close"])
assert all(frame["high"] >= frame["open"])
return frame

View File

@ -11,21 +11,23 @@ from freqtrade.optimize.hyperopt import Hyperopt
from tests.conftest import patch_exchange
@pytest.fixture(scope='function')
@pytest.fixture(scope="function")
def hyperopt_conf(default_conf):
hyperconf = deepcopy(default_conf)
hyperconf.update({
'datadir': Path(default_conf['datadir']),
'runmode': RunMode.HYPEROPT,
'strategy': 'HyperoptableStrategy',
'hyperopt_loss': 'ShortTradeDurHyperOptLoss',
'hyperopt_path': str(Path(__file__).parent / 'hyperopts'),
'epochs': 1,
'timerange': None,
'spaces': ['default'],
'hyperopt_jobs': 1,
'hyperopt_min_trades': 1,
})
hyperconf.update(
{
"datadir": Path(default_conf["datadir"]),
"runmode": RunMode.HYPEROPT,
"strategy": "HyperoptableStrategy",
"hyperopt_loss": "ShortTradeDurHyperOptLoss",
"hyperopt_path": str(Path(__file__).parent / "hyperopts"),
"epochs": 1,
"timerange": None,
"spaces": ["default"],
"hyperopt_jobs": 1,
"hyperopt_min_trades": 1,
}
)
return hyperconf
@ -36,32 +38,29 @@ def backtesting_cleanup():
Backtesting.cleanup()
@pytest.fixture(scope='function')
@pytest.fixture(scope="function")
def hyperopt(hyperopt_conf, mocker):
patch_exchange(mocker)
return Hyperopt(hyperopt_conf)
@pytest.fixture(scope='function')
@pytest.fixture(scope="function")
def hyperopt_results():
return pd.DataFrame(
{
'pair': ['ETH/USDT', 'ETH/USDT', 'ETH/USDT', 'ETH/USDT'],
'profit_ratio': [-0.1, 0.2, -0.12, 0.3],
'profit_abs': [-0.2, 0.4, -0.21, 0.6],
'trade_duration': [10, 30, 10, 10],
'amount': [0.1, 0.1, 0.1, 0.1],
'exit_reason': [ExitType.STOP_LOSS, ExitType.ROI, ExitType.STOP_LOSS, ExitType.ROI],
'open_date':
[
"pair": ["ETH/USDT", "ETH/USDT", "ETH/USDT", "ETH/USDT"],
"profit_ratio": [-0.1, 0.2, -0.12, 0.3],
"profit_abs": [-0.2, 0.4, -0.21, 0.6],
"trade_duration": [10, 30, 10, 10],
"amount": [0.1, 0.1, 0.1, 0.1],
"exit_reason": [ExitType.STOP_LOSS, ExitType.ROI, ExitType.STOP_LOSS, ExitType.ROI],
"open_date": [
datetime(2019, 1, 1, 9, 15, 0),
datetime(2019, 1, 2, 8, 55, 0),
datetime(2019, 1, 3, 9, 15, 0),
datetime(2019, 1, 4, 9, 15, 0),
],
'close_date':
[
"close_date": [
datetime(2019, 1, 1, 9, 25, 0),
datetime(2019, 1, 2, 9, 25, 0),
datetime(2019, 1, 3, 9, 25, 0),

File diff suppressed because it is too large Load Diff

View File

@ -16,25 +16,26 @@ from tests.conftest import EXMS, patch_exchange
def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> None:
default_conf['use_exit_signal'] = False
default_conf['max_open_trades'] = 10
mocker.patch(f'{EXMS}.get_fee', fee)
mocker.patch('freqtrade.optimize.backtesting.amount_to_contract_precision',
lambda x, *args, **kwargs: round(x, 8))
default_conf["use_exit_signal"] = False
default_conf["max_open_trades"] = 10
mocker.patch(f"{EXMS}.get_fee", fee)
mocker.patch(
"freqtrade.optimize.backtesting.amount_to_contract_precision",
lambda x, *args, **kwargs: round(x, 8),
)
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(f"{EXMS}.get_max_pair_stake_amount", return_value=float("inf"))
patch_exchange(mocker)
default_conf.update({
"stake_amount": 100.0,
"dry_run_wallet": 1000.0,
"strategy": "StrategyTestV3"
})
default_conf.update(
{"stake_amount": 100.0, "dry_run_wallet": 1000.0, "strategy": "StrategyTestV3"}
)
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
pair = 'UNITTEST/BTC'
timerange = TimeRange('date', None, 1517227800, 0)
data = history.load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'],
timerange=timerange)
pair = "UNITTEST/BTC"
timerange = TimeRange("date", None, 1517227800, 0)
data = history.load_data(
datadir=testdatadir, timeframe="5m", pairs=["UNITTEST/BTC"], timerange=timerange
)
backtesting.strategy.position_adjustment_enable = True
processed = backtesting.strategy.advise_all_indicators(data)
min_date, max_date = get_timerange(processed)
@ -43,47 +44,50 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) ->
start_date=min_date,
end_date=max_date,
)
results = result['results']
results = result["results"]
assert not results.empty
assert len(results) == 2
expected = pd.DataFrame(
{'pair': [pair, pair],
'stake_amount': [500.0, 100.0],
'max_stake_amount': [500.0, 100],
'amount': [4806.87657523, 970.63960782],
'open_date': pd.to_datetime([dt_utc(2018, 1, 29, 18, 40, 0),
dt_utc(2018, 1, 30, 3, 30, 0)], utc=True
),
'close_date': pd.to_datetime([dt_utc(2018, 1, 29, 22, 00, 0),
dt_utc(2018, 1, 30, 4, 10, 0)], utc=True),
'open_rate': [0.10401764891917063, 0.10302485],
'close_rate': [0.10453904064307624, 0.10354126528822055],
'fee_open': [0.0025, 0.0025],
'fee_close': [0.0025, 0.0025],
'trade_duration': [200, 40],
'profit_ratio': [0.0, 0.0],
'profit_abs': [0.0, 0.0],
'exit_reason': [ExitType.ROI.value, ExitType.ROI.value],
'initial_stop_loss_abs': [0.0940005, 0.092722365],
'initial_stop_loss_ratio': [-0.1, -0.1],
'stop_loss_abs': [0.0940005, 0.092722365],
'stop_loss_ratio': [-0.1, -0.1],
'min_rate': [0.10370188, 0.10300000000000001],
'max_rate': [0.10481985, 0.10388887000000001],
'is_open': [False, False],
'enter_tag': ['', ''],
'leverage': [1.0, 1.0],
'is_short': [False, False],
'open_timestamp': [1517251200000, 1517283000000],
'close_timestamp': [1517263200000, 1517285400000],
})
results_no = results.drop(columns=['orders'])
{
"pair": [pair, pair],
"stake_amount": [500.0, 100.0],
"max_stake_amount": [500.0, 100],
"amount": [4806.87657523, 970.63960782],
"open_date": pd.to_datetime(
[dt_utc(2018, 1, 29, 18, 40, 0), dt_utc(2018, 1, 30, 3, 30, 0)], utc=True
),
"close_date": pd.to_datetime(
[dt_utc(2018, 1, 29, 22, 00, 0), dt_utc(2018, 1, 30, 4, 10, 0)], utc=True
),
"open_rate": [0.10401764891917063, 0.10302485],
"close_rate": [0.10453904064307624, 0.10354126528822055],
"fee_open": [0.0025, 0.0025],
"fee_close": [0.0025, 0.0025],
"trade_duration": [200, 40],
"profit_ratio": [0.0, 0.0],
"profit_abs": [0.0, 0.0],
"exit_reason": [ExitType.ROI.value, ExitType.ROI.value],
"initial_stop_loss_abs": [0.0940005, 0.092722365],
"initial_stop_loss_ratio": [-0.1, -0.1],
"stop_loss_abs": [0.0940005, 0.092722365],
"stop_loss_ratio": [-0.1, -0.1],
"min_rate": [0.10370188, 0.10300000000000001],
"max_rate": [0.10481985, 0.10388887000000001],
"is_open": [False, False],
"enter_tag": ["", ""],
"leverage": [1.0, 1.0],
"is_short": [False, False],
"open_timestamp": [1517251200000, 1517283000000],
"close_timestamp": [1517263200000, 1517285400000],
}
)
results_no = results.drop(columns=["orders"])
pd.testing.assert_frame_equal(results_no, expected, check_exact=True)
data_pair = processed[pair]
assert len(results.iloc[0]['orders']) == 6
assert len(results.iloc[1]['orders']) == 2
assert len(results.iloc[0]["orders"]) == 6
assert len(results.iloc[1]["orders"]) == 2
for _, t in results.iterrows():
ln = data_pair.loc[data_pair["date"] == t["open_date"]]
@ -91,65 +95,65 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) ->
assert ln is not None
# check close trade rate aligns to close rate or is between high and low
ln = data_pair.loc[data_pair["date"] == t["close_date"]]
assert (round(ln.iloc[0]["open"], 6) == round(t["close_rate"], 6) or
round(ln.iloc[0]["low"], 6) < round(
t["close_rate"], 6) < round(ln.iloc[0]["high"], 6))
assert round(ln.iloc[0]["open"], 6) == round(t["close_rate"], 6) or round(
ln.iloc[0]["low"], 6
) < round(t["close_rate"], 6) < round(ln.iloc[0]["high"], 6)
@pytest.mark.parametrize('leverage', [
1, 2
])
@pytest.mark.parametrize("leverage", [1, 2])
def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, leverage) -> None:
default_conf['use_exit_signal'] = False
mocker.patch(f'{EXMS}.get_fee', fee)
default_conf["use_exit_signal"] = False
mocker.patch(f"{EXMS}.get_fee", fee)
mocker.patch(f"{EXMS}.get_min_pair_stake_amount", return_value=10)
mocker.patch(f"{EXMS}.get_max_pair_stake_amount", return_value=float('inf'))
mocker.patch(f"{EXMS}.get_max_pair_stake_amount", return_value=float("inf"))
mocker.patch(f"{EXMS}.get_max_leverage", return_value=10)
mocker.patch(f"{EXMS}.get_maintenance_ratio_and_amt", return_value=(0.1, 0.1))
mocker.patch('freqtrade.optimize.backtesting.Backtesting._run_funding_fees')
mocker.patch("freqtrade.optimize.backtesting.Backtesting._run_funding_fees")
patch_exchange(mocker)
default_conf.update({
"stake_amount": 100.0,
"dry_run_wallet": 1000.0,
"strategy": "StrategyTestV3",
"trading_mode": "futures",
"margin_mode": "isolated",
})
default_conf['pairlists'] = [{'method': 'StaticPairList', 'allow_inactive': True}]
default_conf.update(
{
"stake_amount": 100.0,
"dry_run_wallet": 1000.0,
"strategy": "StrategyTestV3",
"trading_mode": "futures",
"margin_mode": "isolated",
}
)
default_conf["pairlists"] = [{"method": "StaticPairList", "allow_inactive": True}]
backtesting = Backtesting(default_conf)
backtesting._can_short = True
backtesting._set_strategy(backtesting.strategylist[0])
pair = 'XRP/USDT:USDT'
pair = "XRP/USDT:USDT"
row_enter = [
pd.Timestamp(year=2020, month=1, day=1, hour=4, minute=0),
2.1, # Open
2.2, # High
1.9, # Low
2.1, # Close
1, # enter_long
0, # exit_long
0, # enter_short
0, # exit_short
'', # enter_tag
'', # exit_tag
]
pd.Timestamp(year=2020, month=1, day=1, hour=4, minute=0),
2.1, # Open
2.2, # High
1.9, # Low
2.1, # Close
1, # enter_long
0, # exit_long
0, # enter_short
0, # exit_short
"", # enter_tag
"", # exit_tag
]
# Exit row - with slightly different values
row_exit = [
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0),
2.2, # Open
2.3, # High
2.0, # Low
2.2, # Close
1, # enter_long
0, # exit_long
0, # enter_short
0, # exit_short
'', # enter_tag
'', # exit_tag
]
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0),
2.2, # Open
2.3, # High
2.0, # Low
2.2, # Close
1, # enter_long
0, # exit_long
0, # enter_short
0, # exit_short
"", # enter_tag
"", # exit_tag
]
backtesting.strategy.leverage = MagicMock(return_value=leverage)
trade = backtesting._enter_trade(pair, row=row_enter, direction='long')
trade = backtesting._enter_trade(pair, row=row_enter, direction="long")
current_time = row_enter[0].to_pydatetime()
assert trade
assert pytest.approx(trade.stake_amount) == 100.0
@ -164,7 +168,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera
assert pytest.approx(trade.amount) == 47.61904762 * leverage
assert len(trade.orders) == 1
# Increase position by 100
backtesting.strategy.adjust_trade_position = MagicMock(return_value=(100, 'PartIncrease'))
backtesting.strategy.adjust_trade_position = MagicMock(return_value=(100, "PartIncrease"))
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row_enter, current_time)
@ -173,7 +177,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera
assert pytest.approx(trade.stake_amount) == 200.0
assert pytest.approx(trade.amount) == 95.23809524 * leverage
assert len(trade.orders) == 2
assert trade.orders[-1].ft_order_tag == 'PartIncrease'
assert trade.orders[-1].ft_order_tag == "PartIncrease"
assert pytest.approx(trade.liquidation_price) == liq_price
# Reduce by more than amount - no change to trade.
@ -190,14 +194,14 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera
assert pytest.approx(trade.liquidation_price) == liq_price
# Reduce position by 50
backtesting.strategy.adjust_trade_position = MagicMock(return_value=(-100, 'partDecrease'))
backtesting.strategy.adjust_trade_position = MagicMock(return_value=(-100, "partDecrease"))
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row_exit, current_time)
assert trade
assert pytest.approx(trade.stake_amount) == 100.0
assert pytest.approx(trade.amount) == 47.61904762 * leverage
assert len(trade.orders) == 3
assert trade.orders[-1].ft_order_tag == 'partDecrease'
assert trade.orders[-1].ft_order_tag == "partDecrease"
assert trade.nr_of_successful_entries == 2
assert trade.nr_of_successful_exits == 1
assert pytest.approx(trade.liquidation_price) == liq_price

View File

@ -32,161 +32,162 @@ def test_parse_args_backtesting(mocker) -> None:
further argument parsing is done in test_arguments.py
"""
mocker.patch.object(Path, "is_file", MagicMock(side_effect=[False, True]))
backtesting_mock = mocker.patch('freqtrade.commands.start_backtesting')
backtesting_mock = mocker.patch("freqtrade.commands.start_backtesting")
backtesting_mock.__name__ = PropertyMock("start_backtesting")
# it's sys.exit(0) at the end of backtesting
with pytest.raises(SystemExit):
main(['backtesting'])
main(["backtesting"])
assert backtesting_mock.call_count == 1
call_args = backtesting_mock.call_args[0][0]
assert call_args['config'] == ['config.json']
assert call_args['verbosity'] == 0
assert call_args['command'] == 'backtesting'
assert call_args['func'] is not None
assert callable(call_args['func'])
assert call_args['timeframe'] is None
assert call_args["config"] == ["config.json"]
assert call_args["verbosity"] == 0
assert call_args["command"] == "backtesting"
assert call_args["func"] is not None
assert callable(call_args["func"])
assert call_args["timeframe"] is None
def test_main_start_hyperopt(mocker) -> None:
mocker.patch.object(Path, 'is_file', MagicMock(side_effect=[False, True]))
hyperopt_mock = mocker.patch('freqtrade.commands.start_hyperopt', MagicMock())
hyperopt_mock.__name__ = PropertyMock('start_hyperopt')
mocker.patch.object(Path, "is_file", MagicMock(side_effect=[False, True]))
hyperopt_mock = mocker.patch("freqtrade.commands.start_hyperopt", MagicMock())
hyperopt_mock.__name__ = PropertyMock("start_hyperopt")
# it's sys.exit(0) at the end of hyperopt
with pytest.raises(SystemExit):
main(['hyperopt'])
main(["hyperopt"])
assert hyperopt_mock.call_count == 1
call_args = hyperopt_mock.call_args[0][0]
assert call_args['config'] == ['config.json']
assert call_args['verbosity'] == 0
assert call_args['command'] == 'hyperopt'
assert call_args['func'] is not None
assert callable(call_args['func'])
assert call_args["config"] == ["config.json"]
assert call_args["verbosity"] == 0
assert call_args["command"] == "hyperopt"
assert call_args["func"] is not None
assert callable(call_args["func"])
def test_main_fatal_exception(mocker, default_conf, caplog) -> None:
patch_exchange(mocker)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock())
mocker.patch('freqtrade.worker.Worker._worker', MagicMock(side_effect=Exception))
mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock())
mocker.patch("freqtrade.worker.Worker._worker", MagicMock(side_effect=Exception))
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock())
mocker.patch("freqtrade.freqtradebot.init_db", MagicMock())
args = ['trade', '-c', 'tests/testdata/testconfigs/main_test_config.json']
args = ["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"]
# Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit):
main(args)
assert log_has('Using config: tests/testdata/testconfigs/main_test_config.json ...', caplog)
assert log_has('Fatal exception!', caplog)
assert log_has("Using config: tests/testdata/testconfigs/main_test_config.json ...", caplog)
assert log_has("Fatal exception!", caplog)
def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None:
patch_exchange(mocker)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock())
mocker.patch('freqtrade.worker.Worker._worker', MagicMock(side_effect=KeyboardInterrupt))
mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock())
mocker.patch("freqtrade.worker.Worker._worker", MagicMock(side_effect=KeyboardInterrupt))
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.wallets.Wallets.update', MagicMock())
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock())
mocker.patch("freqtrade.wallets.Wallets.update", MagicMock())
mocker.patch("freqtrade.freqtradebot.init_db", MagicMock())
args = ['trade', '-c', 'tests/testdata/testconfigs/main_test_config.json']
args = ["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"]
# Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit):
main(args)
assert log_has('Using config: tests/testdata/testconfigs/main_test_config.json ...', caplog)
assert log_has('SIGINT received, aborting ...', caplog)
assert log_has("Using config: tests/testdata/testconfigs/main_test_config.json ...", caplog)
assert log_has("SIGINT received, aborting ...", caplog)
def test_main_operational_exception(mocker, default_conf, caplog) -> None:
patch_exchange(mocker)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock())
mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock())
mocker.patch(
'freqtrade.worker.Worker._worker',
MagicMock(side_effect=FreqtradeException('Oh snap!'))
"freqtrade.worker.Worker._worker", MagicMock(side_effect=FreqtradeException("Oh snap!"))
)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.wallets.Wallets.update', MagicMock())
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
mocker.patch("freqtrade.wallets.Wallets.update", MagicMock())
mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock())
mocker.patch("freqtrade.freqtradebot.init_db", MagicMock())
args = ['trade', '-c', 'tests/testdata/testconfigs/main_test_config.json']
args = ["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"]
# Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit):
main(args)
assert log_has('Using config: tests/testdata/testconfigs/main_test_config.json ...', caplog)
assert log_has('Oh snap!', caplog)
assert log_has("Using config: tests/testdata/testconfigs/main_test_config.json ...", caplog)
assert log_has("Oh snap!", caplog)
def test_main_operational_exception1(mocker, default_conf, caplog) -> None:
patch_exchange(mocker)
mocker.patch(
'freqtrade.commands.list_commands.list_available_exchanges',
MagicMock(side_effect=ValueError('Oh snap!'))
"freqtrade.commands.list_commands.list_available_exchanges",
MagicMock(side_effect=ValueError("Oh snap!")),
)
patched_configuration_load_config_file(mocker, default_conf)
args = ['list-exchanges']
args = ["list-exchanges"]
# Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit):
main(args)
assert log_has('Fatal exception!', caplog)
assert not log_has_re(r'SIGINT.*', caplog)
assert log_has("Fatal exception!", caplog)
assert not log_has_re(r"SIGINT.*", caplog)
mocker.patch(
'freqtrade.commands.list_commands.list_available_exchanges',
MagicMock(side_effect=KeyboardInterrupt)
"freqtrade.commands.list_commands.list_available_exchanges",
MagicMock(side_effect=KeyboardInterrupt),
)
with pytest.raises(SystemExit):
main(args)
assert log_has_re(r'SIGINT.*', caplog)
assert log_has_re(r"SIGINT.*", caplog)
def test_main_ConfigurationError(mocker, default_conf, caplog) -> None:
patch_exchange(mocker)
mocker.patch(
'freqtrade.commands.list_commands.list_available_exchanges',
MagicMock(side_effect=ConfigurationError('Oh snap!'))
"freqtrade.commands.list_commands.list_available_exchanges",
MagicMock(side_effect=ConfigurationError("Oh snap!")),
)
patched_configuration_load_config_file(mocker, default_conf)
args = ['list-exchanges']
args = ["list-exchanges"]
# Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit):
main(args)
assert log_has_re('Configuration error: Oh snap!', caplog)
assert log_has_re("Configuration error: Oh snap!", caplog)
def test_main_reload_config(mocker, default_conf, caplog) -> None:
patch_exchange(mocker)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock())
mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock())
# Simulate Running, reload, running workflow
worker_mock = MagicMock(side_effect=[State.RUNNING,
State.RELOAD_CONFIG,
State.RUNNING,
OperationalException("Oh snap!")])
mocker.patch('freqtrade.worker.Worker._worker', worker_mock)
worker_mock = MagicMock(
side_effect=[
State.RUNNING,
State.RELOAD_CONFIG,
State.RUNNING,
OperationalException("Oh snap!"),
]
)
mocker.patch("freqtrade.worker.Worker._worker", worker_mock)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.wallets.Wallets.update', MagicMock())
reconfigure_mock = mocker.patch('freqtrade.worker.Worker._reconfigure', MagicMock())
mocker.patch("freqtrade.wallets.Wallets.update", MagicMock())
reconfigure_mock = mocker.patch("freqtrade.worker.Worker._reconfigure", MagicMock())
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock())
mocker.patch("freqtrade.freqtradebot.init_db", MagicMock())
args = Arguments([
'trade',
'-c',
'tests/testdata/testconfigs/main_test_config.json'
]).get_parsed_arg()
args = Arguments(
["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"]
).get_parsed_arg()
worker = Worker(args=args, config=default_conf)
with pytest.raises(SystemExit):
main(['trade', '-c', 'tests/testdata/testconfigs/main_test_config.json'])
main(["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"])
assert log_has('Using config: tests/testdata/testconfigs/main_test_config.json ...', caplog)
assert log_has("Using config: tests/testdata/testconfigs/main_test_config.json ...", caplog)
assert worker_mock.call_count == 4
assert reconfigure_mock.call_count == 1
assert isinstance(worker.freqtrade, FreqtradeBot)
@ -194,27 +195,24 @@ def test_main_reload_config(mocker, default_conf, caplog) -> None:
def test_reconfigure(mocker, default_conf) -> None:
patch_exchange(mocker)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock())
mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock())
mocker.patch(
'freqtrade.worker.Worker._worker',
MagicMock(side_effect=OperationalException('Oh snap!'))
"freqtrade.worker.Worker._worker", MagicMock(side_effect=OperationalException("Oh snap!"))
)
mocker.patch('freqtrade.wallets.Wallets.update', MagicMock())
mocker.patch("freqtrade.wallets.Wallets.update", MagicMock())
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock())
mocker.patch("freqtrade.freqtradebot.init_db", MagicMock())
args = Arguments([
'trade',
'-c',
'tests/testdata/testconfigs/main_test_config.json'
]).get_parsed_arg()
args = Arguments(
["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"]
).get_parsed_arg()
worker = Worker(args=args, config=default_conf)
freqtrade = worker.freqtrade
# Renew mock to return modified data
conf = deepcopy(default_conf)
conf['stake_amount'] += 1
conf["stake_amount"] += 1
patched_configuration_load_config_file(mocker, conf)
worker._config = conf
@ -224,4 +222,4 @@ def test_reconfigure(mocker, default_conf) -> None:
# Verify we have a new instance with the new config
assert freqtrade is not freqtrade2
assert freqtrade.config['stake_amount'] + 1 == freqtrade2.config['stake_amount']
assert freqtrade.config["stake_amount"] + 1 == freqtrade2.config["stake_amount"]

View File

@ -23,33 +23,31 @@ from freqtrade.misc import (
def test_file_dump_json(mocker) -> None:
file_open = mocker.patch('freqtrade.misc.Path.open', MagicMock())
json_dump = mocker.patch('rapidjson.dump', MagicMock())
file_dump_json(Path('somefile'), [1, 2, 3])
file_open = mocker.patch("freqtrade.misc.Path.open", MagicMock())
json_dump = mocker.patch("rapidjson.dump", MagicMock())
file_dump_json(Path("somefile"), [1, 2, 3])
assert file_open.call_count == 1
assert json_dump.call_count == 1
file_open = mocker.patch('freqtrade.misc.gzip.open', MagicMock())
json_dump = mocker.patch('rapidjson.dump', MagicMock())
file_dump_json(Path('somefile'), [1, 2, 3], True)
file_open = mocker.patch("freqtrade.misc.gzip.open", MagicMock())
json_dump = mocker.patch("rapidjson.dump", MagicMock())
file_dump_json(Path("somefile"), [1, 2, 3], True)
assert file_open.call_count == 1
assert json_dump.call_count == 1
def test_file_load_json(mocker, testdatadir) -> None:
# 7m .json does not exist
ret = file_load_json(testdatadir / 'UNITTEST_BTC-7m.json')
ret = file_load_json(testdatadir / "UNITTEST_BTC-7m.json")
assert not ret
# 1m json exists (but no .gz exists)
ret = file_load_json(testdatadir / 'UNITTEST_BTC-1m.json')
ret = file_load_json(testdatadir / "UNITTEST_BTC-1m.json")
assert ret
# 8 .json is empty and will fail if it's loaded. .json.gz is a copy of 1.json
ret = file_load_json(testdatadir / 'UNITTEST_BTC-8m.json')
ret = file_load_json(testdatadir / "UNITTEST_BTC-8m.json")
assert ret
def test_is_file_in_dir(tmp_path):
# Create a temporary directory and file
dir_path = tmp_path / "subdir"
dir_path.mkdir()
@ -66,69 +64,72 @@ def test_is_file_in_dir(tmp_path):
assert is_file_in_dir(file_path2, tmp_path) is False
@pytest.mark.parametrize("pair,expected_result", [
("ETH/BTC", 'ETH_BTC'),
("ETH/USDT", 'ETH_USDT'),
("ETH/USDT:USDT", 'ETH_USDT_USDT'), # swap with USDT as settlement currency
("ETH/USD:USD", 'ETH_USD_USD'), # swap with USD as settlement currency
("AAVE/USD:USD", 'AAVE_USD_USD'), # swap with USDT as settlement currency
("ETH/USDT:USDT-210625", 'ETH_USDT_USDT-210625'), # expiring futures
("Fabric Token/ETH", 'Fabric_Token_ETH'),
("ETHH20", 'ETHH20'),
(".XBTBON2H", '_XBTBON2H'),
("ETHUSD.d", 'ETHUSD_d'),
("ADA-0327", 'ADA-0327'),
("BTC-USD-200110", 'BTC-USD-200110'),
("BTC-PERP:USDT", 'BTC-PERP_USDT'),
("F-AKRO/USDT", 'F-AKRO_USDT'),
("LC+/ETH", 'LC__ETH'),
("CMT@18/ETH", 'CMT_18_ETH'),
("LBTC:1022/SAI", 'LBTC_1022_SAI'),
("$PAC/BTC", '_PAC_BTC'),
("ACC_OLD/BTC", 'ACC_OLD_BTC'),
])
@pytest.mark.parametrize(
"pair,expected_result",
[
("ETH/BTC", "ETH_BTC"),
("ETH/USDT", "ETH_USDT"),
("ETH/USDT:USDT", "ETH_USDT_USDT"), # swap with USDT as settlement currency
("ETH/USD:USD", "ETH_USD_USD"), # swap with USD as settlement currency
("AAVE/USD:USD", "AAVE_USD_USD"), # swap with USDT as settlement currency
("ETH/USDT:USDT-210625", "ETH_USDT_USDT-210625"), # expiring futures
("Fabric Token/ETH", "Fabric_Token_ETH"),
("ETHH20", "ETHH20"),
(".XBTBON2H", "_XBTBON2H"),
("ETHUSD.d", "ETHUSD_d"),
("ADA-0327", "ADA-0327"),
("BTC-USD-200110", "BTC-USD-200110"),
("BTC-PERP:USDT", "BTC-PERP_USDT"),
("F-AKRO/USDT", "F-AKRO_USDT"),
("LC+/ETH", "LC__ETH"),
("CMT@18/ETH", "CMT_18_ETH"),
("LBTC:1022/SAI", "LBTC_1022_SAI"),
("$PAC/BTC", "_PAC_BTC"),
("ACC_OLD/BTC", "ACC_OLD_BTC"),
],
)
def test_pair_to_filename(pair, expected_result):
pair_s = pair_to_filename(pair)
assert pair_s == expected_result
def test_safe_value_fallback():
dict1 = {'keya': None, 'keyb': 2, 'keyc': 5, 'keyd': None}
assert safe_value_fallback(dict1, 'keya', 'keyb') == 2
assert safe_value_fallback(dict1, 'keyb', 'keya') == 2
dict1 = {"keya": None, "keyb": 2, "keyc": 5, "keyd": None}
assert safe_value_fallback(dict1, "keya", "keyb") == 2
assert safe_value_fallback(dict1, "keyb", "keya") == 2
assert safe_value_fallback(dict1, 'keyb', 'keyc') == 2
assert safe_value_fallback(dict1, 'keya', 'keyc') == 5
assert safe_value_fallback(dict1, "keyb", "keyc") == 2
assert safe_value_fallback(dict1, "keya", "keyc") == 5
assert safe_value_fallback(dict1, 'keyc', 'keyb') == 5
assert safe_value_fallback(dict1, "keyc", "keyb") == 5
assert safe_value_fallback(dict1, 'keya', 'keyd') is None
assert safe_value_fallback(dict1, "keya", "keyd") is None
assert safe_value_fallback(dict1, 'keyNo', 'keyNo') is None
assert safe_value_fallback(dict1, 'keyNo', 'keyNo', 55) == 55
assert safe_value_fallback(dict1, 'keyNo', default_value=55) == 55
assert safe_value_fallback(dict1, 'keyNo', None, default_value=55) == 55
assert safe_value_fallback(dict1, "keyNo", "keyNo") is None
assert safe_value_fallback(dict1, "keyNo", "keyNo", 55) == 55
assert safe_value_fallback(dict1, "keyNo", default_value=55) == 55
assert safe_value_fallback(dict1, "keyNo", None, default_value=55) == 55
def test_safe_value_fallback2():
dict1 = {'keya': None, 'keyb': 2, 'keyc': 5, 'keyd': None}
dict2 = {'keya': 20, 'keyb': None, 'keyc': 6, 'keyd': None}
assert safe_value_fallback2(dict1, dict2, 'keya', 'keya') == 20
assert safe_value_fallback2(dict2, dict1, 'keya', 'keya') == 20
dict1 = {"keya": None, "keyb": 2, "keyc": 5, "keyd": None}
dict2 = {"keya": 20, "keyb": None, "keyc": 6, "keyd": None}
assert safe_value_fallback2(dict1, dict2, "keya", "keya") == 20
assert safe_value_fallback2(dict2, dict1, "keya", "keya") == 20
assert safe_value_fallback2(dict1, dict2, 'keyb', 'keyb') == 2
assert safe_value_fallback2(dict2, dict1, 'keyb', 'keyb') == 2
assert safe_value_fallback2(dict1, dict2, "keyb", "keyb") == 2
assert safe_value_fallback2(dict2, dict1, "keyb", "keyb") == 2
assert safe_value_fallback2(dict1, dict2, 'keyc', 'keyc') == 5
assert safe_value_fallback2(dict2, dict1, 'keyc', 'keyc') == 6
assert safe_value_fallback2(dict1, dict2, "keyc", "keyc") == 5
assert safe_value_fallback2(dict2, dict1, "keyc", "keyc") == 6
assert safe_value_fallback2(dict1, dict2, 'keyd', 'keyd') is None
assert safe_value_fallback2(dict2, dict1, 'keyd', 'keyd') is None
assert safe_value_fallback2(dict2, dict1, 'keyd', 'keyd', 1234) == 1234
assert safe_value_fallback2(dict1, dict2, "keyd", "keyd") is None
assert safe_value_fallback2(dict2, dict1, "keyd", "keyd") is None
assert safe_value_fallback2(dict2, dict1, "keyd", "keyd", 1234) == 1234
assert safe_value_fallback2(dict1, dict2, 'keyNo', 'keyNo') is None
assert safe_value_fallback2(dict2, dict1, 'keyNo', 'keyNo') is None
assert safe_value_fallback2(dict2, dict1, 'keyNo', 'keyNo', 1234) == 1234
assert safe_value_fallback2(dict1, dict2, "keyNo", "keyNo") is None
assert safe_value_fallback2(dict2, dict1, "keyNo", "keyNo") is None
assert safe_value_fallback2(dict2, dict1, "keyNo", "keyNo", 1234) == 1234
def test_plural() -> None:
@ -163,38 +164,51 @@ def test_plural() -> None:
assert plural(-1.5, "ox", "oxen") == "oxen"
@pytest.mark.parametrize('conn_url,expected', [
("postgresql+psycopg2://scott123:scott123@host:1245/dbname",
"postgresql+psycopg2://scott123:*****@host:1245/dbname"),
("postgresql+psycopg2://scott123:scott123@host.name.com/dbname",
"postgresql+psycopg2://scott123:*****@host.name.com/dbname"),
("mariadb+mariadbconnector://app_user:Password123!@127.0.0.1:3306/company",
"mariadb+mariadbconnector://app_user:*****@127.0.0.1:3306/company"),
("mysql+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4",
"mysql+pymysql://user:*****@some_mariadb/dbname?charset=utf8mb4"),
("sqlite:////freqtrade/user_data/tradesv3.sqlite",
"sqlite:////freqtrade/user_data/tradesv3.sqlite"),
])
@pytest.mark.parametrize(
"conn_url,expected",
[
(
"postgresql+psycopg2://scott123:scott123@host:1245/dbname",
"postgresql+psycopg2://scott123:*****@host:1245/dbname",
),
(
"postgresql+psycopg2://scott123:scott123@host.name.com/dbname",
"postgresql+psycopg2://scott123:*****@host.name.com/dbname",
),
(
"mariadb+mariadbconnector://app_user:Password123!@127.0.0.1:3306/company",
"mariadb+mariadbconnector://app_user:*****@127.0.0.1:3306/company",
),
(
"mysql+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4",
"mysql+pymysql://user:*****@some_mariadb/dbname?charset=utf8mb4",
),
(
"sqlite:////freqtrade/user_data/tradesv3.sqlite",
"sqlite:////freqtrade/user_data/tradesv3.sqlite",
),
],
)
def test_parse_db_uri_for_logging(conn_url, expected) -> None:
assert parse_db_uri_for_logging(conn_url) == expected
def test_deep_merge_dicts():
a = {'first': {'rows': {'pass': 'dog', 'number': '1', 'test': None}}}
b = {'first': {'rows': {'fail': 'cat', 'number': '5', 'test': 'asdf'}}}
res = {'first': {'rows': {'pass': 'dog', 'fail': 'cat', 'number': '5', 'test': 'asdf'}}}
res2 = {'first': {'rows': {'pass': 'dog', 'fail': 'cat', 'number': '1', 'test': None}}}
a = {"first": {"rows": {"pass": "dog", "number": "1", "test": None}}}
b = {"first": {"rows": {"fail": "cat", "number": "5", "test": "asdf"}}}
res = {"first": {"rows": {"pass": "dog", "fail": "cat", "number": "5", "test": "asdf"}}}
res2 = {"first": {"rows": {"pass": "dog", "fail": "cat", "number": "1", "test": None}}}
assert deep_merge_dicts(b, deepcopy(a)) == res
assert deep_merge_dicts(a, deepcopy(b)) == res2
res2['first']['rows']['test'] = 'asdf'
res2["first"]["rows"]["test"] = "asdf"
assert deep_merge_dicts(a, deepcopy(b), allow_null_overrides=False) == res2
def test_dataframe_json(ohlcv_history):
from pandas.testing import assert_frame_equal
json = dataframe_to_json(ohlcv_history)
dataframe = json_to_dataframe(json)
@ -202,7 +216,7 @@ def test_dataframe_json(ohlcv_history):
assert len(ohlcv_history) == len(dataframe)
assert_frame_equal(ohlcv_history, dataframe)
ohlcv_history.at[1, 'date'] = pd.NaT
ohlcv_history.at[1, "date"] = pd.NaT
json = dataframe_to_json(ohlcv_history)
dataframe = json_to_dataframe(json)

View File

@ -31,7 +31,7 @@ from tests.conftest import get_args, log_has, log_has_re, patch_exchange
def fig_generating_mock(fig, *args, **kwargs):
""" Return Fig - used to mock add_indicators and plot_trades"""
"""Return Fig - used to mock add_indicators and plot_trades"""
return fig
@ -51,18 +51,18 @@ def generate_empty_figure():
def test_init_plotscript(default_conf, mocker, testdatadir):
default_conf['timerange'] = "20180110-20180112"
default_conf['trade_source'] = "file"
default_conf['timeframe'] = "5m"
default_conf['exportfilename'] = testdatadir / "backtest-result.json"
default_conf["timerange"] = "20180110-20180112"
default_conf["trade_source"] = "file"
default_conf["timeframe"] = "5m"
default_conf["exportfilename"] = testdatadir / "backtest-result.json"
supported_markets = ["TRX/BTC", "ADA/BTC"]
ret = init_plotscript(default_conf, supported_markets)
assert "ohlcv" in ret
assert "trades" in ret
assert "pairs" in ret
assert 'timerange' in ret
assert "timerange" in ret
default_conf['pairs'] = ["TRX/BTC", "ADA/BTC"]
default_conf["pairs"] = ["TRX/BTC", "ADA/BTC"]
ret = init_plotscript(default_conf, supported_markets, 20)
assert "ohlcv" in ret
assert "TRX/BTC" in ret["ohlcv"]
@ -73,15 +73,16 @@ def test_add_indicators(default_conf, testdatadir, caplog):
pair = "UNITTEST/BTC"
timerange = TimeRange()
data = history.load_pair_history(pair=pair, timeframe='1m',
datadir=testdatadir, timerange=timerange)
data = history.load_pair_history(
pair=pair, timeframe="1m", datadir=testdatadir, timerange=timerange
)
indicators1 = {"ema10": {}}
indicators2 = {"macd": {"color": "red"}}
strategy = StrategyResolver.load_strategy(default_conf)
# Generate entry/exit signals and indicators
data = strategy.analyze_ticker(data, {'pair': pair})
data = strategy.analyze_ticker(data, {"pair": pair})
fig = generate_empty_figure()
# Row 1
@ -99,39 +100,43 @@ def test_add_indicators(default_conf, testdatadir, caplog):
assert macd.line.color == "red"
# No indicator found
fig3 = add_indicators(fig=deepcopy(fig), row=3, indicators={'no_indicator': {}}, data=data)
fig3 = add_indicators(fig=deepcopy(fig), row=3, indicators={"no_indicator": {}}, data=data)
assert fig == fig3
assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog)
def test_add_areas(default_conf, testdatadir, caplog):
pair = "UNITTEST/BTC"
timerange = TimeRange(None, 'line', 0, -1000)
timerange = TimeRange(None, "line", 0, -1000)
data = history.load_pair_history(pair=pair, timeframe='1m',
datadir=testdatadir, timerange=timerange)
indicators = {"macd": {"color": "red",
"fill_color": "black",
"fill_to": "macdhist",
"fill_label": "MACD Fill"}}
data = history.load_pair_history(
pair=pair, timeframe="1m", datadir=testdatadir, timerange=timerange
)
indicators = {
"macd": {
"color": "red",
"fill_color": "black",
"fill_to": "macdhist",
"fill_label": "MACD Fill",
}
}
ind_no_label = {"macd": {"fill_color": "red",
"fill_to": "macdhist"}}
ind_no_label = {"macd": {"fill_color": "red", "fill_to": "macdhist"}}
ind_plain = {"macd": {"fill_to": "macdhist"}}
strategy = StrategyResolver.load_strategy(default_conf)
# Generate entry/exit signals and indicators
data = strategy.analyze_ticker(data, {'pair': pair})
data = strategy.analyze_ticker(data, {"pair": pair})
fig = generate_empty_figure()
# indicator mentioned in fill_to does not exist
fig1 = add_areas(fig, 1, data, {'ema10': {'fill_to': 'no_fill_indicator'}})
fig1 = add_areas(fig, 1, data, {"ema10": {"fill_to": "no_fill_indicator"}})
assert fig == fig1
assert log_has_re(r'fill_to: "no_fill_indicator" ignored\..*', caplog)
# indicator does not exist
fig2 = add_areas(fig, 1, data, {'no_indicator': {'fill_to': 'ema10'}})
fig2 = add_areas(fig, 1, data, {"no_indicator": {"fill_to": "ema10"}})
assert fig == fig2
assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog)
@ -168,56 +173,60 @@ def test_plot_trades(testdatadir, caplog):
pair = "ADA/BTC"
filename = testdatadir / "backtest_results/backtest-result.json"
trades = load_backtest_data(filename)
trades = trades.loc[trades['pair'] == pair]
trades = trades.loc[trades["pair"] == pair]
fig = plot_trades(fig, trades)
figure = fig1.layout.figure
# Check entry - color, should be in first graph, ...
trade_entries = find_trace_in_fig_data(figure.data, 'Trade entry')
trade_entries = find_trace_in_fig_data(figure.data, "Trade entry")
assert isinstance(trade_entries, go.Scatter)
assert trade_entries.yaxis == 'y'
assert trade_entries.yaxis == "y"
assert len(trades) == len(trade_entries.x)
assert trade_entries.marker.color == 'cyan'
assert trade_entries.marker.symbol == 'circle-open'
assert trade_entries.text[0] == '3.99%, buy_tag, roi, 15 min'
assert trade_entries.marker.color == "cyan"
assert trade_entries.marker.symbol == "circle-open"
assert trade_entries.text[0] == "3.99%, buy_tag, roi, 15 min"
trade_exit = find_trace_in_fig_data(figure.data, 'Exit - Profit')
trade_exit = find_trace_in_fig_data(figure.data, "Exit - Profit")
assert isinstance(trade_exit, go.Scatter)
assert trade_exit.yaxis == 'y'
assert len(trades.loc[trades['profit_ratio'] > 0]) == len(trade_exit.x)
assert trade_exit.marker.color == 'green'
assert trade_exit.marker.symbol == 'square-open'
assert trade_exit.text[0] == '3.99%, buy_tag, roi, 15 min'
assert trade_exit.yaxis == "y"
assert len(trades.loc[trades["profit_ratio"] > 0]) == len(trade_exit.x)
assert trade_exit.marker.color == "green"
assert trade_exit.marker.symbol == "square-open"
assert trade_exit.text[0] == "3.99%, buy_tag, roi, 15 min"
trade_sell_loss = find_trace_in_fig_data(figure.data, 'Exit - Loss')
trade_sell_loss = find_trace_in_fig_data(figure.data, "Exit - Loss")
assert isinstance(trade_sell_loss, go.Scatter)
assert trade_sell_loss.yaxis == 'y'
assert len(trades.loc[trades['profit_ratio'] <= 0]) == len(trade_sell_loss.x)
assert trade_sell_loss.marker.color == 'red'
assert trade_sell_loss.marker.symbol == 'square-open'
assert trade_sell_loss.text[5] == '-10.45%, stop_loss, 720 min'
assert trade_sell_loss.yaxis == "y"
assert len(trades.loc[trades["profit_ratio"] <= 0]) == len(trade_sell_loss.x)
assert trade_sell_loss.marker.color == "red"
assert trade_sell_loss.marker.symbol == "square-open"
assert trade_sell_loss.text[5] == "-10.45%, stop_loss, 720 min"
def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog):
row_mock = mocker.patch('freqtrade.plot.plotting.add_indicators',
MagicMock(side_effect=fig_generating_mock))
trades_mock = mocker.patch('freqtrade.plot.plotting.plot_trades',
MagicMock(side_effect=fig_generating_mock))
row_mock = mocker.patch(
"freqtrade.plot.plotting.add_indicators", MagicMock(side_effect=fig_generating_mock)
)
trades_mock = mocker.patch(
"freqtrade.plot.plotting.plot_trades", MagicMock(side_effect=fig_generating_mock)
)
pair = "UNITTEST/BTC"
timerange = TimeRange(None, 'line', 0, -1000)
data = history.load_pair_history(pair=pair, timeframe='1m',
datadir=testdatadir, timerange=timerange)
data['enter_long'] = 0
data['exit_long'] = 0
data['enter_short'] = 0
data['exit_short'] = 0
timerange = TimeRange(None, "line", 0, -1000)
data = history.load_pair_history(
pair=pair, timeframe="1m", datadir=testdatadir, timerange=timerange
)
data["enter_long"] = 0
data["exit_long"] = 0
data["enter_short"] = 0
data["exit_short"] = 0
indicators1 = []
indicators2 = []
fig = generate_candlestick_graph(pair=pair, data=data, trades=None,
indicators1=indicators1, indicators2=indicators2)
fig = generate_candlestick_graph(
pair=pair, data=data, trades=None, indicators1=indicators1, indicators2=indicators2
)
assert isinstance(fig, go.Figure)
assert fig.layout.title.text == pair
figure = fig.layout.figure
@ -240,24 +249,28 @@ def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, t
def test_generate_candlestick_graph_no_trades(default_conf, mocker, testdatadir):
row_mock = mocker.patch('freqtrade.plot.plotting.add_indicators',
MagicMock(side_effect=fig_generating_mock))
trades_mock = mocker.patch('freqtrade.plot.plotting.plot_trades',
MagicMock(side_effect=fig_generating_mock))
pair = 'UNITTEST/BTC'
timerange = TimeRange(None, 'line', 0, -1000)
data = history.load_pair_history(pair=pair, timeframe='1m',
datadir=testdatadir, timerange=timerange)
row_mock = mocker.patch(
"freqtrade.plot.plotting.add_indicators", MagicMock(side_effect=fig_generating_mock)
)
trades_mock = mocker.patch(
"freqtrade.plot.plotting.plot_trades", MagicMock(side_effect=fig_generating_mock)
)
pair = "UNITTEST/BTC"
timerange = TimeRange(None, "line", 0, -1000)
data = history.load_pair_history(
pair=pair, timeframe="1m", datadir=testdatadir, timerange=timerange
)
strategy = StrategyResolver.load_strategy(default_conf)
# Generate buy/sell signals and indicators
data = strategy.analyze_ticker(data, {'pair': pair})
data = strategy.analyze_ticker(data, {"pair": pair})
indicators1 = []
indicators2 = []
fig = generate_candlestick_graph(pair=pair, data=data, trades=None,
indicators1=indicators1, indicators2=indicators2)
fig = generate_candlestick_graph(
pair=pair, data=data, trades=None, indicators1=indicators1, indicators2=indicators2
)
assert isinstance(fig, go.Figure)
assert fig.layout.title.text == pair
figure = fig.layout.figure
@ -273,12 +286,12 @@ def test_generate_candlestick_graph_no_trades(default_conf, mocker, testdatadir)
enter_long = find_trace_in_fig_data(figure.data, "enter_long")
assert isinstance(enter_long, go.Scatter)
# All buy-signals should be plotted
assert int(data['enter_long'].sum()) == len(enter_long.x)
assert int(data["enter_long"].sum()) == len(enter_long.x)
exit_long = find_trace_in_fig_data(figure.data, "exit_long")
assert isinstance(exit_long, go.Scatter)
# All buy-signals should be plotted
assert int(data['exit_long'].sum()) == len(exit_long.x)
assert int(data["exit_long"].sum()) == len(exit_long.x)
assert find_trace_in_fig_data(figure.data, "Bollinger Band")
@ -294,16 +307,15 @@ def test_generate_Plot_filename():
def test_generate_plot_file(mocker, caplog, user_dir):
fig = generate_empty_figure()
plot_mock = mocker.patch("freqtrade.plot.plotting.plot", MagicMock())
store_plot_file(fig, filename="freqtrade-plot-UNITTEST_BTC-5m.html",
directory=user_dir / "plot")
store_plot_file(
fig, filename="freqtrade-plot-UNITTEST_BTC-5m.html", directory=user_dir / "plot"
)
expected_fn = str(user_dir / "plot/freqtrade-plot-UNITTEST_BTC-5m.html")
assert plot_mock.call_count == 1
assert plot_mock.call_args[0][0] == fig
assert (plot_mock.call_args_list[0][1]['filename']
== expected_fn)
assert log_has(f"Stored plot as {expected_fn}",
caplog)
assert plot_mock.call_args_list[0][1]["filename"] == expected_fn
assert log_has(f"Stored plot as {expected_fn}", caplog)
def test_add_profit(testdatadir):
@ -311,15 +323,16 @@ def test_add_profit(testdatadir):
bt_data = load_backtest_data(filename)
timerange = TimeRange.parse_timerange("20180110-20180112")
df = history.load_pair_history(pair="TRX/BTC", timeframe='5m',
datadir=testdatadir, timerange=timerange)
df = history.load_pair_history(
pair="TRX/BTC", timeframe="5m", datadir=testdatadir, timerange=timerange
)
fig = generate_empty_figure()
cum_profits = create_cum_profit(df.set_index('date'),
bt_data[bt_data["pair"] == 'TRX/BTC'],
"cum_profits", timeframe="5m")
cum_profits = create_cum_profit(
df.set_index("date"), bt_data[bt_data["pair"] == "TRX/BTC"], "cum_profits", timeframe="5m"
)
fig1 = add_profit(fig, row=2, data=cum_profits, column='cum_profits', name='Profits')
fig1 = add_profit(fig, row=2, data=cum_profits, column="cum_profits", name="Profits")
figure = fig1.layout.figure
profits = find_trace_in_fig_data(figure.data, "Profits")
assert isinstance(profits, go.Scatter)
@ -331,22 +344,15 @@ def test_generate_profit_graph(testdatadir):
trades = load_backtest_data(filename)
timerange = TimeRange.parse_timerange("20180110-20180112")
pairs = ["TRX/BTC", "XLM/BTC"]
trades = trades[trades['close_date'] < pd.Timestamp('2018-01-12', tz='UTC')]
trades = trades[trades["close_date"] < pd.Timestamp("2018-01-12", tz="UTC")]
data = history.load_data(datadir=testdatadir,
pairs=pairs,
timeframe='5m',
timerange=timerange)
data = history.load_data(datadir=testdatadir, pairs=pairs, timeframe="5m", timerange=timerange)
trades = trades[trades['pair'].isin(pairs)]
trades = trades[trades["pair"].isin(pairs)]
fig = generate_profit_graph(
pairs,
data,
trades,
timeframe="5m",
stake_currency='BTC',
starting_balance=0)
pairs, data, trades, timeframe="5m", stake_currency="BTC", starting_balance=0
)
assert isinstance(fig, go.Figure)
assert fig.layout.title.text == "Freqtrade Profit plot"
@ -379,40 +385,48 @@ def test_generate_profit_graph(testdatadir):
with pytest.raises(OperationalException, match=r"No trades found.*"):
# Pair cannot be empty - so it's an empty dataframe.
generate_profit_graph(pairs, data, trades.loc[trades['pair'].isnull()], timeframe="5m",
stake_currency='BTC', starting_balance=0)
generate_profit_graph(
pairs,
data,
trades.loc[trades["pair"].isnull()],
timeframe="5m",
stake_currency="BTC",
starting_balance=0,
)
def test_start_plot_dataframe(mocker):
aup = mocker.patch("freqtrade.plot.plotting.load_and_plot_trades", MagicMock())
args = [
"plot-dataframe",
"--config", "tests/testdata/testconfigs/main_test_config.json",
"--pairs", "ETH/BTC"
"--config",
"tests/testdata/testconfigs/main_test_config.json",
"--pairs",
"ETH/BTC",
]
start_plot_dataframe(get_args(args))
assert aup.call_count == 1
called_config = aup.call_args_list[0][0][0]
assert "pairs" in called_config
assert called_config['pairs'] == ["ETH/BTC"]
assert called_config["pairs"] == ["ETH/BTC"]
def test_load_and_plot_trades(default_conf, mocker, caplog, testdatadir):
patch_exchange(mocker)
default_conf['trade_source'] = 'file'
default_conf['exportfilename'] = testdatadir / "backtest-result.json"
default_conf['indicators1'] = ["sma5", "ema10"]
default_conf['indicators2'] = ["macd"]
default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"]
default_conf["trade_source"] = "file"
default_conf["exportfilename"] = testdatadir / "backtest-result.json"
default_conf["indicators1"] = ["sma5", "ema10"]
default_conf["indicators2"] = ["macd"]
default_conf["pairs"] = ["ETH/BTC", "LTC/BTC"]
candle_mock = MagicMock()
store_mock = MagicMock()
mocker.patch.multiple(
"freqtrade.plot.plotting",
generate_candlestick_graph=candle_mock,
store_plot_file=store_mock
store_plot_file=store_mock,
)
load_and_plot_trades(default_conf)
@ -420,8 +434,8 @@ def test_load_and_plot_trades(default_conf, mocker, caplog, testdatadir):
assert candle_mock.call_count == 2
assert store_mock.call_count == 2
assert candle_mock.call_args_list[0][1]['indicators1'] == ['sma5', 'ema10']
assert candle_mock.call_args_list[0][1]['indicators2'] == ['macd']
assert candle_mock.call_args_list[0][1]["indicators1"] == ["sma5", "ema10"]
assert candle_mock.call_args_list[0][1]["indicators2"] == ["macd"]
assert log_has("End of plotting process. 2 plots generated", caplog)
@ -430,49 +444,46 @@ def test_start_plot_profit(mocker):
aup = mocker.patch("freqtrade.plot.plotting.plot_profit", MagicMock())
args = [
"plot-profit",
"--config", "tests/testdata/testconfigs/main_test_config.json",
"--pairs", "ETH/BTC"
"--config",
"tests/testdata/testconfigs/main_test_config.json",
"--pairs",
"ETH/BTC",
]
start_plot_profit(get_args(args))
assert aup.call_count == 1
called_config = aup.call_args_list[0][0][0]
assert "pairs" in called_config
assert called_config['pairs'] == ["ETH/BTC"]
assert called_config["pairs"] == ["ETH/BTC"]
def test_start_plot_profit_error(mocker):
args = [
'plot-profit',
'--pairs', 'ETH/BTC'
]
args = ["plot-profit", "--pairs", "ETH/BTC"]
argsp = get_args(args)
# Make sure we use no config. Details: #2241
# not resetting config causes random failures if config.json exists
argsp['config'] = []
argsp["config"] = []
with pytest.raises(OperationalException):
start_plot_profit(argsp)
def test_plot_profit(default_conf, mocker, testdatadir):
patch_exchange(mocker)
default_conf['trade_source'] = 'file'
default_conf['exportfilename'] = testdatadir / 'backtest-result_test_nofile.json'
default_conf['pairs'] = ['ETH/BTC', 'LTC/BTC']
default_conf["trade_source"] = "file"
default_conf["exportfilename"] = testdatadir / "backtest-result_test_nofile.json"
default_conf["pairs"] = ["ETH/BTC", "LTC/BTC"]
profit_mock = MagicMock()
store_mock = MagicMock()
mocker.patch.multiple(
"freqtrade.plot.plotting",
generate_profit_graph=profit_mock,
store_plot_file=store_mock
"freqtrade.plot.plotting", generate_profit_graph=profit_mock, store_plot_file=store_mock
)
with pytest.raises(OperationalException,
match=r"No trades found, cannot generate Profit-plot.*"):
with pytest.raises(
OperationalException, match=r"No trades found, cannot generate Profit-plot.*"
):
plot_profit(default_conf)
default_conf['exportfilename'] = testdatadir / "backtest_results/backtest-result.json"
default_conf["exportfilename"] = testdatadir / "backtest_results/backtest-result.json"
plot_profit(default_conf)
@ -480,53 +491,78 @@ def test_plot_profit(default_conf, mocker, testdatadir):
assert profit_mock.call_count == 1
assert store_mock.call_count == 1
assert profit_mock.call_args_list[0][0][0] == default_conf['pairs']
assert store_mock.call_args_list[0][1]['auto_open'] is False
assert profit_mock.call_args_list[0][0][0] == default_conf["pairs"]
assert store_mock.call_args_list[0][1]["auto_open"] is False
del default_conf['timeframe']
del default_conf["timeframe"]
with pytest.raises(OperationalException, match=r"Timeframe must be set.*--timeframe.*"):
plot_profit(default_conf)
@pytest.mark.parametrize("ind1,ind2,plot_conf,exp", [
# No indicators, use plot_conf
([], [], {},
{'main_plot': {'sma': {}, 'ema3': {}, 'ema5': {}},
'subplots': {'Other': {'macd': {}, 'macdsignal': {}}}}),
# use indicators
(['sma', 'ema3'], ['macd'], {},
{'main_plot': {'sma': {}, 'ema3': {}}, 'subplots': {'Other': {'macd': {}}}}),
# only main_plot - adds empty subplots
([], [], {'main_plot': {'sma': {}}},
{'main_plot': {'sma': {}}, 'subplots': {}}),
# Main and subplots
([], [], {'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}},
{'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}}),
# no main_plot, adds empty main_plot
([], [], {'subplots': {'RSI': {'rsi': {'color': 'red'}}}},
{'main_plot': {}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}}),
# indicator 1 / 2 should have prevalence
(['sma', 'ema3'], ['macd'],
{'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}},
{'main_plot': {'sma': {}, 'ema3': {}}, 'subplots': {'Other': {'macd': {}}}}
),
# indicator 1 - overrides plot_config main_plot
(['sma', 'ema3'], [],
{'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}},
{'main_plot': {'sma': {}, 'ema3': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}}
),
# indicator 2 - overrides plot_config subplots
([], ['macd', 'macd_signal'],
{'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}},
{'main_plot': {'sma': {}}, 'subplots': {'Other': {'macd': {}, 'macd_signal': {}}}}
),
])
@pytest.mark.parametrize(
"ind1,ind2,plot_conf,exp",
[
# No indicators, use plot_conf
(
[],
[],
{},
{
"main_plot": {"sma": {}, "ema3": {}, "ema5": {}},
"subplots": {"Other": {"macd": {}, "macdsignal": {}}},
},
),
# use indicators
(
["sma", "ema3"],
["macd"],
{},
{"main_plot": {"sma": {}, "ema3": {}}, "subplots": {"Other": {"macd": {}}}},
),
# only main_plot - adds empty subplots
([], [], {"main_plot": {"sma": {}}}, {"main_plot": {"sma": {}}, "subplots": {}}),
# Main and subplots
(
[],
[],
{"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
{"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
),
# no main_plot, adds empty main_plot
(
[],
[],
{"subplots": {"RSI": {"rsi": {"color": "red"}}}},
{"main_plot": {}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
),
# indicator 1 / 2 should have prevalence
(
["sma", "ema3"],
["macd"],
{"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
{"main_plot": {"sma": {}, "ema3": {}}, "subplots": {"Other": {"macd": {}}}},
),
# indicator 1 - overrides plot_config main_plot
(
["sma", "ema3"],
[],
{"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
{"main_plot": {"sma": {}, "ema3": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
),
# indicator 2 - overrides plot_config subplots
(
[],
["macd", "macd_signal"],
{"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
{"main_plot": {"sma": {}}, "subplots": {"Other": {"macd": {}, "macd_signal": {}}}},
),
],
)
def test_create_plotconfig(ind1, ind2, plot_conf, exp):
res = create_plotconfig(ind1, ind2, plot_conf)
assert 'main_plot' in res
assert 'subplots' in res
assert isinstance(res['main_plot'], dict)
assert isinstance(res['subplots'], dict)
assert "main_plot" in res
assert "subplots" in res
assert isinstance(res["main_plot"], dict)
assert isinstance(res["subplots"], dict)
assert res == exp

View File

@ -3,12 +3,14 @@ import talib.abstract as ta
def test_talib_bollingerbands_near_zero_values():
inputs = pd.DataFrame([
{'close': 0.00000010},
{'close': 0.00000011},
{'close': 0.00000012},
{'close': 0.00000013},
{'close': 0.00000014}
])
inputs = pd.DataFrame(
[
{"close": 0.00000010},
{"close": 0.00000011},
{"close": 0.00000012},
{"close": 0.00000013},
{"close": 0.00000014},
]
)
bollinger = ta.BBANDS(inputs, matype=0, timeperiod=2)
assert bollinger['upperband'][3] != bollinger['middleband'][3]
assert bollinger["upperband"][3] != bollinger["middleband"][3]

View File

@ -18,185 +18,179 @@ from tests.conftest import (
def test_sync_wallet_at_boot(mocker, default_conf):
default_conf['dry_run'] = False
default_conf["dry_run"] = False
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value={
"BNT": {
"free": 1.0,
"used": 2.0,
"total": 3.0
},
"GAS": {
"free": 0.260739,
"used": 0.0,
"total": 0.260739
},
"USDT": {
"free": 20,
"used": 20,
"total": 40
},
})
get_balances=MagicMock(
return_value={
"BNT": {"free": 1.0, "used": 2.0, "total": 3.0},
"GAS": {"free": 0.260739, "used": 0.0, "total": 0.260739},
"USDT": {"free": 20, "used": 20, "total": 40},
}
),
)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
assert len(freqtrade.wallets._wallets) == 3
assert freqtrade.wallets._wallets['BNT'].free == 1.0
assert freqtrade.wallets._wallets['BNT'].used == 2.0
assert freqtrade.wallets._wallets['BNT'].total == 3.0
assert freqtrade.wallets._wallets['GAS'].free == 0.260739
assert freqtrade.wallets._wallets['GAS'].used == 0.0
assert freqtrade.wallets._wallets['GAS'].total == 0.260739
assert freqtrade.wallets.get_free('BNT') == 1.0
assert 'USDT' in freqtrade.wallets._wallets
assert freqtrade.wallets._wallets["BNT"].free == 1.0
assert freqtrade.wallets._wallets["BNT"].used == 2.0
assert freqtrade.wallets._wallets["BNT"].total == 3.0
assert freqtrade.wallets._wallets["GAS"].free == 0.260739
assert freqtrade.wallets._wallets["GAS"].used == 0.0
assert freqtrade.wallets._wallets["GAS"].total == 0.260739
assert freqtrade.wallets.get_free("BNT") == 1.0
assert "USDT" in freqtrade.wallets._wallets
assert freqtrade.wallets._last_wallet_refresh is not None
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value={
"BNT": {
"free": 1.2,
"used": 1.9,
"total": 3.5
},
"GAS": {
"free": 0.270739,
"used": 0.1,
"total": 0.260439
},
})
get_balances=MagicMock(
return_value={
"BNT": {"free": 1.2, "used": 1.9, "total": 3.5},
"GAS": {"free": 0.270739, "used": 0.1, "total": 0.260439},
}
),
)
freqtrade.wallets.update()
# USDT is missing from the 2nd result - so should not be in this either.
assert len(freqtrade.wallets._wallets) == 2
assert freqtrade.wallets._wallets['BNT'].free == 1.2
assert freqtrade.wallets._wallets['BNT'].used == 1.9
assert freqtrade.wallets._wallets['BNT'].total == 3.5
assert freqtrade.wallets._wallets['GAS'].free == 0.270739
assert freqtrade.wallets._wallets['GAS'].used == 0.1
assert freqtrade.wallets._wallets['GAS'].total == 0.260439
assert freqtrade.wallets.get_free('GAS') == 0.270739
assert freqtrade.wallets.get_used('GAS') == 0.1
assert freqtrade.wallets.get_total('GAS') == 0.260439
update_mock = mocker.patch('freqtrade.wallets.Wallets._update_live')
assert freqtrade.wallets._wallets["BNT"].free == 1.2
assert freqtrade.wallets._wallets["BNT"].used == 1.9
assert freqtrade.wallets._wallets["BNT"].total == 3.5
assert freqtrade.wallets._wallets["GAS"].free == 0.270739
assert freqtrade.wallets._wallets["GAS"].used == 0.1
assert freqtrade.wallets._wallets["GAS"].total == 0.260439
assert freqtrade.wallets.get_free("GAS") == 0.270739
assert freqtrade.wallets.get_used("GAS") == 0.1
assert freqtrade.wallets.get_total("GAS") == 0.260439
update_mock = mocker.patch("freqtrade.wallets.Wallets._update_live")
freqtrade.wallets.update(False)
assert update_mock.call_count == 0
freqtrade.wallets.update()
assert update_mock.call_count == 1
assert freqtrade.wallets.get_free('NOCURRENCY') == 0
assert freqtrade.wallets.get_used('NOCURRENCY') == 0
assert freqtrade.wallets.get_total('NOCURRENCY') == 0
assert freqtrade.wallets.get_free("NOCURRENCY") == 0
assert freqtrade.wallets.get_used("NOCURRENCY") == 0
assert freqtrade.wallets.get_total("NOCURRENCY") == 0
def test_sync_wallet_missing_data(mocker, default_conf):
default_conf['dry_run'] = False
default_conf["dry_run"] = False
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value={
"BNT": {
"free": 1.0,
"used": 2.0,
"total": 3.0
},
"GAS": {
"free": 0.260739,
"total": 0.260739
},
})
get_balances=MagicMock(
return_value={
"BNT": {"free": 1.0, "used": 2.0, "total": 3.0},
"GAS": {"free": 0.260739, "total": 0.260739},
}
),
)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
assert len(freqtrade.wallets._wallets) == 2
assert freqtrade.wallets._wallets['BNT'].free == 1.0
assert freqtrade.wallets._wallets['BNT'].used == 2.0
assert freqtrade.wallets._wallets['BNT'].total == 3.0
assert freqtrade.wallets._wallets['GAS'].free == 0.260739
assert freqtrade.wallets._wallets['GAS'].used is None
assert freqtrade.wallets._wallets['GAS'].total == 0.260739
assert freqtrade.wallets.get_free('GAS') == 0.260739
assert freqtrade.wallets._wallets["BNT"].free == 1.0
assert freqtrade.wallets._wallets["BNT"].used == 2.0
assert freqtrade.wallets._wallets["BNT"].total == 3.0
assert freqtrade.wallets._wallets["GAS"].free == 0.260739
assert freqtrade.wallets._wallets["GAS"].used is None
assert freqtrade.wallets._wallets["GAS"].total == 0.260739
assert freqtrade.wallets.get_free("GAS") == 0.260739
def test_get_trade_stake_amount_no_stake_amount(default_conf, mocker) -> None:
patch_wallet(mocker, free=default_conf['stake_amount'] * 0.5)
patch_wallet(mocker, free=default_conf["stake_amount"] * 0.5)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
freqtrade.wallets.get_trade_stake_amount('ETH/BTC', 1)
with pytest.raises(DependencyException, match=r".*stake amount.*"):
freqtrade.wallets.get_trade_stake_amount("ETH/BTC", 1)
@pytest.mark.parametrize("balance_ratio,capital,result1,result2", [
(1, None, 50, 66.66666),
(0.99, None, 49.5, 66.0),
(0.50, None, 25, 33.3333),
# Tests with capital ignore balance_ratio
(1, 100, 50, 0.0),
(0.99, 200, 50, 66.66666),
(0.99, 150, 50, 50),
(0.50, 50, 25, 0.0),
(0.50, 10, 5, 0.0),
])
def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_ratio, capital,
result1, result2, limit_buy_order_open,
fee, mocker) -> None:
@pytest.mark.parametrize(
"balance_ratio,capital,result1,result2",
[
(1, None, 50, 66.66666),
(0.99, None, 49.5, 66.0),
(0.50, None, 25, 33.3333),
# Tests with capital ignore balance_ratio
(1, 100, 50, 0.0),
(0.99, 200, 50, 66.66666),
(0.99, 150, 50, 50),
(0.50, 50, 25, 0.0),
(0.50, 10, 5, 0.0),
],
)
def test_get_trade_stake_amount_unlimited_amount(
default_conf,
ticker,
balance_ratio,
capital,
result1,
result2,
limit_buy_order_open,
fee,
mocker,
) -> None:
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
create_order=MagicMock(return_value=limit_buy_order_open),
get_fee=fee
get_fee=fee,
)
conf = deepcopy(default_conf)
conf['stake_amount'] = UNLIMITED_STAKE_AMOUNT
conf['dry_run_wallet'] = 100
conf['tradable_balance_ratio'] = balance_ratio
conf["stake_amount"] = UNLIMITED_STAKE_AMOUNT
conf["dry_run_wallet"] = 100
conf["tradable_balance_ratio"] = balance_ratio
if capital is not None:
conf['available_capital'] = capital
conf["available_capital"] = capital
freqtrade = get_patched_freqtradebot(mocker, conf)
# no open trades, order amount should be 'balance / max_open_trades'
result = freqtrade.wallets.get_trade_stake_amount('ETH/USDT', 2)
result = freqtrade.wallets.get_trade_stake_amount("ETH/USDT", 2)
assert result == result1
# create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)'
freqtrade.execute_entry('ETH/USDT', result)
freqtrade.execute_entry("ETH/USDT", result)
result = freqtrade.wallets.get_trade_stake_amount('LTC/USDT', 2)
result = freqtrade.wallets.get_trade_stake_amount("LTC/USDT", 2)
assert result == result1
# create 2 trades, order amount should be None
freqtrade.execute_entry('LTC/BTC', result)
freqtrade.execute_entry("LTC/BTC", result)
result = freqtrade.wallets.get_trade_stake_amount('XRP/USDT', 2)
result = freqtrade.wallets.get_trade_stake_amount("XRP/USDT", 2)
assert result == 0
freqtrade.config['dry_run_wallet'] = 200
freqtrade.config["dry_run_wallet"] = 200
freqtrade.wallets.start_cap = 200
result = freqtrade.wallets.get_trade_stake_amount('XRP/USDT', 3)
result = freqtrade.wallets.get_trade_stake_amount("XRP/USDT", 3)
assert round(result, 4) == round(result2, 4)
# set max_open_trades = None, so do not trade
result = freqtrade.wallets.get_trade_stake_amount('NEO/USDT', 0)
result = freqtrade.wallets.get_trade_stake_amount("NEO/USDT", 0)
assert result == 0
@pytest.mark.parametrize('stake_amount,min_stake,stake_available,max_stake,trade_amount,expected', [
(22, 11, 50, 10000, None, 22),
(100, 11, 500, 10000, None, 100),
(1000, 11, 500, 10000, None, 500), # Above stake_available
(700, 11, 1000, 400, None, 400), # Above max_stake, below stake available
(20, 15, 10, 10000, None, 0), # Minimum stake > stake_available
(9, 11, 100, 10000, None, 11), # Below min stake
(1, 15, 10, 10000, None, 0), # Below min stake and min_stake > stake_available
(20, 50, 100, 10000, None, 0), # Below min stake and stake * 1.3 > min_stake
(1000, None, 1000, 10000, None, 1000), # No min-stake-amount could be determined
(2000, 15, 2000, 3000, 1500, 1500), # Rebuy - resulting in too high stake amount. Adjusting.
])
@pytest.mark.parametrize(
"stake_amount,min_stake,stake_available,max_stake,trade_amount,expected",
[
(22, 11, 50, 10000, None, 22),
(100, 11, 500, 10000, None, 100),
(1000, 11, 500, 10000, None, 500), # Above stake_available
(700, 11, 1000, 400, None, 400), # Above max_stake, below stake available
(20, 15, 10, 10000, None, 0), # Minimum stake > stake_available
(9, 11, 100, 10000, None, 11), # Below min stake
(1, 15, 10, 10000, None, 0), # Below min stake and min_stake > stake_available
(20, 50, 100, 10000, None, 0), # Below min stake and stake * 1.3 > min_stake
(1000, None, 1000, 10000, None, 1000), # No min-stake-amount could be determined
# Rebuy - resulting in too high stake amount. Adjusting.
(2000, 15, 2000, 3000, 1500, 1500),
],
)
def test_validate_stake_amount(
mocker,
default_conf,
@ -209,33 +203,41 @@ def test_validate_stake_amount(
):
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch("freqtrade.wallets.Wallets.get_available_stake_amount",
return_value=stake_available)
mocker.patch(
"freqtrade.wallets.Wallets.get_available_stake_amount", return_value=stake_available
)
res = freqtrade.wallets.validate_stake_amount(
'XRP/USDT', stake_amount, min_stake, max_stake, trade_amount)
"XRP/USDT", stake_amount, min_stake, max_stake, trade_amount
)
assert res == expected
@pytest.mark.parametrize('available_capital,closed_profit,open_stakes,free,expected', [
(None, 10, 100, 910, 1000),
(None, 0, 0, 2500, 2500),
(None, 500, 0, 2500, 2000),
(None, 500, 0, 2500, 2000),
(None, -70, 0, 1930, 2000),
# Only available balance matters when it's set.
(100, 0, 0, 0, 100),
(1000, 0, 2, 5, 1000),
(1235, 2250, 2, 5, 1235),
(1235, -2250, 2, 5, 1235),
])
def test_get_starting_balance(mocker, default_conf, available_capital, closed_profit,
open_stakes, free, expected):
@pytest.mark.parametrize(
"available_capital,closed_profit,open_stakes,free,expected",
[
(None, 10, 100, 910, 1000),
(None, 0, 0, 2500, 2500),
(None, 500, 0, 2500, 2000),
(None, 500, 0, 2500, 2000),
(None, -70, 0, 1930, 2000),
# Only available balance matters when it's set.
(100, 0, 0, 0, 100),
(1000, 0, 2, 5, 1000),
(1235, 2250, 2, 5, 1235),
(1235, -2250, 2, 5, 1235),
],
)
def test_get_starting_balance(
mocker, default_conf, available_capital, closed_profit, open_stakes, free, expected
):
if available_capital:
default_conf['available_capital'] = available_capital
mocker.patch("freqtrade.persistence.models.Trade.get_total_closed_profit",
return_value=closed_profit)
mocker.patch("freqtrade.persistence.models.Trade.total_open_trades_stakes",
return_value=open_stakes)
default_conf["available_capital"] = available_capital
mocker.patch(
"freqtrade.persistence.models.Trade.get_total_closed_profit", return_value=closed_profit
)
mocker.patch(
"freqtrade.persistence.models.Trade.total_open_trades_stakes", return_value=open_stakes
)
mocker.patch("freqtrade.wallets.Wallets.get_free", return_value=free)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
@ -244,9 +246,9 @@ def test_get_starting_balance(mocker, default_conf, available_capital, closed_pr
def test_sync_wallet_futures_live(mocker, default_conf):
default_conf['dry_run'] = False
default_conf['trading_mode'] = 'futures'
default_conf['margin_mode'] = 'isolated'
default_conf["dry_run"] = False
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
mock_result = [
{
"symbol": "ETH/USDT:USDT",
@ -267,8 +269,8 @@ def test_sync_wallet_futures_live(mocker, default_conf):
"markPrice": 2896.41,
"collateral": 20,
"marginType": "isolated",
"side": 'short',
"percentage": None
"side": "short",
"percentage": None,
},
{
"symbol": "ADA/USDT:USDT",
@ -289,8 +291,8 @@ def test_sync_wallet_futures_live(mocker, default_conf):
"markPrice": 0.91,
"collateral": 20,
"marginType": "isolated",
"side": 'short',
"percentage": None
"side": "short",
"percentage": None,
},
{
# Closed position
@ -312,20 +314,18 @@ def test_sync_wallet_futures_live(mocker, default_conf):
"markPrice": 15.41,
"collateral": 0.0,
"marginType": "isolated",
"side": 'short',
"percentage": None
}
"side": "short",
"percentage": None,
},
]
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value={
"USDT": {
"free": 900,
"used": 100,
"total": 1000
},
}),
fetch_positions=MagicMock(return_value=mock_result)
get_balances=MagicMock(
return_value={
"USDT": {"free": 900, "used": 100, "total": 1000},
}
),
fetch_positions=MagicMock(return_value=mock_result),
)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
@ -333,23 +333,23 @@ def test_sync_wallet_futures_live(mocker, default_conf):
assert len(freqtrade.wallets._wallets) == 1
assert len(freqtrade.wallets._positions) == 2
assert 'USDT' in freqtrade.wallets._wallets
assert 'ETH/USDT:USDT' in freqtrade.wallets._positions
assert "USDT" in freqtrade.wallets._wallets
assert "ETH/USDT:USDT" in freqtrade.wallets._positions
assert freqtrade.wallets._last_wallet_refresh is not None
# Remove ETH/USDT:USDT position
del mock_result[0]
freqtrade.wallets.update()
assert len(freqtrade.wallets._positions) == 1
assert 'ETH/USDT:USDT' not in freqtrade.wallets._positions
assert "ETH/USDT:USDT" not in freqtrade.wallets._positions
def test_sync_wallet_dry(mocker, default_conf_usdt, fee):
default_conf_usdt['dry_run'] = True
default_conf_usdt["dry_run"] = True
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
assert len(freqtrade.wallets._wallets) == 1
assert len(freqtrade.wallets._positions) == 0
assert freqtrade.wallets.get_total('USDT') == 1000
assert freqtrade.wallets.get_total("USDT") == 1000
create_mock_trades_usdt(fee, is_short=None)
@ -358,23 +358,23 @@ def test_sync_wallet_dry(mocker, default_conf_usdt, fee):
assert len(freqtrade.wallets._wallets) == 5
assert len(freqtrade.wallets._positions) == 0
bal = freqtrade.wallets.get_all_balances()
assert bal['NEO'].total == 10
assert bal['XRP'].total == 10
assert bal['LTC'].total == 2
assert bal['USDT'].total == 922.74
assert bal["NEO"].total == 10
assert bal["XRP"].total == 10
assert bal["LTC"].total == 2
assert bal["USDT"].total == 922.74
assert freqtrade.wallets.get_starting_balance() == default_conf_usdt['dry_run_wallet']
total = freqtrade.wallets.get_total('LTC')
free = freqtrade.wallets.get_free('LTC')
used = freqtrade.wallets.get_used('LTC')
assert freqtrade.wallets.get_starting_balance() == default_conf_usdt["dry_run_wallet"]
total = freqtrade.wallets.get_total("LTC")
free = freqtrade.wallets.get_free("LTC")
used = freqtrade.wallets.get_used("LTC")
assert free != 0
assert free + used == total
def test_sync_wallet_futures_dry(mocker, default_conf, fee):
default_conf['dry_run'] = True
default_conf['trading_mode'] = 'futures'
default_conf['margin_mode'] = 'isolated'
default_conf["dry_run"] = True
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
freqtrade = get_patched_freqtradebot(mocker, default_conf)
assert len(freqtrade.wallets._wallets) == 1
assert len(freqtrade.wallets._positions) == 0
@ -386,15 +386,15 @@ def test_sync_wallet_futures_dry(mocker, default_conf, fee):
assert len(freqtrade.wallets._wallets) == 1
assert len(freqtrade.wallets._positions) == 4
positions = freqtrade.wallets.get_all_positions()
assert positions['ETH/BTC'].side == 'short'
assert positions['ETC/BTC'].side == 'long'
assert positions['XRP/BTC'].side == 'long'
assert positions['LTC/BTC'].side == 'short'
assert positions["ETH/BTC"].side == "short"
assert positions["ETC/BTC"].side == "long"
assert positions["XRP/BTC"].side == "long"
assert positions["LTC/BTC"].side == "short"
assert freqtrade.wallets.get_starting_balance() == default_conf['dry_run_wallet']
total = freqtrade.wallets.get_total('BTC')
free = freqtrade.wallets.get_free('BTC')
used = freqtrade.wallets.get_used('BTC')
assert freqtrade.wallets.get_starting_balance() == default_conf["dry_run_wallet"]
total = freqtrade.wallets.get_total("BTC")
free = freqtrade.wallets.get_free("BTC")
used = freqtrade.wallets.get_used("BTC")
assert free + used == total
@ -421,14 +421,14 @@ def test_check_exit_amount(mocker, default_conf, fee):
def test_check_exit_amount_futures(mocker, default_conf, fee):
default_conf['trading_mode'] = 'futures'
default_conf['margin_mode'] = 'isolated'
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
freqtrade = get_patched_freqtradebot(mocker, default_conf)
total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=123)
create_mock_trades(fee, is_short=None)
trade = Trade.session.scalars(select(Trade)).first()
trade.trading_mode = 'futures'
trade.trading_mode = "futures"
assert trade.amount == 123
assert freqtrade.wallets.check_exit_amount(trade) is True