Improve login-mixin structure

This commit is contained in:
Matthias 2020-11-22 11:49:41 +01:00
parent 8d9c66a638
commit 8f958ef723
14 changed files with 35 additions and 35 deletions

View File

@ -367,7 +367,7 @@ class FreqtradeBot(LoggingMixin):
"but checking to sell open trades.") "but checking to sell open trades.")
return trades_created return trades_created
if PairLocks.is_global_lock(): if PairLocks.is_global_lock():
self.log_once(logger.info, "Global pairlock active. Not creating new trades.") self.log_once("Global pairlock active. Not creating new trades.", logger.info)
return trades_created return trades_created
# Create entity and execute trade for each pair from whitelist # Create entity and execute trade for each pair from whitelist
for pair in whitelist: for pair in whitelist:
@ -553,7 +553,7 @@ class FreqtradeBot(LoggingMixin):
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(pair, self.strategy.timeframe) analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(pair, self.strategy.timeframe)
if self.strategy.is_pair_locked( if self.strategy.is_pair_locked(
pair, analyzed_df.iloc[-1]['date'] if len(analyzed_df) > 0 else None): pair, analyzed_df.iloc[-1]['date'] if len(analyzed_df) > 0 else None):
self.log_once(logger.info, f"Pair {pair} is currently locked.") self.log_once(f"Pair {pair} is currently locked.", logger.info)
return False return False
# get_free_open_trades is checked before create_trade is called # get_free_open_trades is checked before create_trade is called

View File

@ -1,5 +1,6 @@
from typing import Callable
from cachetools import TTLCache, cached from cachetools import TTLCache, cached
@ -19,12 +20,12 @@ class LoggingMixin():
self.refresh_period = refresh_period self.refresh_period = refresh_period
self._log_cache: TTLCache = TTLCache(maxsize=1024, ttl=self.refresh_period) self._log_cache: TTLCache = TTLCache(maxsize=1024, ttl=self.refresh_period)
def log_once(self, logmethod, message: str) -> None: def log_once(self, message: str, logmethod: Callable) -> None:
""" """
Logs message - not more often than "refresh_period" to avoid log spamming Logs message - not more often than "refresh_period" to avoid log spamming
Logs the log-message as debug as well to simplify debugging. Logs the log-message as debug as well to simplify debugging.
:param logmethod: Function that'll be called. Most likely `logger.info`.
:param message: String containing the message to be sent to the function. :param message: String containing the message to be sent to the function.
:param logmethod: Function that'll be called. Most likely `logger.info`.
:return: None. :return: None.
""" """
@cached(cache=self._log_cache) @cached(cache=self._log_cache)

View File

@ -76,9 +76,8 @@ class AgeFilter(IPairList):
self._symbolsChecked[ticker['symbol']] = int(arrow.utcnow().float_timestamp) * 1000 self._symbolsChecked[ticker['symbol']] = int(arrow.utcnow().float_timestamp) * 1000
return True return True
else: else:
self.log_once(logger.info, self.log_once(f"Removed {ticker['symbol']} from whitelist, because age "
f"Removed {ticker['symbol']} from whitelist, because age "
f"{len(daily_candles)} is less than {self._min_days_listed} " f"{len(daily_candles)} is less than {self._min_days_listed} "
f"{plural(self._min_days_listed, 'day')}") f"{plural(self._min_days_listed, 'day')}", logger.info)
return False return False
return False return False

View File

@ -59,8 +59,8 @@ class PrecisionFilter(IPairList):
logger.debug(f"{ticker['symbol']} - {sp} : {stop_gap_price}") logger.debug(f"{ticker['symbol']} - {sp} : {stop_gap_price}")
if sp <= stop_gap_price: if sp <= stop_gap_price:
self.log_once(logger.info, f"Removed {ticker['symbol']} from whitelist, because " self.log_once(f"Removed {ticker['symbol']} from whitelist, because "
f"stop price {sp} would be <= stop limit {stop_gap_price}") f"stop price {sp} would be <= stop limit {stop_gap_price}", logger.info)
return False return False
return True return True

View File

