Merge branch 'develop' into develop

This commit is contained in:
Michael Egger 2017-11-19 04:51:36 +01:00 committed by GitHub
commit a509ebc68c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 218 additions and 104 deletions

View File

@ -1,3 +1,10 @@
[MASTER]
extension-pkg-whitelist=numpy,talib
[BASIC] [BASIC]
good-names=logger good-names=logger
ignore=vendor ignore=vendor
[TYPECHECK]
ignored-modules=numpy,talib

View File

@ -1,3 +1,4 @@
""" FreqTrade bot """
__version__ = '0.14.2' __version__ = '0.14.2'
from . import main from . import main

View File

@ -1,3 +1,6 @@
"""
Functions to analyze ticker data with indicators and produce buy and sell signals
"""
from enum import Enum from enum import Enum
import logging import logging
from datetime import timedelta from datetime import timedelta
@ -7,11 +10,12 @@ import talib.abstract as ta
from pandas import DataFrame, to_datetime from pandas import DataFrame, to_datetime
from freqtrade.exchange import get_ticker_history from freqtrade.exchange import get_ticker_history
from freqtrade.vendor.qtpylib.indicators import awesome_oscillator, crossed_above, crossed_below from freqtrade.vendor.qtpylib.indicators import awesome_oscillator, crossed_above
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class SignalType(Enum): class SignalType(Enum):
""" Enum to distinguish between buy and sell signals """
BUY = "buy" BUY = "buy"
SELL = "sell" SELL = "sell"

View File

