Merge branch 'develop' into pr/cyberjunky/6615

This commit is contained in:
Matthias
2022-04-10 09:22:03 +02:00
32 changed files with 371 additions and 107 deletions

View File

@@ -52,7 +52,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
assert results[0] == {
'trade_id': 1,
'pair': 'ETH/BTC',
'base_currency': 'BTC',
'base_currency': 'ETH',
'quote_currency': 'BTC',
'open_date': ANY,
'open_timestamp': ANY,
'is_open': ANY,
@@ -135,7 +136,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
assert results[0] == {
'trade_id': 1,
'pair': 'ETH/BTC',
'base_currency': 'BTC',
'base_currency': 'ETH',
'quote_currency': 'BTC',
'open_date': ANY,
'open_timestamp': ANY,
'is_open': ANY,
@@ -1230,8 +1232,8 @@ def test_rpc_force_entry(mocker, default_conf, ticker, fee, limit_buy_order_open
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
pair = 'TKN/BTC'
trade = rpc._rpc_force_entry(pair, None)
assert trade is None
with pytest.raises(RPCException, match=r"Failed to enter position for TKN/BTC."):
trade = rpc._rpc_force_entry(pair, None)
def test_rpc_force_entry_stopped(mocker, default_conf) -> None:

View File

@@ -931,6 +931,8 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short,
'open_order': None,
'open_rate': 0.123,
'pair': 'ETH/BTC',
'base_currency': 'ETH',
'quote_currency': 'BTC',
'stake_amount': 0.001,
'stop_loss_abs': ANY,
'stop_loss_pct': ANY,
@@ -1097,7 +1099,7 @@ def test_api_force_entry(botclient, mocker, fee, endpoint):
# Test creating trade
fbuy_mock = MagicMock(return_value=Trade(
pair='ETH/ETH',
pair='ETH/BTC',
amount=1,
amount_requested=1,
exchange='binance',
@@ -1130,7 +1132,9 @@ def test_api_force_entry(botclient, mocker, fee, endpoint):
'open_date': ANY,
'open_timestamp': ANY,
'open_rate': 0.245441,
'pair': 'ETH/ETH',
'pair': 'ETH/BTC',
'base_currency': 'ETH',
'quote_currency': 'BTC',
'stake_amount': 1,
'stop_loss_abs': None,
'stop_loss_pct': None,

View File

@@ -184,7 +184,8 @@ def test_telegram_status(default_conf, update, mocker) -> None:
_rpc_trade_status=MagicMock(return_value=[{
'trade_id': 1,
'pair': 'ETH/BTC',
'base_currency': 'BTC',
'base_currency': 'ETH',
'quote_currency': 'BTC',
'open_date': arrow.utcnow(),
'close_date': None,
'open_rate': 1.099e-05,
@@ -398,8 +399,8 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None:
fields = re.sub('[ ]+', ' ', line[2].strip()).split(' ')
assert int(fields[0]) == 1
assert 'L' in fields[1]
assert 'ETH/BTC' in fields[2]
# assert 'L' in fields[1]
assert 'ETH/BTC' in fields[1]
assert msg_mock.call_count == 1
@@ -1253,7 +1254,7 @@ def test_force_exit_no_pair(default_conf, update, ticker, fee, mocker) -> None:
assert reduce(lambda acc, x: acc + len(x), keyboard, 0) == 5
assert keyboard[-1][0].text == "Cancel"
assert keyboard[1][0].callback_data == 'force_exit__2 L'
assert keyboard[1][0].callback_data == 'force_exit__2 '
update = MagicMock()
update.callback_query = MagicMock()
update.callback_query.data = keyboard[1][0].callback_data

View File

@@ -523,7 +523,7 @@ def test_custom_exit(default_conf, fee, caplog) -> None:
assert res.exit_type == ExitType.CUSTOM_EXIT
assert res.exit_flag is True
assert res.exit_reason == 'h' * 64
assert log_has_re('Custom sell reason returned from custom_exit is too long.*', caplog)
assert log_has_re('Custom exit reason returned from custom_exit is too long.*', caplog)
@pytest.mark.parametrize('side', TRADE_SIDES)

View File

@@ -18,7 +18,8 @@ from freqtrade.configuration.deprecated_settings import (check_conflicting_setti
process_removed_setting,
process_temporary_deprecated_settings)
from freqtrade.configuration.environment_vars import flat_vars_to_nested_dict
from freqtrade.configuration.load_config import load_config_file, load_file, log_config_error_range
from freqtrade.configuration.load_config import (load_config_file, load_file, load_from_files,
log_config_error_range)
from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL, ENV_VAR_PREFIX
from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException
@@ -206,6 +207,32 @@ def test_from_config(default_conf, mocker, caplog) -> None:
assert isinstance(validated_conf['user_data_dir'], Path)
def test_from_recursive_files(testdatadir) -> None:
files = testdatadir / "testconfigs/testconfig.json"
conf = Configuration.from_files([files])
assert conf
# Exchange comes from "the first config"
assert conf['exchange']
# Pricing comes from the 2nd config
assert conf['entry_pricing']
assert conf['entry_pricing']['price_side'] == "same"
assert conf['exit_pricing']
# The other key comes from pricing2, which is imported by pricing.json
assert conf['exit_pricing']['price_side'] == "other"
assert len(conf['config_files']) == 4
assert 'testconfig.json' in conf['config_files'][0]
assert 'test_pricing_conf.json' in conf['config_files'][1]
assert 'test_base_config.json' in conf['config_files'][2]
assert 'test_pricing2_conf.json' in conf['config_files'][3]
files = testdatadir / "testconfigs/recursive.json"
with pytest.raises(OperationalException, match="Config loop detected."):
load_from_files([files])
def test_print_config(default_conf, mocker, caplog) -> None:
conf1 = deepcopy(default_conf)
# Delete non-json elements from default_conf

View File

@@ -3663,6 +3663,7 @@ def test_exit_profit_only(
})
freqtrade = FreqtradeBot(default_conf_usdt)
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
freqtrade.strategy.custom_exit = MagicMock(return_value=None)
if exit_type == ExitType.EXIT_SIGNAL.value:
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
else:
@@ -3671,10 +3672,15 @@ def test_exit_profit_only(
freqtrade.enter_positions()
trade = Trade.query.first()
trade.is_short = is_short
assert trade.is_short == is_short
oobj = Order.parse_from_ccxt_object(limit_order[eside], limit_order[eside]['symbol'], eside)
trade.update_trade(oobj)
freqtrade.wallets.update()
if profit_only:
assert freqtrade.handle_trade(trade) is False
# Custom-exit is called
freqtrade.strategy.custom_exit.call_count == 1
patch_get_signal(freqtrade, enter_long=False, exit_short=is_short, exit_long=not is_short)
assert freqtrade.handle_trade(trade) is handle_first

View File

@@ -1209,6 +1209,27 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
PRIMARY KEY (id),
CHECK (is_open IN (0, 1))
);"""
create_table_order = """CREATE TABLE orders (
id INTEGER NOT NULL,
ft_trade_id INTEGER,
ft_order_side VARCHAR(25) NOT NULL,
ft_pair VARCHAR(25) NOT NULL,
ft_is_open BOOLEAN NOT NULL,
order_id VARCHAR(255) NOT NULL,
status VARCHAR(255),
symbol VARCHAR(25),
order_type VARCHAR(50),
side VARCHAR(25),
price FLOAT,
amount FLOAT,
filled FLOAT,
remaining FLOAT,
cost FLOAT,
order_date DATETIME,
order_filled_date DATETIME,
order_update_date DATETIME,
PRIMARY KEY (id)
);"""
insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee,
open_rate, stake_amount, amount, open_date,
stop_loss, initial_stop_loss, max_rate, ticker_interval,
@@ -1222,15 +1243,66 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
stake=default_conf.get("stake_amount"),
amount=amount
)
insert_orders = f"""
insert into orders (
ft_trade_id,
ft_order_side,
ft_pair,
ft_is_open,
order_id,
status,
symbol,
order_type,
side,
price,
amount,
filled,
remaining,
cost)
values (
1,
'buy',
'ETC/BTC',
0,
'buy_order',
'closed',
'ETC/BTC',
'limit',
'buy',
0.00258580,
{amount},
{amount},
0,
{amount * 0.00258580}
),
(
1,
'stoploss',
'ETC/BTC',
0,
'stop_order_id222',
'closed',
'ETC/BTC',
'limit',
'sell',
0.00258580,
{amount},
{amount},
0,
{amount * 0.00258580}
)
"""
engine = create_engine('sqlite://')
mocker.patch('freqtrade.persistence.models.create_engine', lambda *args, **kwargs: engine)
# Create table using the old format
with engine.begin() as connection:
connection.execute(text(create_table_old))
connection.execute(text(create_table_order))
connection.execute(text("create index ix_trades_is_open on trades(is_open)"))
connection.execute(text("create index ix_trades_pair on trades(pair)"))
connection.execute(text(insert_table_old))
connection.execute(text(insert_orders))
# fake previous backup
connection.execute(text("create table trades_bak as select * from trades"))
@@ -1267,8 +1339,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
assert trade.open_trade_value == trade._calc_open_trade_value()
assert trade.close_profit_abs is None
assert log_has("Moving open orders to Orders table.", caplog)
orders = Order.query.all()
orders = trade.orders
assert len(orders) == 2
assert orders[0].order_id == 'buy_order'
assert orders[0].ft_order_side == 'buy'
@@ -1277,7 +1348,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
assert orders[1].ft_order_side == 'stoploss'
def test_migrate_mid_state(mocker, default_conf, fee, caplog):
def test_migrate_too_old(mocker, default_conf, fee, caplog):
"""
Test Database migration (starting with new pairformat)
"""
@@ -1301,6 +1372,7 @@ def test_migrate_mid_state(mocker, default_conf, fee, caplog):
PRIMARY KEY (id),
CHECK (is_open IN (0, 1))
);"""
insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee_open, fee_close,
open_rate, stake_amount, amount, open_date)
VALUES ('binance', 'ETC/BTC', 1, {fee}, {fee},
@@ -1319,26 +1391,8 @@ def test_migrate_mid_state(mocker, default_conf, fee, caplog):
connection.execute(text(insert_table_old))
# Run init to test migration
init_db(default_conf['db_url'], default_conf['dry_run'])
assert len(Trade.query.filter(Trade.id == 1).all()) == 1
trade = Trade.query.filter(Trade.id == 1).first()
assert trade.fee_open == fee.return_value
assert trade.fee_close == fee.return_value
assert trade.open_rate_requested is None
assert trade.close_rate_requested is None
assert trade.is_open == 1
assert trade.amount == amount
assert trade.stake_amount == default_conf.get("stake_amount")
assert trade.pair == "ETC/BTC"
assert trade.exchange == "binance"
assert trade.max_rate == 0.0
assert trade.stop_loss == 0.0
assert trade.initial_stop_loss == 0.0
assert trade.open_trade_value == trade._calc_open_trade_value()
assert log_has("trying trades_bak0", caplog)
assert log_has("Running database migration for trades - backup: trades_bak0, orders_bak0",
caplog)
with pytest.raises(OperationalException, match=r'Your database seems to be very old'):
init_db(default_conf['db_url'], default_conf['dry_run'])
def test_migrate_get_last_sequence_ids():
@@ -1561,6 +1615,8 @@ def test_to_json(fee):
assert result == {'trade_id': None,
'pair': 'ADA/USDT',
'base_currency': 'ADA',
'quote_currency': 'USDT',
'is_open': None,
'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"),
'open_timestamp': int(trade.open_date.timestamp() * 1000),
@@ -1637,6 +1693,8 @@ def test_to_json(fee):
assert result == {'trade_id': None,
'pair': 'XRP/BTC',
'base_currency': 'XRP',
'quote_currency': 'BTC',
'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"),
'open_timestamp': int(trade.open_date.timestamp() * 1000),
'close_date': trade.close_date.strftime("%Y-%m-%d %H:%M:%S"),

View File

@@ -0,0 +1,6 @@
{
// This file fails as it's loading itself over and over
"add_config_files": [
"./recursive.json"
]
}

View File

@@ -0,0 +1,12 @@
{
"stake_currency": "",
"dry_run": true,
"exchange": {
"name": "",
"key": "",
"secret": "",
"pair_whitelist": [],
"ccxt_async_config": {
}
}
}

View File

@@ -0,0 +1,18 @@
{
"entry_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1,
"price_last_balance": 0.0,
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"exit_pricing":{
"price_side": "other",
"use_order_book": true,
"order_book_top": 1,
"price_last_balance": 0.0
}
}

View File

@@ -0,0 +1,21 @@
{
"entry_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1,
"price_last_balance": 0.0,
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"exit_pricing":{
"price_side": "same",
"use_order_book": true,
"order_book_top": 1,
"price_last_balance": 0.0
},
"add_config_files": [
"./test_pricing2_conf.json"
]
}

View File

@@ -0,0 +1,6 @@
{
"add_config_files": [
"test_base_config.json",
"test_pricing_conf.json"
]
}