@ -64,9 +64,9 @@ class PriceFilter(IPairList):
:return: True if the pair can stay, false if it should be removed :return: True if the pair can stay, false if it should be removed
""" """
if ticker['last'] is None or ticker['last'] == 0: if ticker['last'] is None or ticker['last'] == 0:
self.log_once(logger.info, self.log_once(f"Removed {ticker['symbol']} from whitelist, because "
f"Removed {ticker['symbol']} from whitelist, because " "ticker['last'] is empty (Usually no trade in the last 24h).",
"ticker['last'] is empty (Usually no trade in the last 24h).") logger.info)
return False return False
# Perform low_price_ratio check. # Perform low_price_ratio check.
@ -74,22 +74,22 @@ class PriceFilter(IPairList):
compare = self._exchange.price_get_one_pip(ticker['symbol'], ticker['last']) compare = self._exchange.price_get_one_pip(ticker['symbol'], ticker['last'])
changeperc = compare / ticker['last'] changeperc = compare / ticker['last']
if changeperc > self._low_price_ratio: if changeperc > self._low_price_ratio:
self.log_once(logger.info, f"Removed {ticker['symbol']} from whitelist, " self.log_once(f"Removed {ticker['symbol']} from whitelist, "
f"because 1 unit is {changeperc * 100:.3f}%") f"because 1 unit is {changeperc * 100:.3f}%", logger.info)
return False return False
# Perform min_price check. # Perform min_price check.
if self._min_price != 0: if self._min_price != 0:
if ticker['last'] < self._min_price: if ticker['last'] < self._min_price:
self.log_once(logger.info, f"Removed {ticker['symbol']} from whitelist, " self.log_once(f"Removed {ticker['symbol']} from whitelist, "
f"because last price < {self._min_price:.8f}") f"because last price < {self._min_price:.8f}", logger.info)
return False return False
# Perform max_price check. # Perform max_price check.
if self._max_price != 0: if self._max_price != 0:
if ticker['last'] > self._max_price: if ticker['last'] > self._max_price:
self.log_once(logger.info, f"Removed {ticker['symbol']} from whitelist, " self.log_once(f"Removed {ticker['symbol']} from whitelist, "
f"because last price > {self._max_price:.8f}") f"because last price > {self._max_price:.8f}", logger.info)
return False return False
return True return True

View File

@ -45,9 +45,9 @@ class SpreadFilter(IPairList):
if 'bid' in ticker and 'ask' in ticker: if 'bid' in ticker and 'ask' in ticker:
spread = 1 - ticker['bid'] / ticker['ask'] spread = 1 - ticker['bid'] / ticker['ask']
if spread > self._max_spread_ratio: if spread > self._max_spread_ratio:
self.log_once(logger.info, self.log_once(f"Removed {ticker['symbol']} from whitelist, because spread "
f"Removed {ticker['symbol']} from whitelist, because spread " f"{spread * 100:.3f}% > {self._max_spread_ratio * 100}%",
f"{spread * 100:.3f}% > {self._max_spread_ratio * 100}%") logger.info)
return False return False
else: else:
return True return True

View File

@ -111,6 +111,6 @@ class VolumePairList(IPairList):
# Limit pairlist to the requested number of pairs # Limit pairlist to the requested number of pairs
pairs = pairs[:self._number_pairs] pairs = pairs[:self._number_pairs]
self.log_once(logger.info, f"Searching {self._number_pairs} pairs: {pairs}") self.log_once(f"Searching {self._number_pairs} pairs: {pairs}", logger.info)
return pairs return pairs

View File

@ -78,10 +78,10 @@ class RangeStabilityFilter(IPairList):
if pct_change >= self._min_rate_of_change: if pct_change >= self._min_rate_of_change:
result = True result = True
else: else:
self.log_once(logger.info, self.log_once(f"Removed {pair} from whitelist, because rate of change "
f"Removed {pair} from whitelist, because rate of change "
f"over {plural(self._days, 'day')} is {pct_change:.3f}, " f"over {plural(self._days, 'day')} is {pct_change:.3f}, "
f"which is below the threshold of {self._min_rate_of_change}.") f"which is below the threshold of {self._min_rate_of_change}.",
logger.info)
result = False result = False
self._pair_cache[pair] = result self._pair_cache[pair] = result

