Merge branch 'feat/short' into funding-fee

This commit is contained in:
Sam Germain
2021-10-03 02:15:54 -06:00
40 changed files with 1897 additions and 1323 deletions

View File

@@ -8,12 +8,12 @@ from zipfile import ZipFile
import arrow
import pytest
from freqtrade.commands import (start_convert_data, start_create_userdir, start_download_data,
start_hyperopt_list, start_hyperopt_show, start_install_ui,
start_list_data, start_list_exchanges, start_list_markets,
start_list_strategies, start_list_timeframes, start_new_strategy,
start_show_trades, start_test_pairlist, start_trading,
start_webserver)
from freqtrade.commands import (start_convert_data, start_convert_trades, start_create_userdir,
start_download_data, start_hyperopt_list, start_hyperopt_show,
start_install_ui, start_list_data, start_list_exchanges,
start_list_markets, start_list_strategies, start_list_timeframes,
start_new_strategy, start_show_trades, start_test_pairlist,
start_trading, start_webserver)
from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui,
get_ui_download_url, read_ui_version)
from freqtrade.configuration import setup_utils_configuration
@@ -208,11 +208,10 @@ def test_list_timeframes(mocker, capsys):
assert re.search(r"^1d$", captured.out, re.MULTILINE)
def test_list_markets(mocker, markets, capsys):
def test_list_markets(mocker, markets_static, capsys):
api_mock = MagicMock()
api_mock.markets = markets
patch_exchange(mocker, api_mock=api_mock, id='bittrex')
patch_exchange(mocker, api_mock=api_mock, id='bittrex', mock_markets=markets_static)
# Test with no --config
args = [
@@ -237,7 +236,7 @@ def test_list_markets(mocker, markets, capsys):
"TKN/BTC, XLTCUSDT, XRP/BTC.\n"
in captured.out)
patch_exchange(mocker, api_mock=api_mock, id="binance")
patch_exchange(mocker, api_mock=api_mock, id="binance", mock_markets=markets_static)
# Test with --exchange
args = [
"list-markets",
@@ -250,7 +249,7 @@ def test_list_markets(mocker, markets, capsys):
assert re.match("\nExchange Binance has 10 active markets:\n",
captured.out)
patch_exchange(mocker, api_mock=api_mock, id="bittrex")
patch_exchange(mocker, api_mock=api_mock, id="bittrex", mock_markets=markets_static)
# Test with --all: all markets
args = [
"list-markets", "--all",
@@ -760,6 +759,22 @@ def test_download_data_trades(mocker, caplog):
assert convert_mock.call_count == 1
def test_start_convert_trades(mocker, caplog):
convert_mock = mocker.patch('freqtrade.commands.data_commands.convert_trades_to_ohlcv',
MagicMock(return_value=[]))
patch_exchange(mocker)
mocker.patch(
'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={})
)
args = [
"trades-to-ohlcv",
"--exchange", "kraken",
"--pairs", "ETH/BTC", "XRP/BTC",
]
start_convert_trades(get_args(args))
assert convert_mock.call_count == 1
def test_start_list_strategies(mocker, caplog, capsys):
args = [

View File

@@ -27,6 +27,8 @@ from freqtrade.resolvers import ExchangeResolver
from freqtrade.worker import Worker
from tests.conftest_trades import (leverage_trade, mock_trade_1, mock_trade_2, mock_trade_3,
mock_trade_4, mock_trade_5, mock_trade_6, short_trade)
from tests.conftest_trades_usdt import (mock_trade_usdt_1, mock_trade_usdt_2, mock_trade_usdt_3,
mock_trade_usdt_4, mock_trade_usdt_5, mock_trade_usdt_6)
logging.getLogger('').setLevel(logging.INFO)
@@ -102,8 +104,10 @@ def patch_exchange(
mocker.patch('freqtrade.exchange.Exchange.precisionMode', PropertyMock(return_value=2))
if mock_markets:
if isinstance(mock_markets, bool):
mock_markets = get_markets()
mocker.patch('freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=get_markets()))
PropertyMock(return_value=mock_markets))
if mock_supported_modes:
mocker.patch(
@@ -279,6 +283,7 @@ def create_mock_trades_with_leverage(fee, use_db: bool = True):
Trade.query.session.add(trade)
else:
LocalTrade.add_bt_trade(trade)
# Simulate dry_run entries
trade = mock_trade_1(fee)
add_trade(trade)
@@ -303,6 +308,40 @@ def create_mock_trades_with_leverage(fee, use_db: bool = True):
trade = leverage_trade(fee)
add_trade(trade)
if use_db:
Trade.query.session.flush()
def create_mock_trades_usdt(fee, use_db: bool = True):
"""
Create some fake trades ...
"""
def add_trade(trade):
if use_db:
Trade.query.session.add(trade)
else:
LocalTrade.add_bt_trade(trade)
# Simulate dry_run entries
trade = mock_trade_usdt_1(fee)
add_trade(trade)
trade = mock_trade_usdt_2(fee)
add_trade(trade)
trade = mock_trade_usdt_3(fee)
add_trade(trade)
trade = mock_trade_usdt_4(fee)
add_trade(trade)
trade = mock_trade_usdt_5(fee)
add_trade(trade)
trade = mock_trade_usdt_6(fee)
add_trade(trade)
if use_db:
Trade.query.session.flush()
@@ -343,6 +382,11 @@ def default_conf(testdatadir):
return get_default_conf(testdatadir)
@pytest.fixture(scope="function")
def default_conf_usdt(testdatadir):
return get_default_conf_usdt(testdatadir)
def get_default_conf(testdatadir):
""" Returns validated configuration suitable for most tests """
configuration = {
@@ -417,6 +461,32 @@ def get_default_conf(testdatadir):
return configuration
def get_default_conf_usdt(testdatadir):
configuration = get_default_conf(testdatadir)
configuration.update({
"stake_amount": 60.0,
"stake_currency": "USDT",
"exchange": {
"name": "binance",
"enabled": True,
"key": "key",
"secret": "secret",
"pair_whitelist": [
"ETH/USDT",
"LTC/USDT",
"XRP/USDT",
"NEO/USDT",
"TKN/USDT",
],
"pair_blacklist": [
"DOGE/USDT",
"HOT/USDT",
]
},
})
return configuration
@pytest.fixture
def update():
_update = Update(0)
@@ -456,12 +526,41 @@ def ticker_sell_down():
})
@pytest.fixture
def ticker_usdt():
return MagicMock(return_value={
'bid': 2.0,
'ask': 2.02,
'last': 2.0,
})
@pytest.fixture
def ticker_usdt_sell_up():
return MagicMock(return_value={
'bid': 2.2,
'ask': 2.3,
'last': 2.2,
})
@pytest.fixture
def ticker_usdt_sell_down():
return MagicMock(return_value={
'bid': 2.01,
'ask': 2.0,
'last': 2.01,
})
@pytest.fixture
def markets():
return get_markets()
def get_markets():
# See get_markets_static() for immutable markets and do not modify them unless absolutely
# necessary!
return {
'ETH/BTC': {
'id': 'ethbtc',
@@ -701,6 +800,81 @@ def get_markets():
},
'info': {},
},
'XRP/USDT': {
'id': 'xrpusdt',
'symbol': 'XRP/USDT',
'base': 'XRP',
'quote': 'USDT',
'active': True,
'precision': {
'price': 8,
'amount': 8,
'cost': 8,
},
'lot': 0.00000001,
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': {
'min': 0.0001,
'max': 500000,
},
},
'info': {},
},
'NEO/USDT': {
'id': 'neousdt',
'symbol': 'NEO/USDT',
'base': 'NEO',
'quote': 'USDT',
'active': True,
'precision': {
'price': 8,
'amount': 8,
'cost': 8,
},
'lot': 0.00000001,
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': {
'min': 0.0001,
'max': 500000,
},
},
'info': {},
},
'TKN/USDT': {
'id': 'tknusdt',
'symbol': 'TKN/USDT',
'base': 'TKN',
'quote': 'USDT',
'active': True,
'precision': {
'price': 8,
'amount': 8,
'cost': 8,
},
'lot': 0.00000001,
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': {
'min': 0.0001,
'max': 500000,
},
},
'info': {},
},
'LTC/USD': {
'id': 'USD-LTC',
'symbol': 'LTC/USD',
@@ -778,11 +952,22 @@ def get_markets():
@pytest.fixture
def shitcoinmarkets(markets):
def markets_static():
# These markets are used in some tests that would need adaptation should anything change in
# market list. Do not modify this list without a good reason! Do not modify market parameters
# of listed pairs in get_markets() without a good reason either!
static_markets = ['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC']
all_markets = get_markets()
return {m: all_markets[m] for m in static_markets}
@pytest.fixture
def shitcoinmarkets(markets_static):
"""
Fixture with shitcoin markets - used to test filters in pairlists
"""
shitmarkets = deepcopy(markets)
shitmarkets = deepcopy(markets_static)
shitmarkets.update({
'HOT/BTC': {
'id': 'HOTBTC',
@@ -1624,27 +1809,34 @@ def result(testdatadir):
@pytest.fixture(scope="function")
def trades_for_order():
return [{'info': {'id': 34567,
'orderId': 123456,
'price': '0.24544100',
'qty': '8.00000000',
'commission': '0.00800000',
'commissionAsset': 'LTC',
'time': 1521663363189,
'isBuyer': True,
'isMaker': False,
'isBestMatch': True},
'timestamp': 1521663363189,
'datetime': '2018-03-21T20:16:03.189Z',
'symbol': 'LTC/ETH',
'id': '34567',
'order': '123456',
'type': None,
'side': 'buy',
'price': 0.245441,
'cost': 1.963528,
'amount': 8.0,
'fee': {'cost': 0.008, 'currency': 'LTC'}}]
return [{
'info': {
'id': 34567,
'orderId': 123456,
'price': '2.0',
'qty': '8.00000000',
'commission': '0.00800000',
'commissionAsset': 'LTC',
'time': 1521663363189,
'isBuyer': True,
'isMaker': False,
'isBestMatch': True
},
'timestamp': 1521663363189,
'datetime': '2018-03-21T20:16:03.189Z',
'symbol': 'LTC/USDT',
'id': '34567',
'order': '123456',
'type': None,
'side': 'buy',
'price': 2.0,
'cost': 16.0,
'amount': 8.0,
'fee': {
'cost': 0.008,
'currency': 'LTC'
}
}]
@pytest.fixture(scope="function")
@@ -1788,14 +1980,6 @@ def trades_for_order2():
'fee': {'cost': 0.004, 'currency': 'LTC'}}]
@pytest.fixture(scope="function")
def trades_for_order3(trades_for_order2):
# Different fee currencies for each trade
trades_for_order = deepcopy(trades_for_order2)
trades_for_order[0]['fee'] = {'cost': 0.02, 'currency': 'BNB'}
return trades_for_order
@pytest.fixture
def buy_order_fee():
return {
@@ -1917,6 +2101,22 @@ def open_trade():
)
@pytest.fixture(scope="function")
def open_trade_usdt():
return Trade(
pair='ADA/USDT',
open_rate=2.0,
exchange='binance',
open_order_id='123456789',
amount=30.0,
fee_open=0.0,
fee_close=0.0,
stake_amount=60.0,
open_date=arrow.utcnow().shift(minutes=-601).datetime,
is_open=True
)
@pytest.fixture
def saved_hyperopt_results():
hyperopt_res = [
@@ -2060,7 +2260,7 @@ def saved_hyperopt_results():
@pytest.fixture(scope='function')
def limit_buy_order_usdt_open():
return {
'id': 'mocked_limit_buy',
'id': 'mocked_limit_buy_usdt',
'type': 'limit',
'side': 'buy',
'symbol': 'mocked',
@@ -2087,7 +2287,7 @@ def limit_buy_order_usdt(limit_buy_order_usdt_open):
@pytest.fixture
def limit_sell_order_usdt_open():
return {
'id': 'mocked_limit_sell',
'id': 'mocked_limit_sell_usdt',
'type': 'limit',
'side': 'sell',
'pair': 'mocked',

View File

@@ -0,0 +1,305 @@
from datetime import datetime, timedelta, timezone
from freqtrade.persistence.models import Order, Trade
MOCK_TRADE_COUNT = 6
def mock_order_usdt_1():
return {
'id': '1234',
'symbol': 'ADA/USDT',
'status': 'closed',
'side': 'buy',
'type': 'limit',
'price': 2.0,
'amount': 10.0,
'filled': 10.0,
'remaining': 0.0,
}
def mock_trade_usdt_1(fee):
trade = Trade(
pair='ADA/USDT',
stake_amount=20.0,
amount=10.0,
amount_requested=10.0,
fee_open=fee.return_value,
fee_close=fee.return_value,
is_open=True,
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=17),
open_rate=2.0,
exchange='binance',
open_order_id='dry_run_buy_12345',
strategy='StrategyTestV2',
timeframe=5,
)
o = Order.parse_from_ccxt_object(mock_order_usdt_1(), 'ADA/USDT', 'buy')
trade.orders.append(o)
return trade
def mock_order_usdt_2():
return {
'id': '1235',
'symbol': 'ETC/USDT',
'status': 'closed',
'side': 'buy',
'type': 'limit',
'price': 2.0,
'amount': 100.0,
'filled': 100.0,
'remaining': 0.0,
}
def mock_order_usdt_2_sell():
return {
'id': '12366',
'symbol': 'ETC/USDT',
'status': 'closed',
'side': 'sell',
'type': 'limit',
'price': 2.05,
'amount': 100.0,
'filled': 100.0,
'remaining': 0.0,
}
def mock_trade_usdt_2(fee):
"""
Closed trade...
"""
trade = Trade(
pair='ETC/USDT',
stake_amount=200.0,
amount=100.0,
amount_requested=100.0,
fee_open=fee.return_value,
fee_close=fee.return_value,
open_rate=2.0,
close_rate=2.05,
close_profit=5.0,
close_profit_abs=3.9875,
exchange='binance',
is_open=False,
open_order_id='dry_run_sell_12345',
strategy='StrategyTestV2',
timeframe=5,
sell_reason='sell_signal',
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20),
close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2),
)
o = Order.parse_from_ccxt_object(mock_order_usdt_2(), 'ETC/USDT', 'buy')
trade.orders.append(o)
o = Order.parse_from_ccxt_object(mock_order_usdt_2_sell(), 'ETC/USDT', 'sell')
trade.orders.append(o)
return trade
def mock_order_usdt_3():
return {
'id': '41231a12a',
'symbol': 'XRP/USDT',
'status': 'closed',
'side': 'buy',
'type': 'limit',
'price': 1.0,
'amount': 30.0,
'filled': 30.0,
'remaining': 0.0,
}
def mock_order_usdt_3_sell():
return {
'id': '41231a666a',
'symbol': 'XRP/USDT',
'status': 'closed',
'side': 'sell',
'type': 'stop_loss_limit',
'price': 1.1,
'average': 1.1,
'amount': 30.0,
'filled': 30.0,
'remaining': 0.0,
}
def mock_trade_usdt_3(fee):
"""
Closed trade
"""
trade = Trade(
pair='XRP/USDT',
stake_amount=30.0,
amount=30.0,
amount_requested=30.0,
fee_open=fee.return_value,
fee_close=fee.return_value,
open_rate=1.0,
close_rate=1.1,
close_profit=10.0,
close_profit_abs=9.8425,
exchange='binance',
is_open=False,
strategy='StrategyTestV2',
timeframe=5,
sell_reason='roi',
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20),
close_date=datetime.now(tz=timezone.utc),
)
o = Order.parse_from_ccxt_object(mock_order_usdt_3(), 'XRP/USDT', 'buy')
trade.orders.append(o)
o = Order.parse_from_ccxt_object(mock_order_usdt_3_sell(), 'XRP/USDT', 'sell')
trade.orders.append(o)
return trade
def mock_order_usdt_4():
return {
'id': 'prod_buy_12345',
'symbol': 'ETC/USDT',
'status': 'open',
'side': 'buy',
'type': 'limit',
'price': 2.0,
'amount': 10.0,
'filled': 0.0,
'remaining': 30.0,
}
def mock_trade_usdt_4(fee):
"""
Simulate prod entry
"""
trade = Trade(
pair='ETC/USDT',
stake_amount=20.0,
amount=10.0,
amount_requested=10.01,
fee_open=fee.return_value,
fee_close=fee.return_value,
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=14),
is_open=True,
open_rate=2.0,
exchange='binance',
open_order_id='prod_buy_12345',
strategy='StrategyTestV2',
timeframe=5,
)
o = Order.parse_from_ccxt_object(mock_order_usdt_4(), 'ETC/USDT', 'buy')
trade.orders.append(o)
return trade
def mock_order_usdt_5():
return {
'id': 'prod_buy_3455',
'symbol': 'XRP/USDT',
'status': 'closed',
'side': 'buy',
'type': 'limit',
'price': 2.0,
'amount': 10.0,
'filled': 10.0,
'remaining': 0.0,
}
def mock_order_usdt_5_stoploss():
return {
'id': 'prod_stoploss_3455',
'symbol': 'XRP/USDT',
'status': 'open',
'side': 'sell',
'type': 'stop_loss_limit',
'price': 2.0,
'amount': 10.0,
'filled': 0.0,
'remaining': 30.0,
}
def mock_trade_usdt_5(fee):
"""
Simulate prod entry with stoploss
"""
trade = Trade(
pair='XRP/USDT',
stake_amount=20.0,
amount=10.0,
amount_requested=10.01,
fee_open=fee.return_value,
fee_close=fee.return_value,
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=12),
is_open=True,
open_rate=2.0,
exchange='binance',
strategy='SampleStrategy',
stoploss_order_id='prod_stoploss_3455',
timeframe=5,
)
o = Order.parse_from_ccxt_object(mock_order_usdt_5(), 'XRP/USDT', 'buy')
trade.orders.append(o)
o = Order.parse_from_ccxt_object(mock_order_usdt_5_stoploss(), 'XRP/USDT', 'stoploss')
trade.orders.append(o)
return trade
def mock_order_usdt_6():
return {
'id': 'prod_buy_6',
'symbol': 'LTC/USDT',
'status': 'closed',
'side': 'buy',
'type': 'limit',
'price': 10.0,
'amount': 2.0,
'filled': 2.0,
'remaining': 0.0,
}
def mock_order_usdt_6_sell():
return {
'id': 'prod_sell_6',
'symbol': 'LTC/USDT',
'status': 'open',
'side': 'sell',
'type': 'limit',
'price': 12.0,
'amount': 2.0,
'filled': 0.0,
'remaining': 2.0,
}
def mock_trade_usdt_6(fee):
"""
Simulate prod entry with open sell order
"""
trade = Trade(
pair='LTC/USDT',
stake_amount=20.0,
amount=2.0,
amount_requested=2.0,
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=5),
fee_open=fee.return_value,
fee_close=fee.return_value,
is_open=True,
open_rate=10.0,
exchange='binance',
strategy='SampleStrategy',
open_order_id="prod_sell_6",
timeframe=5,
)
o = Order.parse_from_ccxt_object(mock_order_usdt_6(), 'LTC/USDT', 'buy')
trade.orders.append(o)
o = Order.parse_from_ccxt_object(mock_order_usdt_6_sell(), 'LTC/USDT', 'sell')
trade.orders.append(o)
return trade

