mirror of
https://github.com/freqtrade/freqtrade.git
synced 2026-02-25 13:51:45 +00:00
Merge branch 'develop' into feat/pairlistconfig
This commit is contained in:
@@ -3,7 +3,7 @@ import json
|
||||
import logging
|
||||
import re
|
||||
from copy import deepcopy
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from unittest.mock import MagicMock, Mock, PropertyMock
|
||||
@@ -12,7 +12,6 @@ import arrow
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from telegram import Chat, Message, Update
|
||||
|
||||
from freqtrade import constants
|
||||
from freqtrade.commands import Arguments
|
||||
@@ -550,13 +549,6 @@ def get_default_conf_usdt(testdatadir):
|
||||
return configuration
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def update():
|
||||
_update = Update(0)
|
||||
_update.message = Message(0, datetime.utcnow(), Chat(0, 0))
|
||||
return _update
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fee():
|
||||
return MagicMock(return_value=0.0025)
|
||||
|
||||
@@ -9,7 +9,7 @@ import pytest
|
||||
from arrow import Arrow
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN
|
||||
from freqtrade.constants import BACKTEST_BREAKDOWNS, DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN
|
||||
from freqtrade.data import history
|
||||
from freqtrade.data.btanalysis import (get_latest_backtest_filename, load_backtest_data,
|
||||
load_backtest_stats)
|
||||
@@ -470,6 +470,9 @@ def test__get_resample_from_period():
|
||||
with pytest.raises(ValueError, match=r"Period noooo is not supported."):
|
||||
_get_resample_from_period('noooo')
|
||||
|
||||
for period in BACKTEST_BREAKDOWNS:
|
||||
assert isinstance(_get_resample_from_period(period), str)
|
||||
|
||||
|
||||
def test_show_sorted_pairlist(testdatadir, default_conf, capsys):
|
||||
filename = testdatadir / "backtest_results/backtest-result.json"
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
# pragma pylint: disable=missing-docstring, C0103
|
||||
import logging
|
||||
from importlib import import_module
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import create_engine, select, text
|
||||
from sqlalchemy.schema import CreateTable
|
||||
|
||||
from freqtrade.constants import DEFAULT_DB_PROD_URL
|
||||
from freqtrade.enums import TradingMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.persistence import Trade, init_db
|
||||
from freqtrade.persistence.base import ModelBase
|
||||
from freqtrade.persistence.migrations import get_last_sequence_ids, set_sequence_ids
|
||||
from freqtrade.persistence.models import PairLock
|
||||
from tests.conftest import log_has
|
||||
@@ -411,3 +414,14 @@ def test_migrate_pairlocks(mocker, default_conf, fee, caplog):
|
||||
assert len(pairlocks) == 1
|
||||
pairlocks[0].pair == 'ETH/BTC'
|
||||
pairlocks[0].side == '*'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('dialect', [
|
||||
'sqlite', 'postgresql', 'mysql', 'oracle', 'mssql',
|
||||
])
|
||||
def test_create_table_compiles(dialect):
|
||||
|
||||
dialect_mod = import_module(f"sqlalchemy.dialects.{dialect}")
|
||||
for table in ModelBase.metadata.tables.values():
|
||||
create_sql = str(CreateTable(table).compile(dialect=dialect_mod.dialect()))
|
||||
assert 'CREATE TABLE' in create_sql
|
||||
|
||||
@@ -2481,7 +2481,7 @@ def test_select_filled_orders(fee):
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_order_to_ccxt(limit_buy_order_open):
|
||||
def test_order_to_ccxt(limit_buy_order_open, limit_sell_order_usdt_open):
|
||||
|
||||
order = Order.parse_from_ccxt_object(limit_buy_order_open, 'mocked', 'buy')
|
||||
order.ft_trade_id = 1
|
||||
@@ -2495,11 +2495,23 @@ def test_order_to_ccxt(limit_buy_order_open):
|
||||
del raw_order['fee']
|
||||
del raw_order['datetime']
|
||||
del raw_order['info']
|
||||
assert raw_order['stopPrice'] is None
|
||||
del raw_order['stopPrice']
|
||||
assert raw_order.get('stopPrice') is None
|
||||
raw_order.pop('stopPrice', None)
|
||||
del limit_buy_order_open['datetime']
|
||||
assert raw_order == limit_buy_order_open
|
||||
|
||||
order1 = Order.parse_from_ccxt_object(limit_sell_order_usdt_open, 'mocked', 'sell')
|
||||
order1.ft_order_side = 'stoploss'
|
||||
order1.stop_price = order1.price * 0.9
|
||||
order1.ft_trade_id = 1
|
||||
order1.session.add(order1)
|
||||
Order.session.commit()
|
||||
|
||||
order_resp1 = Order.order_by_id(limit_sell_order_usdt_open['id'])
|
||||
raw_order1 = order_resp1.to_ccxt_object()
|
||||
|
||||
assert raw_order1.get('stopPrice') is not None
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize('data', [
|
||||
|
||||
@@ -546,51 +546,67 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
|
||||
'free': 10.0,
|
||||
'balance': 12.0,
|
||||
'used': 2.0,
|
||||
'bot_owned': 9.9, # available stake - reducing by reserved amount
|
||||
'est_stake': 10.0, # In futures mode, "free" is used here.
|
||||
'est_stake_bot': 9.9,
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
'is_bot_managed': True,
|
||||
},
|
||||
{
|
||||
'free': 1.0,
|
||||
'balance': 5.0,
|
||||
'currency': 'ETH',
|
||||
'bot_owned': 0,
|
||||
'est_stake': 0.30794,
|
||||
'est_stake_bot': 0,
|
||||
'used': 4.0,
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
|
||||
'is_bot_managed': False,
|
||||
},
|
||||
{
|
||||
'free': 5.0,
|
||||
'balance': 10.0,
|
||||
'currency': 'USDT',
|
||||
'bot_owned': 0,
|
||||
'est_stake': 0.0011562404610161968,
|
||||
'est_stake_bot': 0,
|
||||
'used': 5.0,
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
'is_bot_managed': False,
|
||||
},
|
||||
{
|
||||
'free': 0.0,
|
||||
'balance': 0.0,
|
||||
'currency': 'ETH/USDT:USDT',
|
||||
'est_stake': 20,
|
||||
'est_stake_bot': 20,
|
||||
'used': 0,
|
||||
'stake': 'BTC',
|
||||
'is_position': True,
|
||||
'leverage': 5.0,
|
||||
'position': 1000.0,
|
||||
'side': 'short',
|
||||
'is_bot_managed': True,
|
||||
}
|
||||
]
|
||||
assert pytest.approx(result['total_bot']) == 29.9
|
||||
assert pytest.approx(result['total']) == 30.309096
|
||||
assert result['starting_capital'] == 10
|
||||
# Very high starting capital ratio, because the futures position really has the wrong unit.
|
||||
# TODO: improve this test (see comment above)
|
||||
assert result['starting_capital_ratio'] == pytest.approx(1.98999999)
|
||||
|
||||
|
||||
def test_rpc_start(mocker, default_conf) -> None:
|
||||
|
||||
@@ -283,7 +283,7 @@ def test_api__init__(default_conf, mocker):
|
||||
"username": "TestUser",
|
||||
"password": "testPass",
|
||||
}})
|
||||
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init')
|
||||
mocker.patch('freqtrade.rpc.api_server.webserver.ApiServer.start_api', MagicMock())
|
||||
apiserver = ApiServer(default_conf)
|
||||
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
|
||||
@@ -341,7 +341,7 @@ def test_api_run(default_conf, mocker, caplog):
|
||||
"username": "TestUser",
|
||||
"password": "testPass",
|
||||
}})
|
||||
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init')
|
||||
|
||||
server_inst_mock = MagicMock()
|
||||
server_inst_mock.run_in_thread = MagicMock()
|
||||
@@ -419,7 +419,7 @@ def test_api_cleanup(default_conf, mocker, caplog):
|
||||
"username": "TestUser",
|
||||
"password": "testPass",
|
||||
}})
|
||||
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init')
|
||||
|
||||
server_mock = MagicMock()
|
||||
server_mock.cleanup = MagicMock()
|
||||
@@ -480,13 +480,18 @@ def test_api_balance(botclient, mocker, rpc_balance, tickers):
|
||||
'free': 12.0,
|
||||
'balance': 12.0,
|
||||
'used': 0.0,
|
||||
'bot_owned': pytest.approx(11.879999),
|
||||
'est_stake': 12.0,
|
||||
'est_stake_bot': pytest.approx(11.879999),
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
'is_bot_managed': True,
|
||||
}
|
||||
assert response['total'] == 12.159513094
|
||||
assert response['total_bot'] == pytest.approx(11.879999)
|
||||
assert 'starting_capital' in response
|
||||
assert 'starting_capital_fiat' in response
|
||||
assert 'starting_capital_pct' in response
|
||||
@@ -1895,7 +1900,7 @@ def test_api_ws_send_msg(default_conf, mocker, caplog):
|
||||
"password": _TEST_PASS,
|
||||
"ws_token": _TEST_WS_TOKEN
|
||||
}})
|
||||
mocker.patch('freqtrade.rpc.telegram.Updater')
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init')
|
||||
mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api')
|
||||
apiserver = ApiServer(default_conf)
|
||||
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,10 @@ def get_webhook_dict() -> dict:
|
||||
"enabled": True,
|
||||
"url": "https://maker.ifttt.com/trigger/freqtrade_test/with/key/c764udvJ5jfSlswVRukZZ2/",
|
||||
"webhookentry": {
|
||||
# Intentionally broken, as "entry" should have priority.
|
||||
"value1": "Buying {pair55555}",
|
||||
},
|
||||
"entry": {
|
||||
"value1": "Buying {pair}",
|
||||
"value2": "limit {limit:8f}",
|
||||
"value3": "{stake_amount:8f} {stake_currency}",
|
||||
@@ -89,15 +93,15 @@ def test_send_msg_webhook(default_conf, mocker):
|
||||
webhook.send_msg(msg=msg)
|
||||
assert msg_mock.call_count == 1
|
||||
assert (msg_mock.call_args[0][0]["value1"] ==
|
||||
default_conf["webhook"]["webhookentry"]["value1"].format(**msg))
|
||||
default_conf["webhook"]["entry"]["value1"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value2"] ==
|
||||
default_conf["webhook"]["webhookentry"]["value2"].format(**msg))
|
||||
default_conf["webhook"]["entry"]["value2"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value3"] ==
|
||||
default_conf["webhook"]["webhookentry"]["value3"].format(**msg))
|
||||
default_conf["webhook"]["entry"]["value3"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value4"] ==
|
||||
default_conf["webhook"]["webhookentry"]["value4"].format(**msg))
|
||||
default_conf["webhook"]["entry"]["value4"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value5"] ==
|
||||
default_conf["webhook"]["webhookentry"]["value5"].format(**msg))
|
||||
default_conf["webhook"]["entry"]["value5"].format(**msg))
|
||||
# Test short
|
||||
msg_mock.reset_mock()
|
||||
|
||||
@@ -116,15 +120,15 @@ def test_send_msg_webhook(default_conf, mocker):
|
||||
webhook.send_msg(msg=msg)
|
||||
assert msg_mock.call_count == 1
|
||||
assert (msg_mock.call_args[0][0]["value1"] ==
|
||||
default_conf["webhook"]["webhookentry"]["value1"].format(**msg))
|
||||
default_conf["webhook"]["entry"]["value1"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value2"] ==
|
||||
default_conf["webhook"]["webhookentry"]["value2"].format(**msg))
|
||||
default_conf["webhook"]["entry"]["value2"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value3"] ==
|
||||
default_conf["webhook"]["webhookentry"]["value3"].format(**msg))
|
||||
default_conf["webhook"]["entry"]["value3"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value4"] ==
|
||||
default_conf["webhook"]["webhookentry"]["value4"].format(**msg))
|
||||
default_conf["webhook"]["entry"]["value4"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value5"] ==
|
||||
default_conf["webhook"]["webhookentry"]["value5"].format(**msg))
|
||||
default_conf["webhook"]["entry"]["value5"].format(**msg))
|
||||
# Test buy cancel
|
||||
msg_mock.reset_mock()
|
||||
|
||||
@@ -328,6 +332,7 @@ def test_send_msg_webhook(default_conf, mocker):
|
||||
|
||||
def test_exception_send_msg(default_conf, mocker, caplog):
|
||||
default_conf["webhook"] = get_webhook_dict()
|
||||
del default_conf["webhook"]["entry"]
|
||||
del default_conf["webhook"]["webhookentry"]
|
||||
|
||||
webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
|
||||
|
||||
@@ -2107,6 +2107,7 @@ def test_enter_positions(mocker, default_conf_usdt, return_value, side_effect,
|
||||
assert mock_ct.call_count == len(default_conf_usdt['exchange']['pair_whitelist'])
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_exit_positions(mocker, default_conf_usdt, limit_order, is_short, caplog) -> None:
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
@@ -2115,12 +2116,33 @@ def test_exit_positions(mocker, default_conf_usdt, limit_order, is_short, caplog
|
||||
mocker.patch(f'{EXMS}.fetch_order', return_value=limit_order[entry_side(is_short)])
|
||||
mocker.patch(f'{EXMS}.get_trades_for_order', return_value=[])
|
||||
|
||||
# TODO: should not be magicmock
|
||||
trade = MagicMock()
|
||||
trade.is_short = is_short
|
||||
trade.open_order_id = '123'
|
||||
trade.open_fee = 0.001
|
||||
order_id = '123'
|
||||
trade = Trade(
|
||||
open_order_id=order_id,
|
||||
pair='ETH/USDT',
|
||||
fee_open=0.001,
|
||||
fee_close=0.001,
|
||||
open_rate=0.01,
|
||||
open_date=arrow.utcnow().datetime,
|
||||
stake_amount=0.01,
|
||||
amount=11,
|
||||
exchange="binance",
|
||||
is_short=is_short,
|
||||
leverage=1,
|
||||
)
|
||||
trade.orders.append(Order(
|
||||
ft_order_side=entry_side(is_short),
|
||||
price=0.01,
|
||||
ft_pair=trade.pair,
|
||||
ft_amount=trade.amount,
|
||||
ft_price=trade.open_rate,
|
||||
order_id=order_id,
|
||||
|
||||
))
|
||||
Trade.session.add(trade)
|
||||
Trade.commit()
|
||||
trades = [trade]
|
||||
freqtrade.wallets.update()
|
||||
n = freqtrade.exit_positions(trades)
|
||||
assert n == 0
|
||||
# Test amount not modified by fee-logic
|
||||
@@ -2133,17 +2155,40 @@ def test_exit_positions(mocker, default_conf_usdt, limit_order, is_short, caplog
|
||||
assert gra.call_count == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_exit_positions_exception(mocker, default_conf_usdt, limit_order, caplog, is_short) -> None:
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
order = limit_order[entry_side(is_short)]
|
||||
mocker.patch(f'{EXMS}.fetch_order', return_value=order)
|
||||
|
||||
# TODO: should not be magicmock
|
||||
trade = MagicMock()
|
||||
trade.is_short = is_short
|
||||
order_id = '123'
|
||||
trade = Trade(
|
||||
open_order_id=order_id,
|
||||
pair='ETH/USDT',
|
||||
fee_open=0.001,
|
||||
fee_close=0.001,
|
||||
open_rate=0.01,
|
||||
open_date=arrow.utcnow().datetime,
|
||||
stake_amount=0.01,
|
||||
amount=11,
|
||||
exchange="binance",
|
||||
is_short=is_short,
|
||||
leverage=1,
|
||||
)
|
||||
trade.orders.append(Order(
|
||||
ft_order_side=entry_side(is_short),
|
||||
price=0.01,
|
||||
ft_pair=trade.pair,
|
||||
ft_amount=trade.amount,
|
||||
ft_price=trade.open_rate,
|
||||
order_id=order_id,
|
||||
|
||||
))
|
||||
trade.open_order_id = None
|
||||
trade.pair = 'ETH/USDT'
|
||||
Trade.session.add(trade)
|
||||
Trade.commit()
|
||||
freqtrade.wallets.update()
|
||||
trades = [trade]
|
||||
|
||||
# Test raise of DependencyException exception
|
||||
|
||||
Reference in New Issue
Block a user