Merge branch 'develop' into pr/Antreasgr/4838

This commit is contained in:
Matthias
2021-05-27 14:58:19 +02:00
95 changed files with 2815 additions and 1369 deletions

View File

@@ -918,242 +918,244 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):
captured.out)
def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
def test_hyperopt_list(mocker, capsys, caplog, saved_hyperopt_results,
saved_hyperopt_results_legacy):
for _ in (saved_hyperopt_results, saved_hyperopt_results_legacy):
mocker.patch(
'freqtrade.optimize.hyperopt_tools.HyperoptTools.load_previous_results',
MagicMock(return_value=saved_hyperopt_results_legacy)
)
args = [
"hyperopt-list",
"--no-details",
"--no-color",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12",
" 6/12", " 7/12", " 8/12", " 9/12", " 10/12",
" 11/12", " 12/12"])
args = [
"hyperopt-list",
"--best",
"--no-details",
"--no-color",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 1/12", " 5/12", " 10/12"])
assert all(x not in captured.out
for x in [" 2/12", " 3/12", " 4/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 11/12", " 12/12"])
args = [
"hyperopt-list",
"--profitable",
"--no-details",
"--no-color",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 2/12", " 10/12"])
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 11/12", " 12/12"])
args = [
"hyperopt-list",
"--profitable",
"--no-color",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 2/12", " 10/12", "Best result:", "Buy hyperspace params",
"Sell hyperspace params", "ROI table", "Stoploss"])
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--min-trades", "20",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 3/12", " 6/12", " 7/12", " 9/12", " 11/12"])
assert all(x not in captured.out
for x in [" 1/12", " 2/12", " 4/12", " 5/12", " 8/12", " 10/12", " 12/12"])
args = [
"hyperopt-list",
"--profitable",
"--no-details",
"--no-color",
"--max-trades", "20",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 2/12", " 10/12"])
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 11/12", " 12/12"])
args = [
"hyperopt-list",
"--profitable",
"--no-details",
"--no-color",
"--min-avg-profit", "0.11",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 2/12"])
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 10/12", " 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--max-avg-profit", "0.10",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 1/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 11/12"])
assert all(x not in captured.out
for x in [" 2/12", " 4/12", " 10/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--min-total-profit", "0.4",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 10/12"])
assert all(x not in captured.out
for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12",
" 9/12", " 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--max-total-profit", "0.4",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 1/12", " 2/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12",
" 9/12", " 11/12"])
assert all(x not in captured.out
for x in [" 4/12", " 10/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--min-objective", "0.1",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 10/12"])
assert all(x not in captured.out
for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12",
" 9/12", " 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--max-objective", "0.1",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 1/12", " 2/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12",
" 9/12", " 11/12"])
assert all(x not in captured.out
for x in [" 4/12", " 10/12", " 12/12"])
args = [
"hyperopt-list",
"--profitable",
"--no-details",
"--no-color",
"--min-avg-time", "2000",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 10/12"])
assert all(x not in captured.out
for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12",
" 8/12", " 9/12", " 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--max-avg-time", "1500",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 2/12", " 6/12"])
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 7/12", " 8/12"
" 9/12", " 10/12", " 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--export-csv", "test_file.csv",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
log_has("CSV file created: test_file.csv", caplog)
f = Path("test_file.csv")
assert 'Best,1,2,-1.25%,-1.2222,-0.00125625,,-2.51,"3,930.0 m",0.43662' in f.read_text()
assert f.is_file()
f.unlink()
def test_hyperopt_show(mocker, capsys, saved_hyperopt_results):
mocker.patch(
'freqtrade.optimize.hyperopt_tools.HyperoptTools.load_previous_results',
MagicMock(return_value=hyperopt_results)
)
args = [
"hyperopt-list",
"--no-details",
"--no-color",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12",
" 6/12", " 7/12", " 8/12", " 9/12", " 10/12",
" 11/12", " 12/12"])
args = [
"hyperopt-list",
"--best",
"--no-details",
"--no-color",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 1/12", " 5/12", " 10/12"])
assert all(x not in captured.out
for x in [" 2/12", " 3/12", " 4/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 11/12", " 12/12"])
args = [
"hyperopt-list",
"--profitable",
"--no-details",
"--no-color",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 2/12", " 10/12"])
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 11/12", " 12/12"])
args = [
"hyperopt-list",
"--profitable",
"--no-color",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 2/12", " 10/12", "Best result:", "Buy hyperspace params",
"Sell hyperspace params", "ROI table", "Stoploss"])
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--min-trades", "20",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 3/12", " 6/12", " 7/12", " 9/12", " 11/12"])
assert all(x not in captured.out
for x in [" 1/12", " 2/12", " 4/12", " 5/12", " 8/12", " 10/12", " 12/12"])
args = [
"hyperopt-list",
"--profitable",
"--no-details",
"--no-color",
"--max-trades", "20",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 2/12", " 10/12"])
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 11/12", " 12/12"])
args = [
"hyperopt-list",
"--profitable",
"--no-details",
"--no-color",
"--min-avg-profit", "0.11",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 2/12"])
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 10/12", " 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--max-avg-profit", "0.10",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 1/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 11/12"])
assert all(x not in captured.out
for x in [" 2/12", " 4/12", " 10/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--min-total-profit", "0.4",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 10/12"])
assert all(x not in captured.out
for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12",
" 9/12", " 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--max-total-profit", "0.4",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 1/12", " 2/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12",
" 9/12", " 11/12"])
assert all(x not in captured.out
for x in [" 4/12", " 10/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--min-objective", "0.1",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 10/12"])
assert all(x not in captured.out
for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12",
" 9/12", " 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--max-objective", "0.1",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 1/12", " 2/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12",
" 9/12", " 11/12"])
assert all(x not in captured.out
for x in [" 4/12", " 10/12", " 12/12"])
args = [
"hyperopt-list",
"--profitable",
"--no-details",
"--no-color",
"--min-avg-time", "2000",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 10/12"])
assert all(x not in captured.out
for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12",
" 8/12", " 9/12", " 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--max-avg-time", "1500",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 2/12", " 6/12"])
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 7/12", " 8/12"
" 9/12", " 10/12", " 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--no-color",
"--export-csv", "test_file.csv",
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
log_has("CSV file created: test_file.csv", caplog)
f = Path("test_file.csv")
assert 'Best,1,2,-1.25%,-1.2222,-0.00125625,,-2.51,"3,930.0 m",0.43662' in f.read_text()
assert f.is_file()
f.unlink()
def test_hyperopt_show(mocker, capsys, hyperopt_results):
mocker.patch(
'freqtrade.optimize.hyperopt_tools.HyperoptTools.load_previous_results',
MagicMock(return_value=hyperopt_results)
MagicMock(return_value=saved_hyperopt_results)
)
args = [

View File

@@ -3,7 +3,7 @@ import json
import logging
import re
from copy import deepcopy
from datetime import datetime
from datetime import datetime, timedelta
from functools import reduce
from pathlib import Path
from unittest.mock import MagicMock, Mock, PropertyMock
@@ -21,6 +21,7 @@ from freqtrade.exchange import Exchange
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import LocalTrade, Trade, init_db
from freqtrade.resolvers import ExchangeResolver
from freqtrade.state import RunMode
from freqtrade.worker import Worker
from tests.conftest_trades import (mock_trade_1, mock_trade_2, mock_trade_3, mock_trade_4,
mock_trade_5, mock_trade_6)
@@ -1677,6 +1678,7 @@ def buy_order_fee():
@pytest.fixture(scope="function")
def edge_conf(default_conf):
conf = deepcopy(default_conf)
conf['runmode'] = RunMode.DRY_RUN
conf['max_open_trades'] = -1
conf['tradable_balance_ratio'] = 0.5
conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
@@ -1778,7 +1780,7 @@ def open_trade():
@pytest.fixture
def hyperopt_results():
def saved_hyperopt_results_legacy():
return [
{
'loss': 0.4366182531161519,
@@ -1907,3 +1909,136 @@ def hyperopt_results():
'is_best': False
}
]
@pytest.fixture
def saved_hyperopt_results():
return [
{
'loss': 0.4366182531161519,
'params_dict': {
'mfi-value': 15, 'fastd-value': 20, 'adx-value': 25, 'rsi-value': 28, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 88, 'sell-fastd-value': 97, 'sell-adx-value': 51, 'sell-rsi-value': 67, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1190, 'roi_t2': 541, 'roi_t3': 408, 'roi_p1': 0.026035863879169705, 'roi_p2': 0.12508730043628782, 'roi_p3': 0.27766427921605896, 'stoploss': -0.2562930402099556}, # noqa: E501
'params_details': {'buy': {'mfi-value': 15, 'fastd-value': 20, 'adx-value': 25, 'rsi-value': 28, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 88, 'sell-fastd-value': 97, 'sell-adx-value': 51, 'sell-rsi-value': 67, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.4287874435315165, 408: 0.15112316431545753, 949: 0.026035863879169705, 2139: 0}, 'stoploss': {'stoploss': -0.2562930402099556}}, # noqa: E501
'results_metrics': {'total_trades': 2, 'wins': 0, 'draws': 0, 'losses': 2, 'profit_mean': -0.01254995, 'profit_median': -0.012222, 'profit_total': -0.00125625, 'profit_total_abs': -2.50999, 'holding_avg': timedelta(minutes=3930.0)}, # noqa: E501
'results_explanation': ' 2 trades. Avg profit -1.25%. Total profit -0.00125625 BTC ( -2.51Σ%). Avg duration 3930.0 min.', # noqa: E501
'total_profit': -0.00125625,
'current_epoch': 1,
'is_initial_point': True,
'is_best': True
}, {
'loss': 20.0,
'params_dict': {
'mfi-value': 17, 'fastd-value': 38, 'adx-value': 48, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 96, 'sell-fastd-value': 68, 'sell-adx-value': 63, 'sell-rsi-value': 81, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal', 'roi_t1': 334, 'roi_t2': 683, 'roi_t3': 140, 'roi_p1': 0.06403981740598495, 'roi_p2': 0.055519840060645045, 'roi_p3': 0.3253712811342459, 'stoploss': -0.338070047333259}, # noqa: E501
'params_details': {
'buy': {'mfi-value': 17, 'fastd-value': 38, 'adx-value': 48, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, # noqa: E501
'sell': {'sell-mfi-value': 96, 'sell-fastd-value': 68, 'sell-adx-value': 63, 'sell-rsi-value': 81, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal'}, # noqa: E501
'roi': {0: 0.4449309386008759, 140: 0.11955965746663, 823: 0.06403981740598495, 1157: 0}, # noqa: E501
'stoploss': {'stoploss': -0.338070047333259}},
'results_metrics': {'total_trades': 1, 'wins': 0, 'draws': 0, 'losses': 1, 'profit_mean': 0.012357, 'profit_median': -0.012222, 'profit_total': 6.185e-05, 'profit_total_abs': 0.12357, 'holding_avg': timedelta(minutes=1200.0)}, # noqa: E501
'results_explanation': ' 1 trades. Avg profit 0.12%. Total profit 0.00006185 BTC ( 0.12Σ%). Avg duration 1200.0 min.', # noqa: E501
'total_profit': 6.185e-05,
'current_epoch': 2,
'is_initial_point': True,
'is_best': False
}, {
'loss': 14.241196856510731,
'params_dict': {'mfi-value': 25, 'fastd-value': 16, 'adx-value': 29, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 98, 'sell-fastd-value': 72, 'sell-adx-value': 51, 'sell-rsi-value': 82, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 889, 'roi_t2': 533, 'roi_t3': 263, 'roi_p1': 0.04759065393663096, 'roi_p2': 0.1488819964638463, 'roi_p3': 0.4102801822104605, 'stoploss': -0.05394588767607611}, # noqa: E501
'params_details': {'buy': {'mfi-value': 25, 'fastd-value': 16, 'adx-value': 29, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 98, 'sell-fastd-value': 72, 'sell-adx-value': 51, 'sell-rsi-value': 82, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.6067528326109377, 263: 0.19647265040047726, 796: 0.04759065393663096, 1685: 0}, 'stoploss': {'stoploss': -0.05394588767607611}}, # noqa: E501
'results_metrics': {'total_trades': 621, 'wins': 320, 'draws': 0, 'losses': 301, 'profit_mean': -0.043883302093397747, 'profit_median': -0.012222, 'profit_total': -0.13639474, 'profit_total_abs': -272.515306, 'holding_avg': timedelta(minutes=1691.207729468599)}, # noqa: E501
'results_explanation': ' 621 trades. Avg profit -0.44%. Total profit -0.13639474 BTC (-272.52Σ%). Avg duration 1691.2 min.', # noqa: E501
'total_profit': -0.13639474,
'current_epoch': 3,
'is_initial_point': True,
'is_best': False
}, {
'loss': 100000,
'params_dict': {'mfi-value': 13, 'fastd-value': 35, 'adx-value': 39, 'rsi-value': 29, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 87, 'sell-fastd-value': 54, 'sell-adx-value': 63, 'sell-rsi-value': 93, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1402, 'roi_t2': 676, 'roi_t3': 215, 'roi_p1': 0.06264755784937427, 'roi_p2': 0.14258587851894644, 'roi_p3': 0.20671291201040828, 'stoploss': -0.11818343570194478}, # noqa: E501
'params_details': {'buy': {'mfi-value': 13, 'fastd-value': 35, 'adx-value': 39, 'rsi-value': 29, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 87, 'sell-fastd-value': 54, 'sell-adx-value': 63, 'sell-rsi-value': 93, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.411946348378729, 215: 0.2052334363683207, 891: 0.06264755784937427, 2293: 0}, 'stoploss': {'stoploss': -0.11818343570194478}}, # noqa: E501
'results_metrics': {'total_trades': 0, 'wins': 0, 'draws': 0, 'losses': 0, 'profit_mean': None, 'profit_median': None, 'profit_total': 0, 'profit': 0.0, 'holding_avg': timedelta()}, # noqa: E501
'results_explanation': ' 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.', # noqa: E501
'total_profit': 0, 'current_epoch': 4, 'is_initial_point': True, 'is_best': False
}, {
'loss': 0.22195522184191518,
'params_dict': {'mfi-value': 17, 'fastd-value': 21, 'adx-value': 38, 'rsi-value': 33, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 87, 'sell-fastd-value': 82, 'sell-adx-value': 78, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 1269, 'roi_t2': 601, 'roi_t3': 444, 'roi_p1': 0.07280999507931168, 'roi_p2': 0.08946698095898986, 'roi_p3': 0.1454876733325284, 'stoploss': -0.18181041180901014}, # noqa: E501
'params_details': {'buy': {'mfi-value': 17, 'fastd-value': 21, 'adx-value': 38, 'rsi-value': 33, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 87, 'sell-fastd-value': 82, 'sell-adx-value': 78, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.3077646493708299, 444: 0.16227697603830155, 1045: 0.07280999507931168, 2314: 0}, 'stoploss': {'stoploss': -0.18181041180901014}}, # noqa: E501
'results_metrics': {'total_trades': 14, 'wins': 6, 'draws': 0, 'losses': 8, 'profit_mean': -0.003539515, 'profit_median': -0.012222, 'profit_total': -0.002480140000000001, 'profit_total_abs': -4.955321, 'holding_avg': timedelta(minutes=3402.8571428571427)}, # noqa: E501
'results_explanation': ' 14 trades. Avg profit -0.35%. Total profit -0.00248014 BTC ( -4.96Σ%). Avg duration 3402.9 min.', # noqa: E501
'total_profit': -0.002480140000000001,
'current_epoch': 5,
'is_initial_point': True,
'is_best': True
}, {
'loss': 0.545315889154162,
'params_dict': {'mfi-value': 22, 'fastd-value': 43, 'adx-value': 46, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'bb_lower', 'sell-mfi-value': 87, 'sell-fastd-value': 65, 'sell-adx-value': 94, 'sell-rsi-value': 63, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 319, 'roi_t2': 556, 'roi_t3': 216, 'roi_p1': 0.06251955472249589, 'roi_p2': 0.11659519602202795, 'roi_p3': 0.0953744132197762, 'stoploss': -0.024551752215582423}, # noqa: E501
'params_details': {'buy': {'mfi-value': 22, 'fastd-value': 43, 'adx-value': 46, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'bb_lower'}, 'sell': {'sell-mfi-value': 87, 'sell-fastd-value': 65, 'sell-adx-value': 94, 'sell-rsi-value': 63, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.2744891639643, 216: 0.17911475074452382, 772: 0.06251955472249589, 1091: 0}, 'stoploss': {'stoploss': -0.024551752215582423}}, # noqa: E501
'results_metrics': {'total_trades': 39, 'wins': 20, 'draws': 0, 'losses': 19, 'profit_mean': -0.0021400679487179478, 'profit_median': -0.012222, 'profit_total': -0.0041773, 'profit_total_abs': -8.346264999999997, 'holding_avg': timedelta(minutes=636.9230769230769)}, # noqa: E501
'results_explanation': ' 39 trades. Avg profit -0.21%. Total profit -0.00417730 BTC ( -8.35Σ%). Avg duration 636.9 min.', # noqa: E501
'total_profit': -0.0041773,
'current_epoch': 6,
'is_initial_point': True,
'is_best': False
}, {
'loss': 4.713497421432944,
'params_dict': {'mfi-value': 13, 'fastd-value': 41, 'adx-value': 21, 'rsi-value': 29, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower', 'sell-mfi-value': 99, 'sell-fastd-value': 60, 'sell-adx-value': 81, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 771, 'roi_t2': 620, 'roi_t3': 145, 'roi_p1': 0.0586919200378493, 'roi_p2': 0.04984118697312542, 'roi_p3': 0.37521058680247044, 'stoploss': -0.14613268022709905}, # noqa: E501
'params_details': {
'buy': {'mfi-value': 13, 'fastd-value': 41, 'adx-value': 21, 'rsi-value': 29, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower'}, 'sell': {'sell-mfi-value': 99, 'sell-fastd-value': 60, 'sell-adx-value': 81, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.4837436938134452, 145: 0.10853310701097472, 765: 0.0586919200378493, 1536: 0}, # noqa: E501
'stoploss': {'stoploss': -0.14613268022709905}}, # noqa: E501
'results_metrics': {'total_trades': 318, 'wins': 100, 'draws': 0, 'losses': 218, 'profit_mean': -0.0039833954716981146, 'profit_median': -0.012222, 'profit_total': -0.06339929, 'profit_total_abs': -126.67197600000004, 'holding_avg': timedelta(minutes=3140.377358490566)}, # noqa: E501
'results_explanation': ' 318 trades. Avg profit -0.40%. Total profit -0.06339929 BTC (-126.67Σ%). Avg duration 3140.4 min.', # noqa: E501
'total_profit': -0.06339929,
'current_epoch': 7,
'is_initial_point': True,
'is_best': False
}, {
'loss': 20.0, # noqa: E501
'params_dict': {'mfi-value': 24, 'fastd-value': 43, 'adx-value': 33, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'sar_reversal', 'sell-mfi-value': 89, 'sell-fastd-value': 74, 'sell-adx-value': 70, 'sell-rsi-value': 70, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': False, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal', 'roi_t1': 1149, 'roi_t2': 375, 'roi_t3': 289, 'roi_p1': 0.05571820757172588, 'roi_p2': 0.0606240398618907, 'roi_p3': 0.1729012220156157, 'stoploss': -0.1588514289110401}, # noqa: E501
'params_details': {'buy': {'mfi-value': 24, 'fastd-value': 43, 'adx-value': 33, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'sar_reversal'}, 'sell': {'sell-mfi-value': 89, 'sell-fastd-value': 74, 'sell-adx-value': 70, 'sell-rsi-value': 70, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': False, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal'}, 'roi': {0: 0.2892434694492323, 289: 0.11634224743361658, 664: 0.05571820757172588, 1813: 0}, 'stoploss': {'stoploss': -0.1588514289110401}}, # noqa: E501
'results_metrics': {'total_trades': 1, 'wins': 0, 'draws': 1, 'losses': 0, 'profit_mean': 0.0, 'profit_median': 0.0, 'profit_total': 0.0, 'profit_total_abs': 0.0, 'holding_avg': timedelta(minutes=5340.0)}, # noqa: E501
'results_explanation': ' 1 trades. Avg profit 0.00%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration 5340.0 min.', # noqa: E501
'total_profit': 0.0,
'current_epoch': 8,
'is_initial_point': True,
'is_best': False
}, {
'loss': 2.4731817780991223,
'params_dict': {'mfi-value': 22, 'fastd-value': 20, 'adx-value': 29, 'rsi-value': 40, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'sar_reversal', 'sell-mfi-value': 97, 'sell-fastd-value': 65, 'sell-adx-value': 81, 'sell-rsi-value': 64, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1012, 'roi_t2': 584, 'roi_t3': 422, 'roi_p1': 0.036764323603472565, 'roi_p2': 0.10335480573205287, 'roi_p3': 0.10322347377503042, 'stoploss': -0.2780610808108503}, # noqa: E501
'params_details': {'buy': {'mfi-value': 22, 'fastd-value': 20, 'adx-value': 29, 'rsi-value': 40, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'sar_reversal'}, 'sell': {'sell-mfi-value': 97, 'sell-fastd-value': 65, 'sell-adx-value': 81, 'sell-rsi-value': 64, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.2433426031105559, 422: 0.14011912933552545, 1006: 0.036764323603472565, 2018: 0}, 'stoploss': {'stoploss': -0.2780610808108503}}, # noqa: E501
'results_metrics': {'total_trades': 229, 'wins': 150, 'draws': 0, 'losses': 79, 'profit_mean': -0.0038433433624454144, 'profit_median': -0.012222, 'profit_total': -0.044050070000000004, 'profit_total_abs': -88.01256299999999, 'holding_avg': timedelta(minutes=6505.676855895196)}, # noqa: E501
'results_explanation': ' 229 trades. Avg profit -0.38%. Total profit -0.04405007 BTC ( -88.01Σ%). Avg duration 6505.7 min.', # noqa: E501
'total_profit': -0.044050070000000004, # noqa: E501
'current_epoch': 9,
'is_initial_point': True,
'is_best': False
}, {
'loss': -0.2604606005845212, # noqa: E501
'params_dict': {'mfi-value': 23, 'fastd-value': 24, 'adx-value': 22, 'rsi-value': 24, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 97, 'sell-fastd-value': 70, 'sell-adx-value': 64, 'sell-rsi-value': 80, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal', 'roi_t1': 792, 'roi_t2': 464, 'roi_t3': 215, 'roi_p1': 0.04594053535385903, 'roi_p2': 0.09623192684243963, 'roi_p3': 0.04428219070850663, 'stoploss': -0.16992287161634415}, # noqa: E501
'params_details': {'buy': {'mfi-value': 23, 'fastd-value': 24, 'adx-value': 22, 'rsi-value': 24, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 97, 'sell-fastd-value': 70, 'sell-adx-value': 64, 'sell-rsi-value': 80, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal'}, 'roi': {0: 0.18645465290480528, 215: 0.14217246219629864, 679: 0.04594053535385903, 1471: 0}, 'stoploss': {'stoploss': -0.16992287161634415}}, # noqa: E501
'results_metrics': {'total_trades': 4, 'wins': 0, 'draws': 0, 'losses': 4, 'profit_mean': 0.001080385, 'profit_median': -0.012222, 'profit_total': 0.00021629, 'profit_total_abs': 0.432154, 'holding_avg': timedelta(minutes=2850.0)}, # noqa: E501
'results_explanation': ' 4 trades. Avg profit 0.11%. Total profit 0.00021629 BTC ( 0.43Σ%). Avg duration 2850.0 min.', # noqa: E501
'total_profit': 0.00021629,
'current_epoch': 10,
'is_initial_point': True,
'is_best': True
}, {
'loss': 4.876465945994304, # noqa: E501
'params_dict': {'mfi-value': 20, 'fastd-value': 32, 'adx-value': 49, 'rsi-value': 23, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower', 'sell-mfi-value': 75, 'sell-fastd-value': 56, 'sell-adx-value': 61, 'sell-rsi-value': 62, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 579, 'roi_t2': 614, 'roi_t3': 273, 'roi_p1': 0.05307643172744114, 'roi_p2': 0.1352282078262871, 'roi_p3': 0.1913307406325751, 'stoploss': -0.25728526022513887}, # noqa: E501
'params_details': {'buy': {'mfi-value': 20, 'fastd-value': 32, 'adx-value': 49, 'rsi-value': 23, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower'}, 'sell': {'sell-mfi-value': 75, 'sell-fastd-value': 56, 'sell-adx-value': 61, 'sell-rsi-value': 62, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.3796353801863034, 273: 0.18830463955372825, 887: 0.05307643172744114, 1466: 0}, 'stoploss': {'stoploss': -0.25728526022513887}}, # noqa: E501
# New Hyperopt mode!
'results_metrics': {'total_trades': 117, 'wins': 67, 'draws': 0, 'losses': 50, 'profit_mean': -0.012698609145299145, 'profit_median': -0.012222, 'profit_total': -0.07436117, 'profit_total_abs': -148.573727, 'holding_avg': timedelta(minutes=4282.5641025641025)}, # noqa: E501
'results_explanation': ' 117 trades. Avg profit -1.27%. Total profit -0.07436117 BTC (-148.57Σ%). Avg duration 4282.6 min.', # noqa: E501
'total_profit': -0.07436117,
'current_epoch': 11,
'is_initial_point': True,
'is_best': False
}, {
'loss': 100000,
'params_dict': {'mfi-value': 10, 'fastd-value': 36, 'adx-value': 31, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'sar_reversal', 'sell-mfi-value': 80, 'sell-fastd-value': 71, 'sell-adx-value': 60, 'sell-rsi-value': 85, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1156, 'roi_t2': 581, 'roi_t3': 408, 'roi_p1': 0.06860454019988212, 'roi_p2': 0.12473718444931989, 'roi_p3': 0.2896360635226823, 'stoploss': -0.30889015124682806}, # noqa: E501
'params_details': {'buy': {'mfi-value': 10, 'fastd-value': 36, 'adx-value': 31, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'sar_reversal'}, 'sell': {'sell-mfi-value': 80, 'sell-fastd-value': 71, 'sell-adx-value': 60, 'sell-rsi-value': 85, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.4829777881718843, 408: 0.19334172464920202, 989: 0.06860454019988212, 2145: 0}, 'stoploss': {'stoploss': -0.30889015124682806}}, # noqa: E501
'results_metrics': {'total_trades': 0, 'wins': 0, 'draws': 0, 'losses': 0, 'profit_mean': None, 'profit_median': None, 'profit_total': 0, 'profit_total_abs': 0.0, 'holding_avg': timedelta()}, # noqa: E501
'results_explanation': ' 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.', # noqa: E501
'total_profit': 0,
'current_epoch': 12,
'is_initial_point': True,
'is_best': False
}
]

View File

@@ -246,3 +246,46 @@ def test_get_analyzed_dataframe(mocker, default_conf, ohlcv_history):
assert dataframe.empty
assert isinstance(time, datetime)
assert time == datetime(1970, 1, 1, tzinfo=timezone.utc)
# Test backtest mode
default_conf["runmode"] = RunMode.BACKTEST
dp._set_dataframe_max_index(1)
dataframe, time = dp.get_analyzed_dataframe("XRP/BTC", timeframe)
assert len(dataframe) == 1
dp._set_dataframe_max_index(2)
dataframe, time = dp.get_analyzed_dataframe("XRP/BTC", timeframe)
assert len(dataframe) == 2
dp._set_dataframe_max_index(3)
dataframe, time = dp.get_analyzed_dataframe("XRP/BTC", timeframe)
assert len(dataframe) == 3
dp._set_dataframe_max_index(500)
dataframe, time = dp.get_analyzed_dataframe("XRP/BTC", timeframe)
assert len(dataframe) == len(ohlcv_history)
def test_no_exchange_mode(default_conf):
dp = DataProvider(default_conf, None)
message = "Exchange is not available to DataProvider."
with pytest.raises(OperationalException, match=message):
dp.refresh([()])
with pytest.raises(OperationalException, match=message):
dp.ohlcv('XRP/USDT', '5m')
with pytest.raises(OperationalException, match=message):
dp.market('XRP/USDT')
with pytest.raises(OperationalException, match=message):
dp.ticker('XRP/USDT')
with pytest.raises(OperationalException, match=message):
dp.orderbook('XRP/USDT', 20)
with pytest.raises(OperationalException, match=message):
dp.available_pairs()

View File

@@ -344,6 +344,8 @@ def test_edge_process_no_trades(mocker, edge_conf, caplog):
def test_edge_process_no_pairs(mocker, edge_conf, caplog):
edge_conf['exchange']['pair_whitelist'] = []
mocker.patch('freqtrade.freqtradebot.validate_config_consistency')
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
fee_mock = mocker.patch('freqtrade.exchange.Exchange.get_fee', return_value=0.001)
mocker.patch('freqtrade.edge.edge_positioning.refresh_data')

View File

@@ -2084,6 +2084,46 @@ def test_cancel_stoploss_order(default_conf, mocker, exchange_name):
order_id='_', pair='TKN/BTC')
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name):
default_conf['dry_run'] = False
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', return_value={'for': 123})
mocker.patch('freqtrade.exchange.Ftx.fetch_stoploss_order', return_value={'for': 123})
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order',
return_value={'fee': {}, 'status': 'canceled', 'amount': 1234})
mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order',
return_value={'fee': {}, 'status': 'canceled', 'amount': 1234})
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
assert co == {'fee': {}, 'status': 'canceled', 'amount': 1234}
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order',
return_value='canceled')
mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order',
return_value='canceled')
# Fall back to fetch_stoploss_order
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
assert co == {'for': 123}
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order',
side_effect=InvalidOrderException(""))
mocker.patch('freqtrade.exchange.Ftx.fetch_stoploss_order',
side_effect=InvalidOrderException(""))
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
assert co['amount'] == 555
assert co == {'fee': {}, 'status': 'canceled', 'amount': 555, 'info': {}}
with pytest.raises(InvalidOrderException):
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order',
side_effect=InvalidOrderException("Did not find order"))
mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order',
side_effect=InvalidOrderException("Did not find order"))
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=123)
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_fetch_order(default_conf, mocker, exchange_name):
default_conf['dry_run'] = True

View File

@@ -157,3 +157,26 @@ def test_fetch_stoploss_order(default_conf, mocker):
'fetch_stoploss_order', 'fetch_orders',
retries=API_FETCH_ORDER_RETRY_COUNT + 1,
order_id='_', pair='TKN/BTC')
def test_get_order_id(mocker, default_conf):
exchange = get_patched_exchange(mocker, default_conf, id='ftx')
order = {
'type': STOPLOSS_ORDERTYPE,
'price': 1500,
'id': '1111',
'info': {
'orderId': '1234'
}
}
assert exchange.get_order_id_conditional(order) == '1234'
order = {
'type': 'limit',
'price': 1500,
'id': '1111',
'info': {
'orderId': '1234'
}
}
assert exchange.get_order_id_conditional(order) == '1111'

View File

@@ -90,6 +90,7 @@ def test_get_balances_prod(default_conf, mocker):
'3ST': balance_item.copy(),
'4ST': balance_item.copy(),
'EUR': balance_item.copy(),
'timestamp': 123123
})
kraken_open_orders = [{'symbol': '1ST/EUR',
'type': 'limit',
@@ -138,7 +139,7 @@ def test_get_balances_prod(default_conf, mocker):
default_conf['dry_run'] = False
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
balances = exchange.get_balances()
assert len(balances) == 5
assert len(balances) == 6
assert balances['1ST']['free'] == 9.0
assert balances['1ST']['total'] == 10.0

View File

@@ -185,7 +185,7 @@ tc11 = BTContainer(data=[
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
[1, 5000, 5050, 4950, 5100, 6172, 0, 0],
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
[3, 4850, 5050, 4650, 4750, 6172, 0, 0],
[3, 5000, 5150, 4650, 4750, 6172, 0, 0],
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True,
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
@@ -440,6 +440,23 @@ tc27 = BTContainer(data=[
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=4)]
)
# Test 28: trailing_stop should raise so candle 3 causes a stoploss
# Same case than tc11 - but candle 3 "gaps down" - the stoploss will be above the candle,
# therefore "open" will be used
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
tc28 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
[1, 5000, 5050, 4950, 5100, 6172, 0, 0],
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
[3, 4850, 5050, 4650, 4750, 6172, 0, 0],
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.03, trailing_stop=True,
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
trailing_stop_positive=0.03,
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
)
TESTS = [
tc0,
tc1,
@@ -469,6 +486,7 @@ TESTS = [
tc25,
tc26,
tc27,
tc28,
]
@@ -493,6 +511,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
patch_exchange(mocker)
frame = _build_backtest_dataframe(data.data)
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
backtesting.strategy.advise_buy = lambda a, m: frame
backtesting.strategy.advise_sell = lambda a, m: frame
caplog.set_level(logging.DEBUG)
@@ -501,13 +520,14 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
# Dummy data as we mock the analyze functions
data_processed = {pair: frame.copy()}
min_date, max_date = get_timerange({pair: frame})
results = backtesting.backtest(
result = backtesting.backtest(
processed=data_processed,
start_date=min_date,
end_date=max_date,
max_open_trades=10,
)
results = result['results']
assert len(results) == len(data.trades)
assert round(results["profit_ratio"].sum(), 3) == round(data.profit_perc, 3)

View File

@@ -83,6 +83,7 @@ def simple_backtest(config, contour, mocker, testdatadir) -> None:
patch_exchange(mocker)
config['timeframe'] = '1m'
backtesting = Backtesting(config)
backtesting._set_strategy(backtesting.strategylist[0])
data = load_data_test(contour, testdatadir)
processed = backtesting.strategy.ohlcvdata_to_dataframe(data)
@@ -106,6 +107,7 @@ def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC'):
data = trim_dictlist(data, -201)
patch_exchange(mocker)
backtesting = Backtesting(conf)
backtesting._set_strategy(backtesting.strategylist[0])
processed = backtesting.strategy.ohlcvdata_to_dataframe(data)
min_date, max_date = get_timerange(processed)
return {
@@ -285,6 +287,7 @@ def test_backtesting_init(mocker, default_conf, order_types) -> None:
patch_exchange(mocker)
get_fee = mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5))
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
assert backtesting.config == default_conf
assert backtesting.timeframe == '5m'
assert callable(backtesting.strategy.ohlcvdata_to_dataframe)
@@ -315,11 +318,13 @@ def test_data_with_fee(default_conf, mocker, testdatadir) -> None:
fee_mock = mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5))
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
assert backtesting.fee == 0.1234
assert fee_mock.call_count == 0
default_conf['fee'] = 0.0
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
assert backtesting.fee == 0.0
assert fee_mock.call_count == 0
@@ -330,6 +335,7 @@ def test_data_to_dataframe_bt(default_conf, mocker, testdatadir) -> None:
data = history.load_data(testdatadir, '1m', ['UNITTEST/BTC'], timerange=timerange,
fill_up_missing=True)
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
processed = backtesting.strategy.ohlcvdata_to_dataframe(data)
assert len(processed['UNITTEST/BTC']) == 102
@@ -361,12 +367,13 @@ def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None:
default_conf['timerange'] = '-1510694220'
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
backtesting.strategy.bot_loop_start = MagicMock()
backtesting.start()
# check the logs, that will contain the backtest result
exists = [
'Backtesting with data from 2017-11-14 21:17:00 '
'up to 2017-11-14 22:59:00 (0 days)..'
'up to 2017-11-14 22:59:00 (0 days).'
]
for line in exists:
assert log_has(line, caplog)
@@ -393,6 +400,7 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog, testdatadir) ->
default_conf['timerange'] = '20180101-20180102'
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
with pytest.raises(OperationalException, match='No data found. Terminating.'):
backtesting.start()
@@ -465,6 +473,7 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
default_conf['stake_amount'] = 'unlimited'
default_conf['max_open_trades'] = 2
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
pair = 'UNITTEST/BTC'
row = [
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0),
@@ -508,19 +517,21 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
patch_exchange(mocker)
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)
processed = backtesting.strategy.ohlcvdata_to_dataframe(data)
min_date, max_date = get_timerange(processed)
results = backtesting.backtest(
result = backtesting.backtest(
processed=processed,
start_date=min_date,
end_date=max_date,
max_open_trades=10,
position_stacking=False,
)
results = result['results']
assert not results.empty
assert len(results) == 2
@@ -569,6 +580,7 @@ def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
patch_exchange(mocker)
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
# Run a backtesting for an exiting 1min timeframe
timerange = TimeRange.parse_timerange('1510688220-1510700340')
@@ -583,13 +595,14 @@ def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None
max_open_trades=1,
position_stacking=False,
)
assert not results.empty
assert len(results) == 1
assert not results['results'].empty
assert len(results['results']) == 1
def test_processed(default_conf, mocker, testdatadir) -> None:
patch_exchange(mocker)
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
dict_of_tickerrows = load_data_test('raise', testdatadir)
dataframes = backtesting.strategy.ohlcvdata_to_dataframe(dict_of_tickerrows)
@@ -623,7 +636,7 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad
# While buy-signals are unrealistic, running backtesting
# over and over again should not cause different results
for [contour, numres] in tests:
assert len(simple_backtest(default_conf, contour, mocker, testdatadir)) == numres
assert len(simple_backtest(default_conf, contour, mocker, testdatadir)['results']) == numres
@pytest.mark.parametrize('protections,contour,expected', [
@@ -648,7 +661,7 @@ def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir,
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
# While buy-signals are unrealistic, running backtesting
# over and over again should not cause different results
assert len(simple_backtest(default_conf, contour, mocker, testdatadir)) == expected
assert len(simple_backtest(default_conf, contour, mocker, testdatadir)['results']) == expected
def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
@@ -660,10 +673,11 @@ def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
backtesting.strategy.advise_buy = fun # Override
backtesting.strategy.advise_sell = fun # Override
results = backtesting.backtest(**backtest_conf)
assert results.empty
result = backtesting.backtest(**backtest_conf)
assert result['results'].empty
def test_backtest_only_sell(mocker, default_conf, testdatadir):
@@ -675,10 +689,11 @@ def test_backtest_only_sell(mocker, default_conf, testdatadir):
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
backtesting.strategy.advise_buy = fun # Override
backtesting.strategy.advise_sell = fun # Override
results = backtesting.backtest(**backtest_conf)
assert results.empty
result = backtesting.backtest(**backtest_conf)
assert result['results'].empty
def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir):
@@ -688,12 +703,14 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir):
pair='UNITTEST/BTC', datadir=testdatadir)
default_conf['timeframe'] = '1m'
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
backtesting.strategy.advise_buy = _trend_alternate # Override
backtesting.strategy.advise_sell = _trend_alternate # Override
results = backtesting.backtest(**backtest_conf)
result = backtesting.backtest(**backtest_conf)
# 200 candles in backtest data
# won't buy on first (shifted by 1)
# 100 buys signals
results = result['results']
assert len(results) == 100
# One trade was force-closed at the end
assert len(results.loc[results['is_open']]) == 0
@@ -729,6 +746,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
default_conf['timeframe'] = '5m'
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
backtesting.strategy.advise_buy = _trend_alternate_hold # Override
backtesting.strategy.advise_sell = _trend_alternate_hold # Override
@@ -745,9 +763,9 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
results = backtesting.backtest(**backtest_conf)
# Make sure we have parallel trades
assert len(evaluate_result_multi(results, '5m', 2)) > 0
assert len(evaluate_result_multi(results['results'], '5m', 2)) > 0
# make sure we don't have trades with more than configured max_open_trades
assert len(evaluate_result_multi(results, '5m', 3)) == 0
assert len(evaluate_result_multi(results['results'], '5m', 3)) == 0
backtest_conf = {
'processed': processed,
@@ -757,7 +775,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
'position_stacking': False,
}
results = backtesting.backtest(**backtest_conf)
assert len(evaluate_result_multi(results, '5m', 1)) == 0
assert len(evaluate_result_multi(results['results'], '5m', 1)) == 0
def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir):
@@ -789,9 +807,9 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir):
'Parameter --timerange detected: 1510694220-1510700340 ...',
f'Using data directory: {testdatadir} ...',
'Loading data from 2017-11-14 20:57:00 '
'up to 2017-11-14 22:58:00 (0 days)..',
'up to 2017-11-14 22:58:00 (0 days).',
'Backtesting with data from 2017-11-14 21:17:00 '
'up to 2017-11-14 22:58:00 (0 days)..',
'up to 2017-11-14 22:58:00 (0 days).',
'Parameter --enable-position-stacking detected ...'
]
@@ -802,8 +820,20 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir):
@pytest.mark.filterwarnings("ignore:deprecated")
def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
default_conf['ask_strategy'].update({
"use_sell_signal": True,
"sell_profit_only": False,
"sell_profit_offset": 0.0,
"ignore_roi_if_buy_signal": False,
})
patch_exchange(mocker)
backtestmock = MagicMock(return_value=pd.DataFrame(columns=BT_DATA_COLUMNS))
backtestmock = MagicMock(return_value={
'results': pd.DataFrame(columns=BT_DATA_COLUMNS),
'config': default_conf,
'locks': [],
'rejected_signals': 20,
'final_balance': 1000,
})
mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist',
PropertyMock(return_value=['UNITTEST/BTC']))
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock)
@@ -817,7 +847,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
text_table_strategy=strattable_mock,
generate_pair_metrics=MagicMock(),
generate_sell_reason_stats=sell_reason_mock,
generate_strategy_metrics=strat_summary,
generate_strategy_comparison=strat_summary,
generate_daily_stats=MagicMock(),
)
patched_configuration_load_config_file(mocker, default_conf)
@@ -851,9 +881,9 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
'Parameter --timerange detected: 1510694220-1510700340 ...',
f'Using data directory: {testdatadir} ...',
'Loading data from 2017-11-14 20:57:00 '
'up to 2017-11-14 22:58:00 (0 days)..',
'up to 2017-11-14 22:58:00 (0 days).',
'Backtesting with data from 2017-11-14 21:17:00 '
'up to 2017-11-14 22:58:00 (0 days)..',
'up to 2017-11-14 22:58:00 (0 days).',
'Parameter --enable-position-stacking detected ...',
'Running backtesting for Strategy DefaultStrategy',
'Running backtesting for Strategy TestStrategyLegacy',
@@ -865,41 +895,60 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
@pytest.mark.filterwarnings("ignore:deprecated")
def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdatadir, capsys):
default_conf['ask_strategy'].update({
"use_sell_signal": True,
"sell_profit_only": False,
"sell_profit_offset": 0.0,
"ignore_roi_if_buy_signal": False,
})
patch_exchange(mocker)
result1 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'],
'profit_ratio': [0.0, 0.0],
'profit_abs': [0.0, 0.0],
'open_date': pd.to_datetime(['2018-01-29 18:40:00',
'2018-01-30 03:30:00', ], utc=True
),
'close_date': pd.to_datetime(['2018-01-29 20:45:00',
'2018-01-30 05:35:00', ], utc=True),
'trade_duration': [235, 40],
'is_open': [False, False],
'stake_amount': [0.01, 0.01],
'open_rate': [0.104445, 0.10302485],
'close_rate': [0.104969, 0.103541],
'sell_reason': [SellType.ROI, SellType.ROI]
})
result2 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'],
'profit_ratio': [0.03, 0.01, 0.1],
'profit_abs': [0.01, 0.02, 0.2],
'open_date': pd.to_datetime(['2018-01-29 18:40:00',
'2018-01-30 03:30:00',
'2018-01-30 05:30:00'], utc=True
),
'close_date': pd.to_datetime(['2018-01-29 20:45:00',
'2018-01-30 05:35:00',
'2018-01-30 08:30:00'], utc=True),
'trade_duration': [47, 40, 20],
'is_open': [False, False, False],
'stake_amount': [0.01, 0.01, 0.01],
'open_rate': [0.104445, 0.10302485, 0.122541],
'close_rate': [0.104969, 0.103541, 0.123541],
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
})
backtestmock = MagicMock(side_effect=[
pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'],
'profit_ratio': [0.0, 0.0],
'profit_abs': [0.0, 0.0],
'open_date': pd.to_datetime(['2018-01-29 18:40:00',
'2018-01-30 03:30:00', ], utc=True
),
'close_date': pd.to_datetime(['2018-01-29 20:45:00',
'2018-01-30 05:35:00', ], utc=True),
'trade_duration': [235, 40],
'is_open': [False, False],
'stake_amount': [0.01, 0.01],
'open_rate': [0.104445, 0.10302485],
'close_rate': [0.104969, 0.103541],
'sell_reason': [SellType.ROI, SellType.ROI]
}),
pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'],
'profit_ratio': [0.03, 0.01, 0.1],
'profit_abs': [0.01, 0.02, 0.2],
'open_date': pd.to_datetime(['2018-01-29 18:40:00',
'2018-01-30 03:30:00',
'2018-01-30 05:30:00'], utc=True
),
'close_date': pd.to_datetime(['2018-01-29 20:45:00',
'2018-01-30 05:35:00',
'2018-01-30 08:30:00'], utc=True),
'trade_duration': [47, 40, 20],
'is_open': [False, False, False],
'stake_amount': [0.01, 0.01, 0.01],
'open_rate': [0.104445, 0.10302485, 0.122541],
'close_rate': [0.104969, 0.103541, 0.123541],
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
}),
{
'results': result1,
'config': default_conf,
'locks': [],
'rejected_signals': 20,
'final_balance': 1000,
},
{
'results': result2,
'config': default_conf,
'locks': [],
'rejected_signals': 20,
'final_balance': 1000,
}
])
mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist',
PropertyMock(return_value=['UNITTEST/BTC']))
@@ -930,9 +979,9 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
'Parameter --timerange detected: 1510694220-1510700340 ...',
f'Using data directory: {testdatadir} ...',
'Loading data from 2017-11-14 20:57:00 '
'up to 2017-11-14 22:58:00 (0 days)..',
'up to 2017-11-14 22:58:00 (0 days).',
'Backtesting with data from 2017-11-14 21:17:00 '
'up to 2017-11-14 22:58:00 (0 days)..',
'up to 2017-11-14 22:58:00 (0 days).',
'Parameter --enable-position-stacking detected ...',
'Running backtesting for Strategy DefaultStrategy',
'Running backtesting for Strategy TestStrategyLegacy',