@ -1,9 +1,12 @@
# pragma pylint: disable=W0603
""" Cryptocurrency Exchanges support """
import enum import enum
import logging import logging
from random import randint from random import randint
from typing import List, Dict, Any, Optional from typing import List, Dict, Any, Optional
import arrow import arrow
import requests
from cachetools import cached, TTLCache from cachetools import cached, TTLCache
from freqtrade.exchange.bittrex import Bittrex from freqtrade.exchange.bittrex import Bittrex
@ -63,7 +66,12 @@ def validate_pairs(pairs: List[str]) -> None:
:param pairs: list of pairs :param pairs: list of pairs
:return: None :return: None
""" """
markets = _API.get_markets() try:
markets = _API.get_markets()
except requests.exceptions.RequestException as e:
logger.warning('Unable to validate pairs (assuming they are correct). Reason: %s', e)
return
stake_cur = _CONF['stake_currency'] stake_cur = _CONF['stake_currency']
for pair in pairs: for pair in pairs:
if not pair.startswith(stake_cur): if not pair.startswith(stake_cur):
@ -77,7 +85,7 @@ def validate_pairs(pairs: List[str]) -> None:
def buy(pair: str, rate: float, amount: float) -> str: def buy(pair: str, rate: float, amount: float) -> str:
if _CONF['dry_run']: if _CONF['dry_run']:
global _DRY_RUN_OPEN_ORDERS global _DRY_RUN_OPEN_ORDERS
order_id = 'dry_run_buy_{}'.format(randint(0, 1e6)) order_id = 'dry_run_buy_{}'.format(randint(0, 10**6))
_DRY_RUN_OPEN_ORDERS[order_id] = { _DRY_RUN_OPEN_ORDERS[order_id] = {
'pair': pair, 'pair': pair,
'rate': rate, 'rate': rate,
@ -95,7 +103,7 @@ def buy(pair: str, rate: float, amount: float) -> str:
def sell(pair: str, rate: float, amount: float) -> str: def sell(pair: str, rate: float, amount: float) -> str:
if _CONF['dry_run']: if _CONF['dry_run']:
global _DRY_RUN_OPEN_ORDERS global _DRY_RUN_OPEN_ORDERS
order_id = 'dry_run_sell_{}'.format(randint(0, 1e6)) order_id = 'dry_run_sell_{}'.format(randint(0, 10**6))
_DRY_RUN_OPEN_ORDERS[order_id] = { _DRY_RUN_OPEN_ORDERS[order_id] = {
'pair': pair, 'pair': pair,
'rate': rate, 'rate': rate,

View File

@ -12,15 +12,11 @@ from typing import Dict, Optional, List
import requests import requests
from cachetools import cached, TTLCache from cachetools import cached, TTLCache
from freqtrade import __version__, exchange, persistence from freqtrade import __version__, exchange, persistence, rpc
from freqtrade.analyze import get_signal, SignalType from freqtrade.analyze import get_signal, SignalType
from freqtrade.misc import (
FreqtradeException
)
from freqtrade.misc import State, get_state, update_state, parse_args, throttle, \ from freqtrade.misc import State, get_state, update_state, parse_args, throttle, \
load_config load_config, FreqtradeException
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.rpc import telegram
logger = logging.getLogger('freqtrade') logger = logging.getLogger('freqtrade')
@ -102,7 +98,7 @@ def _process(dynamic_whitelist: Optional[bool] = False) -> bool:
) )
time.sleep(30) time.sleep(30)
except RuntimeError: except RuntimeError:
telegram.send_msg('*Status:* Got RuntimeError:\n```\n{traceback}```{hint}'.format( rpc.send_msg('*Status:* Got RuntimeError:\n```\n{traceback}```{hint}'.format(
traceback=traceback.format_exc(), traceback=traceback.format_exc(),
hint='Issue `/start` if you think it is safe to restart.' hint='Issue `/start` if you think it is safe to restart.'
)) ))
@ -123,15 +119,13 @@ def execute_sell(trade: Trade, limit: float) -> None:
trade.open_order_id = order_id trade.open_order_id = order_id
fmt_exp_profit = round(trade.calc_profit(limit) * 100, 2) fmt_exp_profit = round(trade.calc_profit(limit) * 100, 2)
message = '*{}:* Selling [{}]({}) with limit `{:.8f} (profit: ~{:.2f}%)`'.format( rpc.send_msg('*{}:* Selling [{}]({}) with limit `{:.8f} (profit: ~{:.2f}%)`'.format(
trade.exchange, trade.exchange,
trade.pair.replace('_', '/'), trade.pair.replace('_', '/'),
exchange.get_pair_detail_url(trade.pair), exchange.get_pair_detail_url(trade.pair),
limit, limit,
fmt_exp_profit fmt_exp_profit
) ))
logger.info(message)
telegram.send_msg(message)
def min_roi_reached(trade: Trade, current_rate: float, current_time: datetime) -> bool: def min_roi_reached(trade: Trade, current_rate: float, current_time: datetime) -> bool:
@ -218,14 +212,12 @@ def create_trade(stake_amount: float) -> Optional[Trade]:
order_id = exchange.buy(pair, buy_limit, amount) order_id = exchange.buy(pair, buy_limit, amount)
# Create trade entity and return # Create trade entity and return
message = '*{}:* Buying [{}]({}) with limit `{:.8f}`'.format( rpc.send_msg('*{}:* Buying [{}]({}) with limit `{:.8f}`'.format(
exchange.get_name().upper(), exchange.get_name().upper(),
pair.replace('_', '/'), pair.replace('_', '/'),
exchange.get_pair_detail_url(pair), exchange.get_pair_detail_url(pair),
buy_limit buy_limit
) ))
logger.info(message)
telegram.send_msg(message)
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
return Trade(pair=pair, return Trade(pair=pair,
stake_amount=stake_amount, stake_amount=stake_amount,
@ -245,7 +237,7 @@ def init(config: dict, db_url: Optional[str] = None) -> None:
:return: None :return: None
""" """
# Initialize all modules # Initialize all modules
telegram.init(config) rpc.init(config)
persistence.init(config, db_url) persistence.init(config, db_url)
exchange.init(config) exchange.init(config)
@ -283,11 +275,11 @@ def cleanup(*args, **kwargs) -> None:
Cleanup the application state und finish all pending tasks Cleanup the application state und finish all pending tasks
:return: None :return: None
""" """
telegram.send_msg('*Status:* `Stopping trader...`') rpc.send_msg('*Status:* `Stopping trader...`')
logger.info('Stopping trader and cleaning up modules...') logger.info('Stopping trader and cleaning up modules...')
update_state(State.STOPPED) update_state(State.STOPPED)
persistence.cleanup() persistence.cleanup()
telegram.cleanup() rpc.cleanup()
exit(0) exit(0)
@ -325,7 +317,7 @@ def main():
new_state = get_state() new_state = get_state()
# Log state transition # Log state transition
if new_state != old_state: if new_state != old_state:
telegram.send_msg('*Status:* `{}`'.format(new_state.name.lower())) rpc.send_msg('*Status:* `{}`'.format(new_state.name.lower()))
logger.info('Changing state to: %s', new_state.name) logger.info('Changing state to: %s', new_state.name)
if new_state == State.STOPPED: if new_state == State.STOPPED:

View File

@ -1 +1,42 @@
import logging
from . import telegram from . import telegram
logger = logging.getLogger(__name__)
REGISTERED_MODULES = []
def init(config: dict) -> None:
"""
Initializes all enabled rpc modules
:param config: config to use
:return: None
"""
if config['telegram'].get('enabled', False):
logger.info('Enabling rpc.telegram ...')
REGISTERED_MODULES.append('telegram')
telegram.init(config)
def cleanup() -> None:
"""
Stops all enabled rpc modules
:return: None
"""
if 'telegram' in REGISTERED_MODULES:
logger.debug('Cleaning up rpc.telegram ...')
telegram.cleanup()
def send_msg(msg: str) -> None:
"""
Send given markdown message to all registered rpc modules
:param msg: message
:return: None
"""
logger.info(msg)
if 'telegram' in REGISTERED_MODULES:
telegram.send_msg(msg)

View File

@ -57,7 +57,7 @@ def init(config: dict) -> None:
_UPDATER.dispatcher.add_handler(handle) _UPDATER.dispatcher.add_handler(handle)
_UPDATER.start_polling( _UPDATER.start_polling(
clean=True, clean=True,
bootstrap_retries=3, bootstrap_retries=-1,
timeout=30, timeout=30,
read_latency=60, read_latency=60,
) )

View File

@ -1,3 +1,4 @@
# pragma pylint: disable=missing-docstring
import json import json
import os import os
@ -11,9 +12,9 @@ def load_backtesting_data(ticker_interval: int = 5):
] ]
for pair in pairs: for pair in pairs:
with open('{abspath}/testdata/{pair}-{ticker_interval}.json'.format( with open('{abspath}/testdata/{pair}-{ticker_interval}.json'.format(
abspath=path, abspath=path,
pair=pair, pair=pair,
ticker_interval=ticker_interval, ticker_interval=ticker_interval,
)) as fp: )) as tickerdata:
result[pair] = json.load(fp) result[pair] = json.load(tickerdata)
return result return result

View File

@ -1,4 +1,4 @@
# pragma pylint: disable=missing-docstring # pragma pylint: disable=missing-docstring,W0621
import json import json
import arrow import arrow
import pytest import pytest

View File

@ -1,4 +1,4 @@
# pragma pylint: disable=missing-docstring # pragma pylint: disable=missing-docstring,W0212
import logging import logging
@ -23,13 +23,13 @@ logger = logging.getLogger(__name__)
def format_results(results: DataFrame): def format_results(results: DataFrame):
return 'Made {} buys. Average profit {:.2f}%. ' \ return ('Made {} buys. Average profit {:.2f}%. '
'Total profit was {:.3f}. Average duration {:.1f} mins.'.format( 'Total profit was {:.3f}. Average duration {:.1f} mins.').format(
len(results.index), len(results.index),
results.profit.mean() * 100.0, results.profit.mean() * 100.0,
results.profit.sum(), results.profit.sum(),
results.duration.mean() * 5, results.duration.mean() * 5,
) )
def preprocess(backdata) -> Dict[str, DataFrame]: def preprocess(backdata) -> Dict[str, DataFrame]:
@ -47,11 +47,11 @@ def get_timeframe(data: Dict[str, Dict]) -> Tuple[arrow.Arrow, arrow.Arrow]:
""" """
min_date, max_date = None, None min_date, max_date = None, None
for values in data.values(): for values in data.values():
values = sorted(values, key=lambda d: arrow.get(d['T'])) sorted_values = sorted(values, key=lambda d: arrow.get(d['T']))
if not min_date or values[0]['T'] < min_date: if not min_date or sorted_values[0]['T'] < min_date:
min_date = values[0]['T'] min_date = sorted_values[0]['T']
if not max_date or values[-1]['T'] > max_date: if not max_date or sorted_values[-1]['T'] > max_date:
max_date = values[-1]['T'] max_date = sorted_values[-1]['T']
return arrow.get(min_date), arrow.get(max_date) return arrow.get(min_date), arrow.get(max_date)

View File

@ -1,4 +1,4 @@
# pragma pylint: disable=missing-docstring # pragma pylint: disable=missing-docstring,C0103
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest import pytest
@ -33,4 +33,3 @@ def test_validate_pairs_not_compatible(default_conf, mocker):
mocker.patch.dict('freqtrade.exchange._CONF', default_conf) mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
with pytest.raises(RuntimeError, match=r'not compatible'): with pytest.raises(RuntimeError, match=r'not compatible'):
validate_pairs(default_conf['exchange']['pair_whitelist']) validate_pairs(default_conf['exchange']['pair_whitelist'])

View File

@ -1,4 +1,4 @@
# pragma pylint: disable=missing-docstring # pragma pylint: disable=missing-docstring,W0212
import logging import logging
import os import os
from functools import reduce from functools import reduce
@ -21,6 +21,7 @@ logging.disable(logging.DEBUG) # disable debug logs that slow backtesting a lot
# set TARGET_TRADES to suit your number concurrent trades so its realistic to 20days of data # set TARGET_TRADES to suit your number concurrent trades so its realistic to 20days of data
TARGET_TRADES = 1100 TARGET_TRADES = 1100
TOTAL_TRIES = 4 TOTAL_TRIES = 4
# pylint: disable=C0103
current_tries = 0 current_tries = 0
@ -90,6 +91,7 @@ def test_hyperopt(backtest_conf, mocker):
trade_loss = 1 - 0.35 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.2) trade_loss = 1 - 0.35 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.2)
profit_loss = max(0, 1 - total_profit / 10000) # max profit 10000 profit_loss = max(0, 1 - total_profit / 10000) # max profit 10000
# pylint: disable=W0603
global current_tries global current_tries
current_tries += 1 current_tries += 1
print('{}/{}: {}'.format(current_tries, TOTAL_TRIES, result)) print('{}/{}: {}'.format(current_tries, TOTAL_TRIES, result))

