Merge branch 'freqtrade:develop' into strategy_utils

This commit is contained in:
hippocritical
2023-03-18 20:15:12 +01:00
committed by GitHub
22 changed files with 363 additions and 306 deletions

View File

@@ -1,7 +1,7 @@
import logging
from typing import Any, Dict
from sqlalchemy import func
from sqlalchemy import func, select
from freqtrade.configuration.config_setup import setup_utils_configuration
from freqtrade.enums import RunMode
@@ -20,7 +20,7 @@ def start_convert_db(args: Dict[str, Any]) -> None:
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
init_db(config['db_url'])
session_target = Trade._session
session_target = Trade.session
init_db(config['db_url_from'])
logger.info("Starting db migration.")
@@ -36,16 +36,16 @@ def start_convert_db(args: Dict[str, Any]) -> None:
session_target.commit()
for pairlock in PairLock.query:
for pairlock in PairLock.get_all_locks():
pairlock_count += 1
make_transient(pairlock)
session_target.add(pairlock)
session_target.commit()
# Update sequences
max_trade_id = session_target.query(func.max(Trade.id)).scalar()
max_order_id = session_target.query(func.max(Order.id)).scalar()
max_pairlock_id = session_target.query(func.max(PairLock.id)).scalar()
max_trade_id = session_target.scalar(select(func.max(Trade.id)))
max_order_id = session_target.scalar(select(func.max(Order.id)))
max_pairlock_id = session_target.scalar(select(func.max(PairLock.id)))
set_sequence_ids(session_target.get_bind(),
trade_id=max_trade_id,

View File

@@ -373,7 +373,7 @@ def load_trades_from_db(db_url: str, strategy: Optional[str] = None) -> pd.DataF
filters = []
if strategy:
filters.append(Trade.strategy == strategy)
trades = trade_list_to_dataframe(Trade.get_trades(filters).all())
trades = trade_list_to_dataframe(list(Trade.get_trades(filters).all()))
return trades

View File

@@ -251,7 +251,7 @@ class FreqaiDataKitchen:
(drop_index == 0) & (drop_index_labels == 0)
]
logger.info(
f"dropped {len(unfiltered_df) - len(filtered_df)} training points"
f"{self.pair}: dropped {len(unfiltered_df) - len(filtered_df)} training points"
f" due to NaNs in populated dataset {len(unfiltered_df)}."
)
if (1 - len(filtered_df) / len(unfiltered_df)) > 0.1 and self.live:
@@ -675,7 +675,7 @@ class FreqaiDataKitchen:
]
logger.info(
f"SVM tossed {len(y_pred) - kept_points.sum()}"
f"{self.pair}: SVM tossed {len(y_pred) - kept_points.sum()}"
f" test points from {len(y_pred)} total points."
)
@@ -949,7 +949,7 @@ class FreqaiDataKitchen:
if (len(do_predict) - do_predict.sum()) > 0:
logger.info(
f"DI tossed {len(do_predict) - do_predict.sum()} predictions for "
f"{self.pair}: DI tossed {len(do_predict) - do_predict.sum()} predictions for "
"being too far from training data."
)

View File

@@ -104,6 +104,7 @@ class IFreqaiModel(ABC):
self.data_provider: Optional[DataProvider] = None
self.max_system_threads = max(int(psutil.cpu_count() * 2 - 2), 1)
self.can_short = True # overridden in start() with strategy.can_short
self.model: Any = None
record_params(config, self.full_path)
@@ -338,13 +339,14 @@ class IFreqaiModel(ABC):
except Exception as msg:
logger.warning(
f"Training {pair} raised exception {msg.__class__.__name__}. "
f"Message: {msg}, skipping.")
f"Message: {msg}, skipping.", exc_info=True)
self.model = None
self.dd.pair_dict[pair]["trained_timestamp"] = int(
tr_train.stopts)
if self.plot_features:
if self.plot_features and self.model is not None:
plot_feature_importance(self.model, pair, dk, self.plot_features)
if self.save_backtest_models:
if self.save_backtest_models and self.model is not None:
logger.info('Saving backtest model to disk.')
self.dd.save_data(self.model, pair, dk)
else:

View File

@@ -819,7 +819,7 @@ class FreqtradeBot(LoggingMixin):
trade.orders.append(order_obj)
trade.recalc_trade_from_orders()
Trade.query.session.add(trade)
Trade.session.add(trade)
Trade.commit()
# Updating wallets

