stable/freqtrade/tests/test_backtesting_internal.py

207 lines
7.9 KiB
Python
Raw Normal View History

# pragma pylint: disable=missing-docstring,protected-access
import os
from datetime import datetime
from unittest.mock import MagicMock
import arrow
import pytest
from jsonschema import validate
from freqtrade.exchange import backtesting
from freqtrade.exchange.backtesting import Backtesting
from freqtrade.main import create_trade, init
from freqtrade.misc import CONF_SCHEMA
from freqtrade.persistence import Trade
@pytest.fixture
def conf():
configuration = {
'max_open_trades': 3,
'stake_currency': 'BTC',
'stake_amount': 0.05,
'dry_run': True,
'minimal_roi': {
'60': 0.0,
'40': 0.01,
'20': 0.02,
'0': 0.03
},
'stoploss': -0.40,
'bid_strategy': {
'ask_last_balance': 0.0
},
'exchange': {
'name': 'bittrex',
'key': 'key',
'secret': 'secret',
'pair_whitelist': [
'BTC_RLC'
]
},
'telegram': {
'enabled': False,
'token': 'token',
'chat_id': 'chat_id'
}
}
validate(configuration, CONF_SCHEMA)
return configuration
FILES = [
os.path.join('freqtrade', 'tests', 'testdata', 'btc-edg.json'),
os.path.join('freqtrade', 'tests', 'testdata', 'btc-etc.json')
]
PAIRS = ['BTC_EDG', 'BTC_ETC']
TESTDATA = {
PAIRS[0]: {
'success': True,
'message': '',
'result': [
{'O': 0.00014469, 'H': 0.00014469, 'L': 0.00014469, 'C': 0.00014469, 'V': 10.66173857, 'T': '2017-09-05T18:55:00', 'BV': 0.00154264},
{'O': 0.00014469, 'H': 0.00014477, 'L': 0.00014469, 'C': 0.00014477, 'V': 410.54795113, 'T': '2017-09-05T19:00:00', 'BV': 0.05942728},
{'O': 0.00014477, 'H': 0.00014477, 'L': 0.00014477, 'C': 0.00014477, 'V': 69.10850034, 'T': '2017-09-05T19:05:00', 'BV': 0.01000482},
{'O': 0.00014470, 'H': 0.00014474, 'L': 0.00014400, 'C': 0.00014473, 'V': 7612.36224582, 'T': '2017-09-05T19:10:00', 'BV': 1.09730748},
]
},
PAIRS[1]: {
'success': True,
'message': '',
'result': [
{'O': 0.00391500, 'H': 0.00392700, 'L': 0.00391500, 'C': 0.00392000, 'V': 29.90264260, 'T': '2017-09-05T18:55:00', 'BV': 0.11712504},
{'O': 0.00392680, 'H': 0.00392749, 'L': 0.00391500, 'C': 0.00391500, 'V': 329.35043009, 'T': '2017-09-05T19:00:00', 'BV': 1.29065913},
{'O': 0.00391500, 'H': 0.00392733, 'L': 0.00391500, 'C': 0.00392300, 'V': 186.96019741, 'T': '2017-09-05T19:05:00', 'BV': 0.73332203},
{'O': 0.00391500, 'H': 0.00391500, 'L': 0.00390007, 'C': 0.00390007, 'V': 298.06457786, 'T': '2017-09-05T19:10:00', 'BV': 1.16560055},
{'O': 0.00391490, 'H': 0.00391490, 'L': 0.00389126, 'C': 0.00389126, 'V': 1007.91208513, 'T': '2017-09-05T19:15:00', 'BV': 3.92491826}
]
}
}
@pytest.fixture()
def len_rows():
ticker_history = TESTDATA[PAIRS[0]]
return len(ticker_history['result'])
def _json_load(file):
pair = os.path.splitext(os.path.basename(file.name))[0].replace('-', '_').upper()
return TESTDATA[pair]
def test_init(conf, mocker):
mocker.patch('json.load', side_effect=_json_load)
mocker.patch('glob.glob', return_value=FILES)
mocker.patch.dict('freqtrade.main._CONF', conf)
mocker.patch('freqtrade.main.get_args', backtesting=True)
mocker.patch('freqtrade.exchange', init=MagicMock())
mocker.patch('freqtrade.main.backtesting.TICKER_HISTORY_INTERVAL_H', 1/6)
init(conf)
assert backtesting._TESTDATA == TESTDATA
assert backtesting._LEN_ROWS == 4
# 10 minutes (1/6 hours) should correspond to 2 rows
assert backtesting._ROW_INDEX == 2
assert backtesting._ROW_INTERVAL == 2
# Available test data should not cover the interval
mocker.patch('freqtrade.main.backtesting.TICKER_HISTORY_INTERVAL_H', 24)
with pytest.raises(RuntimeError):
assert init(conf)
def test_time_step(len_rows, mocker):
mocker.patch('freqtrade.exchange.backtesting._LEN_ROWS', len_rows)
for index in range(4):
assert backtesting._ROW_INDEX == index
assert backtesting.time_step() is True
assert backtesting.time_step() is False
def test_returns_minimum_date(mocker):
mocker.patch('freqtrade.exchange.backtesting._TESTDATA', TESTDATA)
mocker.patch('freqtrade.exchange.backtesting._ROW_INDEX', 3)
mocker.patch('freqtrade.exchange.backtesting._ROW_INTERVAL', 2)
assert backtesting.get_minimum_date(PAIRS[0]) == '2017-09-05T19:00:00'
def test_returns_ticker(conf, mocker):
mocker.patch('freqtrade.main.get_args', backtesting=True)
mocker.patch('freqtrade.exchange.backtesting._get_testdata', return_value=(TESTDATA, 4))
mocker.patch('freqtrade.exchange.backtesting.TICKER_HISTORY_INTERVAL_H', 1/6)
first_pair_close = 0.00014477
backtesting = Backtesting(conf)
mocker.patch('freqtrade.exchange.backtesting._ROW_INDEX', 2)
assert backtesting.get_ticker(PAIRS[0]) == \
{'bid': first_pair_close, 'ask': first_pair_close, 'last': first_pair_close}
def test_returns_ticker_history(conf, mocker):
mocker.patch('freqtrade.exchange.backtesting._get_testdata', return_value=(TESTDATA, 4))
mocker.patch('freqtrade.main.backtesting.TICKER_HISTORY_INTERVAL_H', 1/6)
backtesting = Backtesting(conf)
mocker.patch('freqtrade.exchange.backtesting._ROW_INDEX', 3)
mocker.patch('freqtrade.exchange.backtesting._ROW_INTERVAL', 2)
assert backtesting.get_ticker_history(PAIRS[0]) == \
{'success': True,
'message': '',
'result': [
{'O': 0.00014469, 'H': 0.00014477, 'L': 0.00014469, 'C': 0.00014477, 'V': 410.54795113, 'T': '2017-09-05T19:00:00', 'BV': 0.05942728},
{'O': 0.00014477, 'H': 0.00014477, 'L': 0.00014477, 'C': 0.00014477, 'V': 69.10850034, 'T': '2017-09-05T19:05:00', 'BV': 0.01000482}
]}
def test_returns_current_time(mocker):
mocker.patch('freqtrade.exchange.backtesting._TESTDATA', TESTDATA)
mocker.patch('freqtrade.exchange.backtesting._ROW_INDEX', 1)
assert backtesting.current_time(PAIRS[0]) == \
arrow.get('2017-09-05T19:00:00').datetime.replace(tzinfo=None)
def test_results(conf, mocker):
current_time = datetime.utcnow()
logger_mock = MagicMock()
mocker.patch.dict('freqtrade.main._CONF', conf)
mocker.patch('freqtrade.main.get_args', backtesting=True)
mocker.patch('freqtrade.main.backtesting', init=MagicMock())
mocker.patch('freqtrade.main.telegram', init=MagicMock())
mocker.patch('freqtrade.main.backtesting.current_time', return_value=current_time)
mocker.patch('freqtrade.main.get_buy_signal', return_value=True)
mocker.patch('freqtrade.exchange.backtesting.logger', info=logger_mock)
mocker.patch.multiple('freqtrade.main.exchange',
get_ticker=MagicMock(return_value={
'bid': 0.07256061,
'ask': 0.072661,
'last': 0.07256061
}),
buy=MagicMock(return_value='mocked_order_id'))
init(conf)
# Create some test data
trade = create_trade(15.0)
assert trade
trade.close_rate = 0.07256061
trade.close_profit = 100.00
trade.close_date = current_time
trade.open_order_id = None
trade.is_open = False
Trade.session.add(trade)
Trade.session.flush()
backtesting.print_results()
assert logger_mock.call_count == 1
assert '(100.00%)' in logger_mock.call_args_list[-1][0][0]
# Trade should not be closed yet
Trade.session.delete(trade)
trade.close_rate = None
trade.close_profit = None
trade.close_date = None
trade.is_open = True
Trade.session.add(trade)
Trade.session.flush()
backtesting.print_results()
assert logger_mock.call_count == 2
assert 'No closed trade' in logger_mock.call_args_list[-1][0][0]