View File

@ -1,4 +1,4 @@
# pragma pylint: disable=missing-docstring # pragma pylint: disable=missing-docstring,C0103
import copy import copy
from unittest.mock import MagicMock from unittest.mock import MagicMock
@ -16,7 +16,7 @@ from freqtrade.persistence import Trade
def test_process_trade_creation(default_conf, ticker, health, mocker): def test_process_trade_creation(default_conf, ticker, health, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
mocker.patch.multiple('freqtrade.main.exchange', mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
@ -26,7 +26,7 @@ def test_process_trade_creation(default_conf, ticker, health, mocker):
init(default_conf, create_engine('sqlite://')) init(default_conf, create_engine('sqlite://'))
trades = Trade.query.filter(Trade.is_open.is_(True)).all() trades = Trade.query.filter(Trade.is_open.is_(True)).all()
assert len(trades) == 0 assert not trades
result = _process() result = _process()
assert result is True assert result is True
@ -45,7 +45,7 @@ def test_process_trade_creation(default_conf, ticker, health, mocker):
def test_process_exchange_failures(default_conf, ticker, health, mocker): def test_process_exchange_failures(default_conf, ticker, health, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None) sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None)
mocker.patch.multiple('freqtrade.main.exchange', mocker.patch.multiple('freqtrade.main.exchange',
@ -62,7 +62,7 @@ def test_process_exchange_failures(default_conf, ticker, health, mocker):
def test_process_runtime_error(default_conf, ticker, health, mocker): def test_process_runtime_error(default_conf, ticker, health, mocker):
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=msg_mock)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
mocker.patch.multiple('freqtrade.main.exchange', mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
@ -80,8 +80,9 @@ def test_process_runtime_error(default_conf, ticker, health, mocker):
def test_process_trade_handling(default_conf, ticker, limit_buy_order, health, mocker): def test_process_trade_handling(default_conf, ticker, limit_buy_order, health, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
signal = mocker.patch('freqtrade.main.get_signal', side_effect=lambda *args: False if args[1] == SignalType.SELL else True) mocker.patch('freqtrade.main.get_signal',
side_effect=lambda *args: False if args[1] == SignalType.SELL else True)
mocker.patch.multiple('freqtrade.main.exchange', mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
@ -91,7 +92,7 @@ def test_process_trade_handling(default_conf, ticker, limit_buy_order, health, m
init(default_conf, create_engine('sqlite://')) init(default_conf, create_engine('sqlite://'))
trades = Trade.query.filter(Trade.is_open.is_(True)).all() trades = Trade.query.filter(Trade.is_open.is_(True)).all()
assert len(trades) == 0 assert not trades
result = _process() result = _process()
assert result is True assert result is True
trades = Trade.query.filter(Trade.is_open.is_(True)).all() trades = Trade.query.filter(Trade.is_open.is_(True)).all()
@ -104,7 +105,7 @@ def test_process_trade_handling(default_conf, ticker, limit_buy_order, health, m
def test_create_trade(default_conf, ticker, limit_buy_order, mocker): def test_create_trade(default_conf, ticker, limit_buy_order, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
mocker.patch.multiple('freqtrade.main.exchange', mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
@ -134,7 +135,7 @@ def test_create_trade(default_conf, ticker, limit_buy_order, mocker):
def test_create_trade_no_stake_amount(default_conf, ticker, mocker): def test_create_trade_no_stake_amount(default_conf, ticker, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
mocker.patch.multiple('freqtrade.main.exchange', mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
@ -147,7 +148,7 @@ def test_create_trade_no_stake_amount(default_conf, ticker, mocker):
def test_create_trade_no_pairs(default_conf, ticker, mocker): def test_create_trade_no_pairs(default_conf, ticker, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
mocker.patch.multiple('freqtrade.main.exchange', mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
@ -163,7 +164,7 @@ def test_create_trade_no_pairs(default_conf, ticker, mocker):
def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker): def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
mocker.patch.multiple('freqtrade.main.exchange', mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=MagicMock(return_value={ get_ticker=MagicMock(return_value={
@ -195,7 +196,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker):
def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, mocker): def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
mocker.patch.multiple('freqtrade.main.exchange', mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,

View File

@ -1,4 +1,4 @@
# pragma pylint: disable=missing-docstring # pragma pylint: disable=missing-docstring,C0103
import time import time
import os import os
from argparse import Namespace from argparse import Namespace

View File

@ -0,0 +1,58 @@
# pragma pylint: disable=missing-docstring, too-many-arguments, too-many-ancestors, C0103
from unittest.mock import MagicMock
from copy import deepcopy
from freqtrade.rpc import init, cleanup, send_msg
def test_init_telegram_enabled(default_conf, mocker):
module_list = []
mocker.patch('freqtrade.rpc.REGISTERED_MODULES', module_list)
telegram_mock = mocker.patch('freqtrade.rpc.telegram.init', MagicMock())
init(default_conf)
assert telegram_mock.call_count == 1
assert 'telegram' in module_list
def test_init_telegram_disabled(default_conf, mocker):
module_list = []
mocker.patch('freqtrade.rpc.REGISTERED_MODULES', module_list)
telegram_mock = mocker.patch('freqtrade.rpc.telegram.init', MagicMock())
conf = deepcopy(default_conf)
conf['telegram']['enabled'] = False
init(conf)
assert telegram_mock.call_count == 0
assert 'telegram' not in module_list
def test_cleanup_telegram_enabled(mocker):
mocker.patch('freqtrade.rpc.REGISTERED_MODULES', ['telegram'])
telegram_mock = mocker.patch('freqtrade.rpc.telegram.cleanup', MagicMock())
cleanup()
assert telegram_mock.call_count == 1
def test_cleanup_telegram_disabled(mocker):
mocker.patch('freqtrade.rpc.REGISTERED_MODULES', [])
telegram_mock = mocker.patch('freqtrade.rpc.telegram.cleanup', MagicMock())
cleanup()
assert telegram_mock.call_count == 0
def test_send_msg_telegram_enabled(mocker):
mocker.patch('freqtrade.rpc.REGISTERED_MODULES', ['telegram'])
telegram_mock = mocker.patch('freqtrade.rpc.telegram.send_msg', MagicMock())
send_msg('test')
assert telegram_mock.call_count == 1
def test_send_msg_telegram_disabled(mocker):
mocker.patch('freqtrade.rpc.REGISTERED_MODULES', [])
telegram_mock = mocker.patch('freqtrade.rpc.telegram.send_msg', MagicMock())
send_msg('test')
assert telegram_mock.call_count == 0

View File

@ -1,10 +1,9 @@
# pragma pylint: disable=missing-docstring, too-many-arguments, too-many-ancestors # pragma pylint: disable=missing-docstring, too-many-arguments, too-many-ancestors, C0103
import re import re
from datetime import datetime from datetime import datetime
from random import randint from random import randint
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest
from sqlalchemy import create_engine from sqlalchemy import create_engine
from telegram import Update, Message, Chat from telegram import Update, Message, Chat
from telegram.error import NetworkError from telegram.error import NetworkError
@ -14,10 +13,8 @@ from freqtrade.main import init, create_trade
from freqtrade.misc import update_state, State, get_state from freqtrade.misc import update_state, State, get_state
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.rpc import telegram from freqtrade.rpc import telegram
from freqtrade.rpc.telegram import ( from freqtrade.rpc.telegram import authorized_only, is_enabled, send_msg, _status, _status_table, \
_status, _status_table, _profit, _forcesell, _performance, _count, _start, _stop, _balance, _profit, _forcesell, _performance, _count, _start, _stop, _balance, _version, _help
authorized_only, _help, is_enabled, send_msg,
_version)
def test_is_enabled(default_conf, mocker): def test_is_enabled(default_conf, mocker):
@ -81,7 +78,8 @@ def test_status_handle(default_conf, update, ticker, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -111,16 +109,17 @@ def test_status_handle(default_conf, update, ticker, mocker):
# Trigger status while we have a fulfilled order for the open trade # Trigger status while we have a fulfilled order for the open trade
_status(bot=MagicMock(), update=update) _status(bot=MagicMock(), update=update)
assert msg_mock.call_count == 2 assert msg_mock.call_count == 1
assert '[BTC_ETH]' in msg_mock.call_args_list[-1][0][0] assert '[BTC_ETH]' in msg_mock.call_args_list[0][0][0]
def test_status_table_handle(default_conf, update, ticker, mocker): def test_status_table_handle(default_conf, update, ticker, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.main.telegram', 'freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -155,14 +154,15 @@ def test_status_table_handle(default_conf, update, ticker, mocker):
assert int(fields[0]) == 1 assert int(fields[0]) == 1
assert fields[1] == 'BTC_ETH' assert fields[1] == 'BTC_ETH'
assert msg_mock.call_count == 2 assert msg_mock.call_count == 1
def test_profit_handle(default_conf, update, ticker, limit_buy_order, limit_sell_order, mocker): def test_profit_handle(default_conf, update, ticker, limit_buy_order, limit_sell_order, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -184,7 +184,7 @@ def test_profit_handle(default_conf, update, ticker, limit_buy_order, limit_sell
trade.update(limit_buy_order) trade.update(limit_buy_order)
_profit(bot=MagicMock(), update=update) _profit(bot=MagicMock(), update=update)
assert msg_mock.call_count == 2 assert msg_mock.call_count == 1
assert 'no closed trade' in msg_mock.call_args_list[-1][0][0] assert 'no closed trade' in msg_mock.call_args_list[-1][0][0]
msg_mock.reset_mock() msg_mock.reset_mock()
@ -205,11 +205,11 @@ def test_profit_handle(default_conf, update, ticker, limit_buy_order, limit_sell
def test_forcesell_handle(default_conf, update, ticker, mocker): def test_forcesell_handle(default_conf, update, ticker, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
msg_mock = MagicMock() rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=MagicMock())
mocker.patch.multiple('freqtrade.main.exchange', mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker) get_ticker=ticker)
@ -225,19 +225,19 @@ def test_forcesell_handle(default_conf, update, ticker, mocker):
update.message.text = '/forcesell 1' update.message.text = '/forcesell 1'
_forcesell(bot=MagicMock(), update=update) _forcesell(bot=MagicMock(), update=update)
assert msg_mock.call_count == 2 assert rpc_mock.call_count == 2
assert 'Selling [BTC/ETH]' in msg_mock.call_args_list[-1][0][0] assert 'Selling [BTC/ETH]' in rpc_mock.call_args_list[-1][0][0]
assert '0.07256061 (profit: ~-0.64%)' in msg_mock.call_args_list[-1][0][0] assert '0.07256061 (profit: ~-0.64%)' in rpc_mock.call_args_list[-1][0][0]
def test_forcesell_all_handle(default_conf, update, ticker, mocker): def test_forcesell_all_handle(default_conf, update, ticker, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
msg_mock = MagicMock() rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=MagicMock())
mocker.patch.multiple('freqtrade.main.exchange', mocker.patch.multiple('freqtrade.main.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker) get_ticker=ticker)
@ -247,14 +247,13 @@ def test_forcesell_all_handle(default_conf, update, ticker, mocker):
for _ in range(4): for _ in range(4):
Trade.session.add(create_trade(15.0)) Trade.session.add(create_trade(15.0))
Trade.session.flush() Trade.session.flush()
rpc_mock.reset_mock()
msg_mock.reset_mock()
update.message.text = '/forcesell all' update.message.text = '/forcesell all'
_forcesell(bot=MagicMock(), update=update) _forcesell(bot=MagicMock(), update=update)
assert msg_mock.call_count == 4 assert rpc_mock.call_count == 4
for args in msg_mock.call_args_list: for args in rpc_mock.call_args_list:
assert '0.07256061 (profit: ~-0.64%)' in args[0][0] assert '0.07256061 (profit: ~-0.64%)' in args[0][0]
@ -262,7 +261,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -299,7 +298,8 @@ def test_performance_handle(
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -324,9 +324,9 @@ def test_performance_handle(
Trade.session.flush() Trade.session.flush()
_performance(bot=MagicMock(), update=update) _performance(bot=MagicMock(), update=update)
assert msg_mock.call_count == 2 assert msg_mock.call_count == 1
assert 'Performance' in msg_mock.call_args_list[-1][0][0] assert 'Performance' in msg_mock.call_args_list[0][0][0]
assert '<code>BTC_ETH\t10.05%</code>' in msg_mock.call_args_list[-1][0][0] assert '<code>BTC_ETH\t10.05%</code>' in msg_mock.call_args_list[0][0][0]
def test_count_handle(default_conf, update, ticker, mocker): def test_count_handle(default_conf, update, ticker, mocker):
@ -334,7 +334,7 @@ def test_count_handle(default_conf, update, ticker, mocker):
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.main.telegram', 'freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -367,7 +367,7 @@ def test_performance_handle_invalid(default_conf, update, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -385,7 +385,7 @@ def test_performance_handle_invalid(default_conf, update, mocker):
def test_start_handle(default_conf, update, mocker): def test_start_handle(default_conf, update, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -403,7 +403,7 @@ def test_start_handle(default_conf, update, mocker):
def test_start_handle_already_running(default_conf, update, mocker): def test_start_handle_already_running(default_conf, update, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -422,7 +422,7 @@ def test_start_handle_already_running(default_conf, update, mocker):
def test_stop_handle(default_conf, update, mocker): def test_stop_handle(default_conf, update, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -441,7 +441,7 @@ def test_stop_handle(default_conf, update, mocker):
def test_stop_handle_already_stopped(default_conf, update, mocker): def test_stop_handle_already_stopped(default_conf, update, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -473,7 +473,7 @@ def test_balance_handle(default_conf, update, mocker):
}] }]
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -489,7 +489,7 @@ def test_balance_handle(default_conf, update, mocker):
def test_help_handle(default_conf, update, mocker): def test_help_handle(default_conf, update, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -502,7 +502,7 @@ def test_help_handle(default_conf, update, mocker):
def test_version_handle(default_conf, update, mocker): def test_version_handle(default_conf, update, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock(), init=MagicMock(),
send_msg=msg_mock) send_msg=msg_mock)
@ -514,12 +514,12 @@ def test_version_handle(default_conf, update, mocker):
def test_send_msg(default_conf, mocker): def test_send_msg(default_conf, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock()) init=MagicMock())
bot = MagicMock() bot = MagicMock()
send_msg('test', bot) send_msg('test', bot)
assert len(bot.method_calls) == 0 assert not bot.method_calls
bot.reset_mock() bot.reset_mock()
default_conf['telegram']['enabled'] = True default_conf['telegram']['enabled'] = True
@ -529,7 +529,7 @@ def test_send_msg(default_conf, mocker):
def test_send_msg_network_error(default_conf, mocker): def test_send_msg_network_error(default_conf, mocker):
mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.dict('freqtrade.main._CONF', default_conf)
mocker.patch.multiple('freqtrade.main.telegram', mocker.patch.multiple('freqtrade.rpc.telegram',
_CONF=default_conf, _CONF=default_conf,
init=MagicMock()) init=MagicMock())
default_conf['telegram']['enabled'] = True default_conf['telegram']['enabled'] = True