Introduce Pairlocks middleware

This commit is contained in:
Matthias
2020-10-25 10:54:30 +01:00
parent 69e8da30e5
commit e602ac3406
12 changed files with 216 additions and 130 deletions

View File

@@ -0,0 +1,81 @@
from datetime import datetime, timedelta, timezone
import arrow
import pytest
from freqtrade.persistence import PairLocks
from freqtrade.persistence.models import PairLock
@pytest.mark.parametrize('use_db', (False, True))
@pytest.mark.usefixtures("init_persistence")
def test_PairLocks(use_db):
# No lock should be present
if use_db:
assert len(PairLock.query.all()) == 0
else:
PairLocks.use_db = False
assert PairLocks.use_db == use_db
pair = 'ETH/BTC'
assert not PairLocks.is_pair_locked(pair)
PairLocks.lock_pair(pair, arrow.utcnow().shift(minutes=4).datetime)
# ETH/BTC locked for 4 minutes
assert PairLocks.is_pair_locked(pair)
# XRP/BTC should not be locked now
pair = 'XRP/BTC'
assert not PairLocks.is_pair_locked(pair)
# Unlocking a pair that's not locked should not raise an error
PairLocks.unlock_pair(pair)
PairLocks.lock_pair(pair, arrow.utcnow().shift(minutes=4).datetime)
assert PairLocks.is_pair_locked(pair)
# Get both locks from above
locks = PairLocks.get_pair_locks(None)
assert len(locks) == 2
# Unlock original pair
pair = 'ETH/BTC'
PairLocks.unlock_pair(pair)
assert not PairLocks.is_pair_locked(pair)
assert not PairLocks.is_global_lock()
pair = 'BTC/USDT'
# Lock until 14:30
lock_time = datetime(2020, 5, 1, 14, 30, 0, tzinfo=timezone.utc)
PairLocks.lock_pair(pair, lock_time)
assert not PairLocks.is_pair_locked(pair)
assert PairLocks.is_pair_locked(pair, lock_time + timedelta(minutes=-10))
assert not PairLocks.is_global_lock(lock_time + timedelta(minutes=-10))
assert PairLocks.is_pair_locked(pair, lock_time + timedelta(minutes=-50))
assert not PairLocks.is_global_lock(lock_time + timedelta(minutes=-50))
# Should not be locked after time expired
assert not PairLocks.is_pair_locked(pair, lock_time + timedelta(minutes=10))
locks = PairLocks.get_pair_locks(pair, lock_time + timedelta(minutes=-2))
assert len(locks) == 1
assert 'PairLock' in str(locks[0])
# Unlock all
PairLocks.unlock_pair(pair, lock_time + timedelta(minutes=-2))
assert not PairLocks.is_global_lock(lock_time + timedelta(minutes=-50))
# Global lock
PairLocks.lock_pair('*', lock_time)
assert PairLocks.is_global_lock(lock_time + timedelta(minutes=-50))
# Global lock also locks every pair seperately
assert PairLocks.is_pair_locked(pair, lock_time + timedelta(minutes=-50))
assert PairLocks.is_pair_locked('XRP/USDT', lock_time + timedelta(minutes=-50))
if use_db:
assert len(PairLock.query.all()) > 0
else:
# Nothing was pushed to the database
assert len(PairLock.query.all()) == 0
# Reset use-db variable
PairLocks.use_db = True

View File

@@ -12,7 +12,7 @@ from requests.auth import _basic_auth_str
from freqtrade.__init__ import __version__
from freqtrade.loggers import setup_logging, setup_logging_pre
from freqtrade.persistence import PairLock, Trade
from freqtrade.persistence import PairLocks, Trade
from freqtrade.rpc.api_server import BASE_URI, ApiServer
from freqtrade.state import State
from tests.conftest import create_mock_trades, get_patched_freqtradebot, log_has, patch_get_signal
@@ -339,8 +339,8 @@ def test_api_locks(botclient):
assert rc.json['lock_count'] == 0
assert rc.json['lock_count'] == len(rc.json['locks'])
PairLock.lock_pair('ETH/BTC', datetime.utcnow() + timedelta(minutes=4), 'randreason')
PairLock.lock_pair('XRP/BTC', datetime.utcnow() + timedelta(minutes=20), 'deadbeef')
PairLocks.lock_pair('ETH/BTC', datetime.utcnow() + timedelta(minutes=4), 'randreason')
PairLocks.lock_pair('XRP/BTC', datetime.utcnow() + timedelta(minutes=20), 'deadbeef')
rc = client_get(client, f"{BASE_URI}/locks")
assert_response(rc)

View File

