Merge pull request #465 from gcarq/fix/increase_test_coverage

Fix/increase test coverage
This commit is contained in:
Janne Sinivirta 2018-01-29 08:47:26 +02:00 committed by GitHub
commit a5155b3b20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 153 additions and 38 deletions

View File

@ -60,7 +60,7 @@ def common_datearray(dfs):
return np.sort(arr, axis=0)
def file_dump_json(filename, data):
def file_dump_json(filename, data) -> None:
with open(filename, 'w') as fp:
json.dump(data, fp)
@ -287,27 +287,27 @@ def hyperopt_options(parser: argparse.ArgumentParser) -> None:
def parse_timerange(text):
if text is None:
return None
syntax = [('^-(\d{8})$', (None, 'date')),
('^(\d{8})-$', ('date', None)),
('^(\d{8})-(\d{8})$', ('date', 'date')),
('^(-\d+)$', (None, 'line')),
('^(\d+)-$', ('line', None)),
('^(\d+)-(\d+)$', ('index', 'index'))]
syntax = [(r'^-(\d{8})$', (None, 'date')),
(r'^(\d{8})-$', ('date', None)),
(r'^(\d{8})-(\d{8})$', ('date', 'date')),
(r'^(-\d+)$', (None, 'line')),
(r'^(\d+)-$', ('line', None)),
(r'^(\d+)-(\d+)$', ('index', 'index'))]
for rex, stype in syntax:
# Apply the regular expression to text
m = re.match(rex, text)
if m: # Regex has matched
rvals = m.groups()
n = 0
match = re.match(rex, text)
if match: # Regex has matched
rvals = match.groups()
index = 0
start = None
stop = None
if stype[0]:
start = rvals[n]
start = rvals[index]
if stype[0] != 'date':
start = int(start)
n += 1
index += 1
if stype[1]:
stop = rvals[n]
stop = rvals[index]
if stype[1] != 'date':
stop = int(stop)
return (stype, start, stop)

View File

@ -27,8 +27,7 @@ def trim_tickerlist(tickerlist, timerange):
return tickerlist
def load_tickerdata_file(datadir, pair, ticker_interval,
timerange=None):
def load_tickerdata_file(datadir, pair, ticker_interval, timerange=None):
"""
Load a pair from file,
:return dict OR empty if unsuccesful

View File

@ -17,13 +17,6 @@ class IStrategy(ABC):
stoploss -> float: optimal stoploss designed for the strategy
ticker_interval -> int: value of the ticker interval to use for the strategy
"""
@property
def name(self) -> str:
"""
Name of the strategy.
:return: str representation of the class name
"""
return self.__class__.__name__
@abstractmethod
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:

View File

@ -1,3 +1,5 @@
# pragma pylint: disable=attribute-defined-outside-init
"""
This module load custom strategies
"""
@ -21,7 +23,7 @@ class Strategy(object):
DEFAULT_STRATEGY = 'default_strategy'
def __new__(cls):
def __new__(cls) -> object:
"""
Used to create the Singleton
:return: Strategy object
@ -30,15 +32,7 @@ class Strategy(object):
Strategy.__instance = object.__new__(cls)
return Strategy.__instance
def __init__(self):
if Strategy.__instance is None:
self.logger = None
self.minimal_roi = None
self.stoploss = None
self.ticker_interval = None
self.custom_strategy = None
def init(self, config):
def init(self, config: dict) -> None:
"""
Load the custom class from config parameter
:param config:

View File

@ -1,12 +1,14 @@
# pragma pylint: disable=missing-docstring, protected-access, C0103
import os
import json
import logging
import uuid
from shutil import copyfile
from freqtrade import exchange, optimize
from freqtrade.exchange import Bittrex
from freqtrade.optimize.__init__ import make_testdata_path, download_pairs,\
download_backtesting_testdata, load_tickerdata_file
download_backtesting_testdata, load_tickerdata_file, trim_tickerlist, file_dump_json
# Change this if modifying BTC_UNITEST testdatafile
_BTC_UNITTEST_LENGTH = 13681
@ -225,3 +227,73 @@ def test_tickerdata_to_dataframe():
tickerlist = {'BTC_UNITEST': tick}
data = optimize.tickerdata_to_dataframe(tickerlist)
assert len(data['BTC_UNITEST']) == 100
def test_trim_tickerlist():
with open('freqtrade/tests/testdata/BTC_ETH-1.json') as data_file:
ticker_list = json.load(data_file)
ticker_list_len = len(ticker_list)
# Test the pattern ^(-\d+)$
# This pattern remove X element from the beginning
timerange = ((None, 'line'), None, 5)
ticker = trim_tickerlist(ticker_list, timerange)
ticker_len = len(ticker)
assert ticker_list_len == ticker_len + 5
assert ticker_list[0] is not ticker[0] # The first element should be different
assert ticker_list[-1] is ticker[-1] # The last element must be the same
# Test the pattern ^(\d+)-$
# This pattern keep X element from the end
timerange = (('line', None), 5, None)
ticker = trim_tickerlist(ticker_list, timerange)
ticker_len = len(ticker)
assert ticker_len == 5
assert ticker_list[0] is ticker[0] # The first element must be the same
assert ticker_list[-1] is not ticker[-1] # The last element should be different
# Test the pattern ^(\d+)-(\d+)$
# This pattern extract a window
timerange = (('index', 'index'), 5, 10)
ticker = trim_tickerlist(ticker_list, timerange)
ticker_len = len(ticker)
assert ticker_len == 5
assert ticker_list[0] is not ticker[0] # The first element should be different
assert ticker_list[5] is ticker[0] # The list starts at the index 5
assert ticker_list[9] is ticker[-1] # The list ends at the index 9 (5 elements)
# Test a wrong pattern
# This pattern must return the list unchanged
timerange = ((None, None), None, 5)
ticker = trim_tickerlist(ticker_list, timerange)
ticker_len = len(ticker)
assert ticker_list_len == ticker_len
def test_file_dump_json():
file = 'freqtrade/tests/testdata/test_{id}.json'.format(id=str(uuid.uuid4()))
data = {'bar': 'foo'}
# check the file we will create does not exist
assert os.path.isfile(file) is False
# Create the Json file
file_dump_json(file, data)
# Check the file was create
assert os.path.isfile(file) is True
# Open the Json file created and test the data is in it
with open(file) as data_file:
json_from_file = json.load(data_file)
assert 'bar' in json_from_file
assert json_from_file['bar'] == 'foo'
# Remove the file
_clean_test_file(file)