View File

@@ -2806,7 +2806,7 @@ def test_get_valid_pair_combination(default_conf, mocker, markets):
(['LTC'], ['NONEXISTENT'], False, False,
[]),
])
def test_get_markets(default_conf, mocker, markets,
def test_get_markets(default_conf, mocker, markets_static,
base_currencies, quote_currencies, pairs_only, active_only,
expected_keys):
mocker.patch.multiple('freqtrade.exchange.Exchange',
@@ -2814,7 +2814,7 @@ def test_get_markets(default_conf, mocker, markets,
_load_async_markets=MagicMock(),
validate_pairs=MagicMock(),
validate_timeframes=MagicMock(),
markets=PropertyMock(return_value=markets))
markets=PropertyMock(return_value=markets_static))
ex = Exchange(default_conf)
pairs = ex.get_markets(base_currencies, quote_currencies, pairs_only, active_only)
assert sorted(pairs.keys()) == sorted(expected_keys)

View File

@@ -327,5 +327,8 @@ def test_fill_leverage_brackets_kraken(default_conf, mocker):
'LTC/USDT': [1],
'LTC/USD': [1],
'XLTCUSDT': [1],
'LTC/ETH': [1]
'LTC/ETH': [1],
'NEO/USDT': [1],
'TKN/USDT': [1],
'XRP/USDT': [1]
}