View File

@@ -4,7 +4,7 @@ import re
from datetime import datetime
from pathlib import Path
from typing import Dict, List
from unittest.mock import MagicMock
from unittest.mock import ANY, MagicMock
import pandas as pd
import pytest
@@ -17,10 +17,12 @@ from freqtrade.exceptions import OperationalException
from freqtrade.optimize.hyperopt import Hyperopt
from freqtrade.optimize.hyperopt_auto import HyperOptAuto
from freqtrade.optimize.hyperopt_tools import HyperoptTools
from freqtrade.optimize.optimize_reports import generate_strategy_stats
from freqtrade.optimize.space import SKDecimal
from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver
from freqtrade.state import RunMode
from freqtrade.strategy.hyper import IntParameter
from freqtrade.strategy.interface import SellType
from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
patched_configuration_load_config_file)
@@ -28,23 +30,7 @@ from .hyperopts.default_hyperopt import DefaultHyperOpt
# Functions for recurrent object patching
def create_results(mocker, hyperopt, testdatadir) -> List[Dict]:
"""
When creating results, mock the hyperopt so that *by default*
- we don't create any pickle'd files in the filesystem
- we might have a pickle'd file so make sure that we return
false when looking for it
"""
hyperopt.results_file = testdatadir / 'optimize/ut_results.pickle'
mocker.patch.object(Path, "is_file", MagicMock(return_value=False))
stat_mock = MagicMock()
stat_mock.st_size = 1
mocker.patch.object(Path, "stat", MagicMock(return_value=stat_mock))
mocker.patch.object(Path, "unlink", MagicMock(return_value=True))
mocker.patch('freqtrade.optimize.hyperopt.dump', return_value=None)
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
def create_results() -> List[Dict]:
return [{'loss': 1, 'result': 'foo', 'params': {}, 'is_best': True}]
@@ -318,54 +304,49 @@ def test_no_log_if_loss_does_not_improve(hyperopt, caplog) -> None:
assert caplog.record_tuples == []
def test_save_results_saves_epochs(mocker, hyperopt, testdatadir, caplog) -> None:
epochs = create_results(mocker, hyperopt, testdatadir)
mock_dump = mocker.patch('freqtrade.optimize.hyperopt.dump', return_value=None)
mock_dump_json = mocker.patch('freqtrade.optimize.hyperopt.file_dump_json', return_value=None)
results_file = testdatadir / 'optimize' / 'ut_results.pickle'
def test_save_results_saves_epochs(mocker, hyperopt, tmpdir, caplog) -> None:
# Test writing to temp dir and reading again
epochs = create_results()
hyperopt.results_file = Path(tmpdir / 'ut_results.fthypt')
caplog.set_level(logging.DEBUG)
hyperopt.epochs = epochs
hyperopt._save_results()
assert log_has(f"1 epoch saved to '{results_file}'.", caplog)
mock_dump.assert_called_once()
mock_dump_json.assert_called_once()
for epoch in epochs:
hyperopt._save_result(epoch)
assert log_has(f"1 epoch saved to '{hyperopt.results_file}'.", caplog)
hyperopt.epochs = epochs + epochs
hyperopt._save_results()
assert log_has(f"2 epochs saved to '{results_file}'.", caplog)
hyperopt._save_result(epochs[0])
assert log_has(f"2 epochs saved to '{hyperopt.results_file}'.", caplog)
hyperopt_epochs = HyperoptTools.load_previous_results(hyperopt.results_file)
assert len(hyperopt_epochs) == 2
def test_read_results_returns_epochs(mocker, hyperopt, testdatadir, caplog) -> None:
epochs = create_results(mocker, hyperopt, testdatadir)
mock_load = mocker.patch('freqtrade.optimize.hyperopt_tools.load', return_value=epochs)
results_file = testdatadir / 'optimize' / 'ut_results.pickle'
hyperopt_epochs = HyperoptTools._read_results(results_file)
assert log_has(f"Reading epochs from '{results_file}'", caplog)
assert hyperopt_epochs == epochs
mock_load.assert_called_once()
def test_load_previous_results(testdatadir, caplog) -> None:
def test_load_previous_results(mocker, hyperopt, testdatadir, caplog) -> None:
epochs = create_results(mocker, hyperopt, testdatadir)
mock_load = mocker.patch('freqtrade.optimize.hyperopt_tools.load', return_value=epochs)
mocker.patch.object(Path, 'is_file', MagicMock(return_value=True))
statmock = MagicMock()
statmock.st_size = 5
# mocker.patch.object(Path, 'stat', MagicMock(return_value=statmock))
results_file = testdatadir / 'optimize' / 'ut_results.pickle'
results_file = testdatadir / 'hyperopt_results_SampleStrategy.pickle'
hyperopt_epochs = HyperoptTools.load_previous_results(results_file)
assert hyperopt_epochs == epochs
mock_load.assert_called_once()
assert len(hyperopt_epochs) == 5
assert log_has_re(r"Reading pickled epochs from .*", caplog)
del epochs[0]['is_best']
mock_load = mocker.patch('freqtrade.optimize.hyperopt_tools.load', return_value=epochs)
caplog.clear()
with pytest.raises(OperationalException):
# Modern version
results_file = testdatadir / 'strategy_SampleStrategy.fthypt'
hyperopt_epochs = HyperoptTools.load_previous_results(results_file)
assert len(hyperopt_epochs) == 5
assert log_has_re(r"Reading epochs from .*", caplog)
def test_load_previous_results2(mocker, testdatadir, caplog) -> None:
mocker.patch('freqtrade.optimize.hyperopt_tools.HyperoptTools._read_results_pickle',
return_value=[{'asdf': '222'}])
results_file = testdatadir / 'hyperopt_results_SampleStrategy.pickle'
with pytest.raises(OperationalException, match=r"The file .* incompatible.*"):
HyperoptTools.load_previous_results(results_file)
@@ -383,7 +364,8 @@ def test_roi_table_generation(hyperopt) -> None:
def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
@@ -422,9 +404,9 @@ def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
out, err = capsys.readouterr()
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
assert dumper.called
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
# Should be called for historical candle data
assert dumper.call_count == 1
assert dumper2.call_count == 1
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
assert hasattr(hyperopt, "max_open_trades")
@@ -432,18 +414,42 @@ def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
assert hasattr(hyperopt, "position_stacking")
def test_format_results(hyperopt):
# Test with BTC as stake_currency
trades = [
('ETH/BTC', 2, 2, 123),
('LTC/BTC', 1, 1, 123),
('XPR/BTC', -1, -2, -246)
]
labels = ['currency', 'profit_ratio', 'profit_abs', 'trade_duration']
df = pd.DataFrame.from_records(trades, columns=labels)
results_metrics = hyperopt._calculate_results_metrics(df)
results_explanation = hyperopt._format_results_explanation_string(results_metrics)
total_profit = results_metrics['total_profit']
def test_hyperopt_format_results(hyperopt):
bt_result = {
'results': pd.DataFrame({"pair": ["UNITTEST/BTC", "UNITTEST/BTC",
"UNITTEST/BTC", "UNITTEST/BTC"],
"profit_ratio": [0.003312, 0.010801, 0.013803, 0.002780],
"profit_abs": [0.000003, 0.000011, 0.000014, 0.000003],
"open_date": [Arrow(2017, 11, 14, 19, 32, 00).datetime,
Arrow(2017, 11, 14, 21, 36, 00).datetime,
Arrow(2017, 11, 14, 22, 12, 00).datetime,
Arrow(2017, 11, 14, 22, 44, 00).datetime],
"close_date": [Arrow(2017, 11, 14, 21, 35, 00).datetime,
Arrow(2017, 11, 14, 22, 10, 00).datetime,
Arrow(2017, 11, 14, 22, 43, 00).datetime,
Arrow(2017, 11, 14, 22, 58, 00).datetime],
"open_rate": [0.002543, 0.003003, 0.003089, 0.003214],
"close_rate": [0.002546, 0.003014, 0.003103, 0.003217],
"trade_duration": [123, 34, 31, 14],
"is_open": [False, False, False, True],
"stake_amount": [0.01, 0.01, 0.01, 0.01],
"sell_reason": [SellType.ROI, SellType.STOP_LOSS,
SellType.ROI, SellType.FORCE_SELL]
}),
'config': hyperopt.config,
'locks': [],
'final_balance': 0.02,
'rejected_signals': 2,
'backtest_start_time': 1619718665,
'backtest_end_time': 1619718665,
}
results_metrics = generate_strategy_stats({'XRP/BTC': None}, '', bt_result,
Arrow(2017, 11, 14, 19, 32, 00),
Arrow(2017, 12, 14, 19, 32, 00), market_change=0)
results_explanation = HyperoptTools.format_results_explanation_string(results_metrics, 'BTC')
total_profit = results_metrics['profit_total_abs']
results = {
'loss': 0.0,
@@ -457,21 +463,9 @@ def test_format_results(hyperopt):
}
result = HyperoptTools._format_explanation_string(results, 1)
assert result.find(' 66.67%')
assert result.find('Total profit 1.00000000 BTC')
assert result.find('2.0000Σ %')
# Test with EUR as stake_currency
trades = [
('ETH/EUR', 2, 2, 123),
('LTC/EUR', 1, 1, 123),
('XPR/EUR', -1, -2, -246)
]
df = pd.DataFrame.from_records(trades, columns=labels)
results_metrics = hyperopt._calculate_results_metrics(df)
results['total_profit'] = results_metrics['total_profit']
result = HyperoptTools._format_explanation_string(results, 1)
assert result.find('Total profit 1.00000000 EUR')
assert ' 0.71%' in result
assert 'Total profit 0.00003100 BTC' in result
assert '0:50:00 min' in result
@pytest.mark.parametrize("spaces, expected_results", [
@@ -502,10 +496,10 @@ def test_format_results(hyperopt):
(['default', 'buy'],
{'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False}),
])
def test_has_space(hyperopt, spaces, expected_results):
def test_has_space(hyperopt_conf, spaces, expected_results):
for s in ['buy', 'sell', 'roi', 'stoploss', 'trailing']:
hyperopt.config.update({'spaces': spaces})
assert hyperopt.has_space(s) == expected_results[s]
hyperopt_conf.update({'spaces': spaces})
assert HyperoptTools.has_space(hyperopt_conf, s) == expected_results[s]
def test_populate_indicators(hyperopt, testdatadir) -> None:
@@ -576,22 +570,39 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None:
'hyperopt_min_trades': 1,
})
trades = [
('TRX/BTC', 0.023117, 0.000233, 100)
]
labels = ['currency', 'profit_ratio', 'profit_abs', 'trade_duration']
backtest_result = pd.DataFrame.from_records(trades, columns=labels)
backtest_result = {
'results': pd.DataFrame({"pair": ["UNITTEST/BTC", "UNITTEST/BTC",
"UNITTEST/BTC", "UNITTEST/BTC"],
"profit_ratio": [0.003312, 0.010801, 0.013803, 0.002780],
"profit_abs": [0.000003, 0.000011, 0.000014, 0.000003],
"open_date": [Arrow(2017, 11, 14, 19, 32, 00).datetime,
Arrow(2017, 11, 14, 21, 36, 00).datetime,
Arrow(2017, 11, 14, 22, 12, 00).datetime,
Arrow(2017, 11, 14, 22, 44, 00).datetime],
"close_date": [Arrow(2017, 11, 14, 21, 35, 00).datetime,
Arrow(2017, 11, 14, 22, 10, 00).datetime,
Arrow(2017, 11, 14, 22, 43, 00).datetime,
Arrow(2017, 11, 14, 22, 58, 00).datetime],
"open_rate": [0.002543, 0.003003, 0.003089, 0.003214],
"close_rate": [0.002546, 0.003014, 0.003103, 0.003217],
"trade_duration": [123, 34, 31, 14],
"is_open": [False, False, False, True],
"stake_amount": [0.01, 0.01, 0.01, 0.01],
"sell_reason": [SellType.ROI, SellType.STOP_LOSS,
SellType.ROI, SellType.FORCE_SELL]
}),
'config': hyperopt_conf,
'locks': [],
'rejected_signals': 20,
'final_balance': 1000,
}
mocker.patch(
'freqtrade.optimize.hyperopt.Backtesting.backtest',
MagicMock(return_value=backtest_result)
)
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(Arrow(2017, 12, 10), Arrow(2017, 12, 13)))
)
mocker.patch('freqtrade.optimize.hyperopt.Backtesting.backtest', return_value=backtest_result)
mocker.patch('freqtrade.optimize.hyperopt.get_timerange',
return_value=(Arrow(2017, 12, 10), Arrow(2017, 12, 13)))
patch_exchange(mocker)
mocker.patch('freqtrade.optimize.hyperopt.load', MagicMock())
mocker.patch.object(Path, 'open')
mocker.patch('freqtrade.optimize.hyperopt.load', return_value={'XRP/BTC': None})
optimizer_param = {
'adx-value': 0,
@@ -625,11 +636,11 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None:
'trailing_only_offset_is_reached': False,
}
response_expected = {
'loss': 1.9840569076926293,
'results_explanation': (' 1 trades. 1/0/0 Wins/Draws/Losses. '
'Avg profit 2.31%. Median profit 2.31%. Total profit '
'0.00023300 BTC ( 2.31%). '
'Avg duration 100.0 min.'
'loss': 1.9147239021396234,
'results_explanation': (' 4 trades. 4/0/0 Wins/Draws/Losses. '
'Avg profit 0.77%. Median profit 0.71%. Total profit '
'0.00003100 BTC ( 0.00%). '
'Avg duration 0:50:00 min.'
),
'params_details': {'buy': {'adx-enabled': False,
'adx-value': 0,
@@ -640,10 +651,10 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None:
'rsi-enabled': False,
'rsi-value': 0,
'trigger': 'macd_cross_signal'},
'roi': {0: 0.12000000000000001,
20.0: 0.02,
50.0: 0.01,
110.0: 0},
'roi': {"0": 0.12000000000000001,
"20.0": 0.02,
"50.0": 0.01,
"110.0": 0},
'sell': {'sell-adx-enabled': False,
'sell-adx-value': 0,
'sell-fastd-enabled': True,
@@ -659,21 +670,16 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None:
'trailing_stop_positive': 0.02,
'trailing_stop_positive_offset': 0.07}},
'params_dict': optimizer_param,
'results_metrics': {'avg_profit': 2.3117,
'draws': 0,
'duration': 100.0,
'losses': 0,
'winsdrawslosses': ' 1 0 0',
'median_profit': 2.3117,
'profit': 2.3117,
'total_profit': 0.000233,
'trade_count': 1,
'wins': 1},
'total_profit': 0.00023300
'params_not_optimized': {'buy': {}, 'sell': {}},
'results_metrics': ANY,
'total_profit': 3.1e-08
}
hyperopt = Hyperopt(hyperopt_conf)
hyperopt.dimensions = hyperopt.hyperopt_space()
hyperopt.min_date = Arrow(2017, 12, 10)
hyperopt.max_date = Arrow(2017, 12, 13)
hyperopt.init_spaces()
hyperopt.dimensions = hyperopt.dimensions
generate_optimizer_value = hyperopt.generate_optimizer(list(optimizer_param.values()))
assert generate_optimizer_value == response_expected
@@ -690,7 +696,8 @@ def test_clean_hyperopt(mocker, hyperopt_conf, caplog):
def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None:
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
@@ -741,13 +748,14 @@ def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None:
':{},"stoploss":null,"trailing_stop":null}'
)
assert result_str in out # noqa: E501
assert dumper.called
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
# Should be called for historical candle data
assert dumper.call_count == 1
assert dumper2.call_count == 1
def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
@@ -789,13 +797,14 @@ def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
out, err = capsys.readouterr()
assert '{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi":{},"stoploss":null}' in out # noqa: E501
assert dumper.called
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
# Should be called for historical candle data
assert dumper.call_count == 1
assert dumper2.call_count == 1
def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
@@ -836,13 +845,14 @@ def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
out, err = capsys.readouterr()
assert '{"minimal_roi":{},"stoploss":null}' in out
assert dumper.called
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
assert dumper.call_count == 1
assert dumper2.call_count == 1
def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
@@ -884,9 +894,9 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non
out, err = capsys.readouterr()
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
assert dumper.called
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
assert dumper.call_count == 1
assert dumper2.call_count == 1
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
assert hasattr(hyperopt, "max_open_trades")
@@ -922,7 +932,8 @@ def test_simplified_interface_all_failed(mocker, hyperopt_conf) -> None:
def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
@@ -965,8 +976,8 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
out, err = capsys.readouterr()
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
assert dumper.called
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
assert dumper.call_count == 1
assert dumper2.call_count == 1
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
assert hasattr(hyperopt, "max_open_trades")
@@ -975,7 +986,8 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
@@ -1018,8 +1030,8 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
out, err = capsys.readouterr()
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
assert dumper.called
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
assert dumper.call_count == 1
assert dumper2.call_count == 1
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
assert hasattr(hyperopt, "max_open_trades")
@@ -1107,7 +1119,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None:
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
assert hyperopt.backtesting.strategy.buy_rsi.hyperopt is True
assert hyperopt.backtesting.strategy.buy_rsi.in_space is True
assert hyperopt.backtesting.strategy.buy_rsi.value == 35
buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range
assert isinstance(buy_rsi_range, range)
@@ -1132,3 +1144,17 @@ def test_SKDecimal():
assert space.transform([2.0]) == [200]
assert space.transform([1.0]) == [100]
assert space.transform([1.5, 1.6]) == [150, 160]
def test___pprint():
params = {'buy_std': 1.2, 'buy_rsi': 31, 'buy_enable': True, 'buy_what': 'asdf'}
non_params = {'buy_notoptimied': 55}
x = HyperoptTools._pprint(params, non_params)
assert x == """{
"buy_std": 1.2,
"buy_rsi": 31,
"buy_enable": True,
"buy_what": "asdf",
"buy_notoptimied": 55, # value loaded from strategy
}"""

