Merge branch 'develop' into pr/cyberjunky/6615
This commit is contained in:
@@ -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:
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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"),
|
||||
|
6
tests/testdata/testconfigs/recursive.json
vendored
Normal file
6
tests/testdata/testconfigs/recursive.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
// This file fails as it's loading itself over and over
|
||||
"add_config_files": [
|
||||
"./recursive.json"
|
||||
]
|
||||
}
|
12
tests/testdata/testconfigs/test_base_config.json
vendored
Normal file
12
tests/testdata/testconfigs/test_base_config.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"stake_currency": "",
|
||||
"dry_run": true,
|
||||
"exchange": {
|
||||
"name": "",
|
||||
"key": "",
|
||||
"secret": "",
|
||||
"pair_whitelist": [],
|
||||
"ccxt_async_config": {
|
||||
}
|
||||
}
|
||||
}
|
18
tests/testdata/testconfigs/test_pricing2_conf.json
vendored
Normal file
18
tests/testdata/testconfigs/test_pricing2_conf.json
vendored
Normal 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
|
||||
}
|
||||
}
|
21
tests/testdata/testconfigs/test_pricing_conf.json
vendored
Normal file
21
tests/testdata/testconfigs/test_pricing_conf.json
vendored
Normal 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"
|
||||
]
|
||||
}
|
6
tests/testdata/testconfigs/testconfig.json
vendored
Normal file
6
tests/testdata/testconfigs/testconfig.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"add_config_files": [
|
||||
"test_base_config.json",
|
||||
"test_pricing_conf.json"
|
||||
]
|
||||
}
|
Reference in New Issue
Block a user