View File

@@ -39,16 +39,25 @@ def hyperopt(hyperopt_conf, mocker):
def hyperopt_results():
return pd.DataFrame(
{
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'profit_ratio': [-0.1, 0.2, 0.3],
'profit_abs': [-0.2, 0.4, 0.6],
'trade_duration': [10, 30, 10],
'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.ROI],
'pair': ['ETH/USDT', 'ETH/USDT', 'ETH/USDT', 'ETH/USDT'],
'profit_ratio': [-0.1, 0.2, -0.1, 0.3],
'profit_abs': [-0.2, 0.4, -0.2, 0.6],
'trade_duration': [10, 30, 10, 10],
'amount': [0.1, 0.1, 0.1, 0.1],
'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.STOP_LOSS, SellType.ROI],
'open_date':
[
datetime(2019, 1, 1, 9, 15, 0),
datetime(2019, 1, 2, 8, 55, 0),
datetime(2019, 1, 3, 9, 15, 0),
datetime(2019, 1, 4, 9, 15, 0),
],
'close_date':
[
datetime(2019, 1, 1, 9, 26, 3, 478039),
datetime(2019, 2, 1, 9, 26, 3, 478039),
datetime(2019, 3, 1, 9, 26, 3, 478039)
]
datetime(2019, 1, 1, 9, 25, 0),
datetime(2019, 1, 2, 9, 25, 0),
datetime(2019, 1, 3, 9, 25, 0),
datetime(2019, 1, 4, 9, 25, 0),
],
}
)