View File

@@ -1,5 +1,6 @@
import datetime
import re
from datetime import datetime, timedelta, timezone
from datetime import timedelta
from pathlib import Path
import pandas as pd
@@ -7,14 +8,15 @@ import pytest
from arrow import Arrow
from freqtrade.configuration import TimeRange
from freqtrade.constants import LAST_BT_RESULT_FN
from freqtrade.constants import DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN
from freqtrade.data import history
from freqtrade.data.btanalysis import get_latest_backtest_filename, load_backtest_data
from freqtrade.edge import PairInfo
from freqtrade.optimize.optimize_reports import (generate_backtest_stats, generate_daily_stats,
generate_edge_table, generate_pair_metrics,
generate_sell_reason_stats,
generate_strategy_metrics, store_backtest_stats,
generate_strategy_comparison,
generate_trading_stats, store_backtest_stats,
text_table_bt_results, text_table_sell_reason,
text_table_strategy)
from freqtrade.resolvers.strategy_resolver import StrategyResolver
@@ -26,25 +28,22 @@ def test_text_table_bt_results():
results = pd.DataFrame(
{
'pair': ['ETH/BTC', 'ETH/BTC'],
'profit_ratio': [0.1, 0.2],
'profit_abs': [0.2, 0.4],
'trade_duration': [10, 30],
'wins': [2, 0],
'draws': [0, 0],
'losses': [0, 0]
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'profit_ratio': [0.1, 0.2, -0.05],
'profit_abs': [0.2, 0.4, -0.1],
'trade_duration': [10, 30, 20],
}
)
result_str = (
'| 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 |'
'| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % |'
' Avg Duration | Win Draw Loss Win% |\n'
'|---------+--------+----------------+----------------+------------------+----------------+'
'----------------+-------------------------|\n'
'| ETH/BTC | 3 | 8.33 | 25.00 | 0.50000000 | 12.50 |'
' 0:20:00 | 2 0 1 66.7 |\n'
'| TOTAL | 3 | 8.33 | 25.00 | 0.50000000 | 12.50 |'
' 0:20:00 | 2 0 1 66.7 |'
)
pair_results = generate_pair_metrics(data={'ETH/BTC': {}}, stake_currency='BTC',
@@ -80,6 +79,7 @@ def test_generate_backtest_stats(default_conf, testdatadir):
'config': default_conf,
'locks': [],
'final_balance': 1000.02,
'rejected_signals': 20,
'backtest_start_time': Arrow.utcnow().int_timestamp,
'backtest_end_time': Arrow.utcnow().int_timestamp,
}
@@ -96,8 +96,8 @@ def test_generate_backtest_stats(default_conf, testdatadir):
assert 'DefStrat' in stats['strategy']
assert 'strategy_comparison' in stats
strat_stats = stats['strategy']['DefStrat']
assert strat_stats['backtest_start'] == min_date.datetime
assert strat_stats['backtest_end'] == max_date.datetime
assert strat_stats['backtest_start'] == min_date.strftime(DATETIME_PRINT_FORMAT)
assert strat_stats['backtest_end'] == max_date.strftime(DATETIME_PRINT_FORMAT)
assert strat_stats['total_trades'] == len(results['DefStrat']['results'])
# Above sample had no loosing trade
assert strat_stats['max_drawdown'] == 0.0
@@ -127,6 +127,7 @@ def test_generate_backtest_stats(default_conf, testdatadir):
'config': default_conf,
'locks': [],
'final_balance': 1000.02,
'rejected_signals': 20,
'backtest_start_time': Arrow.utcnow().int_timestamp,
'backtest_end_time': Arrow.utcnow().int_timestamp,
}
@@ -140,8 +141,8 @@ def test_generate_backtest_stats(default_conf, testdatadir):
strat_stats = stats['strategy']['DefStrat']
assert strat_stats['max_drawdown'] == 0.013803
assert strat_stats['drawdown_start'] == datetime(2017, 11, 14, 22, 10, tzinfo=timezone.utc)
assert strat_stats['drawdown_end'] == datetime(2017, 11, 14, 22, 43, tzinfo=timezone.utc)
assert strat_stats['drawdown_start'] == '2017-11-14 22:10:00'
assert strat_stats['drawdown_end'] == '2017-11-14 22:43:00'
assert strat_stats['drawdown_end_ts'] == 1510699380000
assert strat_stats['drawdown_start_ts'] == 1510697400000
assert strat_stats['pairlist'] == ['UNITTEST/BTC']
@@ -226,8 +227,6 @@ def test_generate_daily_stats(testdatadir):
assert res['winning_days'] == 14
assert res['draw_days'] == 4
assert res['losing_days'] == 3
assert res['winner_holding_avg'] == timedelta(seconds=1440)
assert res['loser_holding_avg'] == timedelta(days=1, seconds=21420)
# Select empty dataframe!
res = generate_daily_stats(bt_data.loc[bt_data['open_date'] == '2000-01-01', :])
@@ -238,6 +237,23 @@ def test_generate_daily_stats(testdatadir):
assert res['losing_days'] == 0
def test_generate_trading_stats(testdatadir):
filename = testdatadir / "backtest-result_new.json"
bt_data = load_backtest_data(filename)
res = generate_trading_stats(bt_data)
assert isinstance(res, dict)
assert res['winner_holding_avg'] == timedelta(seconds=1440)
assert res['loser_holding_avg'] == timedelta(days=1, seconds=21420)
assert 'wins' in res
assert 'losses' in res
assert 'draws' in res
# Select empty dataframe!
res = generate_trading_stats(bt_data.loc[bt_data['open_date'] == '2000-01-01', :])
assert res['wins'] == 0
assert res['losses'] == 0
def test_text_table_sell_reason():
results = pd.DataFrame(
@@ -254,14 +270,14 @@ def test_text_table_sell_reason():
)
result_str = (
'| Sell Reason | Sells | Wins | Draws | Losses |'
' Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % |\n'
'|---------------+---------+--------+---------+----------+'
'----------------+----------------+------------------+----------------|\n'
'| roi | 2 | 2 | 0 | 0 |'
' 15 | 30 | 0.6 | 15 |\n'
'| stop_loss | 1 | 0 | 0 | 1 |'
' -10 | -10 | -0.2 | -5 |'
'| Sell Reason | Sells | Win Draws Loss Win% | Avg Profit % | Cum Profit % |'
' Tot Profit BTC | Tot Profit % |\n'
'|---------------+---------+--------------------------+----------------+----------------+'
'------------------+----------------|\n'
'| roi | 2 | 2 0 0 100 | 15 | 30 |'
' 0.6 | 15 |\n'
'| stop_loss | 1 | 0 0 1 0 | -10 | -10 |'
' -0.2 | -5 |'
)
sell_reason_stats = generate_sell_reason_stats(max_open_trades=2,
@@ -309,9 +325,12 @@ def test_text_table_strategy(default_conf):
default_conf['max_open_trades'] = 2
default_conf['dry_run_wallet'] = 3
results = {}
date = datetime.datetime(year=2020, month=1, day=1, hour=12, minute=30)
delta = datetime.timedelta(days=1)
results['TestStrategy1'] = {'results': pd.DataFrame(
{
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'close_date': [date, date + delta, date + delta * 2],
'profit_ratio': [0.1, 0.2, 0.3],
'profit_abs': [0.2, 0.4, 0.5],
'trade_duration': [10, 30, 10],
@@ -324,6 +343,7 @@ def test_text_table_strategy(default_conf):
results['TestStrategy2'] = {'results': pd.DataFrame(
{
'pair': ['LTC/BTC', 'LTC/BTC', 'LTC/BTC'],
'close_date': [date, date + delta, date + delta * 2],
'profit_ratio': [0.4, 0.2, 0.3],
'profit_abs': [0.4, 0.4, 0.5],
'trade_duration': [15, 30, 15],
@@ -335,18 +355,17 @@ def test_text_table_strategy(default_conf):
), 'config': default_conf}
result_str = (
'| Strategy | Buys | Avg Profit % | Cum Profit % | Tot'
' Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses |\n'
'| Strategy | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC |'
' Tot Profit % | Avg Duration | Win Draw Loss Win% | Drawdown |\n'
'|---------------+--------+----------------+----------------+------------------+'
'----------------+----------------+--------+---------+----------|\n'
'----------------+----------------+-------------------------+-----------------------|\n'
'| TestStrategy1 | 3 | 20.00 | 60.00 | 1.10000000 |'
' 36.67 | 0:17:00 | 3 | 0 | 0 |\n'
' 36.67 | 0:17:00 | 3 0 0 100 | 0.00000000 BTC 0.00% |\n'
'| TestStrategy2 | 3 | 30.00 | 90.00 | 1.30000000 |'
' 43.33 | 0:20:00 | 3 | 0 | 0 |'
' 43.33 | 0:20:00 | 3 0 0 100 | 0.00000000 BTC 0.00% |'
)
strategy_results = generate_strategy_metrics(all_results=results)
strategy_results = generate_strategy_comparison(all_results=results)
assert text_table_strategy(strategy_results, 'BTC') == result_str

View File

@@ -7,10 +7,11 @@ import pytest
from freqtrade.constants import AVAILABLE_PAIRLISTS
from freqtrade.exceptions import OperationalException
from freqtrade.persistence import Trade
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
from freqtrade.plugins.pairlistmanager import PairListManager
from freqtrade.resolvers import PairListResolver
from tests.conftest import get_patched_freqtradebot, log_has, log_has_re
from tests.conftest import get_patched_exchange, get_patched_freqtradebot, log_has, log_has_re
@pytest.fixture(scope="function")
@@ -406,6 +407,9 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"},
{"method": "PriceFilter", "low_price_ratio": 0.02}],
"USDT", ['ETH/USDT', 'NANO/USDT']),
([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"},
{"method": "PriceFilter", "max_value": 0.000001}],
"USDT", ['NANO/USDT']),
([{"method": "StaticPairList"},
{"method": "RangeStabilityFilter", "lookback_days": 10,
"min_rate_of_change": 0.01, "refresh_period": 1440}],
@@ -488,6 +492,8 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t
r'because last price < .*%$', caplog) or
log_has_re(r'^Removed .* from whitelist, '
r'because last price > .*%$', caplog) or
log_has_re(r'^Removed .* from whitelist, '
r'because min value change of .*', caplog) or
log_has_re(r"^Removed .* from whitelist, because ticker\['last'\] "
r"is empty.*", caplog))
if pairlist['method'] == 'VolumePairList':
@@ -512,6 +518,18 @@ def test_PrecisionFilter_error(mocker, whitelist_conf) -> None:
PairListManager(MagicMock, whitelist_conf)
def test_PerformanceFilter_error(mocker, whitelist_conf, caplog) -> None:
whitelist_conf['pairlists'] = [{"method": "StaticPairList"}, {"method": "PerformanceFilter"}]
if hasattr(Trade, 'query'):
del Trade.query
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
exchange = get_patched_exchange(mocker, whitelist_conf)
pm = PairListManager(exchange, whitelist_conf)
pm.refresh_pairlist()
assert log_has("PerformanceFilter is not available in this mode.", caplog)
def test_gen_pair_whitelist_not_supported(mocker, default_conf, tickers) -> None:
default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10}]
@@ -787,6 +805,10 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo
"[{'PriceFilter': 'PriceFilter - Filtering pairs priced below 0.00002000.'}]",
None
),
({"method": "PriceFilter", "max_value": 0.00002000},
"[{'PriceFilter': 'PriceFilter - Filtering pairs priced Value above 0.00002000.'}]",
None
),
({"method": "PriceFilter"},
"[{'PriceFilter': 'PriceFilter - No price filters configured.'}]",
None
@@ -803,6 +825,10 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo
None,
"PriceFilter requires max_price to be >= 0"
), # OperationalException expected
({"method": "PriceFilter", "max_value": -1.00010000},
None,
"PriceFilter requires max_value to be >= 0"
), # OperationalException expected
({"method": "RangeStabilityFilter", "lookback_days": 10, "min_rate_of_change": 0.01},
"[{'RangeStabilityFilter': 'RangeStabilityFilter - Filtering pairs with rate of change below "
"0.01 over the last days.'}]",

View File

@@ -1,6 +1,7 @@
# pragma pylint: disable=missing-docstring, too-many-arguments, too-many-ancestors,
# pragma pylint: disable=protected-access, C0103
import datetime
from unittest.mock import MagicMock
import pytest
@@ -21,6 +22,12 @@ def test_fiat_convert_is_supported(mocker):
def test_fiat_convert_find_price(mocker):
fiat_convert = CryptoToFiatConverter()
fiat_convert._cryptomap = {}
fiat_convert._backoff = 0
mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._load_cryptomap',
return_value=None)
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='EUR') == 0.0
with pytest.raises(ValueError, match=r'The fiat ABC is not supported.'):
fiat_convert._find_price(crypto_symbol='BTC', fiat_symbol='ABC')
@@ -115,6 +122,28 @@ def test_fiat_convert_without_network(mocker):
CryptoToFiatConverter._coingekko = cmc_temp
def test_fiat_too_many_requests_response(mocker, caplog):
# Because CryptoToFiatConverter is a Singleton we reset the listings
req_exception = "429 Too Many Requests"
listmock = MagicMock(return_value="{}", side_effect=RequestException(req_exception))
mocker.patch.multiple(
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_coins_list=listmock,
)
# with pytest.raises(RequestEsxception):
fiat_convert = CryptoToFiatConverter()
fiat_convert._cryptomap = {}
fiat_convert._load_cryptomap()
length_cryptomap = len(fiat_convert._cryptomap)
assert length_cryptomap == 0
assert fiat_convert._backoff > datetime.datetime.now().timestamp()
assert log_has(
'Too many requests for Coingecko API, backing off and trying again later.',
caplog
)
def test_fiat_invalid_response(mocker, caplog):
# Because CryptoToFiatConverter is a Singleton we reset the listings
listmock = MagicMock(return_value="{'novalidjson':DEADBEEFf}")