View File

@ -1,6 +1,6 @@
import logging import logging
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta
from typing import Any, Dict from typing import Any, Dict
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
@ -46,7 +46,7 @@ class CooldownPeriod(IProtection):
] ]
trade = Trade.get_trades(filters).first() trade = Trade.get_trades(filters).first()
if trade: if trade:
self.log_once(logger.info, f"Cooldown for {pair} for {self._stop_duration}.") self.log_once(f"Cooldown for {pair} for {self._stop_duration}.", logger.info)
until = self.calculate_lock_end([trade], self._stop_duration) until = self.calculate_lock_end([trade], self._stop_duration)
return True, until, self._reason() return True, until, self._reason()

View File

@ -1,6 +1,6 @@
import logging import logging
from abc import ABC, abstractmethod, abstractproperty from abc import ABC, abstractmethod
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple

View File

@ -58,9 +58,8 @@ class LowProfitPairs(IProtection):
profit = sum(trade.close_profit for trade in trades) profit = sum(trade.close_profit for trade in trades)
if profit < self._required_profit: if profit < self._required_profit:
self.log_once( self.log_once(
logger.info,
f"Trading for {pair} stopped due to {profit:.2f} < {self._required_profit} " f"Trading for {pair} stopped due to {profit:.2f} < {self._required_profit} "
f"within {self._lookback_period} minutes.") f"within {self._lookback_period} minutes.", logger.info)
until = self.calculate_lock_end(trades, self._stop_duration) until = self.calculate_lock_end(trades, self._stop_duration)
return True, until, self._reason(profit) return True, until, self._reason(profit)

View File

@ -58,8 +58,8 @@ class StoplossGuard(IProtection):
trades = Trade.get_trades(filters).all() trades = Trade.get_trades(filters).all()
if len(trades) > self._trade_limit: if len(trades) > self._trade_limit:
self.log_once(logger.info, f"Trading stopped due to {self._trade_limit} " self.log_once(f"Trading stopped due to {self._trade_limit} "
f"stoplosses within {self._lookback_period} minutes.") f"stoplosses within {self._lookback_period} minutes.", logger.info)
until = self.calculate_lock_end(trades, self._stop_duration) until = self.calculate_lock_end(trades, self._stop_duration)
return True, until, self._reason() return True, until, self._reason()

View File

@ -102,14 +102,14 @@ def test_log_cached(mocker, static_pl_conf, markets, tickers):
logmock = MagicMock() logmock = MagicMock()
# Assign starting whitelist # Assign starting whitelist
pl = freqtrade.pairlists._pairlist_handlers[0] pl = freqtrade.pairlists._pairlist_handlers[0]
pl.log_once(logmock, 'Hello world') pl.log_once('Hello world', logmock)
assert logmock.call_count == 1 assert logmock.call_count == 1
pl.log_once(logmock, 'Hello world') pl.log_once('Hello world', logmock)
assert logmock.call_count == 1 assert logmock.call_count == 1
assert pl._log_cache.currsize == 1 assert pl._log_cache.currsize == 1
assert ('Hello world',) in pl._log_cache._Cache__data assert ('Hello world',) in pl._log_cache._Cache__data
pl.log_once(logmock, 'Hello world2') pl.log_once('Hello world2', logmock)
assert logmock.call_count == 2 assert logmock.call_count == 2
assert pl._log_cache.currsize == 2 assert pl._log_cache.currsize == 2

View File

@ -165,6 +165,7 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog):
assert PairLocks.is_pair_locked('XRP/BTC') assert PairLocks.is_pair_locked('XRP/BTC')
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
@pytest.mark.parametrize("protectionconf,desc_expected,exception_expected", [ @pytest.mark.parametrize("protectionconf,desc_expected,exception_expected", [
({"method": "StoplossGuard", "lookback_period": 60, "trade_limit": 2}, ({"method": "StoplossGuard", "lookback_period": 60, "trade_limit": 2},
"[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, " "[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, "