View File

@@ -35,6 +35,7 @@ def test_hyperoptlossresolver_wrongname(default_conf) -> None:
def test_loss_calculation_prefer_correct_trade_count(hyperopt_conf, hyperopt_results) -> None:
hyperopt_conf.update({'hyperopt_loss': "ShortTradeDurHyperOptLoss"})
hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, 600,
datetime(2019, 1, 1), datetime(2019, 5, 1))
@@ -50,6 +51,7 @@ def test_loss_calculation_prefer_shorter_trades(hyperopt_conf, hyperopt_results)
resultsb = hyperopt_results.copy()
resultsb.loc[1, 'trade_duration'] = 20
hyperopt_conf.update({'hyperopt_loss': "ShortTradeDurHyperOptLoss"})
hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf)
longer = hl.hyperopt_loss_function(hyperopt_results, 100,
datetime(2019, 1, 1), datetime(2019, 5, 1))
@@ -64,6 +66,7 @@ def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) ->
results_under = hyperopt_results.copy()
results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2
hyperopt_conf.update({'hyperopt_loss': "ShortTradeDurHyperOptLoss"})
hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, 600,
datetime(2019, 1, 1), datetime(2019, 5, 1))
@@ -75,91 +78,28 @@ def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) ->
assert under > correct
def test_sharpe_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy()
results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2
results_under = hyperopt_results.copy()
results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2
default_conf.update({'hyperopt_loss': 'SharpeHyperOptLoss'})
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < correct
assert under > correct
def test_sharpe_loss_daily_prefers_higher_profits(default_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy()
results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2
results_under = hyperopt_results.copy()
results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2
default_conf.update({'hyperopt_loss': 'SharpeHyperOptLossDaily'})
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < correct
assert under > correct
def test_sortino_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy()
results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2
results_under = hyperopt_results.copy()
results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2
default_conf.update({'hyperopt_loss': 'SortinoHyperOptLoss'})
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < correct
assert under > correct
def test_sortino_loss_daily_prefers_higher_profits(default_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy()
results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2
results_under = hyperopt_results.copy()
results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2
default_conf.update({'hyperopt_loss': 'SortinoHyperOptLossDaily'})
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < correct
assert under > correct
def test_onlyprofit_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None:
@pytest.mark.parametrize('lossfunction', [
"OnlyProfitHyperOptLoss",
"SortinoHyperOptLoss",
"SortinoHyperOptLossDaily",
"SharpeHyperOptLoss",
"SharpeHyperOptLossDaily",
])
def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunction) -> None:
results_over = hyperopt_results.copy()
results_over['profit_abs'] = hyperopt_results['profit_abs'] * 2
results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2
results_under = hyperopt_results.copy()
results_under['profit_abs'] = hyperopt_results['profit_abs'] / 2
results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2
default_conf.update({'hyperopt_loss': 'OnlyProfitHyperOptLoss'})
default_conf.update({'hyperopt_loss': lossfunction})
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
over = hl.hyperopt_loss_function(results_over, len(results_over),
datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
under = hl.hyperopt_loss_function(results_under, len(results_under),
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < correct
assert under > correct

View File

@@ -131,9 +131,9 @@ def test_load_pairlist_noexist(mocker, markets, default_conf):
default_conf, {}, 1)
def test_load_pairlist_verify_multi(mocker, markets, default_conf):
def test_load_pairlist_verify_multi(mocker, markets_static, default_conf):
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets))
mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets_static))
plm = PairListManager(freqtrade.exchange, default_conf)
# Call different versions one after the other, should always consider what was passed in
# and have no side-effects (therefore the same check multiple times)