View File

@@ -199,28 +199,31 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
freqtradebot.enter_positions()
result, headers = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
assert "Since" in headers
assert "Pair" in headers
assert 'instantly' == result[0][2]
assert 'ETH/BTC' in result[0][1]
assert '-0.41%' == result[0][3]
assert isnan(fiat_profit_sum)
# Test with fiatconvert
rpc._fiat_converter = CryptoToFiatConverter()
result, headers = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
assert "Since" in headers
assert "Pair" in headers
assert 'instantly' == result[0][2]
assert 'ETH/BTC' in result[0][1]
assert '-0.41% (-0.06)' == result[0][3]
assert '-0.06' == f'{fiat_profit_sum:.2f}'
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
result, headers = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
assert 'instantly' == result[0][2]
assert 'ETH/BTC' in result[0][1]
assert 'nan%' == result[0][3]
assert isnan(fiat_profit_sum)
def test_rpc_daily_profit(default_conf, update, ticker, fee,
@@ -419,7 +422,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
assert stats['trade_count'] == 2
assert stats['first_trade_date'] == 'just now'
assert stats['latest_trade_date'] == 'just now'
assert stats['avg_duration'] == '0:00:00'
assert stats['avg_duration'] in ('0:00:00', '0:00:01')
assert stats['best_pair'] == 'ETH/BTC'
assert prec_satoshi(stats['best_rate'], 6.2)
@@ -430,7 +433,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
assert stats['trade_count'] == 2
assert stats['first_trade_date'] == 'just now'
assert stats['latest_trade_date'] == 'just now'
assert stats['avg_duration'] == '0:00:00'
assert stats['avg_duration'] in ('0:00:00', '0:00:01')
assert stats['best_pair'] == 'ETH/BTC'
assert prec_satoshi(stats['best_rate'], 6.2)
assert isnan(stats['profit_all_coin'])

View File

@@ -710,7 +710,7 @@ def test_api_stats(botclient, mocker, ticker, fee, markets,):
assert 'draws' in rc.json()['durations']
def test_api_performance(botclient, mocker, ticker, fee):
def test_api_performance(botclient, fee):
ftbot, client = botclient
patch_get_signal(ftbot, (True, False))
@@ -728,6 +728,7 @@ def test_api_performance(botclient, mocker, ticker, fee):
)
trade.close_profit = trade.calc_profit_ratio()
trade.close_profit_abs = trade.calc_profit()
Trade.query.session.add(trade)
trade = Trade(
@@ -743,14 +744,16 @@ def test_api_performance(botclient, mocker, ticker, fee):
close_rate=0.391
)
trade.close_profit = trade.calc_profit_ratio()
trade.close_profit_abs = trade.calc_profit()
Trade.query.session.add(trade)
Trade.query.session.flush()
rc = client_get(client, f"{BASE_URI}/performance")
assert_response(rc)
assert len(rc.json()) == 2
assert rc.json() == [{'count': 1, 'pair': 'LTC/ETH', 'profit': 7.61},
{'count': 1, 'pair': 'XRP/ETH', 'profit': -5.57}]
assert rc.json() == [{'count': 1, 'pair': 'LTC/ETH', 'profit': 7.61, 'profit_abs': 0.01872279},
{'count': 1, 'pair': 'XRP/ETH', 'profit': -5.57, 'profit_abs': -0.1150375}]
def test_api_status(botclient, mocker, ticker, fee, markets):
@@ -1142,6 +1145,14 @@ def test_api_plot_config(botclient):
assert_response(rc)
assert rc.json() == ftbot.strategy.plot_config
assert isinstance(rc.json()['main_plot'], dict)
assert isinstance(rc.json()['subplots'], dict)
ftbot.strategy.plot_config = {'main_plot': {'sma': {}}}
rc = client_get(client, f"{BASE_URI}/plot_config")
assert_response(rc)
assert isinstance(rc.json()['main_plot'], dict)
assert isinstance(rc.json()['subplots'], dict)
def test_api_strategies(botclient):

