Prepare protections for backtesting
This commit is contained in:
parent
3426e99b8b
commit
98c88fa58e
@ -202,6 +202,10 @@ class Trade(_DECL_BASE):
|
|||||||
"""
|
"""
|
||||||
__tablename__ = 'trades'
|
__tablename__ = 'trades'
|
||||||
|
|
||||||
|
use_db: bool = True
|
||||||
|
# Trades container for backtesting
|
||||||
|
trades: List['Trade'] = []
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
|
|
||||||
orders = relationship("Order", order_by="Order.id", cascade="all, delete-orphan")
|
orders = relationship("Order", order_by="Order.id", cascade="all, delete-orphan")
|
||||||
@ -562,6 +566,43 @@ class Trade(_DECL_BASE):
|
|||||||
else:
|
else:
|
||||||
return Trade.query
|
return Trade.query
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_trades_proxy(*, pair: str = None, is_open: bool = None,
|
||||||
|
open_date: datetime = None, close_date: datetime = None,
|
||||||
|
) -> List['Trade']:
|
||||||
|
"""
|
||||||
|
Helper function to query Trades.
|
||||||
|
Returns a List of trades, filtered on the parameters given.
|
||||||
|
In live mode, converts the filter to a database query and returns all rows
|
||||||
|
In Backtest mode, uses filters on Trade.trades to get the result.
|
||||||
|
|
||||||
|
:return: unsorted List[Trade]
|
||||||
|
"""
|
||||||
|
if Trade.use_db:
|
||||||
|
trade_filter = []
|
||||||
|
if pair:
|
||||||
|
trade_filter.append(Trade.pair == pair)
|
||||||
|
if open_date:
|
||||||
|
trade_filter.append(Trade.open_date > open_date)
|
||||||
|
if close_date:
|
||||||
|
trade_filter.append(Trade.close_date > close_date)
|
||||||
|
if is_open is not None:
|
||||||
|
trade_filter.append(Trade.is_open.is_(is_open))
|
||||||
|
return Trade.get_trades(trade_filter).all()
|
||||||
|
else:
|
||||||
|
# Offline mode - without database
|
||||||
|
sel_trades = [trade for trade in Trade.trades]
|
||||||
|
if pair:
|
||||||
|
sel_trades = [trade for trade in sel_trades if trade.pair == pair]
|
||||||
|
if open_date:
|
||||||
|
sel_trades = [trade for trade in sel_trades if trade.open_date > open_date]
|
||||||
|
if close_date:
|
||||||
|
sel_trades = [trade for trade in sel_trades if trade.close_date
|
||||||
|
and trade.close_date > close_date]
|
||||||
|
if is_open is not None:
|
||||||
|
sel_trades = [trade for trade in sel_trades if trade.is_open == is_open]
|
||||||
|
return sel_trades
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_open_trades() -> List[Any]:
|
def get_open_trades() -> List[Any]:
|
||||||
"""
|
"""
|
||||||
|
@ -3,7 +3,7 @@ Protection manager class
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from freqtrade.persistence import PairLocks
|
from freqtrade.persistence import PairLocks
|
||||||
from freqtrade.plugins.protections import IProtection
|
from freqtrade.plugins.protections import IProtection
|
||||||
@ -43,8 +43,9 @@ class ProtectionManager():
|
|||||||
"""
|
"""
|
||||||
return [{p.name: p.short_desc()} for p in self._protection_handlers]
|
return [{p.name: p.short_desc()} for p in self._protection_handlers]
|
||||||
|
|
||||||
def global_stop(self) -> bool:
|
def global_stop(self, now: Optional[datetime] = None) -> bool:
|
||||||
now = datetime.now(timezone.utc)
|
if not now:
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
result = False
|
result = False
|
||||||
for protection_handler in self._protection_handlers:
|
for protection_handler in self._protection_handlers:
|
||||||
if protection_handler.has_global_stop:
|
if protection_handler.has_global_stop:
|
||||||
@ -57,8 +58,9 @@ class ProtectionManager():
|
|||||||
result = True
|
result = True
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def stop_per_pair(self, pair) -> bool:
|
def stop_per_pair(self, pair, now: Optional[datetime] = None) -> bool:
|
||||||
now = datetime.now(timezone.utc)
|
if not now:
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
result = False
|
result = False
|
||||||
for protection_handler in self._protection_handlers:
|
for protection_handler in self._protection_handlers:
|
||||||
if protection_handler.has_local_stop:
|
if protection_handler.has_local_stop:
|
||||||
|
@ -35,13 +35,16 @@ class CooldownPeriod(IProtection):
|
|||||||
Get last trade for this pair
|
Get last trade for this pair
|
||||||
"""
|
"""
|
||||||
look_back_until = date_now - timedelta(minutes=self._stop_duration)
|
look_back_until = date_now - timedelta(minutes=self._stop_duration)
|
||||||
filters = [
|
# filters = [
|
||||||
Trade.is_open.is_(False),
|
# Trade.is_open.is_(False),
|
||||||
Trade.close_date > look_back_until,
|
# Trade.close_date > look_back_until,
|
||||||
Trade.pair == pair,
|
# Trade.pair == pair,
|
||||||
]
|
# ]
|
||||||
trade = Trade.get_trades(filters).first()
|
# trade = Trade.get_trades(filters).first()
|
||||||
if trade:
|
trades = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until)
|
||||||
|
if trades:
|
||||||
|
# Get latest trade
|
||||||
|
trade = sorted(trades, key=lambda t: t.close_date)[-1]
|
||||||
self.log_once(f"Cooldown for {pair} for {self.stop_duration_str}.", logger.info)
|
self.log_once(f"Cooldown for {pair} for {self.stop_duration_str}.", logger.info)
|
||||||
until = self.calculate_lock_end([trade], self._stop_duration)
|
until = self.calculate_lock_end([trade], self._stop_duration)
|
||||||
|
|
||||||
|
@ -40,13 +40,15 @@ class LowProfitPairs(IProtection):
|
|||||||
Evaluate recent trades for pair
|
Evaluate recent trades for pair
|
||||||
"""
|
"""
|
||||||
look_back_until = date_now - timedelta(minutes=self._lookback_period)
|
look_back_until = date_now - timedelta(minutes=self._lookback_period)
|
||||||
filters = [
|
# filters = [
|
||||||
Trade.is_open.is_(False),
|
# Trade.is_open.is_(False),
|
||||||
Trade.close_date > look_back_until,
|
# Trade.close_date > look_back_until,
|
||||||
]
|
# ]
|
||||||
if pair:
|
# if pair:
|
||||||
filters.append(Trade.pair == pair)
|
# filters.append(Trade.pair == pair)
|
||||||
trades = Trade.get_trades(filters).all()
|
|
||||||
|
trades = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until)
|
||||||
|
# trades = Trade.get_trades(filters).all()
|
||||||
if len(trades) < self._trade_limit:
|
if len(trades) < self._trade_limit:
|
||||||
# Not enough trades in the relevant period
|
# Not enough trades in the relevant period
|
||||||
return False, None, None
|
return False, None, None
|
||||||
|
@ -44,11 +44,8 @@ class MaxDrawdown(IProtection):
|
|||||||
Evaluate recent trades for drawdown ...
|
Evaluate recent trades for drawdown ...
|
||||||
"""
|
"""
|
||||||
look_back_until = date_now - timedelta(minutes=self._lookback_period)
|
look_back_until = date_now - timedelta(minutes=self._lookback_period)
|
||||||
filters = [
|
|
||||||
Trade.is_open.is_(False),
|
trades = Trade.get_trades_proxy(is_open=False, close_date=look_back_until)
|
||||||
Trade.close_date > look_back_until,
|
|
||||||
]
|
|
||||||
trades = Trade.get_trades(filters).all()
|
|
||||||
|
|
||||||
trades_df = pd.DataFrame([trade.to_json() for trade in trades])
|
trades_df = pd.DataFrame([trade.to_json() for trade in trades])
|
||||||
|
|
||||||
|
@ -43,16 +43,21 @@ class StoplossGuard(IProtection):
|
|||||||
Evaluate recent trades
|
Evaluate recent trades
|
||||||
"""
|
"""
|
||||||
look_back_until = date_now - timedelta(minutes=self._lookback_period)
|
look_back_until = date_now - timedelta(minutes=self._lookback_period)
|
||||||
filters = [
|
# filters = [
|
||||||
Trade.is_open.is_(False),
|
# Trade.is_open.is_(False),
|
||||||
Trade.close_date > look_back_until,
|
# Trade.close_date > look_back_until,
|
||||||
or_(Trade.sell_reason == SellType.STOP_LOSS.value,
|
# or_(Trade.sell_reason == SellType.STOP_LOSS.value,
|
||||||
and_(Trade.sell_reason == SellType.TRAILING_STOP_LOSS.value,
|
# and_(Trade.sell_reason == SellType.TRAILING_STOP_LOSS.value,
|
||||||
Trade.close_profit < 0))
|
# Trade.close_profit < 0))
|
||||||
]
|
# ]
|
||||||
if pair:
|
# if pair:
|
||||||
filters.append(Trade.pair == pair)
|
# filters.append(Trade.pair == pair)
|
||||||
trades = Trade.get_trades(filters).all()
|
# trades = Trade.get_trades(filters).all()
|
||||||
|
|
||||||
|
trades1 = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until)
|
||||||
|
trades = [trade for trade in trades1 if trade.sell_reason == SellType.STOP_LOSS
|
||||||
|
or (trade.sell_reason == SellType.TRAILING_STOP_LOSS
|
||||||
|
and trade.close_profit < 0)]
|
||||||
|
|
||||||
if len(trades) > self._trade_limit:
|
if len(trades) > self._trade_limit:
|
||||||
self.log_once(f"Trading stopped due to {self._trade_limit} "
|
self.log_once(f"Trading stopped due to {self._trade_limit} "
|
||||||
|
Loading…
Reference in New Issue
Block a user