Merge branch 'develop' into develop
This commit is contained in:
commit
a509ebc68c
@ -1,3 +1,10 @@
|
||||
[MASTER]
|
||||
extension-pkg-whitelist=numpy,talib
|
||||
|
||||
[BASIC]
|
||||
good-names=logger
|
||||
ignore=vendor
|
||||
|
||||
[TYPECHECK]
|
||||
ignored-modules=numpy,talib
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
""" FreqTrade bot """
|
||||
__version__ = '0.14.2'
|
||||
|
||||
from . import main
|
||||
|
@ -1,3 +1,6 @@
|
||||
"""
|
||||
Functions to analyze ticker data with indicators and produce buy and sell signals
|
||||
"""
|
||||
from enum import Enum
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
@ -7,11 +10,12 @@ import talib.abstract as ta
|
||||
from pandas import DataFrame, to_datetime
|
||||
|
||||
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__)
|
||||
|
||||
class SignalType(Enum):
|
||||
""" Enum to distinguish between buy and sell signals """
|
||||
BUY = "buy"
|
||||
SELL = "sell"
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
# pragma pylint: disable=W0603
|
||||
""" Cryptocurrency Exchanges support """
|
||||
import enum
|
||||
import logging
|
||||
from random import randint
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
import arrow
|
||||
import requests
|
||||
from cachetools import cached, TTLCache
|
||||
|
||||
from freqtrade.exchange.bittrex import Bittrex
|
||||
@ -63,7 +66,12 @@ def validate_pairs(pairs: List[str]) -> None:
|
||||
:param pairs: list of pairs
|
||||
:return: None
|
||||
"""
|
||||
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']
|
||||
for pair in pairs:
|
||||
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:
|
||||
if _CONF['dry_run']:
|
||||
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] = {
|
||||
'pair': pair,
|
||||
'rate': rate,
|
||||
@ -95,7 +103,7 @@ def buy(pair: str, rate: float, amount: float) -> str:
|
||||
def sell(pair: str, rate: float, amount: float) -> str:
|
||||
if _CONF['dry_run']:
|
||||
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] = {
|
||||
'pair': pair,
|
||||
'rate': rate,
|
||||
|
@ -12,15 +12,11 @@ from typing import Dict, Optional, List
|
||||
import requests
|
||||
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.misc import (
|
||||
FreqtradeException
|
||||
)
|
||||
from freqtrade.misc import State, get_state, update_state, parse_args, throttle, \
|
||||
load_config
|
||||
load_config, FreqtradeException
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.rpc import telegram
|
||||
|
||||
logger = logging.getLogger('freqtrade')
|
||||
|
||||
@ -102,7 +98,7 @@ def _process(dynamic_whitelist: Optional[bool] = False) -> bool:
|
||||
)
|
||||
time.sleep(30)
|
||||
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(),
|
||||
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
|
||||
|
||||
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.pair.replace('_', '/'),
|
||||
exchange.get_pair_detail_url(trade.pair),
|
||||
limit,
|
||||
fmt_exp_profit
|
||||
)
|
||||
logger.info(message)
|
||||
telegram.send_msg(message)
|
||||
))
|
||||
|
||||
|
||||
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)
|
||||
# Create trade entity and return
|
||||
message = '*{}:* Buying [{}]({}) with limit `{:.8f}`'.format(
|
||||
rpc.send_msg('*{}:* Buying [{}]({}) with limit `{:.8f}`'.format(
|
||||
exchange.get_name().upper(),
|
||||
pair.replace('_', '/'),
|
||||
exchange.get_pair_detail_url(pair),
|
||||
buy_limit
|
||||
)
|
||||
logger.info(message)
|
||||
telegram.send_msg(message)
|
||||
))
|
||||
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
|
||||
return Trade(pair=pair,
|
||||
stake_amount=stake_amount,
|
||||
@ -245,7 +237,7 @@ def init(config: dict, db_url: Optional[str] = None) -> None:
|
||||
:return: None
|
||||
"""
|
||||
# Initialize all modules
|
||||
telegram.init(config)
|
||||
rpc.init(config)
|
||||
persistence.init(config, db_url)
|
||||
exchange.init(config)
|
||||
|
||||
@ -283,11 +275,11 @@ def cleanup(*args, **kwargs) -> None:
|
||||
Cleanup the application state und finish all pending tasks
|
||||
:return: None
|
||||
"""
|
||||
telegram.send_msg('*Status:* `Stopping trader...`')
|
||||
rpc.send_msg('*Status:* `Stopping trader...`')
|
||||
logger.info('Stopping trader and cleaning up modules...')
|
||||
update_state(State.STOPPED)
|
||||
persistence.cleanup()
|
||||
telegram.cleanup()
|
||||
rpc.cleanup()
|
||||
exit(0)
|
||||
|
||||
|
||||
@ -325,7 +317,7 @@ def main():
|
||||
new_state = get_state()
|
||||
# Log state transition
|
||||
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)
|
||||
|
||||
if new_state == State.STOPPED:
|
||||
|
@ -1 +1,42 @@
|
||||
import logging
|
||||
|
||||
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)
|
||||
|
@ -57,7 +57,7 @@ def init(config: dict) -> None:
|
||||
_UPDATER.dispatcher.add_handler(handle)
|
||||
_UPDATER.start_polling(
|
||||
clean=True,
|
||||
bootstrap_retries=3,
|
||||
bootstrap_retries=-1,
|
||||
timeout=30,
|
||||
read_latency=60,
|
||||
)
|
||||
|
@ -1,3 +1,4 @@
|
||||
# pragma pylint: disable=missing-docstring
|
||||
import json
|
||||
import os
|
||||
|
||||
@ -14,6 +15,6 @@ def load_backtesting_data(ticker_interval: int = 5):
|
||||
abspath=path,
|
||||
pair=pair,
|
||||
ticker_interval=ticker_interval,
|
||||
)) as fp:
|
||||
result[pair] = json.load(fp)
|
||||
)) as tickerdata:
|
||||
result[pair] = json.load(tickerdata)
|
||||
return result
|
||||
|
@ -1,4 +1,4 @@
|
||||
# pragma pylint: disable=missing-docstring
|
||||
# pragma pylint: disable=missing-docstring,W0621
|
||||
import json
|
||||
import arrow
|
||||
import pytest
|
||||
|
@ -1,4 +1,4 @@
|
||||
# pragma pylint: disable=missing-docstring
|
||||
# pragma pylint: disable=missing-docstring,W0212
|
||||
|
||||
|
||||
import logging
|
||||
@ -23,8 +23,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def format_results(results: DataFrame):
|
||||
return 'Made {} buys. Average profit {:.2f}%. ' \
|
||||
'Total profit was {:.3f}. Average duration {:.1f} mins.'.format(
|
||||
return ('Made {} buys. Average profit {:.2f}%. '
|
||||
'Total profit was {:.3f}. Average duration {:.1f} mins.').format(
|
||||
len(results.index),
|
||||
results.profit.mean() * 100.0,
|
||||
results.profit.sum(),
|
||||
@ -47,11 +47,11 @@ def get_timeframe(data: Dict[str, Dict]) -> Tuple[arrow.Arrow, arrow.Arrow]:
|
||||
"""
|
||||
min_date, max_date = None, None
|
||||
for values in data.values():
|
||||
values = sorted(values, key=lambda d: arrow.get(d['T']))
|
||||
if not min_date or values[0]['T'] < min_date:
|
||||
min_date = values[0]['T']
|
||||
if not max_date or values[-1]['T'] > max_date:
|
||||
max_date = values[-1]['T']
|
||||
sorted_values = sorted(values, key=lambda d: arrow.get(d['T']))
|
||||
if not min_date or sorted_values[0]['T'] < min_date:
|
||||
min_date = sorted_values[0]['T']
|
||||
if not max_date or sorted_values[-1]['T'] > max_date:
|
||||
max_date = sorted_values[-1]['T']
|
||||
return arrow.get(min_date), arrow.get(max_date)
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# pragma pylint: disable=missing-docstring
|
||||
# pragma pylint: disable=missing-docstring,C0103
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
@ -33,4 +33,3 @@ def test_validate_pairs_not_compatible(default_conf, mocker):
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
with pytest.raises(RuntimeError, match=r'not compatible'):
|
||||
validate_pairs(default_conf['exchange']['pair_whitelist'])
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# pragma pylint: disable=missing-docstring
|
||||
# pragma pylint: disable=missing-docstring,W0212
|
||||
import logging
|
||||
import os
|
||||
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
|
||||
TARGET_TRADES = 1100
|
||||
TOTAL_TRIES = 4
|
||||
# pylint: disable=C0103
|
||||
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)
|
||||
profit_loss = max(0, 1 - total_profit / 10000) # max profit 10000
|
||||
|
||||
# pylint: disable=W0603
|
||||
global current_tries
|
||||
current_tries += 1
|
||||
print('{}/{}: {}'.format(current_tries, TOTAL_TRIES, result))
|
||||
|
@ -1,4 +1,4 @@
|
||||
# pragma pylint: disable=missing-docstring
|
||||
# pragma pylint: disable=missing-docstring,C0103
|
||||
import copy
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
@ -16,7 +16,7 @@ from freqtrade.persistence import Trade
|
||||
|
||||
def test_process_trade_creation(default_conf, ticker, health, mocker):
|
||||
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.multiple('freqtrade.main.exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
@ -26,7 +26,7 @@ def test_process_trade_creation(default_conf, ticker, health, mocker):
|
||||
init(default_conf, create_engine('sqlite://'))
|
||||
|
||||
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
||||
assert len(trades) == 0
|
||||
assert not trades
|
||||
|
||||
result = _process()
|
||||
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):
|
||||
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)
|
||||
sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None)
|
||||
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):
|
||||
msg_mock = MagicMock()
|
||||
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.multiple('freqtrade.main.exchange',
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
mocker.patch.multiple('freqtrade.main.telegram', 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.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
||||
mocker.patch('freqtrade.main.get_signal',
|
||||
side_effect=lambda *args: False if args[1] == SignalType.SELL else True)
|
||||
mocker.patch.multiple('freqtrade.main.exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
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://'))
|
||||
|
||||
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
||||
assert len(trades) == 0
|
||||
assert not trades
|
||||
result = _process()
|
||||
assert result is True
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
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',
|
||||
validate_pairs=MagicMock(),
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
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',
|
||||
validate_pairs=MagicMock(),
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
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',
|
||||
validate_pairs=MagicMock(),
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
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',
|
||||
validate_pairs=MagicMock(),
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
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',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
|
@ -1,4 +1,4 @@
|
||||
# pragma pylint: disable=missing-docstring
|
||||
# pragma pylint: disable=missing-docstring,C0103
|
||||
import time
|
||||
import os
|
||||
from argparse import Namespace
|
||||
|
58
freqtrade/tests/test_rpc.py
Normal file
58
freqtrade/tests/test_rpc.py
Normal 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
|
@ -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
|
||||
from datetime import datetime
|
||||
from random import randint
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import create_engine
|
||||
from telegram import Update, Message, Chat
|
||||
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.persistence import Trade
|
||||
from freqtrade.rpc import telegram
|
||||
from freqtrade.rpc.telegram import (
|
||||
_status, _status_table, _profit, _forcesell, _performance, _count, _start, _stop, _balance,
|
||||
authorized_only, _help, is_enabled, send_msg,
|
||||
_version)
|
||||
from freqtrade.rpc.telegram import authorized_only, is_enabled, send_msg, _status, _status_table, \
|
||||
_profit, _forcesell, _performance, _count, _start, _stop, _balance, _version, _help
|
||||
|
||||
|
||||
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('freqtrade.main.get_signal', side_effect=lambda s, t: True)
|
||||
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,
|
||||
init=MagicMock(),
|
||||
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
|
||||
_status(bot=MagicMock(), update=update)
|
||||
|
||||
assert msg_mock.call_count == 2
|
||||
assert '[BTC_ETH]' in msg_mock.call_args_list[-1][0][0]
|
||||
assert msg_mock.call_count == 1
|
||||
assert '[BTC_ETH]' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_status_table_handle(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)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.main.telegram',
|
||||
'freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
send_msg=msg_mock)
|
||||
@ -155,14 +154,15 @@ def test_status_table_handle(default_conf, update, ticker, mocker):
|
||||
|
||||
assert int(fields[0]) == 1
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
|
||||
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,
|
||||
init=MagicMock(),
|
||||
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)
|
||||
|
||||
_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]
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
send_msg=msg_mock)
|
||||
send_msg=MagicMock())
|
||||
mocker.patch.multiple('freqtrade.main.exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker)
|
||||
@ -225,19 +225,19 @@ def test_forcesell_handle(default_conf, update, ticker, mocker):
|
||||
update.message.text = '/forcesell 1'
|
||||
_forcesell(bot=MagicMock(), update=update)
|
||||
|
||||
assert msg_mock.call_count == 2
|
||||
assert 'Selling [BTC/ETH]' in msg_mock.call_args_list[-1][0][0]
|
||||
assert '0.07256061 (profit: ~-0.64%)' in msg_mock.call_args_list[-1][0][0]
|
||||
assert rpc_mock.call_count == 2
|
||||
assert 'Selling [BTC/ETH]' in rpc_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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
send_msg=msg_mock)
|
||||
send_msg=MagicMock())
|
||||
mocker.patch.multiple('freqtrade.main.exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker)
|
||||
@ -247,14 +247,13 @@ def test_forcesell_all_handle(default_conf, update, ticker, mocker):
|
||||
for _ in range(4):
|
||||
Trade.session.add(create_trade(15.0))
|
||||
Trade.session.flush()
|
||||
|
||||
msg_mock.reset_mock()
|
||||
rpc_mock.reset_mock()
|
||||
|
||||
update.message.text = '/forcesell all'
|
||||
_forcesell(bot=MagicMock(), update=update)
|
||||
|
||||
assert msg_mock.call_count == 4
|
||||
for args in msg_mock.call_args_list:
|
||||
assert rpc_mock.call_count == 4
|
||||
for args in rpc_mock.call_args_list:
|
||||
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('freqtrade.main.get_signal', side_effect=lambda s, t: True)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
send_msg=msg_mock)
|
||||
@ -299,7 +298,8 @@ def test_performance_handle(
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
|
||||
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,
|
||||
init=MagicMock(),
|
||||
send_msg=msg_mock)
|
||||
@ -324,9 +324,9 @@ def test_performance_handle(
|
||||
Trade.session.flush()
|
||||
|
||||
_performance(bot=MagicMock(), update=update)
|
||||
assert msg_mock.call_count == 2
|
||||
assert 'Performance' in msg_mock.call_args_list[-1][0][0]
|
||||
assert '<code>BTC_ETH\t10.05%</code>' in msg_mock.call_args_list[-1][0][0]
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Performance' in msg_mock.call_args_list[0][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):
|
||||
@ -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)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.main.telegram',
|
||||
'freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
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('freqtrade.main.get_signal', side_effect=lambda s, t: True)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
send_msg=msg_mock)
|
||||
@ -473,7 +473,7 @@ def test_balance_handle(default_conf, update, mocker):
|
||||
}]
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
send_msg=msg_mock)
|
||||
@ -489,7 +489,7 @@ def test_balance_handle(default_conf, update, mocker):
|
||||
def test_help_handle(default_conf, update, mocker):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
send_msg=msg_mock)
|
||||
@ -502,7 +502,7 @@ def test_help_handle(default_conf, update, mocker):
|
||||
def test_version_handle(default_conf, update, mocker):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock(),
|
||||
send_msg=msg_mock)
|
||||
@ -514,12 +514,12 @@ def test_version_handle(default_conf, update, mocker):
|
||||
|
||||
def test_send_msg(default_conf, mocker):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock())
|
||||
bot = MagicMock()
|
||||
send_msg('test', bot)
|
||||
assert len(bot.method_calls) == 0
|
||||
assert not bot.method_calls
|
||||
bot.reset_mock()
|
||||
|
||||
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):
|
||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||
mocker.patch.multiple('freqtrade.main.telegram',
|
||||
mocker.patch.multiple('freqtrade.rpc.telegram',
|
||||
_CONF=default_conf,
|
||||
init=MagicMock())
|
||||
default_conf['telegram']['enabled'] = True
|
Loading…
Reference in New Issue
Block a user