View File

@@ -4,6 +4,7 @@
import re
from datetime import datetime
from functools import reduce
from random import choice, randint
from string import ascii_uppercase
from unittest.mock import ANY, MagicMock
@@ -54,6 +55,14 @@ class DummyCls(Telegram):
raise Exception('test')
def get_telegram_testobject_with_inline(mocker, default_conf, mock=True, ftbot=None):
inline_msg_mock = MagicMock()
telegram, ftbot, msg_mock = get_telegram_testobject(mocker, default_conf)
mocker.patch('freqtrade.rpc.telegram.Telegram._send_inline_msg', inline_msg_mock)
return telegram, ftbot, msg_mock, inline_msg_mock
def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None):
msg_mock = MagicMock()
if mock:
@@ -902,6 +911,33 @@ def test_forcebuy_handle_exception(default_conf, update, mocker) -> None:
assert msg_mock.call_args_list[0][0][0] == 'Forcebuy not enabled.'
def test_forcebuy_no_pair(default_conf, update, mocker) -> None:
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
fbuy_mock = MagicMock(return_value=None)
mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock)
telegram, freqtradebot, _, inline_msg_mock = get_telegram_testobject_with_inline(mocker,
default_conf)
patch_get_signal(freqtradebot, (True, False))
context = MagicMock()
context.args = []
telegram._forcebuy(update=update, context=context)
assert fbuy_mock.call_count == 0
assert inline_msg_mock.call_count == 1
assert inline_msg_mock.call_args_list[0][0][0] == 'Which pair?'
assert inline_msg_mock.call_args_list[0][1]['callback_query_handler'] == 'forcebuy'
keyboard = inline_msg_mock.call_args_list[0][1]['keyboard']
assert reduce(lambda acc, x: acc + len(x), keyboard, 0) == 4
update = MagicMock()
update.callback_query = MagicMock()
update.callback_query.data = 'XRP/USDT'
telegram._forcebuy_inline(update, None)
assert fbuy_mock.call_count == 1
def test_performance_handle(default_conf, update, ticker, fee,
limit_buy_order, limit_sell_order, mocker) -> None:
@@ -929,7 +965,7 @@ def test_performance_handle(default_conf, update, ticker, fee,
telegram._performance(update=update, context=MagicMock())
assert msg_mock.call_count == 1
assert 'Performance' in msg_mock.call_args_list[0][0][0]
assert '<code>ETH/BTC\t6.20% (1)</code>' in msg_mock.call_args_list[0][0][0]
assert '<code>ETH/BTC\t0.00006217 BTC (6.20%) (1)</code>' in msg_mock.call_args_list[0][0][0]
def test_count_handle(default_conf, update, ticker, fee, mocker) -> None:
@@ -969,6 +1005,11 @@ def test_telegram_lock_handle(default_conf, update, ticker, fee, mocker) -> None
)
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
patch_get_signal(freqtradebot, (True, False))
telegram._locks(update=update, context=MagicMock())
assert msg_mock.call_count == 1
assert 'No active locks.' in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
PairLocks.lock_pair('ETH/BTC', arrow.utcnow().shift(minutes=4).datetime, 'randreason')
PairLocks.lock_pair('XRP/BTC', arrow.utcnow().shift(minutes=20).datetime, 'deadbeef')
@@ -1102,6 +1143,15 @@ def test_edge_enabled(edge_conf, update, mocker) -> None:
assert '<b>Edge only validated following pairs:</b>\n<pre>' in msg_mock.call_args_list[0][0][0]
assert 'Pair Winrate Expectancy Stoploss' in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={}))
telegram._edge(update=update, context=MagicMock())
assert msg_mock.call_count == 1
assert '<b>Edge only validated following pairs:</b>' in msg_mock.call_args_list[0][0][0]
assert 'Winrate' not in msg_mock.call_args_list[0][0][0]
def test_telegram_trades(mocker, update, default_conf, fee):