View File

@@ -125,7 +125,7 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog):
# Test 5m after lock-period - this should try and relock the pair, but end-time
# should be the previous end-time
end_time = PairLocks.get_pair_longest_lock('*').lock_end_time + timedelta(minutes=5)
assert freqtrade.protections.global_stop(end_time)
freqtrade.protections.global_stop(end_time)
assert not PairLocks.is_global_lock(end_time)
@@ -182,7 +182,7 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
min_ago_open=180, min_ago_close=30, profit_rate=0.9,
))
assert freqtrade.protections.stop_per_pair(pair)
freqtrade.protections.stop_per_pair(pair)
assert freqtrade.protections.global_stop() != only_per_pair
assert PairLocks.is_pair_locked(pair)
assert PairLocks.is_global_lock() != only_per_pair

View File

@@ -1015,7 +1015,7 @@ def test_rpc_blacklist(mocker, default_conf) -> None:
assert len(ret['blacklist']) == 4
assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
assert ret['blacklist'] == ['DOGE/BTC', 'HOT/BTC', 'ETH/BTC', 'XRP/.*']
assert ret['blacklist_expanded'] == ['ETH/BTC', 'XRP/BTC']
assert ret['blacklist_expanded'] == ['ETH/BTC', 'XRP/BTC', 'XRP/USDT']
assert 'errors' in ret
assert isinstance(ret['errors'], dict)