@@ -18,7 +18,7 @@ from freqtrade.constants import CANCEL_REASON
from freqtrade.edge import PairInfo
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.loggers import setup_logging
from freqtrade.persistence import PairLock, Trade
from freqtrade.persistence import PairLocks, Trade
from freqtrade.rpc import RPCMessageType
from freqtrade.rpc.telegram import Telegram, authorized_only
from freqtrade.state import State
@@ -1047,8 +1047,8 @@ def test_telegram_lock_handle(default_conf, update, ticker, fee, mocker) -> None
msg_mock.reset_mock()
freqtradebot.state = State.RUNNING
PairLock.lock_pair('ETH/BTC', arrow.utcnow().shift(minutes=4).datetime, 'randreason')
PairLock.lock_pair('XRP/BTC', arrow.utcnow().shift(minutes=20).datetime, 'deadbeef')
PairLocks.lock_pair('ETH/BTC', arrow.utcnow().shift(minutes=4).datetime, 'randreason')
PairLocks.lock_pair('XRP/BTC', arrow.utcnow().shift(minutes=20).datetime, 'deadbeef')
telegram._locks(update=update, context=MagicMock())

View File

@@ -11,7 +11,7 @@ from freqtrade.configuration import TimeRange
from freqtrade.data.dataprovider import DataProvider
from freqtrade.data.history import load_data
from freqtrade.exceptions import StrategyError
from freqtrade.persistence import PairLock, Trade
from freqtrade.persistence import PairLocks, Trade
from freqtrade.resolvers import StrategyResolver
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
from tests.conftest import log_has, log_has_re
@@ -364,7 +364,7 @@ def test_is_pair_locked(default_conf):
default_conf.update({'strategy': 'DefaultStrategy'})
strategy = StrategyResolver.load_strategy(default_conf)
# No lock should be present
assert len(PairLock.query.all()) == 0
assert len(PairLocks.get_pair_locks(None)) == 0
pair = 'ETH/BTC'
assert not strategy.is_pair_locked(pair)

View File

@@ -15,7 +15,8 @@ from freqtrade.exceptions import (DependencyException, ExchangeError, Insufficie
InvalidOrderException, OperationalException, PricingError,
TemporaryError)
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import Order, PairLock, Trade
from freqtrade.persistence import Order, PairLocks, Trade
from freqtrade.persistence.models import PairLock
from freqtrade.rpc import RPCMessageType
from freqtrade.state import RunMode, State
from freqtrade.strategy.interface import SellCheckTuple, SellType

View File

@@ -1,6 +1,5 @@
# pragma pylint: disable=missing-docstring, C0103
import logging
from datetime import datetime, timedelta, timezone
from unittest.mock import MagicMock
import arrow
@@ -9,7 +8,7 @@ from sqlalchemy import create_engine
from freqtrade import constants
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.persistence import Order, PairLock, Trade, clean_dry_run_db, init_db
from freqtrade.persistence import Order, Trade, clean_dry_run_db, init_db
from tests.conftest import create_mock_trades, log_has, log_has_re
@@ -1159,49 +1158,3 @@ def test_select_order(fee):
assert order.ft_order_side == 'stoploss'
order = trades[4].select_order('sell', False)
assert order is None
@pytest.mark.usefixtures("init_persistence")
def test_PairLock(default_conf):
# No lock should be present
assert len(PairLock.query.all()) == 0
pair = 'ETH/BTC'
assert not PairLock.is_pair_locked(pair)
PairLock.lock_pair(pair, arrow.utcnow().shift(minutes=4).datetime)
# ETH/BTC locked for 4 minutes
assert PairLock.is_pair_locked(pair)
# XRP/BTC should not be locked now
pair = 'XRP/BTC'
assert not PairLock.is_pair_locked(pair)
# Unlocking a pair that's not locked should not raise an error
PairLock.unlock_pair(pair)
PairLock.lock_pair(pair, arrow.utcnow().shift(minutes=4).datetime)
assert PairLock.is_pair_locked(pair)
# Get both locks from above
locks = PairLock.get_pair_locks(None)
assert len(locks) == 2
# Unlock original pair
pair = 'ETH/BTC'
PairLock.unlock_pair(pair)
assert not PairLock.is_pair_locked(pair)
pair = 'BTC/USDT'
# Lock until 14:30
lock_time = datetime(2020, 5, 1, 14, 30, 0, tzinfo=timezone.utc)
PairLock.lock_pair(pair, lock_time)
assert not PairLock.is_pair_locked(pair)
assert PairLock.is_pair_locked(pair, lock_time + timedelta(minutes=-10))
assert PairLock.is_pair_locked(pair, lock_time + timedelta(minutes=-50))
# Should not be locked after time expired
assert not PairLock.is_pair_locked(pair, lock_time + timedelta(minutes=10))
locks = PairLock.get_pair_locks(pair, lock_time + timedelta(minutes=-2))
assert len(locks) == 1
assert 'PairLock' in str(locks[0])