View File

@@ -36,10 +36,11 @@ def test_default_strategy(result, fee):
)
assert strategy.confirm_trade_entry(pair='ETH/BTC', order_type='limit', amount=0.1,
rate=20000, time_in_force='gtc') is True
rate=20000, time_in_force='gtc',
current_time=datetime.utcnow()) is True
assert strategy.confirm_trade_exit(pair='ETH/BTC', trade=trade, order_type='limit', amount=0.1,
rate=20000, time_in_force='gtc', sell_reason='roi') is True
rate=20000, time_in_force='gtc', sell_reason='roi',
current_time=datetime.utcnow()) is True
assert strategy.custom_stoploss(pair='ETH/BTC', trade=trade, current_time=datetime.now(),
current_rate=20_000, current_profit=0.05, dataframe=None
) == strategy.stoploss
current_rate=20_000, current_profit=0.05) == strategy.stoploss

View File

@@ -360,7 +360,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili
now = arrow.utcnow().datetime
sl_flag = strategy.stop_loss_reached(current_rate=trade.open_rate * (1 + profit), trade=trade,
current_time=now, current_profit=profit,
force_stoploss=0, high=None, dataframe=None)
force_stoploss=0, high=None)
assert isinstance(sl_flag, SellCheckTuple)
assert sl_flag.sell_type == expected
if expected == SellType.NONE:
@@ -371,7 +371,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili
sl_flag = strategy.stop_loss_reached(current_rate=trade.open_rate * (1 + profit2), trade=trade,
current_time=now, current_profit=profit2,
force_stoploss=0, high=None, dataframe=None)
force_stoploss=0, high=None)
assert sl_flag.sell_type == expected2
if expected2 == SellType.NONE:
assert sl_flag.sell_flag is False
@@ -399,27 +399,27 @@ def test_custom_sell(default_conf, fee, caplog) -> None:
)
now = arrow.utcnow().datetime
res = strategy.should_sell(None, trade, 1, now, False, False, None, None, 0)
res = strategy.should_sell(trade, 1, now, False, False, None, None, 0)
assert res.sell_flag is False
assert res.sell_type == SellType.NONE
strategy.custom_sell = MagicMock(return_value=True)
res = strategy.should_sell(None, trade, 1, now, False, False, None, None, 0)
res = strategy.should_sell(trade, 1, now, False, False, None, None, 0)
assert res.sell_flag is True
assert res.sell_type == SellType.CUSTOM_SELL
assert res.sell_reason == 'custom_sell'
strategy.custom_sell = MagicMock(return_value='hello world')
res = strategy.should_sell(None, trade, 1, now, False, False, None, None, 0)
res = strategy.should_sell(trade, 1, now, False, False, None, None, 0)
assert res.sell_type == SellType.CUSTOM_SELL
assert res.sell_flag is True
assert res.sell_reason == 'hello world'
caplog.clear()
strategy.custom_sell = MagicMock(return_value='h' * 100)
res = strategy.should_sell(None, trade, 1, now, False, False, None, None, 0)
res = strategy.should_sell(trade, 1, now, False, False, None, None, 0)
assert res.sell_type == SellType.CUSTOM_SELL
assert res.sell_flag is True
assert res.sell_reason == 'h' * 64
@@ -636,7 +636,7 @@ def test_hyperopt_parameters():
assert len(list(intpar.range)) == 1
# Range contains ONLY the default / value.
assert list(intpar.range) == [intpar.value]
intpar.hyperopt = True
intpar.in_space = True
assert len(list(intpar.range)) == 6
assert list(intpar.range) == [0, 1, 2, 3, 4, 5]
@@ -671,4 +671,4 @@ def test_auto_hyperopt_interface(default_conf):
strategy.sell_rsi = IntParameter([0, 10], default=5, space='buy')
with pytest.raises(OperationalException, match=r"Inconclusive parameter.*"):
[x for x in strategy.enumerate_parameters('sell')]
[x for x in strategy._detect_parameters('sell')]

