mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-13 03:33:55 +00:00
Merge branch 'develop' into ccxt-async
This commit is contained in:
commit
a2d9126917
|
@ -1,5 +1,5 @@
|
||||||
""" FreqTrade bot """
|
""" FreqTrade bot """
|
||||||
__version__ = '0.17.1'
|
__version__ = '0.17.2'
|
||||||
|
|
||||||
|
|
||||||
class DependencyException(BaseException):
|
class DependencyException(BaseException):
|
||||||
|
|
|
@ -135,8 +135,8 @@ class Exchange(object):
|
||||||
api.urls['api'] = api.urls['test']
|
api.urls['api'] = api.urls['test']
|
||||||
logger.info("Enabled Sandbox API on %s", name)
|
logger.info("Enabled Sandbox API on %s", name)
|
||||||
else:
|
else:
|
||||||
logger.warning(self, "No Sandbox URL in CCXT, exiting. "
|
logger.warning(self._api.name, "No Sandbox URL in CCXT, exiting. "
|
||||||
"Please check your config.json")
|
"Please check your config.json")
|
||||||
raise OperationalException(f'Exchange {name} does not provide a sandbox api')
|
raise OperationalException(f'Exchange {name} does not provide a sandbox api')
|
||||||
|
|
||||||
def _load_async_markets(self) -> None:
|
def _load_async_markets(self) -> None:
|
||||||
|
|
|
@ -98,6 +98,8 @@ class FreqtradeBot(object):
|
||||||
'status': f'{state.name.lower()}'
|
'status': f'{state.name.lower()}'
|
||||||
})
|
})
|
||||||
logger.info('Changing state to: %s', state.name)
|
logger.info('Changing state to: %s', state.name)
|
||||||
|
if state == State.RUNNING:
|
||||||
|
self._startup_messages()
|
||||||
|
|
||||||
if state == State.STOPPED:
|
if state == State.STOPPED:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
@ -114,6 +116,38 @@ class FreqtradeBot(object):
|
||||||
nb_assets=nb_assets)
|
nb_assets=nb_assets)
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
def _startup_messages(self) -> None:
|
||||||
|
if self.config.get('dry_run', False):
|
||||||
|
self.rpc.send_msg({
|
||||||
|
'type': RPCMessageType.WARNING_NOTIFICATION,
|
||||||
|
'status': 'Dry run is enabled. All trades are simulated.'
|
||||||
|
})
|
||||||
|
stake_currency = self.config['stake_currency']
|
||||||
|
stake_amount = self.config['stake_amount']
|
||||||
|
minimal_roi = self.config['minimal_roi']
|
||||||
|
ticker_interval = self.config['ticker_interval']
|
||||||
|
exchange_name = self.config['exchange']['name']
|
||||||
|
strategy_name = self.config.get('strategy', '')
|
||||||
|
self.rpc.send_msg({
|
||||||
|
'type': RPCMessageType.CUSTOM_NOTIFICATION,
|
||||||
|
'status': f'*Exchange:* `{exchange_name}`\n'
|
||||||
|
f'*Stake per trade:* `{stake_amount} {stake_currency}`\n'
|
||||||
|
f'*Minimum ROI:* `{minimal_roi}`\n'
|
||||||
|
f'*Ticker Interval:* `{ticker_interval}`\n'
|
||||||
|
f'*Strategy:* `{strategy_name}`'
|
||||||
|
})
|
||||||
|
if self.config.get('dynamic_whitelist', False):
|
||||||
|
top_pairs = 'top ' + str(self.config.get('dynamic_whitelist', 20))
|
||||||
|
specific_pairs = ''
|
||||||
|
else:
|
||||||
|
top_pairs = 'whitelisted'
|
||||||
|
specific_pairs = '\n' + ', '.join(self.config['exchange'].get('pair_whitelist', ''))
|
||||||
|
self.rpc.send_msg({
|
||||||
|
'type': RPCMessageType.STATUS_NOTIFICATION,
|
||||||
|
'status': f'Searching for {top_pairs} {stake_currency} pairs to buy and sell...'
|
||||||
|
f'{specific_pairs}'
|
||||||
|
})
|
||||||
|
|
||||||
def _throttle(self, func: Callable[..., Any], min_secs: float, *args, **kwargs) -> Any:
|
def _throttle(self, func: Callable[..., Any], min_secs: float, *args, **kwargs) -> Any:
|
||||||
"""
|
"""
|
||||||
Throttles the given callable that it
|
Throttles the given callable that it
|
||||||
|
|
|
@ -24,6 +24,8 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class RPCMessageType(Enum):
|
class RPCMessageType(Enum):
|
||||||
STATUS_NOTIFICATION = 'status'
|
STATUS_NOTIFICATION = 'status'
|
||||||
|
WARNING_NOTIFICATION = 'warning'
|
||||||
|
CUSTOM_NOTIFICATION = 'custom'
|
||||||
BUY_NOTIFICATION = 'buy'
|
BUY_NOTIFICATION = 'buy'
|
||||||
SELL_NOTIFICATION = 'sell'
|
SELL_NOTIFICATION = 'sell'
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,12 @@ class Telegram(RPC):
|
||||||
elif msg['type'] == RPCMessageType.STATUS_NOTIFICATION:
|
elif msg['type'] == RPCMessageType.STATUS_NOTIFICATION:
|
||||||
message = '*Status:* `{status}`'.format(**msg)
|
message = '*Status:* `{status}`'.format(**msg)
|
||||||
|
|
||||||
|
elif msg['type'] == RPCMessageType.WARNING_NOTIFICATION:
|
||||||
|
message = '*Warning:* `{status}`'.format(**msg)
|
||||||
|
|
||||||
|
elif msg['type'] == RPCMessageType.CUSTOM_NOTIFICATION:
|
||||||
|
message = '{status}'.format(**msg)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError('Unknown message type: {}'.format(msg['type']))
|
raise NotImplementedError('Unknown message type: {}'.format(msg['type']))
|
||||||
|
|
||||||
|
|
|
@ -1095,6 +1095,38 @@ def test_send_msg_status_notification(default_conf, mocker) -> None:
|
||||||
assert msg_mock.call_args[0][0] == '*Status:* `running`'
|
assert msg_mock.call_args[0][0] == '*Status:* `running`'
|
||||||
|
|
||||||
|
|
||||||
|
def test_warning_notification(default_conf, mocker) -> None:
|
||||||
|
msg_mock = MagicMock()
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.rpc.telegram.Telegram',
|
||||||
|
_init=MagicMock(),
|
||||||
|
_send_msg=msg_mock
|
||||||
|
)
|
||||||
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
telegram = Telegram(freqtradebot)
|
||||||
|
telegram.send_msg({
|
||||||
|
'type': RPCMessageType.WARNING_NOTIFICATION,
|
||||||
|
'status': 'message'
|
||||||
|
})
|
||||||
|
assert msg_mock.call_args[0][0] == '*Warning:* `message`'
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_notification(default_conf, mocker) -> None:
|
||||||
|
msg_mock = MagicMock()
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.rpc.telegram.Telegram',
|
||||||
|
_init=MagicMock(),
|
||||||
|
_send_msg=msg_mock
|
||||||
|
)
|
||||||
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
telegram = Telegram(freqtradebot)
|
||||||
|
telegram.send_msg({
|
||||||
|
'type': RPCMessageType.CUSTOM_NOTIFICATION,
|
||||||
|
'status': '*Custom:* `Hello World`'
|
||||||
|
})
|
||||||
|
assert msg_mock.call_args[0][0] == '*Custom:* `Hello World`'
|
||||||
|
|
||||||
|
|
||||||
def test_send_msg_unknown_type(default_conf, mocker) -> None:
|
def test_send_msg_unknown_type(default_conf, mocker) -> None:
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
|
|
|
@ -1907,3 +1907,9 @@ def test_get_real_amount_open_trade(default_conf, mocker):
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
assert freqtrade.get_real_amount(trade, order) == amount
|
assert freqtrade.get_real_amount(trade, order) == amount
|
||||||
|
|
||||||
|
|
||||||
|
def test_startup_messages(default_conf, mocker):
|
||||||
|
default_conf['dynamic_whitelist'] = 20
|
||||||
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
assert freqtrade.state is State.RUNNING
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
ccxt==1.17.122
|
ccxt==1.17.126
|
||||||
SQLAlchemy==1.2.10
|
SQLAlchemy==1.2.10
|
||||||
python-telegram-bot==10.1.0
|
python-telegram-bot==10.1.0
|
||||||
arrow==0.12.1
|
arrow==0.12.1
|
||||||
|
@ -23,4 +23,4 @@ coinmarketcap==5.0.3
|
||||||
scikit-optimize==0.5.2
|
scikit-optimize==0.5.2
|
||||||
|
|
||||||
# Required for plotting data
|
# Required for plotting data
|
||||||
#plotly==3.0.0
|
#plotly==3.1.1
|
||||||
|
|
Loading…
Reference in New Issue
Block a user