View File

@@ -937,7 +937,7 @@ def test_api_blacklist(botclient, mocker):
data='{"blacklist": ["XRP/.*"]}')
assert_response(rc)
assert rc.json() == {"blacklist": ["DOGE/BTC", "HOT/BTC", "ETH/BTC", "XRP/.*"],
"blacklist_expanded": ["ETH/BTC", "XRP/BTC"],
"blacklist_expanded": ["ETH/BTC", "XRP/BTC", "XRP/USDT"],
"length": 4,
"method": ["StaticPairList"],
"errors": {},

View File

@@ -1313,6 +1313,34 @@ def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None:
'Reason: cancelled due to timeout.')
def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> None:
default_conf['telegram']['notification_settings']['protection_trigger'] = 'on'
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
time_machine.move_to("2021-09-01 05:00:00 +00:00")
lock = PairLocks.lock_pair('ETH/BTC', arrow.utcnow().shift(minutes=6).datetime, 'randreason')
msg = {
'type': RPCMessageType.PROTECTION_TRIGGER,
}
msg.update(lock.to_json())
telegram.send_msg(msg)
assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. "
"`ETH/BTC` will be locked until `2021-09-01 05:10:00`.")
msg_mock.reset_mock()
# Test global protection
msg = {
'type': RPCMessageType.PROTECTION_TRIGGER_GLOBAL,
}
lock = PairLocks.lock_pair('*', arrow.utcnow().shift(minutes=100).datetime, 'randreason')
msg.update(lock.to_json())
telegram.send_msg(msg)
assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. "
"*All pairs* will be locked until `2021-09-01 06:45:00`.")
def test_send_msg_buy_fill_notification(default_conf, mocker) -> None:
default_conf['telegram']['notification_settings']['buy_fill'] = 'on'

View File

@@ -9,13 +9,13 @@ from freqtrade.strategy import (merge_informative_pair, stoploss_from_absolute,
timeframe_to_minutes)
def generate_test_data(timeframe: str, size: int):
def generate_test_data(timeframe: str, size: int, start: str = '2020-07-05'):
np.random.seed(42)
tf_mins = timeframe_to_minutes(timeframe)
base = np.random.normal(20, 2, size=size)
date = pd.period_range('2020-07-05', periods=size, freq=f'{tf_mins}min').to_timestamp()
date = pd.date_range(start, periods=size, freq=f'{tf_mins}min', tz='UTC')
df = pd.DataFrame({
'date': date,
'open': base,

File diff suppressed because it is too large Load Diff