View File

@@ -54,12 +54,9 @@ def init_db(db_url: str) -> None:
# https://docs.sqlalchemy.org/en/13/orm/contextual.html#thread-local-scope
# Scoped sessions proxy requests to the appropriate thread-local session.
# We should use the scoped_session object - not a seperately initialized version
Trade._session = scoped_session(sessionmaker(bind=engine, autoflush=False))
Order._session = Trade._session
PairLock._session = Trade._session
Trade.query = Trade._session.query_property()
Order.query = Trade._session.query_property()
PairLock.query = Trade._session.query_property()
Trade.session = scoped_session(sessionmaker(bind=engine, autoflush=False))
Order.session = Trade.session
PairLock.session = Trade.session
previous_tables = inspect(engine).get_table_names()
ModelBase.metadata.create_all(engine)

View File

@@ -1,8 +1,8 @@
from datetime import datetime, timezone
from typing import Any, ClassVar, Dict, Optional
from sqlalchemy import String, or_
from sqlalchemy.orm import Mapped, Query, QueryPropertyDescriptor, mapped_column
from sqlalchemy import ScalarResult, String, or_, select
from sqlalchemy.orm import Mapped, mapped_column
from freqtrade.constants import DATETIME_PRINT_FORMAT
from freqtrade.persistence.base import ModelBase, SessionType
@@ -13,8 +13,7 @@ class PairLock(ModelBase):
Pair Locks database model.
"""
__tablename__ = 'pairlocks'
query: ClassVar[QueryPropertyDescriptor]
_session: ClassVar[SessionType]
session: ClassVar[SessionType]
id: Mapped[int] = mapped_column(primary_key=True)
@@ -37,7 +36,8 @@ class PairLock(ModelBase):
f'lock_end_time={lock_end_time}, reason={self.reason}, active={self.active})')
@staticmethod
def query_pair_locks(pair: Optional[str], now: datetime, side: str = '*') -> Query:
def query_pair_locks(
pair: Optional[str], now: datetime, side: str = '*') -> ScalarResult['PairLock']:
"""
Get all currently active locks for this pair
:param pair: Pair to check for. Returns all current locks if pair is empty
@@ -53,9 +53,11 @@ class PairLock(ModelBase):
else:
filters.append(PairLock.side == '*')
return PairLock.query.filter(
*filters
)
return PairLock.session.scalars(select(PairLock).filter(*filters))
@staticmethod
def get_all_locks() -> ScalarResult['PairLock']:
return PairLock.session.scalars(select(PairLock))
def to_json(self) -> Dict[str, Any]:
return {

View File

@@ -1,6 +1,8 @@
import logging
from datetime import datetime, timezone
from typing import List, Optional
from typing import List, Optional, Sequence
from sqlalchemy import select
from freqtrade.exchange import timeframe_to_next_date
from freqtrade.persistence.models import PairLock
@@ -51,15 +53,15 @@ class PairLocks():
active=True
)
if PairLocks.use_db:
PairLock.query.session.add(lock)
PairLock.query.session.commit()
PairLock.session.add(lock)
PairLock.session.commit()
else:
PairLocks.locks.append(lock)
return lock
@staticmethod
def get_pair_locks(
pair: Optional[str], now: Optional[datetime] = None, side: str = '*') -> List[PairLock]:
def get_pair_locks(pair: Optional[str], now: Optional[datetime] = None,
side: str = '*') -> Sequence[PairLock]:
"""
Get all currently active locks for this pair
:param pair: Pair to check for. Returns all current locks if pair is empty
@@ -106,7 +108,7 @@ class PairLocks():
for lock in locks:
lock.active = False
if PairLocks.use_db:
PairLock.query.session.commit()
PairLock.session.commit()
@staticmethod
def unlock_reason(reason: str, now: Optional[datetime] = None) -> None:
@@ -126,11 +128,11 @@ class PairLocks():
PairLock.active.is_(True),
PairLock.reason == reason
]
locks = PairLock.query.filter(*filters)
locks = PairLock.session.scalars(select(PairLock).filter(*filters)).all()
for lock in locks:
logger.info(f"Releasing lock for {lock.pair} with reason '{reason}'.")
lock.active = False
PairLock.query.session.commit()
PairLock.session.commit()
else:
# used in backtesting mode; don't show log messages for speed
locksb = PairLocks.get_pair_locks(None)
@@ -165,11 +167,11 @@ class PairLocks():
)
@staticmethod
def get_all_locks() -> List[PairLock]:
def get_all_locks() -> Sequence[PairLock]:
"""
Return all locks, also locks with expired end date
"""
if PairLocks.use_db:
return PairLock.query.all()
return PairLock.get_all_locks().all()
else:
return PairLocks.locks

View File

@@ -5,11 +5,11 @@ import logging
from collections import defaultdict
from datetime import datetime, timedelta, timezone
from math import isclose
from typing import Any, ClassVar, Dict, List, Optional, cast
from typing import Any, ClassVar, Dict, List, Optional, Sequence, cast
from sqlalchemy import Enum, Float, ForeignKey, Integer, String, UniqueConstraint, desc, func
from sqlalchemy.orm import (Mapped, Query, QueryPropertyDescriptor, lazyload, mapped_column,
relationship)
from sqlalchemy import (Enum, Float, ForeignKey, Integer, ScalarResult, Select, String,
UniqueConstraint, desc, func, select)
from sqlalchemy.orm import Mapped, lazyload, mapped_column, relationship
from freqtrade.constants import (DATETIME_PRINT_FORMAT, MATH_CLOSE_PREC, NON_OPEN_EXCHANGE_STATES,
BuySell, LongShort)
@@ -36,8 +36,7 @@ class Order(ModelBase):
Mirrors CCXT Order structure
"""
__tablename__ = 'orders'
query: ClassVar[QueryPropertyDescriptor]
_session: ClassVar[SessionType]
session: ClassVar[SessionType]
# Uniqueness should be ensured over pair, order_id
# its likely that order_id is unique per Pair on some exchanges.
@@ -263,12 +262,12 @@ class Order(ModelBase):
return o
@staticmethod
def get_open_orders() -> List['Order']:
def get_open_orders() -> Sequence['Order']:
"""
Retrieve open orders from the database
:return: List of open orders
"""
return Order.query.filter(Order.ft_is_open.is_(True)).all()
return Order.session.scalars(select(Order).filter(Order.ft_is_open.is_(True))).all()
@staticmethod
def order_by_id(order_id: str) -> Optional['Order']:
@@ -276,7 +275,7 @@ class Order(ModelBase):
Retrieve order based on order_id
:return: Order or None
"""
return Order.query.filter(Order.order_id == order_id).first()
return Order.session.scalars(select(Order).filter(Order.order_id == order_id)).first()
class LocalTrade():
@@ -1153,7 +1152,9 @@ class LocalTrade():
get open trade count
"""
if Trade.use_db:
return Trade.query.filter(Trade.is_open.is_(True)).count()
return Trade.session.execute(
select(func.count(Trade.id)).filter(Trade.is_open.is_(True))
).scalar_one()
else:
return LocalTrade.bt_open_open_trade_count
@@ -1186,8 +1187,7 @@ class Trade(ModelBase, LocalTrade):
Note: Fields must be aligned with LocalTrade class
"""
__tablename__ = 'trades'
query: ClassVar[QueryPropertyDescriptor]
_session: ClassVar[SessionType]
session: ClassVar[SessionType]
use_db: bool = True
@@ -1287,18 +1287,18 @@ class Trade(ModelBase, LocalTrade):
def delete(self) -> None:
for order in self.orders:
Order.query.session.delete(order)
Order.session.delete(order)
Trade.query.session.delete(self)
Trade.session.delete(self)
Trade.commit()
@staticmethod
def commit():
Trade.query.session.commit()
Trade.session.commit()
@staticmethod
def rollback():
Trade.query.session.rollback()
Trade.session.rollback()
@staticmethod
def get_trades_proxy(*, pair: Optional[str] = None, is_open: Optional[bool] = None,
@@ -1332,7 +1332,7 @@ class Trade(ModelBase, LocalTrade):
)
@staticmethod
def get_trades(trade_filter=None, include_orders: bool = True) -> Query['Trade']:
def get_trades_query(trade_filter=None, include_orders: bool = True) -> Select:
"""
Helper function to query Trades using filters.
NOTE: Not supported in Backtesting.
@@ -1347,22 +1347,35 @@ class Trade(ModelBase, LocalTrade):
if trade_filter is not None:
if not isinstance(trade_filter, list):
trade_filter = [trade_filter]
this_query = Trade.query.filter(*trade_filter)
this_query = select(Trade).filter(*trade_filter)
else:
this_query = Trade.query
this_query = select(Trade)
if not include_orders:
# Don't load order relations
# Consider using noload or raiseload instead of lazyload
this_query = this_query.options(lazyload(Trade.orders))
return this_query
@staticmethod
def get_trades(trade_filter=None, include_orders: bool = True) -> ScalarResult['Trade']:
"""
Helper function to query Trades using filters.
NOTE: Not supported in Backtesting.
:param trade_filter: Optional filter to apply to trades
Can be either a Filter object, or a List of filters
e.g. `(trade_filter=[Trade.id == trade_id, Trade.is_open.is_(True),])`
e.g. `(trade_filter=Trade.id == trade_id)`
:return: unsorted query object
"""
return Trade.session.scalars(Trade.get_trades_query(trade_filter, include_orders))
@staticmethod
def get_open_order_trades() -> List['Trade']:
"""
Returns all open trades
NOTE: Not supported in Backtesting.
"""
return Trade.get_trades(Trade.open_order_id.isnot(None)).all()
return cast(List[Trade], Trade.get_trades(Trade.open_order_id.isnot(None)).all())
@staticmethod
def get_open_trades_without_assigned_fees():
@@ -1392,11 +1405,12 @@ class Trade(ModelBase, LocalTrade):
Retrieves total realized profit
"""
if Trade.use_db:
total_profit = Trade.query.with_entities(
func.sum(Trade.close_profit_abs)).filter(Trade.is_open.is_(False)).scalar()
total_profit: float = Trade.session.execute(
select(func.sum(Trade.close_profit_abs)).filter(Trade.is_open.is_(False))
).scalar_one()
else:
total_profit = sum(
t.close_profit_abs for t in LocalTrade.get_trades_proxy(is_open=False))
total_profit = sum(t.close_profit_abs # type: ignore
for t in LocalTrade.get_trades_proxy(is_open=False))
return total_profit or 0
@staticmethod
@@ -1406,8 +1420,9 @@ class Trade(ModelBase, LocalTrade):
in stake currency
"""
if Trade.use_db:
total_open_stake_amount = Trade.query.with_entities(
func.sum(Trade.stake_amount)).filter(Trade.is_open.is_(True)).scalar()
total_open_stake_amount = Trade.session.scalar(
select(func.sum(Trade.stake_amount)).filter(Trade.is_open.is_(True))
)
else:
total_open_stake_amount = sum(
t.stake_amount for t in LocalTrade.get_trades_proxy(is_open=True))
@@ -1423,15 +1438,18 @@ class Trade(ModelBase, LocalTrade):
if minutes:
start_date = datetime.now(timezone.utc) - timedelta(minutes=minutes)
filters.append(Trade.close_date >= start_date)
pair_rates = Trade.query.with_entities(
Trade.pair,
func.sum(Trade.close_profit).label('profit_sum'),
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
func.count(Trade.pair).label('count')
).filter(*filters)\
.group_by(Trade.pair) \
.order_by(desc('profit_sum_abs')) \
.all()
pair_rates = Trade.session.execute(
select(
Trade.pair,
func.sum(Trade.close_profit).label('profit_sum'),
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
func.count(Trade.pair).label('count')
).filter(*filters)
.group_by(Trade.pair)
.order_by(desc('profit_sum_abs'))
).all()
return [
{
'pair': pair,
@@ -1456,15 +1474,16 @@ class Trade(ModelBase, LocalTrade):
if (pair is not None):
filters.append(Trade.pair == pair)
enter_tag_perf = Trade.query.with_entities(
Trade.enter_tag,
func.sum(Trade.close_profit).label('profit_sum'),
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
func.count(Trade.pair).label('count')
).filter(*filters)\
.group_by(Trade.enter_tag) \
.order_by(desc('profit_sum_abs')) \
.all()
enter_tag_perf = Trade.session.execute(
select(
Trade.enter_tag,
func.sum(Trade.close_profit).label('profit_sum'),
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
func.count(Trade.pair).label('count')
).filter(*filters)
.group_by(Trade.enter_tag)
.order_by(desc('profit_sum_abs'))
).all()
return [
{
@@ -1488,16 +1507,16 @@ class Trade(ModelBase, LocalTrade):
filters: List = [Trade.is_open.is_(False)]
if (pair is not None):
filters.append(Trade.pair == pair)
sell_tag_perf = Trade.query.with_entities(
Trade.exit_reason,
func.sum(Trade.close_profit).label('profit_sum'),
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
func.count(Trade.pair).label('count')
).filter(*filters)\
.group_by(Trade.exit_reason) \
.order_by(desc('profit_sum_abs')) \
.all()
sell_tag_perf = Trade.session.execute(
select(
Trade.exit_reason,
func.sum(Trade.close_profit).label('profit_sum'),
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
func.count(Trade.pair).label('count')
).filter(*filters)
.group_by(Trade.exit_reason)
.order_by(desc('profit_sum_abs'))
).all()
return [
{
@@ -1521,18 +1540,18 @@ class Trade(ModelBase, LocalTrade):
filters: List = [Trade.is_open.is_(False)]
if (pair is not None):
filters.append(Trade.pair == pair)
mix_tag_perf = Trade.query.with_entities(
Trade.id,
Trade.enter_tag,
Trade.exit_reason,
func.sum(Trade.close_profit).label('profit_sum'),
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
func.count(Trade.pair).label('count')
).filter(*filters)\
.group_by(Trade.id) \
.order_by(desc('profit_sum_abs')) \
.all()
mix_tag_perf = Trade.session.execute(
select(
Trade.id,
Trade.enter_tag,
Trade.exit_reason,
func.sum(Trade.close_profit).label('profit_sum'),
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
func.count(Trade.pair).label('count')
).filter(*filters)
.group_by(Trade.id)
.order_by(desc('profit_sum_abs'))
).all()
return_list: List[Dict] = []
for id, enter_tag, exit_reason, profit, profit_abs, count in mix_tag_perf:
@@ -1568,11 +1587,15 @@ class Trade(ModelBase, LocalTrade):
NOTE: Not supported in Backtesting.
:returns: Tuple containing (pair, profit_sum)
"""
best_pair = Trade.query.with_entities(
Trade.pair, func.sum(Trade.close_profit).label('profit_sum')
).filter(Trade.is_open.is_(False) & (Trade.close_date >= start_date)) \
.group_by(Trade.pair) \
.order_by(desc('profit_sum')).first()
best_pair = Trade.session.execute(
select(
Trade.pair,
func.sum(Trade.close_profit).label('profit_sum')
).filter(Trade.is_open.is_(False) & (Trade.close_date >= start_date))
.group_by(Trade.pair)
.order_by(desc('profit_sum'))
).first()
return best_pair
@staticmethod
@@ -1582,12 +1605,13 @@ class Trade(ModelBase, LocalTrade):
NOTE: Not supported in Backtesting.
:returns: Tuple containing (pair, profit_sum)
"""
trading_volume = Order.query.with_entities(
func.sum(Order.cost).label('volume')
).filter(
Order.order_filled_date >= start_date,
Order.status == 'closed'
).scalar()
trading_volume = Trade.session.execute(
select(
func.sum(Order.cost).label('volume')
).filter(
Order.order_filled_date >= start_date,
Order.status == 'closed'
)).scalar_one()
return trading_volume
@staticmethod

View File

@@ -5,7 +5,7 @@ import logging
from abc import abstractmethod
from datetime import date, datetime, timedelta, timezone
from math import isnan
from typing import Any, Dict, Generator, List, Optional, Tuple, Union
from typing import Any, Dict, Generator, List, Optional, Sequence, Tuple, Union
import arrow
import psutil
@@ -13,6 +13,7 @@ from dateutil.relativedelta import relativedelta
from dateutil.tz import tzlocal
from numpy import NAN, inf, int64, mean
from pandas import DataFrame, NaT
from sqlalchemy import func, select
from freqtrade import __version__
from freqtrade.configuration.timerange import TimeRange
@@ -122,7 +123,8 @@ class RPC:
if config['max_open_trades'] != float('inf') else -1),
'minimal_roi': config['minimal_roi'].copy() if 'minimal_roi' in config else {},
'stoploss': config.get('stoploss'),
'stoploss_on_exchange': config.get('stoploss_on_exchange', False),
'stoploss_on_exchange': config.get('order_types',
{}).get('stoploss_on_exchange', False),
'trailing_stop': config.get('trailing_stop'),
'trailing_stop_positive': config.get('trailing_stop_positive'),
'trailing_stop_positive_offset': config.get('trailing_stop_positive_offset'),
@@ -158,7 +160,7 @@ class RPC:
"""
# Fetch open trades
if trade_ids:
trades: List[Trade] = Trade.get_trades(trade_filter=Trade.id.in_(trade_ids)).all()
trades: Sequence[Trade] = Trade.get_trades(trade_filter=Trade.id.in_(trade_ids)).all()
else:
trades = Trade.get_open_trades()
@@ -339,11 +341,13 @@ class RPC:
for day in range(0, timescale):
profitday = start_date - time_offset(day)
# Only query for necessary columns for performance reasons.
trades = Trade.query.session.query(Trade.close_profit_abs).filter(
Trade.is_open.is_(False),
Trade.close_date >= profitday,
Trade.close_date < (profitday + time_offset(1))
).order_by(Trade.close_date).all()
trades = Trade.session.execute(
select(Trade.close_profit_abs)
.filter(Trade.is_open.is_(False),
Trade.close_date >= profitday,
Trade.close_date < (profitday + time_offset(1)))
.order_by(Trade.close_date)
).all()
curdayprofit = sum(
trade.close_profit_abs for trade in trades if trade.close_profit_abs is not None)
@@ -381,14 +385,19 @@ class RPC:
""" Returns the X last trades """
order_by: Any = Trade.id if order_by_id else Trade.close_date.desc()
if limit:
trades = Trade.get_trades([Trade.is_open.is_(False)]).order_by(
order_by).limit(limit).offset(offset)
trades = Trade.session.scalars(
Trade.get_trades_query([Trade.is_open.is_(False)])
.order_by(order_by)
.limit(limit)
.offset(offset))
else:
trades = Trade.get_trades([Trade.is_open.is_(False)]).order_by(
Trade.close_date.desc())
trades = Trade.session.scalars(
Trade.get_trades_query([Trade.is_open.is_(False)])
.order_by(Trade.close_date.desc()))
output = [trade.to_json() for trade in trades]
total_trades = Trade.get_trades([Trade.is_open.is_(False)]).count()
total_trades = Trade.session.scalar(
select(func.count(Trade.id)).filter(Trade.is_open.is_(False)))
return {
"trades": output,
@@ -436,8 +445,8 @@ class RPC:
""" Returns cumulative profit statistics """
trade_filter = ((Trade.is_open.is_(False) & (Trade.close_date >= start_date)) |
Trade.is_open.is_(True))
trades: List[Trade] = Trade.get_trades(
trade_filter, include_orders=False).order_by(Trade.id).all()
trades: Sequence[Trade] = Trade.session.scalars(Trade.get_trades_query(
trade_filter, include_orders=False).order_by(Trade.id)).all()
profit_all_coin = []
profit_all_ratio = []
@@ -946,12 +955,12 @@ class RPC:
def _rpc_delete_lock(self, lockid: Optional[int] = None,
pair: Optional[str] = None) -> Dict[str, Any]:
""" Delete specific lock(s) """
locks = []
locks: Sequence[PairLock] = []
if pair:
locks = PairLocks.get_pair_locks(pair)
if lockid:
locks = PairLock.query.filter(PairLock.id == lockid).all()
locks = PairLock.session.scalars(select(PairLock).filter(PairLock.id == lockid)).all()
for lock in locks:
lock.active = False

View File

@@ -1,6 +1,7 @@
import logging
from packaging import version
from sqlalchemy import select
from freqtrade.constants import Config
from freqtrade.enums.tradingmode import TradingMode
@@ -44,7 +45,7 @@ def _migrate_binance_futures_db(config: Config):
# Should symbol be migrated too?
# order.symbol = new_pair
Trade.commit()
pls = PairLock.query.filter(PairLock.pair.notlike('%:%'))
pls = PairLock.session.scalars(select(PairLock).filter(PairLock.pair.notlike('%:%'))).all()
for pl in pls:
pl.pair = f"{pl.pair}:{config['stake_currency']}"
# print(pls)