View File

@ -406,8 +406,7 @@ def test_performance_handle(
assert '<code>BTC_ETH\t6.20% (1)</code>' in msg_mock.call_args_list[0][0][0]
def test_daily_handle(
default_conf, update, ticker, limit_buy_order, limit_sell_order, mocker):
def test_daily_handle(default_conf, update, ticker, limit_buy_order, limit_sell_order, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
msg_mock = MagicMock()
@ -470,6 +469,25 @@ def test_daily_handle(
assert str(' 2.798 USD') in msg_mock.call_args_list[0][0][0]
assert str(' 3 trades') in msg_mock.call_args_list[0][0][0]
def test_daily_wrong_input(default_conf, update, ticker, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
msg_mock = MagicMock()
mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf,
init=MagicMock(),
send_msg=msg_mock)
mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker)
mocker.patch.multiple('freqtrade.fiat_convert.Pymarketcap',
ticker=MagicMock(return_value={'price_usd': 15000.0}),
_cache_symbols=MagicMock(return_value={'BTC': 1}))
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
init(default_conf, create_engine('sqlite://'))
# Try invalid data
msg_mock.reset_mock()
update_state(State.RUNNING)
@ -478,6 +496,13 @@ def test_daily_handle(
assert msg_mock.call_count == 1
assert 'must be an integer greater than 0' in msg_mock.call_args_list[0][0][0]
# Try invalid data
msg_mock.reset_mock()
update_state(State.RUNNING)
update.message.text = '/daily today'
_daily(bot=MagicMock(), update=update)
assert str('Daily Profit over the last 7 days') in msg_mock.call_args_list[0][0][0]
def test_count_handle(default_conf, update, ticker, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf)
@ -691,3 +716,18 @@ def test_send_msg_network_error(default_conf, mocker):
# Bot should've tried to send it twice
assert len(bot.method_calls) == 2
def test_init(default_conf, update, ticker, limit_buy_order, limit_sell_order, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
msg_mock = MagicMock()
mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf,
init=MagicMock(),
send_msg=msg_mock)
mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker)
init(default_conf, create_engine('sqlite://'))

View File

@ -16,6 +16,7 @@ def test_sanitize_module_name():
def test_search_strategy():
assert Strategy._search_strategy('default_strategy') == '.'
assert Strategy._search_strategy('test_strategy') == 'user_data.strategies.'
assert Strategy._search_strategy('super_duper') is None

View File

@ -5,11 +5,12 @@ import time
from copy import deepcopy
from unittest.mock import MagicMock
import datetime
import pytest
from jsonschema import ValidationError
from freqtrade.analyze import parse_ticker_dataframe
from freqtrade.misc import (common_args_parser, file_dump_json, load_config,
parse_args, parse_timerange, throttle)
parse_args, parse_timerange, throttle, datesarray_to_datetimearray)
def test_throttle():
@ -178,3 +179,18 @@ def test_load_config_missing_attributes(default_conf, mocker):
read_data=json.dumps(conf)))
with pytest.raises(ValidationError, match=r'.*\'exchange\' is a required property.*'):
load_config('somefile')
def test_datesarray_to_datetimearray(ticker_history):
dataframes = parse_ticker_dataframe(ticker_history)
dates = datesarray_to_datetimearray(dataframes['date'])
assert isinstance(dates[0], datetime.datetime)
assert dates[0].year == 2017
assert dates[0].month == 11
assert dates[0].day == 26
assert dates[0].hour == 8
assert dates[0].minute == 50
date_len = len(dates)
assert date_len == 3