Merge branch 'develop' into no-percent-1
This commit is contained in:
@@ -447,11 +447,6 @@ def test_create_datadir_failed(caplog):
|
||||
|
||||
|
||||
def test_create_datadir(caplog, mocker):
|
||||
# Ensure that caplog is empty before starting ...
|
||||
# Should prevent random failures.
|
||||
caplog.clear()
|
||||
# Added assert here to analyze random test-failures ...
|
||||
assert len(caplog.record_tuples) == 0
|
||||
|
||||
cud = mocker.patch("freqtrade.commands.deploy_commands.create_userdata_dir", MagicMock())
|
||||
csf = mocker.patch("freqtrade.commands.deploy_commands.copy_sample_files", MagicMock())
|
||||
@@ -464,7 +459,6 @@ def test_create_datadir(caplog, mocker):
|
||||
|
||||
assert cud.call_count == 1
|
||||
assert csf.call_count == 1
|
||||
assert len(caplog.record_tuples) == 0
|
||||
|
||||
|
||||
def test_start_new_strategy(mocker, caplog):
|
||||
|
@@ -2,15 +2,17 @@ from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from arrow import Arrow
|
||||
from pandas import DataFrame, DateOffset, to_datetime
|
||||
from pandas import DataFrame, DateOffset, to_datetime, Timestamp
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.data.btanalysis import (BT_DATA_COLUMNS,
|
||||
analyze_trade_parallelism,
|
||||
calculate_max_drawdown,
|
||||
combine_tickers_with_mean,
|
||||
create_cum_profit,
|
||||
extract_trades_of_period,
|
||||
load_backtest_data, load_trades,
|
||||
load_trades_from_db, analyze_trade_parallelism)
|
||||
load_trades_from_db)
|
||||
from freqtrade.data.history import load_data, load_pair_history
|
||||
from tests.test_persistence import create_mock_trades
|
||||
|
||||
@@ -163,3 +165,17 @@ def test_create_cum_profit1(testdatadir):
|
||||
assert "cum_profits" in cum_profits.columns
|
||||
assert cum_profits.iloc[0]['cum_profits'] == 0
|
||||
assert cum_profits.iloc[-1]['cum_profits'] == 0.0798005
|
||||
|
||||
|
||||
def test_calculate_max_drawdown(testdatadir):
|
||||
filename = testdatadir / "backtest-result_test.json"
|
||||
bt_data = load_backtest_data(filename)
|
||||
drawdown, h, low = calculate_max_drawdown(bt_data)
|
||||
assert isinstance(drawdown, float)
|
||||
assert pytest.approx(drawdown) == 0.21142322
|
||||
assert isinstance(h, Timestamp)
|
||||
assert isinstance(low, Timestamp)
|
||||
assert h == Timestamp('2018-01-24 14:25:00', tz='UTC')
|
||||
assert low == Timestamp('2018-01-30 04:45:00', tz='UTC')
|
||||
with pytest.raises(ValueError, match='Trade dataframe empty.'):
|
||||
drawdown, h, low = calculate_max_drawdown(DataFrame())
|
||||
|
@@ -511,6 +511,22 @@ def test_validate_pairs_stakecompatibility(default_conf, mocker, caplog):
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker, caplog):
|
||||
api_mock = MagicMock()
|
||||
default_conf['stake_currency'] = ''
|
||||
type(api_mock).markets = PropertyMock(return_value={
|
||||
'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'},
|
||||
'XRP/BTC': {'quote': 'BTC'}, 'NEO/BTC': {'quote': 'BTC'},
|
||||
'HELLO-WORLD': {'quote': 'BTC'},
|
||||
})
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
def test_validate_pairs_stakecompatibility_fail(default_conf, mocker, caplog):
|
||||
default_conf['exchange']['pair_whitelist'].append('HELLO-WORLD')
|
||||
api_mock = MagicMock()
|
||||
|
@@ -369,6 +369,42 @@ def test_sharpe_loss_daily_prefers_higher_profits(default_conf, hyperopt_results
|
||||
assert under > correct
|
||||
|
||||
|
||||
def test_sortino_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None:
|
||||
results_over = hyperopt_results.copy()
|
||||
results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2
|
||||
results_under = hyperopt_results.copy()
|
||||
results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2
|
||||
|
||||
default_conf.update({'hyperopt_loss': 'SortinoHyperOptLoss'})
|
||||
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
|
||||
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
|
||||
datetime(2019, 1, 1), datetime(2019, 5, 1))
|
||||
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
|
||||
datetime(2019, 1, 1), datetime(2019, 5, 1))
|
||||
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
|
||||
datetime(2019, 1, 1), datetime(2019, 5, 1))
|
||||
assert over < correct
|
||||
assert under > correct
|
||||
|
||||
|
||||
def test_sortino_loss_daily_prefers_higher_profits(default_conf, hyperopt_results) -> None:
|
||||
results_over = hyperopt_results.copy()
|
||||
results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2
|
||||
results_under = hyperopt_results.copy()
|
||||
results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2
|
||||
|
||||
default_conf.update({'hyperopt_loss': 'SortinoHyperOptLossDaily'})
|
||||
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
|
||||
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
|
||||
datetime(2019, 1, 1), datetime(2019, 5, 1))
|
||||
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
|
||||
datetime(2019, 1, 1), datetime(2019, 5, 1))
|
||||
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
|
||||
datetime(2019, 1, 1), datetime(2019, 5, 1))
|
||||
assert over < correct
|
||||
assert under > correct
|
||||
|
||||
|
||||
def test_onlyprofit_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None:
|
||||
results_over = hyperopt_results.copy()
|
||||
results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2
|
||||
@@ -390,17 +426,27 @@ def test_onlyprofit_loss_prefers_higher_profits(default_conf, hyperopt_results)
|
||||
def test_log_results_if_loss_improves(hyperopt, capsys) -> None:
|
||||
hyperopt.current_best_loss = 2
|
||||
hyperopt.total_epochs = 2
|
||||
|
||||
hyperopt.print_results(
|
||||
{
|
||||
'is_best': True,
|
||||
'loss': 1,
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
},
|
||||
'total_profit': 0,
|
||||
'current_epoch': 2, # This starts from 1 (in a human-friendly manner)
|
||||
'results_explanation': 'foo.',
|
||||
'is_initial_point': False
|
||||
'is_initial_point': False,
|
||||
'is_best': True
|
||||
}
|
||||
)
|
||||
out, err = capsys.readouterr()
|
||||
assert ' 2/2: foo. Objective: 1.00000' in out
|
||||
assert all(x in out
|
||||
for x in ["Best", "2/2", " 1", "0.10%", "0.00100000 BTC (1.00%)", "20.0 m"])
|
||||
|
||||
|
||||
def test_no_log_if_loss_does_not_improve(hyperopt, caplog) -> None:
|
||||
@@ -422,13 +468,11 @@ def test_save_trials_saves_trials(mocker, hyperopt, testdatadir, caplog) -> None
|
||||
|
||||
hyperopt.trials = trials
|
||||
hyperopt.save_trials(final=True)
|
||||
assert log_has("Saving 1 epoch.", caplog)
|
||||
assert log_has(f"1 epoch saved to '{trials_file}'.", caplog)
|
||||
mock_dump.assert_called_once()
|
||||
|
||||
hyperopt.trials = trials + trials
|
||||
hyperopt.save_trials(final=True)
|
||||
assert log_has("Saving 2 epochs.", caplog)
|
||||
assert log_has(f"2 epochs saved to '{trials_file}'.", caplog)
|
||||
|
||||
|
||||
@@ -466,8 +510,18 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None:
|
||||
|
||||
parallel = mocker.patch(
|
||||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result',
|
||||
'params': {'buy': {}, 'sell': {}, 'roi': {}, 'stoploss': 0.0}}])
|
||||
MagicMock(return_value=[{
|
||||
'loss': 1, 'results_explanation': 'foo result',
|
||||
'params': {'buy': {}, 'sell': {}, 'roi': {}, 'stoploss': 0.0},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
},
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
# Co-test loading ticker-interval from strategy
|
||||
@@ -761,11 +815,23 @@ def test_print_json_spaces_all(mocker, default_conf, caplog, capsys) -> None:
|
||||
|
||||
parallel = mocker.patch(
|
||||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||
'params_details': {'buy': {'mfi-value': None},
|
||||
'sell': {'sell-mfi-value': None},
|
||||
'roi': {}, 'stoploss': {'stoploss': None},
|
||||
'trailing': {'trailing_stop': None}}}])
|
||||
MagicMock(return_value=[{
|
||||
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||
'params_details': {
|
||||
'buy': {'mfi-value': None},
|
||||
'sell': {'sell-mfi-value': None},
|
||||
'roi': {}, 'stoploss': {'stoploss': None},
|
||||
'trailing': {'trailing_stop': None}
|
||||
},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
}
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
||||
@@ -787,7 +853,11 @@ def test_print_json_spaces_all(mocker, default_conf, caplog, capsys) -> None:
|
||||
parallel.assert_called_once()
|
||||
|
||||
out, err = capsys.readouterr()
|
||||
assert '{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi":{},"stoploss":null,"trailing_stop":null}' in out # noqa: E501
|
||||
result_str = (
|
||||
'{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi"'
|
||||
':{},"stoploss":null,"trailing_stop":null}'
|
||||
)
|
||||
assert result_str in out # noqa: E501
|
||||
assert dumper.called
|
||||
# Should be called twice, once for tickerdata, once to save evaluations
|
||||
assert dumper.call_count == 2
|
||||
@@ -804,10 +874,22 @@ def test_print_json_spaces_default(mocker, default_conf, caplog, capsys) -> None
|
||||
|
||||
parallel = mocker.patch(
|
||||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||
'params_details': {'buy': {'mfi-value': None},
|
||||
'sell': {'sell-mfi-value': None},
|
||||
'roi': {}, 'stoploss': {'stoploss': None}}}])
|
||||
MagicMock(return_value=[{
|
||||
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||
'params_details': {
|
||||
'buy': {'mfi-value': None},
|
||||
'sell': {'sell-mfi-value': None},
|
||||
'roi': {}, 'stoploss': {'stoploss': None}
|
||||
},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
}
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
||||
@@ -846,8 +928,18 @@ def test_print_json_spaces_roi_stoploss(mocker, default_conf, caplog, capsys) ->
|
||||
|
||||
parallel = mocker.patch(
|
||||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||
'params_details': {'roi': {}, 'stoploss': {'stoploss': None}}}])
|
||||
MagicMock(return_value=[{
|
||||
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||
'params_details': {'roi': {}, 'stoploss': {'stoploss': None}},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
}
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
||||
@@ -887,7 +979,16 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys)
|
||||
parallel = mocker.patch(
|
||||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||
MagicMock(return_value=[{
|
||||
'loss': 1, 'results_explanation': 'foo result', 'params': {'stoploss': 0.0}}])
|
||||
'loss': 1, 'results_explanation': 'foo result', 'params': {'stoploss': 0.0},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
}
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
||||
@@ -965,7 +1066,17 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None:
|
||||
|
||||
parallel = mocker.patch(
|
||||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}])
|
||||
MagicMock(return_value=[{
|
||||
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
}
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
||||
@@ -1012,7 +1123,17 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None
|
||||
|
||||
parallel = mocker.patch(
|
||||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}])
|
||||
MagicMock(return_value=[{
|
||||
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
}
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
||||
|
@@ -22,14 +22,14 @@ def test_generate_text_table(default_conf, mocker):
|
||||
)
|
||||
|
||||
result_str = (
|
||||
'| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC |'
|
||||
' Tot Profit % | Avg Duration | Wins | Draws | Losses |\n'
|
||||
'|:--------|-------:|---------------:|---------------:|-----------------:|'
|
||||
'---------------:|:---------------|-------:|--------:|---------:|\n'
|
||||
'| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC |'
|
||||
' Tot Profit % | Avg Duration | Wins | Draws | Losses |\n'
|
||||
'|---------+--------+----------------+----------------+------------------+'
|
||||
'----------------+----------------+--------+---------+----------|\n'
|
||||
'| ETH/BTC | 2 | 15.00 | 30.00 | 0.60000000 |'
|
||||
' 15.00 | 0:20:00 | 2 | 0 | 0 |\n'
|
||||
'| TOTAL | 2 | 15.00 | 30.00 | 0.60000000 |'
|
||||
' 15.00 | 0:20:00 | 2 | 0 | 0 |'
|
||||
' 15.00 | 0:20:00 | 2 | 0 | 0 |\n'
|
||||
'| TOTAL | 2 | 15.00 | 30.00 | 0.60000000 |'
|
||||
' 15.00 | 0:20:00 | 2 | 0 | 0 |'
|
||||
)
|
||||
assert generate_text_table(data={'ETH/BTC': {}},
|
||||
stake_currency='BTC', max_open_trades=2,
|
||||
@@ -52,13 +52,13 @@ def test_generate_text_table_sell_reason(default_conf, mocker):
|
||||
)
|
||||
|
||||
result_str = (
|
||||
'| Sell Reason | Sells | Wins | Draws | Losses |'
|
||||
'| Sell Reason | Sells | Wins | Draws | Losses |'
|
||||
' Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % |\n'
|
||||
'|:--------------|--------:|-------:|--------:|---------:|'
|
||||
'---------------:|---------------:|-----------------:|---------------:|\n'
|
||||
'| roi | 2 | 2 | 0 | 0 |'
|
||||
'|---------------+---------+--------+---------+----------+'
|
||||
'----------------+----------------+------------------+----------------|\n'
|
||||
'| roi | 2 | 2 | 0 | 0 |'
|
||||
' 15 | 30 | 0.6 | 15 |\n'
|
||||
'| stop_loss | 1 | 0 | 0 | 1 |'
|
||||
'| stop_loss | 1 | 0 | 0 | 1 |'
|
||||
' -10 | -10 | -0.2 | -5 |'
|
||||
)
|
||||
assert generate_text_table_sell_reason(
|
||||
@@ -95,14 +95,14 @@ def test_generate_text_table_strategy(default_conf, mocker):
|
||||
)
|
||||
|
||||
result_str = (
|
||||
'| Strategy | Buys | Avg Profit % | Cum Profit % | Tot'
|
||||
' Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses |\n'
|
||||
'|:--------------|-------:|---------------:|---------------:|------'
|
||||
'-----------:|---------------:|:---------------|-------:|--------:|---------:|\n'
|
||||
'| TestStrategy1 | 3 | 20.00 | 60.00 | '
|
||||
' 1.10000000 | 30.00 | 0:17:00 | 3 | 0 | 0 |\n'
|
||||
'| TestStrategy2 | 3 | 30.00 | 90.00 | '
|
||||
' 1.30000000 | 45.00 | 0:20:00 | 3 | 0 | 0 |'
|
||||
'| Strategy | Buys | Avg Profit % | Cum Profit % | Tot'
|
||||
' Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses |\n'
|
||||
'|---------------+--------+----------------+----------------+------------------+'
|
||||
'----------------+----------------+--------+---------+----------|\n'
|
||||
'| TestStrategy1 | 3 | 20.00 | 60.00 | 1.10000000 |'
|
||||
' 30.00 | 0:17:00 | 3 | 0 | 0 |\n'
|
||||
'| TestStrategy2 | 3 | 30.00 | 90.00 | 1.30000000 |'
|
||||
' 45.00 | 0:20:00 | 3 | 0 | 0 |'
|
||||
)
|
||||
assert generate_text_table_strategy('BTC', 2, all_results=results) == result_str
|
||||
|
||||
@@ -111,8 +111,7 @@ def test_generate_edge_table(edge_conf, mocker):
|
||||
|
||||
results = {}
|
||||
results['ETH/BTC'] = PairInfo(-0.01, 0.60, 2, 1, 3, 10, 60)
|
||||
|
||||
assert generate_edge_table(results).count(':|') == 7
|
||||
assert generate_edge_table(results).count('+') == 7
|
||||
assert generate_edge_table(results).count('| ETH/BTC |') == 1
|
||||
assert generate_edge_table(results).count(
|
||||
'| Risk Reward Ratio | Required Risk Reward | Expectancy |') == 1
|
||||
|
@@ -51,13 +51,13 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'open_date_hum': ANY,
|
||||
'close_date': None,
|
||||
'close_date_hum': None,
|
||||
'open_rate': 1.099e-05,
|
||||
'open_rate': 1.098e-05,
|
||||
'close_rate': None,
|
||||
'current_rate': 1.098e-05,
|
||||
'amount': 90.99181074,
|
||||
'current_rate': 1.099e-05,
|
||||
'amount': 91.07468124,
|
||||
'stake_amount': 0.001,
|
||||
'close_profit': None,
|
||||
'current_profit': -0.59,
|
||||
'current_profit': -0.41,
|
||||
'stop_loss': 0.0,
|
||||
'initial_stop_loss': 0.0,
|
||||
'initial_stop_loss_pct': None,
|
||||
@@ -78,10 +78,10 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'open_date_hum': ANY,
|
||||
'close_date': None,
|
||||
'close_date_hum': None,
|
||||
'open_rate': 1.099e-05,
|
||||
'open_rate': 1.098e-05,
|
||||
'close_rate': None,
|
||||
'current_rate': ANY,
|
||||
'amount': 90.99181074,
|
||||
'amount': 91.07468124,
|
||||
'stake_amount': 0.001,
|
||||
'close_profit': None,
|
||||
'current_profit': ANY,
|
||||
@@ -121,7 +121,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
|
||||
assert "Pair" in headers
|
||||
assert 'instantly' == result[0][2]
|
||||
assert 'ETH/BTC' in result[0][1]
|
||||
assert '-0.59%' == result[0][3]
|
||||
assert '-0.41%' == result[0][3]
|
||||
# Test with fiatconvert
|
||||
|
||||
rpc._fiat_converter = CryptoToFiatConverter()
|
||||
@@ -130,7 +130,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
|
||||
assert "Pair" in headers
|
||||
assert 'instantly' == result[0][2]
|
||||
assert 'ETH/BTC' in result[0][1]
|
||||
assert '-0.59% (-0.09)' == result[0][3]
|
||||
assert '-0.41% (-0.06)' == result[0][3]
|
||||
|
||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
|
||||
MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
|
||||
@@ -245,9 +245,9 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
||||
assert prec_satoshi(stats['profit_closed_coin'], 6.217e-05)
|
||||
assert prec_satoshi(stats['profit_closed_percent'], 6.2)
|
||||
assert prec_satoshi(stats['profit_closed_fiat'], 0.93255)
|
||||
assert prec_satoshi(stats['profit_all_coin'], 5.632e-05)
|
||||
assert prec_satoshi(stats['profit_all_percent'], 2.81)
|
||||
assert prec_satoshi(stats['profit_all_fiat'], 0.8448)
|
||||
assert prec_satoshi(stats['profit_all_coin'], 5.802e-05)
|
||||
assert prec_satoshi(stats['profit_all_percent'], 2.89)
|
||||
assert prec_satoshi(stats['profit_all_fiat'], 0.8703)
|
||||
assert stats['trade_count'] == 2
|
||||
assert stats['first_trade_date'] == 'just now'
|
||||
assert stats['latest_trade_date'] == 'just now'
|
||||
@@ -668,7 +668,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order) -> None
|
||||
trade = rpc._rpc_forcebuy(pair, None)
|
||||
assert isinstance(trade, Trade)
|
||||
assert trade.pair == pair
|
||||
assert trade.open_rate == ticker()['ask']
|
||||
assert trade.open_rate == ticker()['bid']
|
||||
|
||||
# Test buy duplicate
|
||||
with pytest.raises(RPCException, match=r'position for ETH/BTC already open - id: 1'):
|
||||
|
@@ -426,20 +426,20 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
|
||||
rc = client_get(client, f"{BASE_URI}/status")
|
||||
assert_response(rc)
|
||||
assert len(rc.json) == 1
|
||||
assert rc.json == [{'amount': 90.99181074,
|
||||
assert rc.json == [{'amount': 91.07468124,
|
||||
'base_currency': 'BTC',
|
||||
'close_date': None,
|
||||
'close_date_hum': None,
|
||||
'close_profit': None,
|
||||
'close_rate': None,
|
||||
'current_profit': -0.59,
|
||||
'current_rate': 1.098e-05,
|
||||
'current_profit': -0.41,
|
||||
'current_rate': 1.099e-05,
|
||||
'initial_stop_loss': 0.0,
|
||||
'initial_stop_loss_pct': None,
|
||||
'open_date': ANY,
|
||||
'open_date_hum': 'just now',
|
||||
'open_order': '(limit buy rem=0.00000000)',
|
||||
'open_rate': 1.099e-05,
|
||||
'open_rate': 1.098e-05,
|
||||
'pair': 'ETH/BTC',
|
||||
'stake_amount': 0.001,
|
||||
'stop_loss': 0.0,
|
||||
|
@@ -720,13 +720,13 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'profit',
|
||||
'limit': 1.172e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'limit': 1.173e-05,
|
||||
'amount': 91.07468123861567,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.099e-05,
|
||||
'current_rate': 1.172e-05,
|
||||
'profit_amount': 6.126e-05,
|
||||
'profit_ratio': 0.0611052,
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.173e-05,
|
||||
'profit_amount': 6.314e-05,
|
||||
'profit_ratio': 0.0629778,
|
||||
'stake_currency': 'BTC',
|
||||
'fiat_currency': 'USD',
|
||||
'sell_reason': SellType.FORCE_SELL.value,
|
||||
@@ -779,13 +779,13 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'limit': 1.044e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'limit': 1.043e-05,
|
||||
'amount': 91.07468123861567,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.099e-05,
|
||||
'current_rate': 1.044e-05,
|
||||
'profit_amount': -5.492e-05,
|
||||
'profit_ratio': -0.05478342,
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.043e-05,
|
||||
'profit_amount': -5.497e-05,
|
||||
'profit_ratio': -0.05482878,
|
||||
'stake_currency': 'BTC',
|
||||
'fiat_currency': 'USD',
|
||||
'sell_reason': SellType.FORCE_SELL.value,
|
||||
@@ -827,13 +827,13 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'limit': 1.098e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'limit': 1.099e-05,
|
||||
'amount': 91.07468123861567,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.099e-05,
|
||||
'current_rate': 1.098e-05,
|
||||
'profit_amount': -5.91e-06,
|
||||
'profit_ratio': -0.00589291,
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.099e-05,
|
||||
'profit_amount': -4.09e-06,
|
||||
'profit_ratio': -0.00408133,
|
||||
'stake_currency': 'BTC',
|
||||
'fiat_currency': 'USD',
|
||||
'sell_reason': SellType.FORCE_SELL.value,
|
||||
|
@@ -761,8 +761,8 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
|
||||
assert trade.is_open
|
||||
assert trade.open_date is not None
|
||||
assert trade.exchange == 'bittrex'
|
||||
assert trade.open_rate == 0.00001099
|
||||
assert trade.amount == 90.99181073703367
|
||||
assert trade.open_rate == 0.00001098
|
||||
assert trade.amount == 91.07468123861567
|
||||
|
||||
assert log_has(
|
||||
'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog
|
||||
@@ -906,20 +906,37 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None:
|
||||
assert ("ETH/BTC", default_conf["ticker_interval"]) in refresh_mock.call_args[0][0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ask,last,last_ab,expected", [
|
||||
(20, 10, 0.0, 20), # Full ask side
|
||||
(20, 10, 1.0, 10), # Full last side
|
||||
(20, 10, 0.5, 15), # Between ask and last
|
||||
(20, 10, 0.7, 13), # Between ask and last
|
||||
(20, 10, 0.3, 17), # Between ask and last
|
||||
(5, 10, 1.0, 5), # last bigger than ask
|
||||
(5, 10, 0.5, 5), # last bigger than ask
|
||||
@pytest.mark.parametrize("side,ask,bid,last,last_ab,expected", [
|
||||
('ask', 20, 19, 10, 0.0, 20), # Full ask side
|
||||
('ask', 20, 19, 10, 1.0, 10), # Full last side
|
||||
('ask', 20, 19, 10, 0.5, 15), # Between ask and last
|
||||
('ask', 20, 19, 10, 0.7, 13), # Between ask and last
|
||||
('ask', 20, 19, 10, 0.3, 17), # Between ask and last
|
||||
('ask', 5, 6, 10, 1.0, 5), # last bigger than ask
|
||||
('ask', 5, 6, 10, 0.5, 5), # last bigger than ask
|
||||
('ask', 10, 20, None, 0.5, 10), # last not available - uses ask
|
||||
('ask', 4, 5, None, 0.5, 4), # last not available - uses ask
|
||||
('ask', 4, 5, None, 1, 4), # last not available - uses ask
|
||||
('ask', 4, 5, None, 0, 4), # last not available - uses ask
|
||||
('bid', 10, 20, 10, 0.0, 20), # Full bid side
|
||||
('bid', 10, 20, 10, 1.0, 10), # Full last side
|
||||
('bid', 10, 20, 10, 0.5, 15), # Between bid and last
|
||||
('bid', 10, 20, 10, 0.7, 13), # Between bid and last
|
||||
('bid', 10, 20, 10, 0.3, 17), # Between bid and last
|
||||
('bid', 4, 5, 10, 1.0, 5), # last bigger than bid
|
||||
('bid', 4, 5, 10, 0.5, 5), # last bigger than bid
|
||||
('bid', 10, 20, None, 0.5, 20), # last not available - uses bid
|
||||
('bid', 4, 5, None, 0.5, 5), # last not available - uses bid
|
||||
('bid', 4, 5, None, 1, 5), # last not available - uses bid
|
||||
('bid', 4, 5, None, 0, 5), # last not available - uses bid
|
||||
])
|
||||
def test_get_buy_rate(mocker, default_conf, caplog, ask, last, last_ab, expected) -> None:
|
||||
def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid,
|
||||
last, last_ab, expected) -> None:
|
||||
default_conf['bid_strategy']['ask_last_balance'] = last_ab
|
||||
default_conf['bid_strategy']['price_side'] = side
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
||||
MagicMock(return_value={'ask': ask, 'last': last}))
|
||||
MagicMock(return_value={'ask': ask, 'last': last, 'bid': bid}))
|
||||
|
||||
assert freqtrade.get_buy_rate('ETH/BTC', True) == expected
|
||||
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
|
||||
@@ -1317,7 +1334,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
|
||||
stoploss_order_mock.assert_not_called()
|
||||
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
assert trade.stop_loss == 0.00002344 * 0.95
|
||||
assert trade.stop_loss == 0.00002346 * 0.95
|
||||
|
||||
# setting stoploss_on_exchange_interval to 0 seconds
|
||||
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0
|
||||
@@ -1325,10 +1342,10 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
|
||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||
|
||||
cancel_order_mock.assert_called_once_with(100, 'ETH/BTC')
|
||||
stoploss_order_mock.assert_called_once_with(amount=85.25149190110828,
|
||||
stoploss_order_mock.assert_called_once_with(amount=85.32423208191126,
|
||||
pair='ETH/BTC',
|
||||
order_types=freqtrade.strategy.order_types,
|
||||
stop_price=0.00002344 * 0.95)
|
||||
stop_price=0.00002346 * 0.95)
|
||||
|
||||
# price fell below stoploss, so dry-run sells trade.
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={
|
||||
@@ -1510,12 +1527,12 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
|
||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||
|
||||
# stoploss should be set to 1% as trailing is on
|
||||
assert trade.stop_loss == 0.00002344 * 0.99
|
||||
assert trade.stop_loss == 0.00002346 * 0.99
|
||||
cancel_order_mock.assert_called_once_with(100, 'NEO/BTC')
|
||||
stoploss_order_mock.assert_called_once_with(amount=2131074.168797954,
|
||||
stoploss_order_mock.assert_called_once_with(amount=2132892.491467577,
|
||||
pair='NEO/BTC',
|
||||
order_types=freqtrade.strategy.order_types,
|
||||
stop_price=0.00002344 * 0.99)
|
||||
stop_price=0.00002346 * 0.99)
|
||||
|
||||
|
||||
def test_enter_positions(mocker, default_conf, caplog) -> None:
|
||||
@@ -2292,12 +2309,12 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'profit',
|
||||
'limit': 1.172e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'amount': 91.07468123861567,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.099e-05,
|
||||
'current_rate': 1.172e-05,
|
||||
'profit_amount': 6.126e-05,
|
||||
'profit_ratio': 0.0611052,
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.173e-05,
|
||||
'profit_amount': 6.223e-05,
|
||||
'profit_ratio': 0.0620716,
|
||||
'stake_currency': 'BTC',
|
||||
'fiat_currency': 'USD',
|
||||
'sell_reason': SellType.ROI.value,
|
||||
@@ -2341,12 +2358,12 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker)
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'limit': 1.044e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'amount': 91.07468123861567,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.099e-05,
|
||||
'current_rate': 1.044e-05,
|
||||
'profit_amount': -5.492e-05,
|
||||
'profit_ratio': -0.05478342,
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.043e-05,
|
||||
'profit_amount': -5.406e-05,
|
||||
'profit_ratio': -0.05392257,
|
||||
'stake_currency': 'BTC',
|
||||
'fiat_currency': 'USD',
|
||||
'sell_reason': SellType.STOP_LOSS.value,
|
||||
@@ -2397,12 +2414,12 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'limit': 1.08801e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'amount': 91.07468123861567,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.099e-05,
|
||||
'current_rate': 1.044e-05,
|
||||
'profit_amount': -1.498e-05,
|
||||
'profit_ratio': -0.01493766,
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.043e-05,
|
||||
'profit_amount': -1.408e-05,
|
||||
'profit_ratio': -0.01404051,
|
||||
'stake_currency': 'BTC',
|
||||
'fiat_currency': 'USD',
|
||||
'sell_reason': SellType.STOP_LOSS.value,
|
||||
@@ -2587,7 +2604,7 @@ def test_execute_sell_market_order(default_conf, ticker, fee,
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], sell_reason=SellType.ROI)
|
||||
|
||||
assert not trade.is_open
|
||||
assert trade.close_profit == 0.0611052
|
||||
assert trade.close_profit == 0.0620716
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
last_msg = rpc_mock.call_args_list[-1][0][0]
|
||||
@@ -2597,12 +2614,12 @@ def test_execute_sell_market_order(default_conf, ticker, fee,
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'profit',
|
||||
'limit': 1.172e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'amount': 91.07468123861567,
|
||||
'order_type': 'market',
|
||||
'open_rate': 1.099e-05,
|
||||
'current_rate': 1.172e-05,
|
||||
'profit_amount': 6.126e-05,
|
||||
'profit_ratio': 0.0611052,
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.173e-05,
|
||||
'profit_amount': 6.223e-05,
|
||||
'profit_ratio': 0.0620716,
|
||||
'stake_currency': 'BTC',
|
||||
'fiat_currency': 'USD',
|
||||
'sell_reason': SellType.ROI.value,
|
||||
@@ -3624,13 +3641,20 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
|
||||
|
||||
def test_get_sell_rate(default_conf, mocker, caplog, ticker, order_book_l2) -> None:
|
||||
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
get_order_book=order_book_l2,
|
||||
fetch_ticker=ticker,
|
||||
)
|
||||
@pytest.mark.parametrize('side,ask,bid,expected', [
|
||||
('bid', 10.0, 11.0, 11.0),
|
||||
('bid', 10.0, 11.2, 11.2),
|
||||
('bid', 10.0, 11.0, 11.0),
|
||||
('bid', 9.8, 11.0, 11.0),
|
||||
('bid', 0.0001, 0.002, 0.002),
|
||||
('ask', 10.0, 11.0, 10.0),
|
||||
('ask', 10.11, 11.2, 10.11),
|
||||
('ask', 0.001, 0.002, 0.001),
|
||||
('ask', 0.006, 1.0, 0.006),
|
||||
])
|
||||
def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask, expected) -> None:
|
||||
default_conf['ask_strategy']['price_side'] = side
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'ask': ask, 'bid': bid})
|
||||
pair = "ETH/BTC"
|
||||
|
||||
# Test regular mode
|
||||
@@ -3638,25 +3662,33 @@ def test_get_sell_rate(default_conf, mocker, caplog, ticker, order_book_l2) -> N
|
||||
rate = ft.get_sell_rate(pair, True)
|
||||
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||
assert isinstance(rate, float)
|
||||
assert rate == 0.00001098
|
||||
assert rate == expected
|
||||
# Use caching
|
||||
rate = ft.get_sell_rate(pair, False)
|
||||
assert rate == 0.00001098
|
||||
assert rate == expected
|
||||
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||
|
||||
caplog.clear()
|
||||
|
||||
@pytest.mark.parametrize('side,expected', [
|
||||
('bid', 0.043936), # Value from order_book_l2 fiture - bids side
|
||||
('ask', 0.043949), # Value from order_book_l2 fiture - asks side
|
||||
])
|
||||
def test_get_sell_rate_orderbook(default_conf, mocker, caplog, side, expected, order_book_l2):
|
||||
# Test orderbook mode
|
||||
default_conf['ask_strategy']['price_side'] = side
|
||||
default_conf['ask_strategy']['use_order_book'] = True
|
||||
default_conf['ask_strategy']['order_book_min'] = 1
|
||||
default_conf['ask_strategy']['order_book_max'] = 2
|
||||
# TODO: min/max is irrelevant for this test until refactoring
|
||||
pair = "ETH/BTC"
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_order_book', order_book_l2)
|
||||
ft = get_patched_freqtradebot(mocker, default_conf)
|
||||
rate = ft.get_sell_rate(pair, True)
|
||||
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||
assert isinstance(rate, float)
|
||||
assert rate == 0.043936
|
||||
assert rate == expected
|
||||
rate = ft.get_sell_rate(pair, False)
|
||||
assert rate == 0.043936
|
||||
assert rate == expected
|
||||
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||
|
||||
|
||||
|
@@ -3,15 +3,16 @@ from copy import deepcopy
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pandas as pd
|
||||
import plotly.graph_objects as go
|
||||
import pytest
|
||||
from plotly.subplots import make_subplots
|
||||
|
||||
from freqtrade.commands import start_plot_dataframe, start_plot_profit
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.data import history
|
||||
from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.commands import start_plot_dataframe, start_plot_profit
|
||||
from freqtrade.plot.plotting import (add_indicators, add_profit,
|
||||
create_plotconfig,
|
||||
generate_candlestick_graph,
|
||||
@@ -266,6 +267,7 @@ def test_generate_profit_graph(testdatadir):
|
||||
trades = load_backtest_data(filename)
|
||||
timerange = TimeRange.parse_timerange("20180110-20180112")
|
||||
pairs = ["TRX/BTC", "ADA/BTC"]
|
||||
trades = trades[trades['close_time'] < pd.Timestamp('2018-01-12', tz='UTC')]
|
||||
|
||||
tickers = history.load_data(datadir=testdatadir,
|
||||
pairs=pairs,
|
||||
@@ -283,13 +285,15 @@ def test_generate_profit_graph(testdatadir):
|
||||
assert fig.layout.yaxis3.title.text == "Profit"
|
||||
|
||||
figure = fig.layout.figure
|
||||
assert len(figure.data) == 4
|
||||
assert len(figure.data) == 5
|
||||
|
||||
avgclose = find_trace_in_fig_data(figure.data, "Avg close price")
|
||||
assert isinstance(avgclose, go.Scatter)
|
||||
|
||||
profit = find_trace_in_fig_data(figure.data, "Profit")
|
||||
assert isinstance(profit, go.Scatter)
|
||||
profit = find_trace_in_fig_data(figure.data, "Max drawdown 0.00%")
|
||||
assert isinstance(profit, go.Scatter)
|
||||
|
||||
for pair in pairs:
|
||||
profit_pair = find_trace_in_fig_data(figure.data, f"Profit {pair}")
|
||||
|
@@ -60,7 +60,7 @@ def test_throttle(mocker, default_conf, caplog) -> None:
|
||||
|
||||
assert result == 42
|
||||
assert end - start > 0.1
|
||||
assert log_has_re(r"Throttling with 'throttled_func\(\)': sleep for 0\.10 s.*", caplog)
|
||||
assert log_has_re(r"Throttling with 'throttled_func\(\)': sleep for \d\.\d{2} s.*", caplog)
|
||||
|
||||
result = worker._throttle(throttled_func, throttle_secs=-1)
|
||||
assert result == 42
|
||||
|
Reference in New Issue
Block a user