From 314ab0a84f6bf643b2376d109864e0e74a4f82f9 Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Sat, 3 Feb 2018 16:04:26 -0800 Subject: [PATCH] Add a Constants class that contains Bot constants --- freqtrade/constants.py | 121 +++++++++++++ freqtrade/tests/conftest_old.py | 281 ++++++++++++++++++++++++++++++ freqtrade/tests/test_constants.py | 28 +++ 3 files changed, 430 insertions(+) create mode 100644 freqtrade/constants.py create mode 100644 freqtrade/tests/conftest_old.py create mode 100644 freqtrade/tests/test_constants.py diff --git a/freqtrade/constants.py b/freqtrade/constants.py new file mode 100644 index 000000000..765a4fe3e --- /dev/null +++ b/freqtrade/constants.py @@ -0,0 +1,121 @@ +# pragma pylint: disable=too-few-public-methods + +""" +List bot constants +""" + + +class Constants(object): + """ + Static class that contain all bot constants + """ + DYNAMIC_WHITELIST = 20 # pairs + PROCESS_THROTTLE_SECS = 5 # sec + TICKER_INTERVAL = 5 # min + HYPEROPT_EPOCH = 100 # epochs + RETRY_TIMEOUT = 30 # sec + + # Required json-schema for user specified config + CONF_SCHEMA = { + 'type': 'object', + 'properties': { + 'max_open_trades': {'type': 'integer', 'minimum': 1}, + 'ticker_interval': {'type': 'integer', 'enum': [1, 5, 30, 60, 1440]}, + 'stake_currency': {'type': 'string', 'enum': ['BTC', 'ETH', 'USDT']}, + 'stake_amount': {'type': 'number', 'minimum': 0.0005}, + 'fiat_display_currency': {'type': 'string', 'enum': ['AUD', 'BRL', 'CAD', 'CHF', + 'CLP', 'CNY', 'CZK', 'DKK', + 'EUR', 'GBP', 'HKD', 'HUF', + 'IDR', 'ILS', 'INR', 'JPY', + 'KRW', 'MXN', 'MYR', 'NOK', + 'NZD', 'PHP', 'PKR', 'PLN', + 'RUB', 'SEK', 'SGD', 'THB', + 'TRY', 'TWD', 'ZAR', 'USD']}, + 'dry_run': {'type': 'boolean'}, + 'minimal_roi': { + 'type': 'object', + 'patternProperties': { + '^[0-9.]+$': {'type': 'number'} + }, + 'minProperties': 1 + }, + 'stoploss': {'type': 'number', 'maximum': 0, 'exclusiveMaximum': True}, + 'unfilledtimeout': {'type': 'integer', 'minimum': 0}, + 'bid_strategy': { + 'type': 'object', + 'properties': { + 'ask_last_balance': { + 'type': 'number', + 'minimum': 0, + 'maximum': 1, + 'exclusiveMaximum': False + }, + }, + 'required': ['ask_last_balance'] + }, + 'exchange': {'$ref': '#/definitions/exchange'}, + 'experimental': { + 'type': 'object', + 'properties': { + 'use_sell_signal': {'type': 'boolean'}, + 'sell_profit_only': {'type': 'boolean'} + } + }, + 'telegram': { + 'type': 'object', + 'properties': { + 'enabled': {'type': 'boolean'}, + 'token': {'type': 'string'}, + 'chat_id': {'type': 'string'}, + }, + 'required': ['enabled', 'token', 'chat_id'] + }, + 'initial_state': {'type': 'string', 'enum': ['running', 'stopped']}, + 'internals': { + 'type': 'object', + 'properties': { + 'process_throttle_secs': {'type': 'number'}, + 'interval': {'type': 'integer'} + } + } + }, + 'definitions': { + 'exchange': { + 'type': 'object', + 'properties': { + 'name': {'type': 'string'}, + 'key': {'type': 'string'}, + 'secret': {'type': 'string'}, + 'pair_whitelist': { + 'type': 'array', + 'items': { + 'type': 'string', + 'pattern': '^[0-9A-Z]+_[0-9A-Z]+$' + }, + 'uniqueItems': True + }, + 'pair_blacklist': { + 'type': 'array', + 'items': { + 'type': 'string', + 'pattern': '^[0-9A-Z]+_[0-9A-Z]+$' + }, + 'uniqueItems': True + } + }, + 'required': ['name', 'key', 'secret', 'pair_whitelist'] + } + }, + 'anyOf': [ + {'required': ['exchange']} + ], + 'required': [ + 'max_open_trades', + 'stake_currency', + 'stake_amount', + 'fiat_display_currency', + 'dry_run', + 'bid_strategy', + 'telegram' + ] + } diff --git a/freqtrade/tests/conftest_old.py b/freqtrade/tests/conftest_old.py new file mode 100644 index 000000000..2b1d14268 --- /dev/null +++ b/freqtrade/tests/conftest_old.py @@ -0,0 +1,281 @@ +# pragma pylint: disable=missing-docstring +from datetime import datetime +from unittest.mock import MagicMock +from functools import reduce + +import json +import arrow +import pytest +from jsonschema import validate +from telegram import Chat, Message, Update +from freqtrade.analyze import parse_ticker_dataframe +from freqtrade.strategy.strategy import Strategy + +from freqtrade.misc import CONF_SCHEMA + + +def log_has(line, logs): + # caplog mocker returns log as a tuple: ('freqtrade.analyze', logging.WARNING, 'foobar') + # and we want to match line against foobar in the tuple + return reduce(lambda a, b: a or b, + filter(lambda x: x[2] == line, logs), + False) + + +@pytest.fixture(scope="module") +def default_conf(): + """ Returns validated configuration suitable for most tests """ + configuration = { + "max_open_trades": 1, + "stake_currency": "BTC", + "stake_amount": 0.001, + "fiat_display_currency": "USD", + "ticker_interval": 5, + "dry_run": True, + "minimal_roi": { + "40": 0.0, + "30": 0.01, + "20": 0.02, + "0": 0.04 + }, + "stoploss": -0.10, + "unfilledtimeout": 600, + "bid_strategy": { + "ask_last_balance": 0.0 + }, + "exchange": { + "name": "bittrex", + "enabled": True, + "key": "key", + "secret": "secret", + "pair_whitelist": [ + "BTC_ETH", + "BTC_TKN", + "BTC_TRST", + "BTC_SWT", + "BTC_BCC" + ] + }, + "telegram": { + "enabled": True, + "token": "token", + "chat_id": "0" + }, + "initial_state": "running" + } + validate(configuration, CONF_SCHEMA) + return configuration + + +@pytest.fixture +def update(): + _update = Update(0) + _update.message = Message(0, 0, datetime.utcnow(), Chat(0, 0)) + return _update + + +@pytest.fixture +def ticker(): + return MagicMock(return_value={ + 'bid': 0.00001098, + 'ask': 0.00001099, + 'last': 0.00001098, + }) + + +@pytest.fixture +def ticker_sell_up(): + return MagicMock(return_value={ + 'bid': 0.00001172, + 'ask': 0.00001173, + 'last': 0.00001172, + }) + + +@pytest.fixture +def ticker_sell_down(): + return MagicMock(return_value={ + 'bid': 0.00001044, + 'ask': 0.00001043, + 'last': 0.00001044, + }) + + +@pytest.fixture +def health(): + return MagicMock(return_value=[{ + 'Currency': 'BTC', + 'IsActive': True, + 'LastChecked': '2017-11-13T20:15:00.00', + 'Notice': None + }, { + 'Currency': 'ETH', + 'IsActive': True, + 'LastChecked': '2017-11-13T20:15:00.00', + 'Notice': None + }, { + 'Currency': 'TRST', + 'IsActive': True, + 'LastChecked': '2017-11-13T20:15:00.00', + 'Notice': None + }, { + 'Currency': 'SWT', + 'IsActive': True, + 'LastChecked': '2017-11-13T20:15:00.00', + 'Notice': None + }, { + 'Currency': 'BCC', + 'IsActive': False, + 'LastChecked': '2017-11-13T20:15:00.00', + 'Notice': None + }]) + + +@pytest.fixture +def limit_buy_order(): + return { + 'id': 'mocked_limit_buy', + 'type': 'LIMIT_BUY', + 'pair': 'mocked', + 'opened': str(arrow.utcnow().datetime), + 'rate': 0.00001099, + 'amount': 90.99181073, + 'remaining': 0.0, + 'closed': str(arrow.utcnow().datetime), + } + + +@pytest.fixture +def limit_buy_order_old(): + return { + 'id': 'mocked_limit_buy_old', + 'type': 'LIMIT_BUY', + 'pair': 'BTC_ETH', + 'opened': str(arrow.utcnow().shift(minutes=-601).datetime), + 'rate': 0.00001099, + 'amount': 90.99181073, + 'remaining': 90.99181073, + } + + +@pytest.fixture +def limit_sell_order_old(): + return { + 'id': 'mocked_limit_sell_old', + 'type': 'LIMIT_SELL', + 'pair': 'BTC_ETH', + 'opened': str(arrow.utcnow().shift(minutes=-601).datetime), + 'rate': 0.00001099, + 'amount': 90.99181073, + 'remaining': 90.99181073, + } + + +@pytest.fixture +def limit_buy_order_old_partial(): + return { + 'id': 'mocked_limit_buy_old_partial', + 'type': 'LIMIT_BUY', + 'pair': 'BTC_ETH', + 'opened': str(arrow.utcnow().shift(minutes=-601).datetime), + 'rate': 0.00001099, + 'amount': 90.99181073, + 'remaining': 67.99181073, + } + + +@pytest.fixture +def limit_sell_order(): + return { + 'id': 'mocked_limit_sell', + 'type': 'LIMIT_SELL', + 'pair': 'mocked', + 'opened': str(arrow.utcnow().datetime), + 'rate': 0.00001173, + 'amount': 90.99181073, + 'remaining': 0.0, + 'closed': str(arrow.utcnow().datetime), + } + + +@pytest.fixture +def ticker_history(): + return [ + { + "O": 8.794e-05, + "H": 8.948e-05, + "L": 8.794e-05, + "C": 8.88e-05, + "V": 991.09056638, + "T": "2017-11-26T08:50:00", + "BV": 0.0877869 + }, + { + "O": 8.88e-05, + "H": 8.942e-05, + "L": 8.88e-05, + "C": 8.893e-05, + "V": 658.77935965, + "T": "2017-11-26T08:55:00", + "BV": 0.05874751 + }, + { + "O": 8.891e-05, + "H": 8.893e-05, + "L": 8.875e-05, + "C": 8.877e-05, + "V": 7920.73570705, + "T": "2017-11-26T09:00:00", + "BV": 0.7039405 + } + ] + + +@pytest.fixture +def ticker_history_without_bv(): + return [ + { + "O": 8.794e-05, + "H": 8.948e-05, + "L": 8.794e-05, + "C": 8.88e-05, + "V": 991.09056638, + "T": "2017-11-26T08:50:00" + }, + { + "O": 8.88e-05, + "H": 8.942e-05, + "L": 8.88e-05, + "C": 8.893e-05, + "V": 658.77935965, + "T": "2017-11-26T08:55:00" + }, + { + "O": 8.891e-05, + "H": 8.893e-05, + "L": 8.875e-05, + "C": 8.877e-05, + "V": 7920.73570705, + "T": "2017-11-26T09:00:00" + } + ] + + +@pytest.fixture +def result(): + with open('freqtrade/tests/testdata/BTC_ETH-1.json') as data_file: + return parse_ticker_dataframe(json.load(data_file)) + + +@pytest.fixture +def default_strategy(): + strategy = Strategy() + strategy.init({'strategy': 'default_strategy'}) + return strategy + + +# FIX: +# Create an fixture/function +# that inserts a trade of some type and open-status +# return the open-order-id +# See tests in rpc/main that could use this diff --git a/freqtrade/tests/test_constants.py b/freqtrade/tests/test_constants.py new file mode 100644 index 000000000..e50fbb880 --- /dev/null +++ b/freqtrade/tests/test_constants.py @@ -0,0 +1,28 @@ +""" +Unit test file for constants.py +""" + +from freqtrade.constants import Constants + + +def test_constant_object() -> None: + """ + Test the Constants object has the mandatory Constants + :return: None + """ + constant = Constants() + assert hasattr(constant, 'CONF_SCHEMA') + assert hasattr(constant, 'DYNAMIC_WHITELIST') + assert hasattr(constant, 'PROCESS_THROTTLE_SECS') + assert hasattr(constant, 'TICKER_INTERVAL') + assert hasattr(constant, 'HYPEROPT_EPOCH') + assert hasattr(constant, 'RETRY_TIMEOUT') + + +def test_conf_schema() -> None: + """ + Test the CONF_SCHEMA is from the right type + :return: + """ + constant = Constants() + assert isinstance(constant.CONF_SCHEMA, dict)