Merge remote-tracking branch 'upstream/develop' into pairlists-shuffle

This commit is contained in:
hroff-1902
2020-05-18 23:18:41 +03:00
28 changed files with 132 additions and 116 deletions

View File

@@ -163,7 +163,7 @@ def deploy_new_config(config_path: Path, selections: Dict[str, Any]) -> None:
)
except TemplateNotFound:
selections['exchange'] = render_template(
templatefile=f"subtemplates/exchange_generic.j2",
templatefile="subtemplates/exchange_generic.j2",
arguments=selections
)

View File

@@ -372,8 +372,8 @@ AVAILABLE_CLI_OPTIONS = {
),
"timeframes": Arg(
'-t', '--timeframes',
help=f'Specify which tickers to download. Space-separated list. '
f'Default: `1m 5m`.',
help='Specify which tickers to download. Space-separated list. '
'Default: `1m 5m`.',
choices=['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h',
'6h', '8h', '12h', '1d', '3d', '1w'],
default=['1m', '5m'],

View File

@@ -51,7 +51,7 @@ def deploy_new_strategy(strategy_name: str, strategy_path: Path, subtemplate: st
)
additional_methods = render_template_with_fallback(
templatefile=f"subtemplates/strategy_methods_{subtemplate}.j2",
templatefallbackfile=f"subtemplates/strategy_methods_empty.j2",
templatefallbackfile="subtemplates/strategy_methods_empty.j2",
)
strategy_text = render_template(templatefile='base_strategy.py.j2',

View File

@@ -5,7 +5,7 @@ including ticker and orderbook data, live and historical candle (OHLCV) data
Common Interface for bot and strategy to access data.
"""
import logging
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Optional
from pandas import DataFrame
@@ -13,6 +13,8 @@ from freqtrade.data.history import load_pair_history
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.exchange import Exchange
from freqtrade.state import RunMode
from freqtrade.typing import ListPairsWithTimeframes
logger = logging.getLogger(__name__)
@@ -25,8 +27,8 @@ class DataProvider:
self._pairlists = pairlists
def refresh(self,
pairlist: List[Tuple[str, str]],
helping_pairs: List[Tuple[str, str]] = None) -> None:
pairlist: ListPairsWithTimeframes,
helping_pairs: ListPairsWithTimeframes = None) -> None:
"""
Refresh data, called with each cycle
"""
@@ -36,7 +38,7 @@ class DataProvider:
self._exchange.refresh_latest_ohlcv(pairlist)
@property
def available_pairs(self) -> List[Tuple[str, str]]:
def available_pairs(self) -> ListPairsWithTimeframes:
"""
Return a list of tuples containing (pair, timeframe) for which data is currently cached.
Should be whitelist + open trades.

View File

@@ -1,6 +1,6 @@
import logging
from freqtrade.exceptions import DependencyException, TemporaryError
from freqtrade.exceptions import TemporaryError
logger = logging.getLogger(__name__)
@@ -93,7 +93,7 @@ def retrier_async(f):
count = kwargs.pop('count', API_RETRY_COUNT)
try:
return await f(*args, **kwargs)
except (TemporaryError, DependencyException) as ex:
except TemporaryError as ex:
logger.warning('%s() returned exception: "%s"', f.__name__, ex)
if count > 0:
count -= 1
@@ -111,7 +111,7 @@ def retrier(f):
count = kwargs.pop('count', API_RETRY_COUNT)
try:
return f(*args, **kwargs)
except (TemporaryError, DependencyException) as ex:
except TemporaryError as ex:
logger.warning('%s() returned exception: "%s"', f.__name__, ex)
if count > 0:
count -= 1

View File

@@ -23,6 +23,7 @@ from freqtrade.exceptions import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async
from freqtrade.misc import deep_merge_dicts, safe_value_fallback
from freqtrade.typing import ListPairsWithTimeframes
CcxtModuleType = Any
@@ -366,8 +367,7 @@ class Exchange:
f"Invalid timeframe '{timeframe}'. This exchange supports: {self.timeframes}")
if timeframe and timeframe_to_minutes(timeframe) < 1:
raise OperationalException(
f"Timeframes < 1m are currently not supported by Freqtrade.")
raise OperationalException("Timeframes < 1m are currently not supported by Freqtrade.")
def validate_ordertypes(self, order_types: Dict) -> None:
"""
@@ -676,7 +676,7 @@ class Exchange:
logger.info("Downloaded data for %s with length %s.", pair, len(data))
return data
def refresh_latest_ohlcv(self, pair_list: List[Tuple[str, str]]) -> List[Tuple[str, List]]:
def refresh_latest_ohlcv(self, pair_list: ListPairsWithTimeframes) -> List[Tuple[str, List]]:
"""
Refresh in-memory OHLCV asynchronously and set `_klines` with the result
Loops asynchronously over pair_list and downloads all pairs async (semi-parallel).

View File

@@ -7,7 +7,7 @@ import ccxt
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange import retrier
from freqtrade.exchange.common import retrier
logger = logging.getLogger(__name__)

View File

@@ -7,7 +7,7 @@ import traceback
from datetime import datetime
from math import isclose
from threading import Lock
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Optional
import arrow
from cachetools import TTLCache
@@ -84,7 +84,7 @@ class FreqtradeBot:
self.edge = Edge(self.config, self.exchange, self.strategy) if \
self.config.get('edge', {}).get('enabled', False) else None
self.active_pair_whitelist = self._refresh_whitelist()
self.active_pair_whitelist = self._refresh_active_whitelist()
# Set initial bot state from config
initial_state = self.config.get('initial_state')
@@ -145,10 +145,10 @@ class FreqtradeBot:
# Query trades from persistence layer
trades = Trade.get_open_trades()
self.active_pair_whitelist = self._refresh_whitelist(trades)
self.active_pair_whitelist = self._refresh_active_whitelist(trades)
# Refreshing candles
self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist),
self.dataprovider.refresh(self.pairlists.create_pair_list(self.active_pair_whitelist),
self.strategy.informative_pairs())
with self._sell_lock:
@@ -175,9 +175,10 @@ class FreqtradeBot:
if self.config['cancel_open_orders_on_exit']:
self.cancel_all_open_orders()
def _refresh_whitelist(self, trades: List[Trade] = []) -> List[str]:
def _refresh_active_whitelist(self, trades: List[Trade] = []) -> List[str]:
"""
Refresh whitelist from pairlist or edge and extend it with trades.
Refresh active whitelist from pairlist or edge and extend it with
pairs that have open trades.
"""
# Refresh whitelist
self.pairlists.refresh_pairlist()
@@ -194,12 +195,6 @@ class FreqtradeBot:
_whitelist.extend([trade.pair for trade in trades if trade.pair not in _whitelist])
return _whitelist
def _create_pair_whitelist(self, pairs: List[str]) -> List[Tuple[str, str]]:
"""
Create pair-whitelist tuple with (pair, ticker_interval)
"""
return [(pair, self.config['ticker_interval']) for pair in pairs]
def get_free_open_trades(self):
"""
Return the number of free open trades slots or 0 if

View File

@@ -25,7 +25,7 @@ class VolumePairList(IPairList):
if 'number_assets' not in self._pairlistconfig:
raise OperationalException(
f'`number_assets` not specified. Please check your configuration '
'`number_assets` not specified. Please check your configuration '
'for "pairlist.config.number_assets"')
self._stake_currency = config['stake_currency']

View File

@@ -10,6 +10,7 @@ from cachetools import TTLCache, cached
from freqtrade.exceptions import OperationalException
from freqtrade.pairlist.IPairList import IPairList
from freqtrade.resolvers import PairListResolver
from freqtrade.typing import ListPairsWithTimeframes
logger = logging.getLogger(__name__)
@@ -107,3 +108,9 @@ class PairListManager():
pairlist.remove(p)
return pairlist
def create_pair_list(self, pairs: List[str], timeframe: str = None) -> ListPairsWithTimeframes:
"""
Create list of pair tuples with (pair, ticker_interval)
"""
return [(pair, timeframe or self._config['ticker_interval']) for pair in pairs]

View File

@@ -545,5 +545,5 @@ class RPC:
def _rpc_edge(self) -> List[Dict[str, Any]]:
""" Returns information related to Edge """
if not self._freqtrade.edge:
raise RPCException(f'Edge is not enabled.')
raise RPCException('Edge is not enabled.')
return self._freqtrade.edge.accepted_pairs()

View File

@@ -234,7 +234,7 @@ class Telegram(RPC):
lines.append("*Open Order:* `{open_order}`")
# Filter empty lines using list-comprehension
messages.append("\n".join([l for l in lines if l]).format(**r))
messages.append("\n".join([line for line in lines if line]).format(**r))
for msg in messages:
self._send_msg(msg)
@@ -289,7 +289,7 @@ class Telegram(RPC):
'Day',
f'Profit {stake_cur}',
f'Profit {fiat_disp_cur}',
f'Trades',
'Trades',
],
tablefmt='simple')
message = f'<b>Daily Profit over the last {timescale} days</b>:\n<pre>{stats_tab}</pre>'

View File

@@ -47,9 +47,9 @@ class Webhook(RPC):
valuedict = self._config['webhook'].get('webhooksell', None)
elif msg['type'] == RPCMessageType.SELL_CANCEL_NOTIFICATION:
valuedict = self._config['webhook'].get('webhooksellcancel', None)
elif msg['type'] in(RPCMessageType.STATUS_NOTIFICATION,
RPCMessageType.CUSTOM_NOTIFICATION,
RPCMessageType.WARNING_NOTIFICATION):
elif msg['type'] in (RPCMessageType.STATUS_NOTIFICATION,
RPCMessageType.CUSTOM_NOTIFICATION,
RPCMessageType.WARNING_NOTIFICATION):
valuedict = self._config['webhook'].get('webhookstatus', None)
else:
raise NotImplementedError('Unknown message type: {}'.format(msg['type']))

View File

@@ -7,7 +7,7 @@ import warnings
from abc import ABC, abstractmethod
from datetime import datetime, timezone
from enum import Enum
from typing import Dict, List, NamedTuple, Optional, Tuple
from typing import Dict, NamedTuple, Optional, Tuple
import arrow
from pandas import DataFrame
@@ -17,8 +17,10 @@ from freqtrade.exceptions import StrategyError
from freqtrade.exchange import timeframe_to_minutes
from freqtrade.persistence import Trade
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
from freqtrade.typing import ListPairsWithTimeframes
from freqtrade.wallets import Wallets
logger = logging.getLogger(__name__)
@@ -185,7 +187,7 @@ class IStrategy(ABC):
"""
return False
def informative_pairs(self) -> List[Tuple[str, str]]:
def informative_pairs(self) -> ListPairsWithTimeframes:
"""
Define additional, informative pair/interval combinations to be cached from the exchange.
These pair/interval combinations are non-tradeable, unless they are part

8
freqtrade/typing.py Normal file
View File

@@ -0,0 +1,8 @@
"""
Common Freqtrade types
"""
from typing import List, Tuple
# List of pairs with their timeframes
ListPairsWithTimeframes = List[Tuple[str, str]]

View File

@@ -37,9 +37,7 @@ class Worker:
self._heartbeat_msg: float = 0
# Tell systemd that we completed initialization phase
if self._sd_notify:
logger.debug("sd_notify: READY=1")
self._sd_notify.notify("READY=1")
self._notify("READY=1")
def _init(self, reconfig: bool) -> None:
"""
@@ -60,6 +58,15 @@ class Worker:
self._sd_notify = sdnotify.SystemdNotifier() if \
self._config.get('internals', {}).get('sd_notify', False) else None
def _notify(self, message: str) -> None:
"""
Removes the need to verify in all occurances if sd_notify is enabled
:param message: Message to send to systemd if it's enabled.
"""
if self._sd_notify:
logger.debug(f"sd_notify: {message}")
self._sd_notify.notify(message)
def run(self) -> None:
state = None
while True:
@@ -89,17 +96,13 @@ class Worker:
if state == State.STOPPED:
# Ping systemd watchdog before sleeping in the stopped state
if self._sd_notify:
logger.debug("sd_notify: WATCHDOG=1\\nSTATUS=State: STOPPED.")
self._sd_notify.notify("WATCHDOG=1\nSTATUS=State: STOPPED.")
self._notify("WATCHDOG=1\nSTATUS=State: STOPPED.")
self._throttle(func=self._process_stopped, throttle_secs=self._throttle_secs)
elif state == State.RUNNING:
# Ping systemd watchdog before throttling
if self._sd_notify:
logger.debug("sd_notify: WATCHDOG=1\\nSTATUS=State: RUNNING.")
self._sd_notify.notify("WATCHDOG=1\nSTATUS=State: RUNNING.")
self._notify("WATCHDOG=1\nSTATUS=State: RUNNING.")
self._throttle(func=self._process_running, throttle_secs=self._throttle_secs)
@@ -154,9 +157,7 @@ class Worker:
replaces it with the new instance
"""
# Tell systemd that we initiated reconfiguration
if self._sd_notify:
logger.debug("sd_notify: RELOADING=1")
self._sd_notify.notify("RELOADING=1")
self._notify("RELOADING=1")
# Clean up current freqtrade modules
self.freqtrade.cleanup()
@@ -167,15 +168,11 @@ class Worker:
self.freqtrade.notify_status('config reloaded')
# Tell systemd that we completed reconfiguration
if self._sd_notify:
logger.debug("sd_notify: READY=1")
self._sd_notify.notify("READY=1")
self._notify("READY=1")
def exit(self) -> None:
# Tell systemd that we are exiting now
if self._sd_notify:
logger.debug("sd_notify: STOPPING=1")
self._sd_notify.notify("STOPPING=1")
self._notify("STOPPING=1")
if self.freqtrade:
self.freqtrade.notify_status('process died')