View File

@@ -1371,7 +1371,8 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c
}
mocker.patch('freqtrade.exchange.Binance.cancel_stoploss_order',
side_effect=InvalidOrderException())
mocker.patch('freqtrade.exchange.Binance.fetch_stoploss_order', stoploss_order_hanging)
mocker.patch('freqtrade.exchange.Binance.fetch_stoploss_order',
return_value=stoploss_order_hanging)
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/BTC.*", caplog)
@@ -2430,13 +2431,22 @@ def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> Non
freqtrade._notify_buy_cancel = MagicMock()
trade = MagicMock()
trade.pair = 'LTC/ETH'
trade.pair = 'LTC/USDT'
trade.open_rate = 200
limit_buy_order['filled'] = 0.0
limit_buy_order['status'] = 'open'
reason = CANCEL_REASON['TIMEOUT']
assert freqtrade.handle_cancel_buy(trade, limit_buy_order, reason)
assert cancel_order_mock.call_count == 1
cancel_order_mock.reset_mock()
caplog.clear()
limit_buy_order['filled'] = 0.01
assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason)
assert cancel_order_mock.call_count == 0
assert log_has_re("Order .* for .* not cancelled, as the filled amount.* unsellable.*", caplog)
caplog.clear()
cancel_order_mock.reset_mock()
limit_buy_order['filled'] = 2
assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason)
@@ -2491,7 +2501,8 @@ def test_handle_cancel_buy_corder_empty(mocker, default_conf, limit_buy_order,
freqtrade._notify_buy_cancel = MagicMock()
trade = MagicMock()
trade.pair = 'LTC/ETH'
trade.pair = 'LTC/USDT'
trade.open_rate = 200
limit_buy_order['filled'] = 0.0
limit_buy_order['status'] = 'open'
reason = CANCEL_REASON['TIMEOUT']

View File

@@ -63,7 +63,7 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
amount_to_precision=lambda s, x, y: y,
price_to_precision=lambda s, x, y: y,
fetch_stoploss_order=stoploss_order_mock,
cancel_stoploss_order=cancel_order_mock,
cancel_stoploss_order_with_result=cancel_order_mock,
)
mocker.patch.multiple(

View File

@@ -7,7 +7,7 @@ from unittest.mock import MagicMock
import arrow
import pytest
from sqlalchemy import create_engine
from sqlalchemy import create_engine, inspect
from freqtrade import constants
from freqtrade.exceptions import DependencyException, OperationalException
@@ -627,6 +627,63 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
assert orders[1].order_id == 'stop_order_id222'
assert orders[1].ft_order_side == 'stoploss'
caplog.clear()
# Drop latest column
engine.execute("alter table orders rename to orders_bak")
inspector = inspect(engine)
for index in inspector.get_indexes('orders_bak'):
engine.execute(f"drop index {index['name']}")
# Recreate table
engine.execute("""
CREATE TABLE orders (
id INTEGER NOT NULL,
ft_trade_id INTEGER,
ft_order_side VARCHAR NOT NULL,
ft_pair VARCHAR NOT NULL,
ft_is_open BOOLEAN NOT NULL,
order_id VARCHAR NOT NULL,
status VARCHAR,
symbol VARCHAR,
order_type VARCHAR,
side VARCHAR,
price FLOAT,
amount FLOAT,
filled FLOAT,
remaining FLOAT,
cost FLOAT,
order_date DATETIME,
order_filled_date DATETIME,
order_update_date DATETIME,
PRIMARY KEY (id),
CONSTRAINT _order_pair_order_id UNIQUE (ft_pair, order_id),
FOREIGN KEY(ft_trade_id) REFERENCES trades (id)
)
""")
engine.execute("""
insert into orders ( id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, status,
symbol, order_type, side, price, amount, filled, remaining, cost, order_date,
order_filled_date, order_update_date)
select id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, status,
symbol, order_type, side, price, amount, filled, remaining, cost, order_date,
order_filled_date, order_update_date
from orders_bak
""")
# Run init to test migration
init_db(default_conf['db_url'], default_conf['dry_run'])
assert log_has("trying orders_bak1", caplog)
orders = Order.query.all()
assert len(orders) == 2
assert orders[0].order_id == 'buy_order'
assert orders[0].ft_order_side == 'buy'
assert orders[1].order_id == 'stop_order_id222'
assert orders[1].ft_order_side == 'stoploss'
def test_migrate_mid_state(mocker, default_conf, fee, caplog):
"""

Binary file not shown.

File diff suppressed because one or more lines are too long