Merge branch 'freqtrade:develop' into strategy_utils
This commit is contained in:
commit
763f4f4a3e
@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
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.configuration.config_setup import setup_utils_configuration
|
||||||
from freqtrade.enums import RunMode
|
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)
|
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
||||||
|
|
||||||
init_db(config['db_url'])
|
init_db(config['db_url'])
|
||||||
session_target = Trade._session
|
session_target = Trade.session
|
||||||
init_db(config['db_url_from'])
|
init_db(config['db_url_from'])
|
||||||
logger.info("Starting db migration.")
|
logger.info("Starting db migration.")
|
||||||
|
|
||||||
@ -36,16 +36,16 @@ def start_convert_db(args: Dict[str, Any]) -> None:
|
|||||||
|
|
||||||
session_target.commit()
|
session_target.commit()
|
||||||
|
|
||||||
for pairlock in PairLock.query:
|
for pairlock in PairLock.get_all_locks():
|
||||||
pairlock_count += 1
|
pairlock_count += 1
|
||||||
make_transient(pairlock)
|
make_transient(pairlock)
|
||||||
session_target.add(pairlock)
|
session_target.add(pairlock)
|
||||||
session_target.commit()
|
session_target.commit()
|
||||||
|
|
||||||
# Update sequences
|
# Update sequences
|
||||||
max_trade_id = session_target.query(func.max(Trade.id)).scalar()
|
max_trade_id = session_target.scalar(select(func.max(Trade.id)))
|
||||||
max_order_id = session_target.query(func.max(Order.id)).scalar()
|
max_order_id = session_target.scalar(select(func.max(Order.id)))
|
||||||
max_pairlock_id = session_target.query(func.max(PairLock.id)).scalar()
|
max_pairlock_id = session_target.scalar(select(func.max(PairLock.id)))
|
||||||
|
|
||||||
set_sequence_ids(session_target.get_bind(),
|
set_sequence_ids(session_target.get_bind(),
|
||||||
trade_id=max_trade_id,
|
trade_id=max_trade_id,
|
||||||
|
@ -373,7 +373,7 @@ def load_trades_from_db(db_url: str, strategy: Optional[str] = None) -> pd.DataF
|
|||||||
filters = []
|
filters = []
|
||||||
if strategy:
|
if strategy:
|
||||||
filters.append(Trade.strategy == 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
|
return trades
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ class FreqaiDataKitchen:
|
|||||||
(drop_index == 0) & (drop_index_labels == 0)
|
(drop_index == 0) & (drop_index_labels == 0)
|
||||||
]
|
]
|
||||||
logger.info(
|
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)}."
|
f" due to NaNs in populated dataset {len(unfiltered_df)}."
|
||||||
)
|
)
|
||||||
if (1 - len(filtered_df) / len(unfiltered_df)) > 0.1 and self.live:
|
if (1 - len(filtered_df) / len(unfiltered_df)) > 0.1 and self.live:
|
||||||
@ -675,7 +675,7 @@ class FreqaiDataKitchen:
|
|||||||
]
|
]
|
||||||
|
|
||||||
logger.info(
|
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."
|
f" test points from {len(y_pred)} total points."
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -949,7 +949,7 @@ class FreqaiDataKitchen:
|
|||||||
|
|
||||||
if (len(do_predict) - do_predict.sum()) > 0:
|
if (len(do_predict) - do_predict.sum()) > 0:
|
||||||
logger.info(
|
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."
|
"being too far from training data."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -104,6 +104,7 @@ class IFreqaiModel(ABC):
|
|||||||
self.data_provider: Optional[DataProvider] = None
|
self.data_provider: Optional[DataProvider] = None
|
||||||
self.max_system_threads = max(int(psutil.cpu_count() * 2 - 2), 1)
|
self.max_system_threads = max(int(psutil.cpu_count() * 2 - 2), 1)
|
||||||
self.can_short = True # overridden in start() with strategy.can_short
|
self.can_short = True # overridden in start() with strategy.can_short
|
||||||
|
self.model: Any = None
|
||||||
|
|
||||||
record_params(config, self.full_path)
|
record_params(config, self.full_path)
|
||||||
|
|
||||||
@ -338,13 +339,14 @@ class IFreqaiModel(ABC):
|
|||||||
except Exception as msg:
|
except Exception as msg:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Training {pair} raised exception {msg.__class__.__name__}. "
|
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(
|
self.dd.pair_dict[pair]["trained_timestamp"] = int(
|
||||||
tr_train.stopts)
|
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)
|
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.')
|
logger.info('Saving backtest model to disk.')
|
||||||
self.dd.save_data(self.model, pair, dk)
|
self.dd.save_data(self.model, pair, dk)
|
||||||
else:
|
else:
|
||||||
|
@ -819,7 +819,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
|
|
||||||
trade.orders.append(order_obj)
|
trade.orders.append(order_obj)
|
||||||
trade.recalc_trade_from_orders()
|
trade.recalc_trade_from_orders()
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
# Updating wallets
|
# Updating wallets
|
||||||
|
@ -54,12 +54,9 @@ def init_db(db_url: str) -> None:
|
|||||||
# https://docs.sqlalchemy.org/en/13/orm/contextual.html#thread-local-scope
|
# https://docs.sqlalchemy.org/en/13/orm/contextual.html#thread-local-scope
|
||||||
# Scoped sessions proxy requests to the appropriate thread-local session.
|
# Scoped sessions proxy requests to the appropriate thread-local session.
|
||||||
# We should use the scoped_session object - not a seperately initialized version
|
# We should use the scoped_session object - not a seperately initialized version
|
||||||
Trade._session = scoped_session(sessionmaker(bind=engine, autoflush=False))
|
Trade.session = scoped_session(sessionmaker(bind=engine, autoflush=False))
|
||||||
Order._session = Trade._session
|
Order.session = Trade.session
|
||||||
PairLock._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()
|
|
||||||
|
|
||||||
previous_tables = inspect(engine).get_table_names()
|
previous_tables = inspect(engine).get_table_names()
|
||||||
ModelBase.metadata.create_all(engine)
|
ModelBase.metadata.create_all(engine)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Any, ClassVar, Dict, Optional
|
from typing import Any, ClassVar, Dict, Optional
|
||||||
|
|
||||||
from sqlalchemy import String, or_
|
from sqlalchemy import ScalarResult, String, or_, select
|
||||||
from sqlalchemy.orm import Mapped, Query, QueryPropertyDescriptor, mapped_column
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT
|
from freqtrade.constants import DATETIME_PRINT_FORMAT
|
||||||
from freqtrade.persistence.base import ModelBase, SessionType
|
from freqtrade.persistence.base import ModelBase, SessionType
|
||||||
@ -13,8 +13,7 @@ class PairLock(ModelBase):
|
|||||||
Pair Locks database model.
|
Pair Locks database model.
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'pairlocks'
|
__tablename__ = 'pairlocks'
|
||||||
query: ClassVar[QueryPropertyDescriptor]
|
session: ClassVar[SessionType]
|
||||||
_session: ClassVar[SessionType]
|
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
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})')
|
f'lock_end_time={lock_end_time}, reason={self.reason}, active={self.active})')
|
||||||
|
|
||||||
@staticmethod
|
@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
|
Get all currently active locks for this pair
|
||||||
:param pair: Pair to check for. Returns all current locks if pair is empty
|
:param pair: Pair to check for. Returns all current locks if pair is empty
|
||||||
@ -53,9 +53,11 @@ class PairLock(ModelBase):
|
|||||||
else:
|
else:
|
||||||
filters.append(PairLock.side == '*')
|
filters.append(PairLock.side == '*')
|
||||||
|
|
||||||
return PairLock.query.filter(
|
return PairLock.session.scalars(select(PairLock).filter(*filters))
|
||||||
*filters
|
|
||||||
)
|
@staticmethod
|
||||||
|
def get_all_locks() -> ScalarResult['PairLock']:
|
||||||
|
return PairLock.session.scalars(select(PairLock))
|
||||||
|
|
||||||
def to_json(self) -> Dict[str, Any]:
|
def to_json(self) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timezone
|
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.exchange import timeframe_to_next_date
|
||||||
from freqtrade.persistence.models import PairLock
|
from freqtrade.persistence.models import PairLock
|
||||||
@ -51,15 +53,15 @@ class PairLocks():
|
|||||||
active=True
|
active=True
|
||||||
)
|
)
|
||||||
if PairLocks.use_db:
|
if PairLocks.use_db:
|
||||||
PairLock.query.session.add(lock)
|
PairLock.session.add(lock)
|
||||||
PairLock.query.session.commit()
|
PairLock.session.commit()
|
||||||
else:
|
else:
|
||||||
PairLocks.locks.append(lock)
|
PairLocks.locks.append(lock)
|
||||||
return lock
|
return lock
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_pair_locks(
|
def get_pair_locks(pair: Optional[str], now: Optional[datetime] = None,
|
||||||
pair: Optional[str], now: Optional[datetime] = None, side: str = '*') -> List[PairLock]:
|
side: str = '*') -> Sequence[PairLock]:
|
||||||
"""
|
"""
|
||||||
Get all currently active locks for this pair
|
Get all currently active locks for this pair
|
||||||
:param pair: Pair to check for. Returns all current locks if pair is empty
|
:param pair: Pair to check for. Returns all current locks if pair is empty
|
||||||
@ -106,7 +108,7 @@ class PairLocks():
|
|||||||
for lock in locks:
|
for lock in locks:
|
||||||
lock.active = False
|
lock.active = False
|
||||||
if PairLocks.use_db:
|
if PairLocks.use_db:
|
||||||
PairLock.query.session.commit()
|
PairLock.session.commit()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unlock_reason(reason: str, now: Optional[datetime] = None) -> None:
|
def unlock_reason(reason: str, now: Optional[datetime] = None) -> None:
|
||||||
@ -126,11 +128,11 @@ class PairLocks():
|
|||||||
PairLock.active.is_(True),
|
PairLock.active.is_(True),
|
||||||
PairLock.reason == reason
|
PairLock.reason == reason
|
||||||
]
|
]
|
||||||
locks = PairLock.query.filter(*filters)
|
locks = PairLock.session.scalars(select(PairLock).filter(*filters)).all()
|
||||||
for lock in locks:
|
for lock in locks:
|
||||||
logger.info(f"Releasing lock for {lock.pair} with reason '{reason}'.")
|
logger.info(f"Releasing lock for {lock.pair} with reason '{reason}'.")
|
||||||
lock.active = False
|
lock.active = False
|
||||||
PairLock.query.session.commit()
|
PairLock.session.commit()
|
||||||
else:
|
else:
|
||||||
# used in backtesting mode; don't show log messages for speed
|
# used in backtesting mode; don't show log messages for speed
|
||||||
locksb = PairLocks.get_pair_locks(None)
|
locksb = PairLocks.get_pair_locks(None)
|
||||||
@ -165,11 +167,11 @@ class PairLocks():
|
|||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_locks() -> List[PairLock]:
|
def get_all_locks() -> Sequence[PairLock]:
|
||||||
"""
|
"""
|
||||||
Return all locks, also locks with expired end date
|
Return all locks, also locks with expired end date
|
||||||
"""
|
"""
|
||||||
if PairLocks.use_db:
|
if PairLocks.use_db:
|
||||||
return PairLock.query.all()
|
return PairLock.get_all_locks().all()
|
||||||
else:
|
else:
|
||||||
return PairLocks.locks
|
return PairLocks.locks
|
||||||
|
@ -5,11 +5,11 @@ import logging
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from math import isclose
|
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 import (Enum, Float, ForeignKey, Integer, ScalarResult, Select, String,
|
||||||
from sqlalchemy.orm import (Mapped, Query, QueryPropertyDescriptor, lazyload, mapped_column,
|
UniqueConstraint, desc, func, select)
|
||||||
relationship)
|
from sqlalchemy.orm import Mapped, lazyload, mapped_column, relationship
|
||||||
|
|
||||||
from freqtrade.constants import (DATETIME_PRINT_FORMAT, MATH_CLOSE_PREC, NON_OPEN_EXCHANGE_STATES,
|
from freqtrade.constants import (DATETIME_PRINT_FORMAT, MATH_CLOSE_PREC, NON_OPEN_EXCHANGE_STATES,
|
||||||
BuySell, LongShort)
|
BuySell, LongShort)
|
||||||
@ -36,8 +36,7 @@ class Order(ModelBase):
|
|||||||
Mirrors CCXT Order structure
|
Mirrors CCXT Order structure
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'orders'
|
__tablename__ = 'orders'
|
||||||
query: ClassVar[QueryPropertyDescriptor]
|
session: ClassVar[SessionType]
|
||||||
_session: ClassVar[SessionType]
|
|
||||||
|
|
||||||
# Uniqueness should be ensured over pair, order_id
|
# Uniqueness should be ensured over pair, order_id
|
||||||
# its likely that order_id is unique per Pair on some exchanges.
|
# its likely that order_id is unique per Pair on some exchanges.
|
||||||
@ -263,12 +262,12 @@ class Order(ModelBase):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_open_orders() -> List['Order']:
|
def get_open_orders() -> Sequence['Order']:
|
||||||
"""
|
"""
|
||||||
Retrieve open orders from the database
|
Retrieve open orders from the database
|
||||||
:return: List of open orders
|
: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
|
@staticmethod
|
||||||
def order_by_id(order_id: str) -> Optional['Order']:
|
def order_by_id(order_id: str) -> Optional['Order']:
|
||||||
@ -276,7 +275,7 @@ class Order(ModelBase):
|
|||||||
Retrieve order based on order_id
|
Retrieve order based on order_id
|
||||||
:return: Order or None
|
: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():
|
class LocalTrade():
|
||||||
@ -1153,7 +1152,9 @@ class LocalTrade():
|
|||||||
get open trade count
|
get open trade count
|
||||||
"""
|
"""
|
||||||
if Trade.use_db:
|
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:
|
else:
|
||||||
return LocalTrade.bt_open_open_trade_count
|
return LocalTrade.bt_open_open_trade_count
|
||||||
|
|
||||||
@ -1186,8 +1187,7 @@ class Trade(ModelBase, LocalTrade):
|
|||||||
Note: Fields must be aligned with LocalTrade class
|
Note: Fields must be aligned with LocalTrade class
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'trades'
|
__tablename__ = 'trades'
|
||||||
query: ClassVar[QueryPropertyDescriptor]
|
session: ClassVar[SessionType]
|
||||||
_session: ClassVar[SessionType]
|
|
||||||
|
|
||||||
use_db: bool = True
|
use_db: bool = True
|
||||||
|
|
||||||
@ -1287,18 +1287,18 @@ class Trade(ModelBase, LocalTrade):
|
|||||||
def delete(self) -> None:
|
def delete(self) -> None:
|
||||||
|
|
||||||
for order in self.orders:
|
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()
|
Trade.commit()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def commit():
|
def commit():
|
||||||
Trade.query.session.commit()
|
Trade.session.commit()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def rollback():
|
def rollback():
|
||||||
Trade.query.session.rollback()
|
Trade.session.rollback()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_trades_proxy(*, pair: Optional[str] = None, is_open: Optional[bool] = None,
|
def get_trades_proxy(*, pair: Optional[str] = None, is_open: Optional[bool] = None,
|
||||||
@ -1332,7 +1332,7 @@ class Trade(ModelBase, LocalTrade):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@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.
|
Helper function to query Trades using filters.
|
||||||
NOTE: Not supported in Backtesting.
|
NOTE: Not supported in Backtesting.
|
||||||
@ -1347,22 +1347,35 @@ class Trade(ModelBase, LocalTrade):
|
|||||||
if trade_filter is not None:
|
if trade_filter is not None:
|
||||||
if not isinstance(trade_filter, list):
|
if not isinstance(trade_filter, list):
|
||||||
trade_filter = [trade_filter]
|
trade_filter = [trade_filter]
|
||||||
this_query = Trade.query.filter(*trade_filter)
|
this_query = select(Trade).filter(*trade_filter)
|
||||||
else:
|
else:
|
||||||
this_query = Trade.query
|
this_query = select(Trade)
|
||||||
if not include_orders:
|
if not include_orders:
|
||||||
# Don't load order relations
|
# Don't load order relations
|
||||||
# Consider using noload or raiseload instead of lazyload
|
# Consider using noload or raiseload instead of lazyload
|
||||||
this_query = this_query.options(lazyload(Trade.orders))
|
this_query = this_query.options(lazyload(Trade.orders))
|
||||||
return this_query
|
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
|
@staticmethod
|
||||||
def get_open_order_trades() -> List['Trade']:
|
def get_open_order_trades() -> List['Trade']:
|
||||||
"""
|
"""
|
||||||
Returns all open trades
|
Returns all open trades
|
||||||
NOTE: Not supported in Backtesting.
|
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
|
@staticmethod
|
||||||
def get_open_trades_without_assigned_fees():
|
def get_open_trades_without_assigned_fees():
|
||||||
@ -1392,11 +1405,12 @@ class Trade(ModelBase, LocalTrade):
|
|||||||
Retrieves total realized profit
|
Retrieves total realized profit
|
||||||
"""
|
"""
|
||||||
if Trade.use_db:
|
if Trade.use_db:
|
||||||
total_profit = Trade.query.with_entities(
|
total_profit: float = Trade.session.execute(
|
||||||
func.sum(Trade.close_profit_abs)).filter(Trade.is_open.is_(False)).scalar()
|
select(func.sum(Trade.close_profit_abs)).filter(Trade.is_open.is_(False))
|
||||||
|
).scalar_one()
|
||||||
else:
|
else:
|
||||||
total_profit = sum(
|
total_profit = sum(t.close_profit_abs # type: ignore
|
||||||
t.close_profit_abs for t in LocalTrade.get_trades_proxy(is_open=False))
|
for t in LocalTrade.get_trades_proxy(is_open=False))
|
||||||
return total_profit or 0
|
return total_profit or 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -1406,8 +1420,9 @@ class Trade(ModelBase, LocalTrade):
|
|||||||
in stake currency
|
in stake currency
|
||||||
"""
|
"""
|
||||||
if Trade.use_db:
|
if Trade.use_db:
|
||||||
total_open_stake_amount = Trade.query.with_entities(
|
total_open_stake_amount = Trade.session.scalar(
|
||||||
func.sum(Trade.stake_amount)).filter(Trade.is_open.is_(True)).scalar()
|
select(func.sum(Trade.stake_amount)).filter(Trade.is_open.is_(True))
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
total_open_stake_amount = sum(
|
total_open_stake_amount = sum(
|
||||||
t.stake_amount for t in LocalTrade.get_trades_proxy(is_open=True))
|
t.stake_amount for t in LocalTrade.get_trades_proxy(is_open=True))
|
||||||
@ -1423,15 +1438,18 @@ class Trade(ModelBase, LocalTrade):
|
|||||||
if minutes:
|
if minutes:
|
||||||
start_date = datetime.now(timezone.utc) - timedelta(minutes=minutes)
|
start_date = datetime.now(timezone.utc) - timedelta(minutes=minutes)
|
||||||
filters.append(Trade.close_date >= start_date)
|
filters.append(Trade.close_date >= start_date)
|
||||||
pair_rates = Trade.query.with_entities(
|
|
||||||
|
pair_rates = Trade.session.execute(
|
||||||
|
select(
|
||||||
Trade.pair,
|
Trade.pair,
|
||||||
func.sum(Trade.close_profit).label('profit_sum'),
|
func.sum(Trade.close_profit).label('profit_sum'),
|
||||||
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
|
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
|
||||||
func.count(Trade.pair).label('count')
|
func.count(Trade.pair).label('count')
|
||||||
).filter(*filters)\
|
).filter(*filters)
|
||||||
.group_by(Trade.pair) \
|
.group_by(Trade.pair)
|
||||||
.order_by(desc('profit_sum_abs')) \
|
.order_by(desc('profit_sum_abs'))
|
||||||
.all()
|
).all()
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'pair': pair,
|
'pair': pair,
|
||||||
@ -1456,15 +1474,16 @@ class Trade(ModelBase, LocalTrade):
|
|||||||
if (pair is not None):
|
if (pair is not None):
|
||||||
filters.append(Trade.pair == pair)
|
filters.append(Trade.pair == pair)
|
||||||
|
|
||||||
enter_tag_perf = Trade.query.with_entities(
|
enter_tag_perf = Trade.session.execute(
|
||||||
|
select(
|
||||||
Trade.enter_tag,
|
Trade.enter_tag,
|
||||||
func.sum(Trade.close_profit).label('profit_sum'),
|
func.sum(Trade.close_profit).label('profit_sum'),
|
||||||
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
|
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
|
||||||
func.count(Trade.pair).label('count')
|
func.count(Trade.pair).label('count')
|
||||||
).filter(*filters)\
|
).filter(*filters)
|
||||||
.group_by(Trade.enter_tag) \
|
.group_by(Trade.enter_tag)
|
||||||
.order_by(desc('profit_sum_abs')) \
|
.order_by(desc('profit_sum_abs'))
|
||||||
.all()
|
).all()
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -1488,16 +1507,16 @@ class Trade(ModelBase, LocalTrade):
|
|||||||
filters: List = [Trade.is_open.is_(False)]
|
filters: List = [Trade.is_open.is_(False)]
|
||||||
if (pair is not None):
|
if (pair is not None):
|
||||||
filters.append(Trade.pair == pair)
|
filters.append(Trade.pair == pair)
|
||||||
|
sell_tag_perf = Trade.session.execute(
|
||||||
sell_tag_perf = Trade.query.with_entities(
|
select(
|
||||||
Trade.exit_reason,
|
Trade.exit_reason,
|
||||||
func.sum(Trade.close_profit).label('profit_sum'),
|
func.sum(Trade.close_profit).label('profit_sum'),
|
||||||
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
|
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
|
||||||
func.count(Trade.pair).label('count')
|
func.count(Trade.pair).label('count')
|
||||||
).filter(*filters)\
|
).filter(*filters)
|
||||||
.group_by(Trade.exit_reason) \
|
.group_by(Trade.exit_reason)
|
||||||
.order_by(desc('profit_sum_abs')) \
|
.order_by(desc('profit_sum_abs'))
|
||||||
.all()
|
).all()
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -1521,18 +1540,18 @@ class Trade(ModelBase, LocalTrade):
|
|||||||
filters: List = [Trade.is_open.is_(False)]
|
filters: List = [Trade.is_open.is_(False)]
|
||||||
if (pair is not None):
|
if (pair is not None):
|
||||||
filters.append(Trade.pair == pair)
|
filters.append(Trade.pair == pair)
|
||||||
|
mix_tag_perf = Trade.session.execute(
|
||||||
mix_tag_perf = Trade.query.with_entities(
|
select(
|
||||||
Trade.id,
|
Trade.id,
|
||||||
Trade.enter_tag,
|
Trade.enter_tag,
|
||||||
Trade.exit_reason,
|
Trade.exit_reason,
|
||||||
func.sum(Trade.close_profit).label('profit_sum'),
|
func.sum(Trade.close_profit).label('profit_sum'),
|
||||||
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
|
func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
|
||||||
func.count(Trade.pair).label('count')
|
func.count(Trade.pair).label('count')
|
||||||
).filter(*filters)\
|
).filter(*filters)
|
||||||
.group_by(Trade.id) \
|
.group_by(Trade.id)
|
||||||
.order_by(desc('profit_sum_abs')) \
|
.order_by(desc('profit_sum_abs'))
|
||||||
.all()
|
).all()
|
||||||
|
|
||||||
return_list: List[Dict] = []
|
return_list: List[Dict] = []
|
||||||
for id, enter_tag, exit_reason, profit, profit_abs, count in mix_tag_perf:
|
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.
|
NOTE: Not supported in Backtesting.
|
||||||
:returns: Tuple containing (pair, profit_sum)
|
:returns: Tuple containing (pair, profit_sum)
|
||||||
"""
|
"""
|
||||||
best_pair = Trade.query.with_entities(
|
best_pair = Trade.session.execute(
|
||||||
Trade.pair, func.sum(Trade.close_profit).label('profit_sum')
|
select(
|
||||||
).filter(Trade.is_open.is_(False) & (Trade.close_date >= start_date)) \
|
Trade.pair,
|
||||||
.group_by(Trade.pair) \
|
func.sum(Trade.close_profit).label('profit_sum')
|
||||||
.order_by(desc('profit_sum')).first()
|
).filter(Trade.is_open.is_(False) & (Trade.close_date >= start_date))
|
||||||
|
.group_by(Trade.pair)
|
||||||
|
.order_by(desc('profit_sum'))
|
||||||
|
).first()
|
||||||
|
|
||||||
return best_pair
|
return best_pair
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -1582,12 +1605,13 @@ class Trade(ModelBase, LocalTrade):
|
|||||||
NOTE: Not supported in Backtesting.
|
NOTE: Not supported in Backtesting.
|
||||||
:returns: Tuple containing (pair, profit_sum)
|
:returns: Tuple containing (pair, profit_sum)
|
||||||
"""
|
"""
|
||||||
trading_volume = Order.query.with_entities(
|
trading_volume = Trade.session.execute(
|
||||||
|
select(
|
||||||
func.sum(Order.cost).label('volume')
|
func.sum(Order.cost).label('volume')
|
||||||
).filter(
|
).filter(
|
||||||
Order.order_filled_date >= start_date,
|
Order.order_filled_date >= start_date,
|
||||||
Order.status == 'closed'
|
Order.status == 'closed'
|
||||||
).scalar()
|
)).scalar_one()
|
||||||
return trading_volume
|
return trading_volume
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -5,7 +5,7 @@ import logging
|
|||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from datetime import date, datetime, timedelta, timezone
|
from datetime import date, datetime, timedelta, timezone
|
||||||
from math import isnan
|
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 arrow
|
||||||
import psutil
|
import psutil
|
||||||
@ -13,6 +13,7 @@ from dateutil.relativedelta import relativedelta
|
|||||||
from dateutil.tz import tzlocal
|
from dateutil.tz import tzlocal
|
||||||
from numpy import NAN, inf, int64, mean
|
from numpy import NAN, inf, int64, mean
|
||||||
from pandas import DataFrame, NaT
|
from pandas import DataFrame, NaT
|
||||||
|
from sqlalchemy import func, select
|
||||||
|
|
||||||
from freqtrade import __version__
|
from freqtrade import __version__
|
||||||
from freqtrade.configuration.timerange import TimeRange
|
from freqtrade.configuration.timerange import TimeRange
|
||||||
@ -122,7 +123,8 @@ class RPC:
|
|||||||
if config['max_open_trades'] != float('inf') else -1),
|
if config['max_open_trades'] != float('inf') else -1),
|
||||||
'minimal_roi': config['minimal_roi'].copy() if 'minimal_roi' in config else {},
|
'minimal_roi': config['minimal_roi'].copy() if 'minimal_roi' in config else {},
|
||||||
'stoploss': config.get('stoploss'),
|
'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': config.get('trailing_stop'),
|
||||||
'trailing_stop_positive': config.get('trailing_stop_positive'),
|
'trailing_stop_positive': config.get('trailing_stop_positive'),
|
||||||
'trailing_stop_positive_offset': config.get('trailing_stop_positive_offset'),
|
'trailing_stop_positive_offset': config.get('trailing_stop_positive_offset'),
|
||||||
@ -158,7 +160,7 @@ class RPC:
|
|||||||
"""
|
"""
|
||||||
# Fetch open trades
|
# Fetch open trades
|
||||||
if trade_ids:
|
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:
|
else:
|
||||||
trades = Trade.get_open_trades()
|
trades = Trade.get_open_trades()
|
||||||
|
|
||||||
@ -339,11 +341,13 @@ class RPC:
|
|||||||
for day in range(0, timescale):
|
for day in range(0, timescale):
|
||||||
profitday = start_date - time_offset(day)
|
profitday = start_date - time_offset(day)
|
||||||
# Only query for necessary columns for performance reasons.
|
# Only query for necessary columns for performance reasons.
|
||||||
trades = Trade.query.session.query(Trade.close_profit_abs).filter(
|
trades = Trade.session.execute(
|
||||||
Trade.is_open.is_(False),
|
select(Trade.close_profit_abs)
|
||||||
|
.filter(Trade.is_open.is_(False),
|
||||||
Trade.close_date >= profitday,
|
Trade.close_date >= profitday,
|
||||||
Trade.close_date < (profitday + time_offset(1))
|
Trade.close_date < (profitday + time_offset(1)))
|
||||||
).order_by(Trade.close_date).all()
|
.order_by(Trade.close_date)
|
||||||
|
).all()
|
||||||
|
|
||||||
curdayprofit = sum(
|
curdayprofit = sum(
|
||||||
trade.close_profit_abs for trade in trades if trade.close_profit_abs is not None)
|
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 """
|
""" Returns the X last trades """
|
||||||
order_by: Any = Trade.id if order_by_id else Trade.close_date.desc()
|
order_by: Any = Trade.id if order_by_id else Trade.close_date.desc()
|
||||||
if limit:
|
if limit:
|
||||||
trades = Trade.get_trades([Trade.is_open.is_(False)]).order_by(
|
trades = Trade.session.scalars(
|
||||||
order_by).limit(limit).offset(offset)
|
Trade.get_trades_query([Trade.is_open.is_(False)])
|
||||||
|
.order_by(order_by)
|
||||||
|
.limit(limit)
|
||||||
|
.offset(offset))
|
||||||
else:
|
else:
|
||||||
trades = Trade.get_trades([Trade.is_open.is_(False)]).order_by(
|
trades = Trade.session.scalars(
|
||||||
Trade.close_date.desc())
|
Trade.get_trades_query([Trade.is_open.is_(False)])
|
||||||
|
.order_by(Trade.close_date.desc()))
|
||||||
|
|
||||||
output = [trade.to_json() for trade in trades]
|
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 {
|
return {
|
||||||
"trades": output,
|
"trades": output,
|
||||||
@ -436,8 +445,8 @@ class RPC:
|
|||||||
""" Returns cumulative profit statistics """
|
""" Returns cumulative profit statistics """
|
||||||
trade_filter = ((Trade.is_open.is_(False) & (Trade.close_date >= start_date)) |
|
trade_filter = ((Trade.is_open.is_(False) & (Trade.close_date >= start_date)) |
|
||||||
Trade.is_open.is_(True))
|
Trade.is_open.is_(True))
|
||||||
trades: List[Trade] = Trade.get_trades(
|
trades: Sequence[Trade] = Trade.session.scalars(Trade.get_trades_query(
|
||||||
trade_filter, include_orders=False).order_by(Trade.id).all()
|
trade_filter, include_orders=False).order_by(Trade.id)).all()
|
||||||
|
|
||||||
profit_all_coin = []
|
profit_all_coin = []
|
||||||
profit_all_ratio = []
|
profit_all_ratio = []
|
||||||
@ -946,12 +955,12 @@ class RPC:
|
|||||||
def _rpc_delete_lock(self, lockid: Optional[int] = None,
|
def _rpc_delete_lock(self, lockid: Optional[int] = None,
|
||||||
pair: Optional[str] = None) -> Dict[str, Any]:
|
pair: Optional[str] = None) -> Dict[str, Any]:
|
||||||
""" Delete specific lock(s) """
|
""" Delete specific lock(s) """
|
||||||
locks = []
|
locks: Sequence[PairLock] = []
|
||||||
|
|
||||||
if pair:
|
if pair:
|
||||||
locks = PairLocks.get_pair_locks(pair)
|
locks = PairLocks.get_pair_locks(pair)
|
||||||
if lockid:
|
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:
|
for lock in locks:
|
||||||
lock.active = False
|
lock.active = False
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
from sqlalchemy import select
|
||||||
|
|
||||||
from freqtrade.constants import Config
|
from freqtrade.constants import Config
|
||||||
from freqtrade.enums.tradingmode import TradingMode
|
from freqtrade.enums.tradingmode import TradingMode
|
||||||
@ -44,7 +45,7 @@ def _migrate_binance_futures_db(config: Config):
|
|||||||
# Should symbol be migrated too?
|
# Should symbol be migrated too?
|
||||||
# order.symbol = new_pair
|
# order.symbol = new_pair
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
pls = PairLock.query.filter(PairLock.pair.notlike('%:%'))
|
pls = PairLock.session.scalars(select(PairLock).filter(PairLock.pair.notlike('%:%'))).all()
|
||||||
for pl in pls:
|
for pl in pls:
|
||||||
pl.pair = f"{pl.pair}:{config['stake_currency']}"
|
pl.pair = f"{pl.pair}:{config['stake_currency']}"
|
||||||
# print(pls)
|
# print(pls)
|
||||||
|
@ -299,7 +299,7 @@ def create_mock_trades(fee, is_short: Optional[bool] = False, use_db: bool = Tru
|
|||||||
"""
|
"""
|
||||||
def add_trade(trade):
|
def add_trade(trade):
|
||||||
if use_db:
|
if use_db:
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
else:
|
else:
|
||||||
LocalTrade.add_bt_trade(trade)
|
LocalTrade.add_bt_trade(trade)
|
||||||
is_short1 = is_short if is_short is not None else True
|
is_short1 = is_short if is_short is not None else True
|
||||||
@ -332,11 +332,11 @@ def create_mock_trades_with_leverage(fee, use_db: bool = True):
|
|||||||
Create some fake trades ...
|
Create some fake trades ...
|
||||||
"""
|
"""
|
||||||
if use_db:
|
if use_db:
|
||||||
Trade.query.session.rollback()
|
Trade.session.rollback()
|
||||||
|
|
||||||
def add_trade(trade):
|
def add_trade(trade):
|
||||||
if use_db:
|
if use_db:
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
else:
|
else:
|
||||||
LocalTrade.add_bt_trade(trade)
|
LocalTrade.add_bt_trade(trade)
|
||||||
|
|
||||||
@ -366,7 +366,7 @@ def create_mock_trades_with_leverage(fee, use_db: bool = True):
|
|||||||
add_trade(trade)
|
add_trade(trade)
|
||||||
|
|
||||||
if use_db:
|
if use_db:
|
||||||
Trade.query.session.flush()
|
Trade.session.flush()
|
||||||
|
|
||||||
|
|
||||||
def create_mock_trades_usdt(fee, is_short: Optional[bool] = False, use_db: bool = True):
|
def create_mock_trades_usdt(fee, is_short: Optional[bool] = False, use_db: bool = True):
|
||||||
@ -375,7 +375,7 @@ def create_mock_trades_usdt(fee, is_short: Optional[bool] = False, use_db: bool
|
|||||||
"""
|
"""
|
||||||
def add_trade(trade):
|
def add_trade(trade):
|
||||||
if use_db:
|
if use_db:
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
else:
|
else:
|
||||||
LocalTrade.add_bt_trade(trade)
|
LocalTrade.add_bt_trade(trade)
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ from pathlib import Path
|
|||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from sqlalchemy import create_engine, text
|
from sqlalchemy import create_engine, select, text
|
||||||
|
|
||||||
from freqtrade.constants import DEFAULT_DB_PROD_URL
|
from freqtrade.constants import DEFAULT_DB_PROD_URL
|
||||||
from freqtrade.enums import TradingMode
|
from freqtrade.enums import TradingMode
|
||||||
@ -21,8 +21,8 @@ spot, margin, futures = TradingMode.SPOT, TradingMode.MARGIN, TradingMode.FUTURE
|
|||||||
def test_init_create_session(default_conf):
|
def test_init_create_session(default_conf):
|
||||||
# Check if init create a session
|
# Check if init create a session
|
||||||
init_db(default_conf['db_url'])
|
init_db(default_conf['db_url'])
|
||||||
assert hasattr(Trade, '_session')
|
assert hasattr(Trade, 'session')
|
||||||
assert 'scoped_session' in type(Trade._session).__name__
|
assert 'scoped_session' in type(Trade.session).__name__
|
||||||
|
|
||||||
|
|
||||||
def test_init_custom_db_url(default_conf, tmpdir):
|
def test_init_custom_db_url(default_conf, tmpdir):
|
||||||
@ -34,7 +34,7 @@ def test_init_custom_db_url(default_conf, tmpdir):
|
|||||||
|
|
||||||
init_db(default_conf['db_url'])
|
init_db(default_conf['db_url'])
|
||||||
assert Path(filename).is_file()
|
assert Path(filename).is_file()
|
||||||
r = Trade._session.execute(text("PRAGMA journal_mode"))
|
r = Trade.session.execute(text("PRAGMA journal_mode"))
|
||||||
assert r.first() == ('wal',)
|
assert r.first() == ('wal',)
|
||||||
|
|
||||||
|
|
||||||
@ -235,8 +235,9 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
|
|||||||
# Run init to test migration
|
# Run init to test migration
|
||||||
init_db(default_conf['db_url'])
|
init_db(default_conf['db_url'])
|
||||||
|
|
||||||
assert len(Trade.query.filter(Trade.id == 1).all()) == 1
|
trades = Trade.session.scalars(select(Trade).filter(Trade.id == 1)).all()
|
||||||
trade = Trade.query.filter(Trade.id == 1).first()
|
assert len(trades) == 1
|
||||||
|
trade = trades[0]
|
||||||
assert trade.fee_open == fee.return_value
|
assert trade.fee_open == fee.return_value
|
||||||
assert trade.fee_close == fee.return_value
|
assert trade.fee_close == fee.return_value
|
||||||
assert trade.open_rate_requested is None
|
assert trade.open_rate_requested is None
|
||||||
@ -404,9 +405,9 @@ def test_migrate_pairlocks(mocker, default_conf, fee, caplog):
|
|||||||
|
|
||||||
init_db(default_conf['db_url'])
|
init_db(default_conf['db_url'])
|
||||||
|
|
||||||
assert len(PairLock.query.all()) == 2
|
assert len(PairLock.get_all_locks().all()) == 2
|
||||||
assert len(PairLock.query.filter(PairLock.pair == '*').all()) == 1
|
assert len(PairLock.session.scalars(select(PairLock).filter(PairLock.pair == '*')).all()) == 1
|
||||||
pairlocks = PairLock.query.filter(PairLock.pair == 'ETH/BTC').all()
|
pairlocks = PairLock.session.scalars(select(PairLock).filter(PairLock.pair == 'ETH/BTC')).all()
|
||||||
assert len(pairlocks) == 1
|
assert len(pairlocks) == 1
|
||||||
pairlocks[0].pair == 'ETH/BTC'
|
pairlocks[0].pair == 'ETH/BTC'
|
||||||
pairlocks[0].side == '*'
|
pairlocks[0].side == '*'
|
||||||
|
@ -4,6 +4,7 @@ from types import FunctionType
|
|||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
import pytest
|
import pytest
|
||||||
|
from sqlalchemy import select
|
||||||
|
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT
|
from freqtrade.constants import DATETIME_PRINT_FORMAT
|
||||||
from freqtrade.enums import TradingMode
|
from freqtrade.enums import TradingMode
|
||||||
@ -1494,7 +1495,7 @@ def test_stoploss_reinitialization(default_conf, fee):
|
|||||||
assert trade.stop_loss_pct == -0.05
|
assert trade.stop_loss_pct == -0.05
|
||||||
assert trade.initial_stop_loss == 0.95
|
assert trade.initial_stop_loss == 0.95
|
||||||
assert trade.initial_stop_loss_pct == -0.05
|
assert trade.initial_stop_loss_pct == -0.05
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
# Lower stoploss
|
# Lower stoploss
|
||||||
@ -1556,7 +1557,7 @@ def test_stoploss_reinitialization_leverage(default_conf, fee):
|
|||||||
assert trade.stop_loss_pct == -0.1
|
assert trade.stop_loss_pct == -0.1
|
||||||
assert trade.initial_stop_loss == 0.98
|
assert trade.initial_stop_loss == 0.98
|
||||||
assert trade.initial_stop_loss_pct == -0.1
|
assert trade.initial_stop_loss_pct == -0.1
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
# Lower stoploss
|
# Lower stoploss
|
||||||
@ -1618,7 +1619,7 @@ def test_stoploss_reinitialization_short(default_conf, fee):
|
|||||||
assert trade.stop_loss_pct == -0.1
|
assert trade.stop_loss_pct == -0.1
|
||||||
assert trade.initial_stop_loss == 1.02
|
assert trade.initial_stop_loss == 1.02
|
||||||
assert trade.initial_stop_loss_pct == -0.1
|
assert trade.initial_stop_loss_pct == -0.1
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
# Lower stoploss
|
# Lower stoploss
|
||||||
Trade.stoploss_reinitialization(-0.15)
|
Trade.stoploss_reinitialization(-0.15)
|
||||||
@ -1793,17 +1794,17 @@ def test_get_trades_proxy(fee, use_db, is_short):
|
|||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
@pytest.mark.parametrize('is_short', [True, False])
|
@pytest.mark.parametrize('is_short', [True, False])
|
||||||
def test_get_trades__query(fee, is_short):
|
def test_get_trades__query(fee, is_short):
|
||||||
query = Trade.get_trades([])
|
query = Trade.get_trades_query([])
|
||||||
# without orders there should be no join issued.
|
# without orders there should be no join issued.
|
||||||
query1 = Trade.get_trades([], include_orders=False)
|
query1 = Trade.get_trades_query([], include_orders=False)
|
||||||
|
|
||||||
# Empty "with-options -> default - selectin"
|
# Empty "with-options -> default - selectin"
|
||||||
assert query._with_options == ()
|
assert query._with_options == ()
|
||||||
assert query1._with_options != ()
|
assert query1._with_options != ()
|
||||||
|
|
||||||
create_mock_trades(fee, is_short)
|
create_mock_trades(fee, is_short)
|
||||||
query = Trade.get_trades([])
|
query = Trade.get_trades_query([])
|
||||||
query1 = Trade.get_trades([], include_orders=False)
|
query1 = Trade.get_trades_query([], include_orders=False)
|
||||||
|
|
||||||
assert query._with_options == ()
|
assert query._with_options == ()
|
||||||
assert query1._with_options != ()
|
assert query1._with_options != ()
|
||||||
@ -2016,6 +2017,7 @@ def test_Trade_object_idem():
|
|||||||
'get_open_trades_without_assigned_fees',
|
'get_open_trades_without_assigned_fees',
|
||||||
'get_open_order_trades',
|
'get_open_order_trades',
|
||||||
'get_trades',
|
'get_trades',
|
||||||
|
'get_trades_query',
|
||||||
'get_exit_reason_performance',
|
'get_exit_reason_performance',
|
||||||
'get_enter_tag_performance',
|
'get_enter_tag_performance',
|
||||||
'get_mix_tag_performance',
|
'get_mix_tag_performance',
|
||||||
@ -2443,8 +2445,8 @@ def test_order_to_ccxt(limit_buy_order_open):
|
|||||||
|
|
||||||
order = Order.parse_from_ccxt_object(limit_buy_order_open, 'mocked', 'buy')
|
order = Order.parse_from_ccxt_object(limit_buy_order_open, 'mocked', 'buy')
|
||||||
order.ft_trade_id = 1
|
order.ft_trade_id = 1
|
||||||
order.query.session.add(order)
|
order.session.add(order)
|
||||||
Order.query.session.commit()
|
Order.session.commit()
|
||||||
|
|
||||||
order_resp = Order.order_by_id(limit_buy_order_open['id'])
|
order_resp = Order.order_by_id(limit_buy_order_open['id'])
|
||||||
assert order_resp
|
assert order_resp
|
||||||
@ -2546,7 +2548,7 @@ def test_recalc_trade_from_orders_dca(data) -> None:
|
|||||||
leverage=1.0,
|
leverage=1.0,
|
||||||
trading_mode=TradingMode.SPOT
|
trading_mode=TradingMode.SPOT
|
||||||
)
|
)
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
|
|
||||||
for idx, (order, result) in enumerate(data['orders']):
|
for idx, (order, result) in enumerate(data['orders']):
|
||||||
amount = order[1]
|
amount = order[1]
|
||||||
@ -2575,11 +2577,11 @@ def test_recalc_trade_from_orders_dca(data) -> None:
|
|||||||
trade.recalc_trade_from_orders()
|
trade.recalc_trade_from_orders()
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
orders1 = Order.query.all()
|
orders1 = Order.session.scalars(select(Order)).all()
|
||||||
assert orders1
|
assert orders1
|
||||||
assert len(orders1) == idx + 1
|
assert len(orders1) == idx + 1
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert len(trade.orders) == idx + 1
|
assert len(trade.orders) == idx + 1
|
||||||
if idx < len(data) - 1:
|
if idx < len(data) - 1:
|
||||||
@ -2596,6 +2598,6 @@ def test_recalc_trade_from_orders_dca(data) -> None:
|
|||||||
assert pytest.approx(trade.close_profit_abs) == data['end_profit']
|
assert pytest.approx(trade.close_profit_abs) == data['end_profit']
|
||||||
assert pytest.approx(trade.close_profit) == data['end_profit_ratio']
|
assert pytest.approx(trade.close_profit) == data['end_profit_ratio']
|
||||||
assert not trade.is_open
|
assert not trade.is_open
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
|
@ -711,8 +711,8 @@ def test_PrecisionFilter_error(mocker, whitelist_conf) -> None:
|
|||||||
|
|
||||||
def test_PerformanceFilter_error(mocker, whitelist_conf, caplog) -> None:
|
def test_PerformanceFilter_error(mocker, whitelist_conf, caplog) -> None:
|
||||||
whitelist_conf['pairlists'] = [{"method": "StaticPairList"}, {"method": "PerformanceFilter"}]
|
whitelist_conf['pairlists'] = [{"method": "StaticPairList"}, {"method": "PerformanceFilter"}]
|
||||||
if hasattr(Trade, 'query'):
|
if hasattr(Trade, 'session'):
|
||||||
del Trade.query
|
del Trade.session
|
||||||
mocker.patch(f'{EXMS}.exchange_has', MagicMock(return_value=True))
|
mocker.patch(f'{EXMS}.exchange_has', MagicMock(return_value=True))
|
||||||
exchange = get_patched_exchange(mocker, whitelist_conf)
|
exchange = get_patched_exchange(mocker, whitelist_conf)
|
||||||
pm = PairListManager(exchange, whitelist_conf, MagicMock())
|
pm = PairListManager(exchange, whitelist_conf, MagicMock())
|
||||||
|
@ -14,7 +14,7 @@ def test_PairLocks(use_db):
|
|||||||
PairLocks.use_db = use_db
|
PairLocks.use_db = use_db
|
||||||
# No lock should be present
|
# No lock should be present
|
||||||
if use_db:
|
if use_db:
|
||||||
assert len(PairLock.query.all()) == 0
|
assert len(PairLock.get_all_locks().all()) == 0
|
||||||
|
|
||||||
assert PairLocks.use_db == use_db
|
assert PairLocks.use_db == use_db
|
||||||
|
|
||||||
@ -88,13 +88,13 @@ def test_PairLocks(use_db):
|
|||||||
|
|
||||||
if use_db:
|
if use_db:
|
||||||
locks = PairLocks.get_all_locks()
|
locks = PairLocks.get_all_locks()
|
||||||
locks_db = PairLock.query.all()
|
locks_db = PairLock.get_all_locks().all()
|
||||||
assert len(locks) == len(locks_db)
|
assert len(locks) == len(locks_db)
|
||||||
assert len(locks_db) > 0
|
assert len(locks_db) > 0
|
||||||
else:
|
else:
|
||||||
# Nothing was pushed to the database
|
# Nothing was pushed to the database
|
||||||
assert len(PairLocks.get_all_locks()) > 0
|
assert len(PairLocks.get_all_locks()) > 0
|
||||||
assert len(PairLock.query.all()) == 0
|
assert len(PairLock.get_all_locks().all()) == 0
|
||||||
# Reset use-db variable
|
# Reset use-db variable
|
||||||
PairLocks.reset_locks()
|
PairLocks.reset_locks()
|
||||||
PairLocks.use_db = True
|
PairLocks.use_db = True
|
||||||
@ -107,7 +107,7 @@ def test_PairLocks_getlongestlock(use_db):
|
|||||||
# No lock should be present
|
# No lock should be present
|
||||||
PairLocks.use_db = use_db
|
PairLocks.use_db = use_db
|
||||||
if use_db:
|
if use_db:
|
||||||
assert len(PairLock.query.all()) == 0
|
assert len(PairLock.get_all_locks().all()) == 0
|
||||||
|
|
||||||
assert PairLocks.use_db == use_db
|
assert PairLocks.use_db == use_db
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ def test_PairLocks_reason(use_db):
|
|||||||
PairLocks.use_db = use_db
|
PairLocks.use_db = use_db
|
||||||
# No lock should be present
|
# No lock should be present
|
||||||
if use_db:
|
if use_db:
|
||||||
assert len(PairLock.query.all()) == 0
|
assert len(PairLock.get_all_locks().all()) == 0
|
||||||
|
|
||||||
assert PairLocks.use_db == use_db
|
assert PairLocks.use_db == use_db
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
|
|||||||
trade.close(close_price)
|
trade.close(close_price)
|
||||||
trade.exit_reason = exit_reason
|
trade.exit_reason = exit_reason
|
||||||
|
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
return trade
|
return trade
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ from unittest.mock import ANY, MagicMock, PropertyMock
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from numpy import isnan
|
from numpy import isnan
|
||||||
|
from sqlalchemy import select
|
||||||
|
|
||||||
from freqtrade.edge import PairInfo
|
from freqtrade.edge import PairInfo
|
||||||
from freqtrade.enums import SignalDirection, State, TradingMode
|
from freqtrade.enums import SignalDirection, State, TradingMode
|
||||||
@ -354,7 +355,7 @@ def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog, is_short):
|
|||||||
with pytest.raises(RPCException, match='invalid argument'):
|
with pytest.raises(RPCException, match='invalid argument'):
|
||||||
rpc._rpc_delete('200')
|
rpc._rpc_delete('200')
|
||||||
|
|
||||||
trades = Trade.query.all()
|
trades = Trade.session.scalars(select(Trade)).all()
|
||||||
trades[1].stoploss_order_id = '1234'
|
trades[1].stoploss_order_id = '1234'
|
||||||
trades[2].stoploss_order_id = '1234'
|
trades[2].stoploss_order_id = '1234'
|
||||||
assert len(trades) > 2
|
assert len(trades) > 2
|
||||||
@ -717,7 +718,7 @@ def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None:
|
|||||||
mocker.patch(f'{EXMS}._dry_is_price_crossed', MagicMock(return_value=False))
|
mocker.patch(f'{EXMS}._dry_is_price_crossed', MagicMock(return_value=False))
|
||||||
freqtradebot.enter_positions()
|
freqtradebot.enter_positions()
|
||||||
# make an limit-buy open trade
|
# make an limit-buy open trade
|
||||||
trade = Trade.query.filter(Trade.id == '3').first()
|
trade = Trade.session.scalars(select(Trade).filter(Trade.id == '3')).first()
|
||||||
filled_amount = trade.amount / 2
|
filled_amount = trade.amount / 2
|
||||||
# Fetch order - it's open first, and closed after cancel_order is called.
|
# Fetch order - it's open first, and closed after cancel_order is called.
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
@ -753,7 +754,7 @@ def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None:
|
|||||||
|
|
||||||
freqtradebot.config['max_open_trades'] = 3
|
freqtradebot.config['max_open_trades'] = 3
|
||||||
freqtradebot.enter_positions()
|
freqtradebot.enter_positions()
|
||||||
trade = Trade.query.filter(Trade.id == '2').first()
|
trade = Trade.session.scalars(select(Trade).filter(Trade.id == '2')).first()
|
||||||
amount = trade.amount
|
amount = trade.amount
|
||||||
# make an limit-buy open trade, if there is no 'filled', don't sell it
|
# make an limit-buy open trade, if there is no 'filled', don't sell it
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
@ -771,7 +772,7 @@ def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None:
|
|||||||
assert cancel_order_mock.call_count == 2
|
assert cancel_order_mock.call_count == 2
|
||||||
assert trade.amount == amount
|
assert trade.amount == amount
|
||||||
|
|
||||||
trade = Trade.query.filter(Trade.id == '3').first()
|
trade = Trade.session.scalars(select(Trade).filter(Trade.id == '3')).first()
|
||||||
|
|
||||||
# make an limit-sell open trade
|
# make an limit-sell open trade
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
|
@ -14,6 +14,7 @@ from fastapi import FastAPI, WebSocketDisconnect
|
|||||||
from fastapi.exceptions import HTTPException
|
from fastapi.exceptions import HTTPException
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from requests.auth import _basic_auth_str
|
from requests.auth import _basic_auth_str
|
||||||
|
from sqlalchemy import select
|
||||||
|
|
||||||
from freqtrade.__init__ import __version__
|
from freqtrade.__init__ import __version__
|
||||||
from freqtrade.enums import CandleType, RunMode, State, TradingMode
|
from freqtrade.enums import CandleType, RunMode, State, TradingMode
|
||||||
@ -624,7 +625,7 @@ def test_api_trades(botclient, mocker, fee, markets, is_short):
|
|||||||
assert rc.json()['offset'] == 0
|
assert rc.json()['offset'] == 0
|
||||||
|
|
||||||
create_mock_trades(fee, is_short=is_short)
|
create_mock_trades(fee, is_short=is_short)
|
||||||
Trade.query.session.flush()
|
Trade.session.flush()
|
||||||
|
|
||||||
rc = client_get(client, f"{BASE_URI}/trades")
|
rc = client_get(client, f"{BASE_URI}/trades")
|
||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
@ -652,7 +653,7 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets, is_short):
|
|||||||
assert_response(rc, 404)
|
assert_response(rc, 404)
|
||||||
assert rc.json()['detail'] == 'Trade not found.'
|
assert rc.json()['detail'] == 'Trade not found.'
|
||||||
|
|
||||||
Trade.query.session.rollback()
|
Trade.rollback()
|
||||||
create_mock_trades(fee, is_short=is_short)
|
create_mock_trades(fee, is_short=is_short)
|
||||||
|
|
||||||
rc = client_get(client, f"{BASE_URI}/trade/3")
|
rc = client_get(client, f"{BASE_URI}/trade/3")
|
||||||
@ -677,7 +678,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets, is_short):
|
|||||||
create_mock_trades(fee, is_short=is_short)
|
create_mock_trades(fee, is_short=is_short)
|
||||||
|
|
||||||
ftbot.strategy.order_types['stoploss_on_exchange'] = True
|
ftbot.strategy.order_types['stoploss_on_exchange'] = True
|
||||||
trades = Trade.query.all()
|
trades = Trade.session.scalars(select(Trade)).all()
|
||||||
trades[1].stoploss_order_id = '1234'
|
trades[1].stoploss_order_id = '1234'
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
assert len(trades) > 2
|
assert len(trades) > 2
|
||||||
@ -685,7 +686,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets, is_short):
|
|||||||
rc = client_delete(client, f"{BASE_URI}/trades/1")
|
rc = client_delete(client, f"{BASE_URI}/trades/1")
|
||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
assert rc.json()['result_msg'] == 'Deleted trade 1. Closed 1 open orders.'
|
assert rc.json()['result_msg'] == 'Deleted trade 1. Closed 1 open orders.'
|
||||||
assert len(trades) - 1 == len(Trade.query.all())
|
assert len(trades) - 1 == len(Trade.session.scalars(select(Trade)).all())
|
||||||
assert cancel_mock.call_count == 1
|
assert cancel_mock.call_count == 1
|
||||||
|
|
||||||
cancel_mock.reset_mock()
|
cancel_mock.reset_mock()
|
||||||
@ -694,11 +695,11 @@ def test_api_delete_trade(botclient, mocker, fee, markets, is_short):
|
|||||||
assert_response(rc, 502)
|
assert_response(rc, 502)
|
||||||
assert cancel_mock.call_count == 0
|
assert cancel_mock.call_count == 0
|
||||||
|
|
||||||
assert len(trades) - 1 == len(Trade.query.all())
|
assert len(trades) - 1 == len(Trade.session.scalars(select(Trade)).all())
|
||||||
rc = client_delete(client, f"{BASE_URI}/trades/2")
|
rc = client_delete(client, f"{BASE_URI}/trades/2")
|
||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
assert rc.json()['result_msg'] == 'Deleted trade 2. Closed 2 open orders.'
|
assert rc.json()['result_msg'] == 'Deleted trade 2. Closed 2 open orders.'
|
||||||
assert len(trades) - 2 == len(Trade.query.all())
|
assert len(trades) - 2 == len(Trade.session.scalars(select(Trade)).all())
|
||||||
assert stoploss_mock.call_count == 1
|
assert stoploss_mock.call_count == 1
|
||||||
|
|
||||||
rc = client_delete(client, f"{BASE_URI}/trades/502")
|
rc = client_delete(client, f"{BASE_URI}/trades/502")
|
||||||
@ -943,7 +944,7 @@ def test_api_performance(botclient, fee):
|
|||||||
)
|
)
|
||||||
trade.close_profit = trade.calc_profit_ratio(trade.close_rate)
|
trade.close_profit = trade.calc_profit_ratio(trade.close_rate)
|
||||||
trade.close_profit_abs = trade.calc_profit(trade.close_rate)
|
trade.close_profit_abs = trade.calc_profit(trade.close_rate)
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
|
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair='XRP/ETH',
|
pair='XRP/ETH',
|
||||||
@ -960,7 +961,7 @@ def test_api_performance(botclient, fee):
|
|||||||
trade.close_profit = trade.calc_profit_ratio(trade.close_rate)
|
trade.close_profit = trade.calc_profit_ratio(trade.close_rate)
|
||||||
trade.close_profit_abs = trade.calc_profit(trade.close_rate)
|
trade.close_profit_abs = trade.calc_profit(trade.close_rate)
|
||||||
|
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
rc = client_get(client, f"{BASE_URI}/performance")
|
rc = client_get(client, f"{BASE_URI}/performance")
|
||||||
@ -1290,7 +1291,7 @@ def test_api_forceexit(botclient, mocker, ticker, fee, markets):
|
|||||||
data={"tradeid": "1"})
|
data={"tradeid": "1"})
|
||||||
assert_response(rc, 502)
|
assert_response(rc, 502)
|
||||||
assert rc.json() == {"error": "Error querying /api/v1/forceexit: invalid argument"}
|
assert rc.json() == {"error": "Error querying /api/v1/forceexit: invalid argument"}
|
||||||
Trade.query.session.rollback()
|
Trade.rollback()
|
||||||
|
|
||||||
create_mock_trades(fee)
|
create_mock_trades(fee)
|
||||||
trade = Trade.get_trades([Trade.id == 5]).first()
|
trade = Trade.get_trades([Trade.id == 5]).first()
|
||||||
@ -1299,7 +1300,7 @@ def test_api_forceexit(botclient, mocker, ticker, fee, markets):
|
|||||||
data={"tradeid": "5", "ordertype": "market", "amount": 23})
|
data={"tradeid": "5", "ordertype": "market", "amount": 23})
|
||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
assert rc.json() == {'result': 'Created sell order for trade 5.'}
|
assert rc.json() == {'result': 'Created sell order for trade 5.'}
|
||||||
Trade.query.session.rollback()
|
Trade.rollback()
|
||||||
|
|
||||||
trade = Trade.get_trades([Trade.id == 5]).first()
|
trade = Trade.get_trades([Trade.id == 5]).first()
|
||||||
assert pytest.approx(trade.amount) == 100
|
assert pytest.approx(trade.amount) == 100
|
||||||
@ -1309,7 +1310,7 @@ def test_api_forceexit(botclient, mocker, ticker, fee, markets):
|
|||||||
data={"tradeid": "5"})
|
data={"tradeid": "5"})
|
||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
assert rc.json() == {'result': 'Created sell order for trade 5.'}
|
assert rc.json() == {'result': 'Created sell order for trade 5.'}
|
||||||
Trade.query.session.rollback()
|
Trade.rollback()
|
||||||
|
|
||||||
trade = Trade.get_trades([Trade.id == 5]).first()
|
trade = Trade.get_trades([Trade.id == 5]).first()
|
||||||
assert trade.is_open is False
|
assert trade.is_open is False
|
||||||
|
@ -14,6 +14,7 @@ import arrow
|
|||||||
import pytest
|
import pytest
|
||||||
import time_machine
|
import time_machine
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
from sqlalchemy import select
|
||||||
from telegram import Chat, Message, ReplyKeyboardMarkup, Update
|
from telegram import Chat, Message, ReplyKeyboardMarkup, Update
|
||||||
from telegram.error import BadRequest, NetworkError, TelegramError
|
from telegram.error import BadRequest, NetworkError, TelegramError
|
||||||
|
|
||||||
@ -692,7 +693,7 @@ def test_profit_handle(default_conf_usdt, update, ticker_usdt, ticker_sell_up, f
|
|||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.enter_positions()
|
freqtradebot.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
|
|
||||||
context = MagicMock()
|
context = MagicMock()
|
||||||
# Test with invalid 2nd argument (should silently pass)
|
# Test with invalid 2nd argument (should silently pass)
|
||||||
@ -945,7 +946,7 @@ def test_telegram_forceexit_handle(default_conf, update, ticker, fee,
|
|||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.enter_positions()
|
freqtradebot.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
# Increase the price and sell it
|
# Increase the price and sell it
|
||||||
@ -1020,7 +1021,7 @@ def test_telegram_force_exit_down_handle(default_conf, update, ticker, fee,
|
|||||||
fetch_ticker=ticker_sell_down
|
fetch_ticker=ticker_sell_down
|
||||||
)
|
)
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
# /forceexit 1
|
# /forceexit 1
|
||||||
|
@ -10,6 +10,7 @@ from unittest.mock import ANY, MagicMock, PropertyMock, patch
|
|||||||
import arrow
|
import arrow
|
||||||
import pytest
|
import pytest
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
from sqlalchemy import select
|
||||||
|
|
||||||
from freqtrade.constants import CANCEL_REASON, UNLIMITED_STAKE_AMOUNT
|
from freqtrade.constants import CANCEL_REASON, UNLIMITED_STAKE_AMOUNT
|
||||||
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, RPCMessageType, RunMode,
|
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, RPCMessageType, RunMode,
|
||||||
@ -247,7 +248,7 @@ def test_edge_overrides_stoploss(limit_order, fee, caplog, mocker,
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
#############################################
|
#############################################
|
||||||
ticker_val.update({
|
ticker_val.update({
|
||||||
@ -278,7 +279,7 @@ def test_total_open_trades_stakes(mocker, default_conf_usdt, ticker_usdt, fee) -
|
|||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
|
|
||||||
assert trade is not None
|
assert trade is not None
|
||||||
assert trade.stake_amount == 60.0
|
assert trade.stake_amount == 60.0
|
||||||
@ -286,7 +287,7 @@ def test_total_open_trades_stakes(mocker, default_conf_usdt, ticker_usdt, fee) -
|
|||||||
assert trade.open_date is not None
|
assert trade.open_date is not None
|
||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.order_by(Trade.id.desc()).first()
|
trade = Trade.session.scalars(select(Trade).order_by(Trade.id.desc())).first()
|
||||||
|
|
||||||
assert trade is not None
|
assert trade is not None
|
||||||
assert trade.stake_amount == 60.0
|
assert trade.stake_amount == 60.0
|
||||||
@ -317,7 +318,7 @@ def test_create_trade(default_conf_usdt, ticker_usdt, limit_order,
|
|||||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
||||||
freqtrade.create_trade('ETH/USDT')
|
freqtrade.create_trade('ETH/USDT')
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade is not None
|
assert trade is not None
|
||||||
assert pytest.approx(trade.stake_amount) == 60.0
|
assert pytest.approx(trade.stake_amount) == 60.0
|
||||||
@ -568,12 +569,12 @@ def test_process_trade_creation(default_conf_usdt, ticker_usdt, limit_order, lim
|
|||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
||||||
|
|
||||||
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
trades = Trade.get_open_trades()
|
||||||
assert not trades
|
assert not trades
|
||||||
|
|
||||||
freqtrade.process()
|
freqtrade.process()
|
||||||
|
|
||||||
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
trades = Trade.get_open_trades()
|
||||||
assert len(trades) == 1
|
assert len(trades) == 1
|
||||||
trade = trades[0]
|
trade = trades[0]
|
||||||
assert trade is not None
|
assert trade is not None
|
||||||
@ -640,11 +641,11 @@ def test_process_trade_handling(default_conf_usdt, ticker_usdt, limit_buy_order_
|
|||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
trades = Trade.get_open_trades()
|
||||||
assert not trades
|
assert not trades
|
||||||
freqtrade.process()
|
freqtrade.process()
|
||||||
|
|
||||||
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
trades = Trade.get_open_trades()
|
||||||
assert len(trades) == 1
|
assert len(trades) == 1
|
||||||
|
|
||||||
# Nothing happened ...
|
# Nothing happened ...
|
||||||
@ -671,7 +672,7 @@ def test_process_trade_no_whitelist_pair(default_conf_usdt, ticker_usdt, limit_b
|
|||||||
assert pair not in default_conf_usdt['exchange']['pair_whitelist']
|
assert pair not in default_conf_usdt['exchange']['pair_whitelist']
|
||||||
|
|
||||||
# create open trade not in whitelist
|
# create open trade not in whitelist
|
||||||
Trade.query.session.add(Trade(
|
Trade.session.add(Trade(
|
||||||
pair=pair,
|
pair=pair,
|
||||||
stake_amount=0.001,
|
stake_amount=0.001,
|
||||||
fee_open=fee.return_value,
|
fee_open=fee.return_value,
|
||||||
@ -681,7 +682,7 @@ def test_process_trade_no_whitelist_pair(default_conf_usdt, ticker_usdt, limit_b
|
|||||||
open_rate=0.01,
|
open_rate=0.01,
|
||||||
exchange='binance',
|
exchange='binance',
|
||||||
))
|
))
|
||||||
Trade.query.session.add(Trade(
|
Trade.session.add(Trade(
|
||||||
pair='ETH/USDT',
|
pair='ETH/USDT',
|
||||||
stake_amount=0.001,
|
stake_amount=0.001,
|
||||||
fee_open=fee.return_value,
|
fee_open=fee.return_value,
|
||||||
@ -838,7 +839,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
|
|
||||||
# Should create an open trade with an open order id
|
# Should create an open trade with an open order id
|
||||||
# As the order is not fulfilled yet
|
# As the order is not fulfilled yet
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
@ -865,7 +866,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
|
|
||||||
mocker.patch(f'{EXMS}.create_order', MagicMock(return_value=order))
|
mocker.patch(f'{EXMS}.create_order', MagicMock(return_value=order))
|
||||||
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
||||||
trade = Trade.query.all()[2]
|
trade = Trade.session.scalars(select(Trade)).all()[2]
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
@ -883,7 +884,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
order['id'] = '555'
|
order['id'] = '555'
|
||||||
mocker.patch(f'{EXMS}.create_order', MagicMock(return_value=order))
|
mocker.patch(f'{EXMS}.create_order', MagicMock(return_value=order))
|
||||||
assert freqtrade.execute_entry(pair, stake_amount)
|
assert freqtrade.execute_entry(pair, stake_amount)
|
||||||
trade = Trade.query.all()[3]
|
trade = Trade.session.scalars(select(Trade)).all()[3]
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
@ -896,7 +897,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
|
|
||||||
freqtrade.strategy.custom_stake_amount = lambda **kwargs: 150.0
|
freqtrade.strategy.custom_stake_amount = lambda **kwargs: 150.0
|
||||||
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
||||||
trade = Trade.query.all()[4]
|
trade = Trade.session.scalars(select(Trade)).all()[4]
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
assert pytest.approx(trade.stake_amount) == 150
|
assert pytest.approx(trade.stake_amount) == 150
|
||||||
@ -905,7 +906,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
order['id'] = '557'
|
order['id'] = '557'
|
||||||
freqtrade.strategy.custom_stake_amount = lambda **kwargs: 20 / 0
|
freqtrade.strategy.custom_stake_amount = lambda **kwargs: 20 / 0
|
||||||
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
||||||
trade = Trade.query.all()[5]
|
trade = Trade.session.scalars(select(Trade)).all()[5]
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
assert pytest.approx(trade.stake_amount) == 2.0
|
assert pytest.approx(trade.stake_amount) == 2.0
|
||||||
@ -934,7 +935,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
order['id'] = '5566'
|
order['id'] = '5566'
|
||||||
freqtrade.strategy.custom_entry_price = lambda **kwargs: 0.508
|
freqtrade.strategy.custom_entry_price = lambda **kwargs: 0.508
|
||||||
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
||||||
trade = Trade.query.all()[6]
|
trade = Trade.session.scalars(select(Trade)).all()[6]
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_rate_requested == 0.508
|
assert trade.open_rate_requested == 0.508
|
||||||
@ -951,7 +952,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
||||||
trade = Trade.query.all()[7]
|
trade = Trade.session.scalars(select(Trade)).all()[7]
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_rate_requested == 10
|
assert trade.open_rate_requested == 10
|
||||||
@ -961,7 +962,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
order['id'] = '5568'
|
order['id'] = '5568'
|
||||||
freqtrade.strategy.custom_entry_price = lambda **kwargs: "string price"
|
freqtrade.strategy.custom_entry_price = lambda **kwargs: "string price"
|
||||||
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
||||||
trade = Trade.query.all()[8]
|
trade = Trade.session.scalars(select(Trade)).all()[8]
|
||||||
# Trade(id=9, pair=ETH/USDT, amount=0.20000000, is_short=False,
|
# Trade(id=9, pair=ETH/USDT, amount=0.20000000, is_short=False,
|
||||||
# leverage=1.0, open_rate=10.00000000, open_since=...)
|
# leverage=1.0, open_rate=10.00000000, open_since=...)
|
||||||
# Trade(id=9, pair=ETH/USDT, amount=0.60000000, is_short=True,
|
# Trade(id=9, pair=ETH/USDT, amount=0.60000000, is_short=True,
|
||||||
@ -982,7 +983,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
freqtrade.exchange.get_max_pair_stake_amount = MagicMock(return_value=500)
|
freqtrade.exchange.get_max_pair_stake_amount = MagicMock(return_value=500)
|
||||||
|
|
||||||
assert freqtrade.execute_entry(pair, 2000, is_short=is_short)
|
assert freqtrade.execute_entry(pair, 2000, is_short=is_short)
|
||||||
trade = Trade.query.all()[9]
|
trade = Trade.session.scalars(select(Trade)).all()[9]
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert pytest.approx(trade.stake_amount) == 500
|
assert pytest.approx(trade.stake_amount) == 500
|
||||||
|
|
||||||
@ -991,7 +992,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
freqtrade.strategy.leverage.reset_mock()
|
freqtrade.strategy.leverage.reset_mock()
|
||||||
assert freqtrade.execute_entry(pair, 200, leverage_=3)
|
assert freqtrade.execute_entry(pair, 200, leverage_=3)
|
||||||
assert freqtrade.strategy.leverage.call_count == 0
|
assert freqtrade.strategy.leverage.call_count == 0
|
||||||
trade = Trade.query.all()[10]
|
trade = Trade.session.scalars(select(Trade)).all()[10]
|
||||||
assert trade.leverage == 1 if trading_mode == 'spot' else 3
|
assert trade.leverage == 1 if trading_mode == 'spot' else 3
|
||||||
|
|
||||||
|
|
||||||
@ -1053,7 +1054,7 @@ def test_execute_entry_min_leverage(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
freqtrade.strategy.leverage = MagicMock(return_value=5.0)
|
freqtrade.strategy.leverage = MagicMock(return_value=5.0)
|
||||||
|
|
||||||
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade.leverage == 5.0
|
assert trade.leverage == 5.0
|
||||||
# assert trade.stake_amount == 2
|
# assert trade.stake_amount == 2
|
||||||
|
|
||||||
@ -1158,7 +1159,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_
|
|||||||
# as a trade actually happened
|
# as a trade actually happened
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
@ -1271,7 +1272,7 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf_usdt, fee, caplog,
|
|||||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
@ -1316,7 +1317,7 @@ def test_create_stoploss_order_invalid_order(
|
|||||||
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
freqtrade.create_stoploss_order(trade, 200)
|
freqtrade.create_stoploss_order(trade, 200)
|
||||||
@ -1367,7 +1368,7 @@ def test_create_stoploss_order_insufficient_funds(
|
|||||||
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
freqtrade.create_stoploss_order(trade, 200)
|
freqtrade.create_stoploss_order(trade, 200)
|
||||||
@ -1435,7 +1436,7 @@ def test_handle_stoploss_on_exchange_trailing(
|
|||||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
@ -1554,7 +1555,7 @@ def test_handle_stoploss_on_exchange_trailing_error(
|
|||||||
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
|
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
|
||||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
@ -1668,7 +1669,7 @@ def test_handle_stoploss_on_exchange_custom_stop(
|
|||||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
@ -1796,7 +1797,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, limit_orde
|
|||||||
freqtrade.active_pair_whitelist = freqtrade.edge.adjust(freqtrade.active_pair_whitelist)
|
freqtrade.active_pair_whitelist = freqtrade.edge.adjust(freqtrade.active_pair_whitelist)
|
||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
trade.stoploss_order_id = 100
|
trade.stoploss_order_id = 100
|
||||||
@ -2162,7 +2163,7 @@ def test_handle_trade(
|
|||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
@ -2217,7 +2218,7 @@ def test_handle_overlapping_signals(
|
|||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
# Buy and Sell triggering, so doing nothing ...
|
# Buy and Sell triggering, so doing nothing ...
|
||||||
trades = Trade.query.all()
|
trades = Trade.session.scalars(select(Trade)).all()
|
||||||
|
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
assert nb_trades == 0
|
assert nb_trades == 0
|
||||||
@ -2225,7 +2226,7 @@ def test_handle_overlapping_signals(
|
|||||||
# Buy is triggering, so buying ...
|
# Buy is triggering, so buying ...
|
||||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trades = Trade.query.all()
|
trades = Trade.session.scalars(select(Trade)).all()
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
@ -2235,7 +2236,7 @@ def test_handle_overlapping_signals(
|
|||||||
# Buy and Sell are not triggering, so doing nothing ...
|
# Buy and Sell are not triggering, so doing nothing ...
|
||||||
patch_get_signal(freqtrade, enter_long=False)
|
patch_get_signal(freqtrade, enter_long=False)
|
||||||
assert freqtrade.handle_trade(trades[0]) is False
|
assert freqtrade.handle_trade(trades[0]) is False
|
||||||
trades = Trade.query.all()
|
trades = Trade.session.scalars(select(Trade)).all()
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
@ -2248,7 +2249,7 @@ def test_handle_overlapping_signals(
|
|||||||
else:
|
else:
|
||||||
patch_get_signal(freqtrade, enter_long=True, exit_long=True)
|
patch_get_signal(freqtrade, enter_long=True, exit_long=True)
|
||||||
assert freqtrade.handle_trade(trades[0]) is False
|
assert freqtrade.handle_trade(trades[0]) is False
|
||||||
trades = Trade.query.all()
|
trades = Trade.session.scalars(select(Trade)).all()
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
@ -2260,7 +2261,7 @@ def test_handle_overlapping_signals(
|
|||||||
patch_get_signal(freqtrade, enter_long=False, exit_short=True)
|
patch_get_signal(freqtrade, enter_long=False, exit_short=True)
|
||||||
else:
|
else:
|
||||||
patch_get_signal(freqtrade, enter_long=False, exit_long=True)
|
patch_get_signal(freqtrade, enter_long=False, exit_long=True)
|
||||||
trades = Trade.query.all()
|
trades = Trade.session.scalars(select(Trade)).all()
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert freqtrade.handle_trade(trades[0]) is True
|
assert freqtrade.handle_trade(trades[0]) is True
|
||||||
@ -2291,7 +2292,7 @@ def test_handle_trade_roi(default_conf_usdt, ticker_usdt, limit_order_open, fee,
|
|||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
|
|
||||||
@ -2333,7 +2334,7 @@ def test_handle_trade_use_exit_signal(
|
|||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
|
|
||||||
@ -2370,7 +2371,7 @@ def test_close_trade(
|
|||||||
# Create trade and sell it
|
# Create trade and sell it
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
@ -2427,7 +2428,7 @@ def test_manage_open_orders_entry_usercustom(
|
|||||||
open_trade.is_short = is_short
|
open_trade.is_short = is_short
|
||||||
open_trade.orders[0].side = 'sell' if is_short else 'buy'
|
open_trade.orders[0].side = 'sell' if is_short else 'buy'
|
||||||
open_trade.orders[0].ft_order_side = 'sell' if is_short else 'buy'
|
open_trade.orders[0].ft_order_side = 'sell' if is_short else 'buy'
|
||||||
Trade.query.session.add(open_trade)
|
Trade.session.add(open_trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
# Ensure default is to return empty (so not mocked yet)
|
# Ensure default is to return empty (so not mocked yet)
|
||||||
@ -2438,7 +2439,8 @@ def test_manage_open_orders_entry_usercustom(
|
|||||||
freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False)
|
freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False)
|
||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
assert cancel_order_mock.call_count == 0
|
assert cancel_order_mock.call_count == 0
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
trades = Trade.session.scalars(
|
||||||
|
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
assert nb_trades == 1
|
assert nb_trades == 1
|
||||||
assert freqtrade.strategy.check_entry_timeout.call_count == 1
|
assert freqtrade.strategy.check_entry_timeout.call_count == 1
|
||||||
@ -2446,7 +2448,8 @@ def test_manage_open_orders_entry_usercustom(
|
|||||||
|
|
||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
assert cancel_order_mock.call_count == 0
|
assert cancel_order_mock.call_count == 0
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
trades = Trade.session.scalars(
|
||||||
|
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
assert nb_trades == 1
|
assert nb_trades == 1
|
||||||
assert freqtrade.strategy.check_entry_timeout.call_count == 1
|
assert freqtrade.strategy.check_entry_timeout.call_count == 1
|
||||||
@ -2456,7 +2459,8 @@ def test_manage_open_orders_entry_usercustom(
|
|||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
assert cancel_order_wr_mock.call_count == 1
|
assert cancel_order_wr_mock.call_count == 1
|
||||||
assert rpc_mock.call_count == 2
|
assert rpc_mock.call_count == 2
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
trades = Trade.session.scalars(
|
||||||
|
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
assert nb_trades == 0
|
assert nb_trades == 0
|
||||||
assert freqtrade.strategy.check_entry_timeout.call_count == 1
|
assert freqtrade.strategy.check_entry_timeout.call_count == 1
|
||||||
@ -2486,7 +2490,7 @@ def test_manage_open_orders_entry(
|
|||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
|
|
||||||
open_trade.is_short = is_short
|
open_trade.is_short = is_short
|
||||||
Trade.query.session.add(open_trade)
|
Trade.session.add(open_trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False)
|
freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False)
|
||||||
@ -2495,7 +2499,8 @@ def test_manage_open_orders_entry(
|
|||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
assert cancel_order_mock.call_count == 1
|
assert cancel_order_mock.call_count == 1
|
||||||
assert rpc_mock.call_count == 2
|
assert rpc_mock.call_count == 2
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
trades = Trade.session.scalars(
|
||||||
|
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
assert nb_trades == 0
|
assert nb_trades == 0
|
||||||
# Custom user buy-timeout is never called
|
# Custom user buy-timeout is never called
|
||||||
@ -2524,7 +2529,7 @@ def test_adjust_entry_cancel(
|
|||||||
)
|
)
|
||||||
|
|
||||||
open_trade.is_short = is_short
|
open_trade.is_short = is_short
|
||||||
Trade.query.session.add(open_trade)
|
Trade.session.add(open_trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
# Timeout to not interfere
|
# Timeout to not interfere
|
||||||
@ -2533,9 +2538,10 @@ def test_adjust_entry_cancel(
|
|||||||
# check that order is cancelled
|
# check that order is cancelled
|
||||||
freqtrade.strategy.adjust_entry_price = MagicMock(return_value=None)
|
freqtrade.strategy.adjust_entry_price = MagicMock(return_value=None)
|
||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
trades = Trade.session.scalars(
|
||||||
|
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
|
||||||
assert len(trades) == 0
|
assert len(trades) == 0
|
||||||
assert len(Order.query.all()) == 0
|
assert len(Order.session.scalars(select(Order)).all()) == 0
|
||||||
assert log_has_re(
|
assert log_has_re(
|
||||||
f"{'Sell' if is_short else 'Buy'} order user requested order cancel*", caplog)
|
f"{'Sell' if is_short else 'Buy'} order user requested order cancel*", caplog)
|
||||||
assert log_has_re(
|
assert log_has_re(
|
||||||
@ -2565,7 +2571,7 @@ def test_adjust_entry_maintain_replace(
|
|||||||
)
|
)
|
||||||
|
|
||||||
open_trade.is_short = is_short
|
open_trade.is_short = is_short
|
||||||
Trade.query.session.add(open_trade)
|
Trade.session.add(open_trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
# Timeout to not interfere
|
# Timeout to not interfere
|
||||||
@ -2574,7 +2580,8 @@ def test_adjust_entry_maintain_replace(
|
|||||||
# Check that order is maintained
|
# Check that order is maintained
|
||||||
freqtrade.strategy.adjust_entry_price = MagicMock(return_value=old_order['price'])
|
freqtrade.strategy.adjust_entry_price = MagicMock(return_value=old_order['price'])
|
||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
trades = Trade.session.scalars(
|
||||||
|
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
|
||||||
assert len(trades) == 1
|
assert len(trades) == 1
|
||||||
assert len(Order.get_open_orders()) == 1
|
assert len(Order.get_open_orders()) == 1
|
||||||
# Entry adjustment is called
|
# Entry adjustment is called
|
||||||
@ -2584,9 +2591,10 @@ def test_adjust_entry_maintain_replace(
|
|||||||
freqtrade.get_valid_enter_price_and_stake = MagicMock(return_value={100, 10, 1})
|
freqtrade.get_valid_enter_price_and_stake = MagicMock(return_value={100, 10, 1})
|
||||||
freqtrade.strategy.adjust_entry_price = MagicMock(return_value=1234)
|
freqtrade.strategy.adjust_entry_price = MagicMock(return_value=1234)
|
||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
trades = Trade.session.scalars(
|
||||||
|
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
|
||||||
assert len(trades) == 1
|
assert len(trades) == 1
|
||||||
nb_all_orders = len(Order.query.all())
|
nb_all_orders = len(Order.session.scalars(select(Order)).all())
|
||||||
assert nb_all_orders == 2
|
assert nb_all_orders == 2
|
||||||
# New order seems to be in closed status?
|
# New order seems to be in closed status?
|
||||||
# nb_open_orders = len(Order.get_open_orders())
|
# nb_open_orders = len(Order.get_open_orders())
|
||||||
@ -2618,14 +2626,15 @@ def test_check_handle_cancelled_buy(
|
|||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
open_trade.orders = []
|
open_trade.orders = []
|
||||||
open_trade.is_short = is_short
|
open_trade.is_short = is_short
|
||||||
Trade.query.session.add(open_trade)
|
Trade.session.add(open_trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
# check it does cancel buy orders over the time limit
|
# check it does cancel buy orders over the time limit
|
||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
assert cancel_order_mock.call_count == 0
|
assert cancel_order_mock.call_count == 0
|
||||||
assert rpc_mock.call_count == 2
|
assert rpc_mock.call_count == 2
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
trades = Trade.session.scalars(
|
||||||
|
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
|
||||||
assert len(trades) == 0
|
assert len(trades) == 0
|
||||||
assert log_has_re(
|
assert log_has_re(
|
||||||
f"{'Sell' if is_short else 'Buy'} order cancelled on exchange for Trade.*", caplog)
|
f"{'Sell' if is_short else 'Buy'} order cancelled on exchange for Trade.*", caplog)
|
||||||
@ -2649,14 +2658,15 @@ def test_manage_open_orders_buy_exception(
|
|||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
|
|
||||||
open_trade.is_short = is_short
|
open_trade.is_short = is_short
|
||||||
Trade.query.session.add(open_trade)
|
Trade.session.add(open_trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
# check it does cancel buy orders over the time limit
|
# check it does cancel buy orders over the time limit
|
||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
assert cancel_order_mock.call_count == 0
|
assert cancel_order_mock.call_count == 0
|
||||||
assert rpc_mock.call_count == 1
|
assert rpc_mock.call_count == 1
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
trades = Trade.session.scalars(
|
||||||
|
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
assert nb_trades == 1
|
assert nb_trades == 1
|
||||||
|
|
||||||
@ -2691,7 +2701,7 @@ def test_manage_open_orders_exit_usercustom(
|
|||||||
open_trade_usdt.close_date = arrow.utcnow().shift(minutes=-601).datetime
|
open_trade_usdt.close_date = arrow.utcnow().shift(minutes=-601).datetime
|
||||||
open_trade_usdt.close_profit_abs = 0.001
|
open_trade_usdt.close_profit_abs = 0.001
|
||||||
|
|
||||||
Trade.query.session.add(open_trade_usdt)
|
Trade.session.add(open_trade_usdt)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
# Ensure default is false
|
# Ensure default is false
|
||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
@ -2771,7 +2781,7 @@ def test_manage_open_orders_exit(
|
|||||||
open_trade_usdt.close_profit_abs = 0.001
|
open_trade_usdt.close_profit_abs = 0.001
|
||||||
open_trade_usdt.is_short = is_short
|
open_trade_usdt.is_short = is_short
|
||||||
|
|
||||||
Trade.query.session.add(open_trade_usdt)
|
Trade.session.add(open_trade_usdt)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
freqtrade.strategy.check_exit_timeout = MagicMock(return_value=False)
|
freqtrade.strategy.check_exit_timeout = MagicMock(return_value=False)
|
||||||
@ -2811,7 +2821,7 @@ def test_check_handle_cancelled_exit(
|
|||||||
open_trade_usdt.close_date = arrow.utcnow().shift(minutes=-601).datetime
|
open_trade_usdt.close_date = arrow.utcnow().shift(minutes=-601).datetime
|
||||||
open_trade_usdt.is_short = is_short
|
open_trade_usdt.is_short = is_short
|
||||||
|
|
||||||
Trade.query.session.add(open_trade_usdt)
|
Trade.session.add(open_trade_usdt)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
# check it does cancel sell orders over the time limit
|
# check it does cancel sell orders over the time limit
|
||||||
@ -2848,7 +2858,7 @@ def test_manage_open_orders_partial(
|
|||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
prior_stake = open_trade.stake_amount
|
prior_stake = open_trade.stake_amount
|
||||||
Trade.query.session.add(open_trade)
|
Trade.session.add(open_trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
# check it does cancel buy orders over the time limit
|
# check it does cancel buy orders over the time limit
|
||||||
@ -2856,7 +2866,8 @@ def test_manage_open_orders_partial(
|
|||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
assert cancel_order_mock.call_count == 1
|
assert cancel_order_mock.call_count == 1
|
||||||
assert rpc_mock.call_count == 3
|
assert rpc_mock.call_count == 3
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
trades = Trade.session.scalars(
|
||||||
|
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
|
||||||
assert len(trades) == 1
|
assert len(trades) == 1
|
||||||
assert trades[0].amount == 23.0
|
assert trades[0].amount == 23.0
|
||||||
assert trades[0].stake_amount == open_trade.open_rate * trades[0].amount / leverage
|
assert trades[0].stake_amount == open_trade.open_rate * trades[0].amount / leverage
|
||||||
@ -2893,7 +2904,7 @@ def test_manage_open_orders_partial_fee(
|
|||||||
|
|
||||||
open_trade.fee_open = fee()
|
open_trade.fee_open = fee()
|
||||||
open_trade.fee_close = fee()
|
open_trade.fee_close = fee()
|
||||||
Trade.query.session.add(open_trade)
|
Trade.session.add(open_trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
# cancelling a half-filled order should update the amount to the bought amount
|
# cancelling a half-filled order should update the amount to the bought amount
|
||||||
# and apply fees if necessary.
|
# and apply fees if necessary.
|
||||||
@ -2903,7 +2914,8 @@ def test_manage_open_orders_partial_fee(
|
|||||||
|
|
||||||
assert cancel_order_mock.call_count == 1
|
assert cancel_order_mock.call_count == 1
|
||||||
assert rpc_mock.call_count == 3
|
assert rpc_mock.call_count == 3
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
trades = Trade.session.scalars(
|
||||||
|
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
|
||||||
assert len(trades) == 1
|
assert len(trades) == 1
|
||||||
# Verify that trade has been updated
|
# Verify that trade has been updated
|
||||||
assert trades[0].amount == (limit_buy_order_old_partial['amount'] -
|
assert trades[0].amount == (limit_buy_order_old_partial['amount'] -
|
||||||
@ -2943,7 +2955,7 @@ def test_manage_open_orders_partial_except(
|
|||||||
|
|
||||||
open_trade.fee_open = fee()
|
open_trade.fee_open = fee()
|
||||||
open_trade.fee_close = fee()
|
open_trade.fee_close = fee()
|
||||||
Trade.query.session.add(open_trade)
|
Trade.session.add(open_trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
# cancelling a half-filled order should update the amount to the bought amount
|
# cancelling a half-filled order should update the amount to the bought amount
|
||||||
# and apply fees if necessary.
|
# and apply fees if necessary.
|
||||||
@ -2953,7 +2965,8 @@ def test_manage_open_orders_partial_except(
|
|||||||
|
|
||||||
assert cancel_order_mock.call_count == 1
|
assert cancel_order_mock.call_count == 1
|
||||||
assert rpc_mock.call_count == 3
|
assert rpc_mock.call_count == 3
|
||||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
trades = Trade.session.scalars(
|
||||||
|
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
|
||||||
assert len(trades) == 1
|
assert len(trades) == 1
|
||||||
# Verify that trade has been updated
|
# Verify that trade has been updated
|
||||||
|
|
||||||
@ -2982,7 +2995,7 @@ def test_manage_open_orders_exception(default_conf_usdt, ticker_usdt, open_trade
|
|||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
|
|
||||||
Trade.query.session.add(open_trade_usdt)
|
Trade.session.add(open_trade_usdt)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
@ -3011,7 +3024,7 @@ def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_order, is_
|
|||||||
freqtrade._notify_enter_cancel = MagicMock()
|
freqtrade._notify_enter_cancel = MagicMock()
|
||||||
|
|
||||||
trade = mock_trade_usdt_4(fee, is_short)
|
trade = mock_trade_usdt_4(fee, is_short)
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
l_order['filled'] = 0.0
|
l_order['filled'] = 0.0
|
||||||
@ -3061,7 +3074,7 @@ def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf_usdt, is_sho
|
|||||||
reason = CANCEL_REASON['TIMEOUT']
|
reason = CANCEL_REASON['TIMEOUT']
|
||||||
|
|
||||||
trade = mock_trade_usdt_4(fee, is_short)
|
trade = mock_trade_usdt_4(fee, is_short)
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
assert freqtrade.handle_cancel_enter(trade, limit_buy_order_canceled_empty, reason)
|
assert freqtrade.handle_cancel_enter(trade, limit_buy_order_canceled_empty, reason)
|
||||||
assert cancel_order_mock.call_count == 0
|
assert cancel_order_mock.call_count == 0
|
||||||
@ -3095,7 +3108,7 @@ def test_handle_cancel_enter_corder_empty(mocker, default_conf_usdt, limit_order
|
|||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
freqtrade._notify_enter_cancel = MagicMock()
|
freqtrade._notify_enter_cancel = MagicMock()
|
||||||
trade = mock_trade_usdt_4(fee, is_short)
|
trade = mock_trade_usdt_4(fee, is_short)
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
l_order['filled'] = 0.0
|
l_order['filled'] = 0.0
|
||||||
l_order['status'] = 'open'
|
l_order['status'] = 'open'
|
||||||
@ -3261,7 +3274,7 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_
|
|||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
rpc_mock.reset_mock()
|
rpc_mock.reset_mock()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade.is_short == is_short
|
assert trade.is_short == is_short
|
||||||
assert trade
|
assert trade
|
||||||
assert freqtrade.strategy.confirm_trade_exit.call_count == 0
|
assert freqtrade.strategy.confirm_trade_exit.call_count == 0
|
||||||
@ -3342,7 +3355,7 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd
|
|||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
@ -3415,7 +3428,7 @@ def test_execute_trade_exit_custom_exit_price(
|
|||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
rpc_mock.reset_mock()
|
rpc_mock.reset_mock()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
assert freqtrade.strategy.confirm_trade_exit.call_count == 0
|
assert freqtrade.strategy.confirm_trade_exit.call_count == 0
|
||||||
@ -3492,7 +3505,7 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(
|
|||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade.is_short == is_short
|
assert trade.is_short == is_short
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
@ -3566,7 +3579,7 @@ def test_execute_trade_exit_sloe_cancel_exception(
|
|||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
PairLock.session = MagicMock()
|
PairLock.session = MagicMock()
|
||||||
|
|
||||||
freqtrade.config['dry_run'] = False
|
freqtrade.config['dry_run'] = False
|
||||||
@ -3611,7 +3624,7 @@ def test_execute_trade_exit_with_stoploss_on_exchange(
|
|||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
trades = [trade]
|
trades = [trade]
|
||||||
@ -3631,7 +3644,7 @@ def test_execute_trade_exit_with_stoploss_on_exchange(
|
|||||||
exit_check=ExitCheckTuple(exit_type=ExitType.STOP_LOSS)
|
exit_check=ExitCheckTuple(exit_type=ExitType.STOP_LOSS)
|
||||||
)
|
)
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
assert cancel_order.call_count == 1
|
assert cancel_order.call_count == 1
|
||||||
@ -3669,7 +3682,7 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(
|
|||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trades = [trade]
|
trades = [trade]
|
||||||
assert trade.stoploss_order_id is None
|
assert trade.stoploss_order_id is None
|
||||||
|
|
||||||
@ -3755,7 +3768,7 @@ def test_execute_trade_exit_market_order(
|
|||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
@ -3830,7 +3843,7 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_u
|
|||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
@ -3898,7 +3911,7 @@ def test_exit_profit_only(
|
|||||||
exit_type=ExitType.NONE))
|
exit_type=ExitType.NONE))
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade.is_short == is_short
|
assert trade.is_short == is_short
|
||||||
oobj = Order.parse_from_ccxt_object(limit_order[eside], limit_order[eside]['symbol'], eside)
|
oobj = Order.parse_from_ccxt_object(limit_order[eside], limit_order[eside]['symbol'], eside)
|
||||||
trade.update_order(limit_order[eside])
|
trade.update_order(limit_order[eside])
|
||||||
@ -3941,7 +3954,7 @@ def test_sell_not_enough_balance(default_conf_usdt, limit_order, limit_order_ope
|
|||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
amnt = trade.amount
|
amnt = trade.amount
|
||||||
|
|
||||||
oobj = Order.parse_from_ccxt_object(limit_order['buy'], limit_order['buy']['symbol'], 'buy')
|
oobj = Order.parse_from_ccxt_object(limit_order['buy'], limit_order['buy']['symbol'], 'buy')
|
||||||
@ -4009,7 +4022,7 @@ def test_locked_pairs(default_conf_usdt, ticker_usdt, fee,
|
|||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
@ -4064,7 +4077,7 @@ def test_ignore_roi_if_entry_signal(default_conf_usdt, limit_order, limit_order_
|
|||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
oobj = Order.parse_from_ccxt_object(
|
oobj = Order.parse_from_ccxt_object(
|
||||||
limit_order[eside], limit_order[eside]['symbol'], eside)
|
limit_order[eside], limit_order[eside]['symbol'], eside)
|
||||||
@ -4114,7 +4127,7 @@ def test_trailing_stop_loss(default_conf_usdt, limit_order_open,
|
|||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade.is_short == is_short
|
assert trade.is_short == is_short
|
||||||
assert freqtrade.handle_trade(trade) is False
|
assert freqtrade.handle_trade(trade) is False
|
||||||
|
|
||||||
@ -4189,7 +4202,7 @@ def test_trailing_stop_loss_positive(
|
|||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade.is_short == is_short
|
assert trade.is_short == is_short
|
||||||
oobj = Order.parse_from_ccxt_object(limit_order[eside], limit_order[eside]['symbol'], eside)
|
oobj = Order.parse_from_ccxt_object(limit_order[eside], limit_order[eside]['symbol'], eside)
|
||||||
trade.update_order(limit_order[eside])
|
trade.update_order(limit_order[eside])
|
||||||
@ -4286,7 +4299,7 @@ def test_disable_ignore_roi_if_entry_signal(default_conf_usdt, limit_order, limi
|
|||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
|
|
||||||
oobj = Order.parse_from_ccxt_object(
|
oobj = Order.parse_from_ccxt_object(
|
||||||
@ -4752,7 +4765,7 @@ def test_order_book_depth_of_market(
|
|||||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
if is_high_delta:
|
if is_high_delta:
|
||||||
assert trade is None
|
assert trade is None
|
||||||
else:
|
else:
|
||||||
@ -4763,7 +4776,7 @@ def test_order_book_depth_of_market(
|
|||||||
assert trade.open_date is not None
|
assert trade.open_date is not None
|
||||||
assert trade.exchange == 'binance'
|
assert trade.exchange == 'binance'
|
||||||
|
|
||||||
assert len(Trade.query.all()) == 1
|
assert len(Trade.session.scalars(select(Trade)).all()) == 1
|
||||||
|
|
||||||
# Simulate fulfilled LIMIT_BUY order for trade
|
# Simulate fulfilled LIMIT_BUY order for trade
|
||||||
oobj = Order.parse_from_ccxt_object(
|
oobj = Order.parse_from_ccxt_object(
|
||||||
@ -4860,7 +4873,7 @@ def test_order_book_exit_pricing(
|
|||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
time.sleep(0.01) # Race condition fix
|
time.sleep(0.01) # Race condition fix
|
||||||
@ -4932,7 +4945,7 @@ def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_
|
|||||||
|
|
||||||
n = bot.enter_positions()
|
n = bot.enter_positions()
|
||||||
assert n == 2
|
assert n == 2
|
||||||
trades = Trade.query.all()
|
trades = Trade.session.scalars(select(Trade)).all()
|
||||||
assert len(trades) == 2
|
assert len(trades) == 2
|
||||||
|
|
||||||
bot.config['max_open_trades'] = 3
|
bot.config['max_open_trades'] = 3
|
||||||
@ -4965,7 +4978,7 @@ def test_cancel_all_open_orders(mocker, default_conf_usdt, fee, limit_order, lim
|
|||||||
|
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||||
create_mock_trades(fee, is_short=is_short)
|
create_mock_trades(fee, is_short=is_short)
|
||||||
trades = Trade.query.all()
|
trades = Trade.session.scalars(select(Trade)).all()
|
||||||
assert len(trades) == MOCK_TRADE_COUNT
|
assert len(trades) == MOCK_TRADE_COUNT
|
||||||
freqtrade.cancel_all_open_orders()
|
freqtrade.cancel_all_open_orders()
|
||||||
assert buy_mock.call_count == buy_calls
|
assert buy_mock.call_count == buy_calls
|
||||||
@ -4981,7 +4994,7 @@ def test_check_for_open_trades(mocker, default_conf_usdt, fee, is_short):
|
|||||||
assert freqtrade.rpc.send_msg.call_count == 0
|
assert freqtrade.rpc.send_msg.call_count == 0
|
||||||
|
|
||||||
create_mock_trades(fee, is_short)
|
create_mock_trades(fee, is_short)
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
trade.is_short = is_short
|
trade.is_short = is_short
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
|
|
||||||
@ -5149,7 +5162,7 @@ def test_reupdate_enter_order_fees(mocker, default_conf_usdt, fee, caplog, is_sh
|
|||||||
exchange='binance',
|
exchange='binance',
|
||||||
is_short=is_short
|
is_short=is_short
|
||||||
)
|
)
|
||||||
Trade.query.session.add(trade)
|
Trade.session.add(trade)
|
||||||
|
|
||||||
freqtrade.handle_insufficient_funds(trade)
|
freqtrade.handle_insufficient_funds(trade)
|
||||||
# assert log_has_re(r"Trying to reupdate buy fees for .*", caplog)
|
# assert log_has_re(r"Trying to reupdate buy fees for .*", caplog)
|
||||||
@ -5546,10 +5559,10 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
|||||||
assert freqtrade.execute_entry(pair, stake_amount)
|
assert freqtrade.execute_entry(pair, stake_amount)
|
||||||
# Should create an closed trade with an no open order id
|
# Should create an closed trade with an no open order id
|
||||||
# Order is filled and trade is open
|
# Order is filled and trade is open
|
||||||
orders = Order.query.all()
|
orders = Order.session.scalars(select(Order)).all()
|
||||||
assert orders
|
assert orders
|
||||||
assert len(orders) == 1
|
assert len(orders) == 1
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
@ -5559,7 +5572,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
|||||||
# Assume it does nothing since order is closed and trade is open
|
# Assume it does nothing since order is closed and trade is open
|
||||||
freqtrade.update_trades_without_assigned_fees()
|
freqtrade.update_trades_without_assigned_fees()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
@ -5569,7 +5582,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
|||||||
|
|
||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
@ -5595,10 +5608,10 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
|||||||
mocker.patch(f'{EXMS}.fetch_order_or_stoploss_order', MagicMock(return_value=open_dca_order_1))
|
mocker.patch(f'{EXMS}.fetch_order_or_stoploss_order', MagicMock(return_value=open_dca_order_1))
|
||||||
assert freqtrade.execute_entry(pair, stake_amount, trade=trade)
|
assert freqtrade.execute_entry(pair, stake_amount, trade=trade)
|
||||||
|
|
||||||
orders = Order.query.all()
|
orders = Order.session.scalars(select(Order)).all()
|
||||||
assert orders
|
assert orders
|
||||||
assert len(orders) == 2
|
assert len(orders) == 2
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_order_id == '651'
|
assert trade.open_order_id == '651'
|
||||||
assert trade.open_rate == 11
|
assert trade.open_rate == 11
|
||||||
@ -5628,14 +5641,14 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
|||||||
mocker.patch(f'{EXMS}.fetch_order_or_stoploss_order', fetch_order_mm)
|
mocker.patch(f'{EXMS}.fetch_order_or_stoploss_order', fetch_order_mm)
|
||||||
freqtrade.update_trades_without_assigned_fees()
|
freqtrade.update_trades_without_assigned_fees()
|
||||||
|
|
||||||
orders = Order.query.all()
|
orders = Order.session.scalars(select(Order)).all()
|
||||||
assert orders
|
assert orders
|
||||||
assert len(orders) == 2
|
assert len(orders) == 2
|
||||||
# Assert that the trade is found as open and without fees
|
# Assert that the trade is found as open and without fees
|
||||||
trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
|
trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
|
||||||
assert len(trades) == 1
|
assert len(trades) == 1
|
||||||
# Assert trade is as expected
|
# Assert trade is as expected
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_order_id == '651'
|
assert trade.open_order_id == '651'
|
||||||
assert trade.open_rate == 11
|
assert trade.open_rate == 11
|
||||||
@ -5672,14 +5685,14 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
|||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
|
|
||||||
# Assert trade is as expected (averaged dca)
|
# Assert trade is as expected (averaged dca)
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
assert pytest.approx(trade.open_rate) == 9.90909090909
|
assert pytest.approx(trade.open_rate) == 9.90909090909
|
||||||
assert trade.amount == 22
|
assert trade.amount == 22
|
||||||
assert pytest.approx(trade.stake_amount) == 218
|
assert pytest.approx(trade.stake_amount) == 218
|
||||||
|
|
||||||
orders = Order.query.all()
|
orders = Order.session.scalars(select(Order)).all()
|
||||||
assert orders
|
assert orders
|
||||||
assert len(orders) == 2
|
assert len(orders) == 2
|
||||||
|
|
||||||
@ -5714,14 +5727,14 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
|||||||
assert freqtrade.execute_entry(pair, stake_amount, trade=trade)
|
assert freqtrade.execute_entry(pair, stake_amount, trade=trade)
|
||||||
|
|
||||||
# Assert trade is as expected (averaged dca)
|
# Assert trade is as expected (averaged dca)
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
assert pytest.approx(trade.open_rate) == 8.729729729729
|
assert pytest.approx(trade.open_rate) == 8.729729729729
|
||||||
assert trade.amount == 37
|
assert trade.amount == 37
|
||||||
assert trade.stake_amount == 323
|
assert trade.stake_amount == 323
|
||||||
|
|
||||||
orders = Order.query.all()
|
orders = Order.session.scalars(select(Order)).all()
|
||||||
assert orders
|
assert orders
|
||||||
assert len(orders) == 3
|
assert len(orders) == 3
|
||||||
|
|
||||||
@ -5752,7 +5765,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
|||||||
sub_trade_amt=15)
|
sub_trade_amt=15)
|
||||||
|
|
||||||
# Assert trade is as expected (averaged dca)
|
# Assert trade is as expected (averaged dca)
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
assert trade.is_open
|
assert trade.is_open
|
||||||
@ -5760,7 +5773,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
|||||||
assert trade.stake_amount == 192.05405405405406
|
assert trade.stake_amount == 192.05405405405406
|
||||||
assert pytest.approx(trade.open_rate) == 8.729729729729
|
assert pytest.approx(trade.open_rate) == 8.729729729729
|
||||||
|
|
||||||
orders = Order.query.all()
|
orders = Order.session.scalars(select(Order)).all()
|
||||||
assert orders
|
assert orders
|
||||||
assert len(orders) == 4
|
assert len(orders) == 4
|
||||||
|
|
||||||
@ -5825,10 +5838,10 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
|||||||
assert freqtrade.execute_entry(pair, amount)
|
assert freqtrade.execute_entry(pair, amount)
|
||||||
# Should create an closed trade with an no open order id
|
# Should create an closed trade with an no open order id
|
||||||
# Order is filled and trade is open
|
# Order is filled and trade is open
|
||||||
orders = Order.query.all()
|
orders = Order.session.scalars(select(Order)).all()
|
||||||
assert orders
|
assert orders
|
||||||
assert len(orders) == 1
|
assert len(orders) == 1
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
@ -5838,7 +5851,7 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
|||||||
# Assume it does nothing since order is closed and trade is open
|
# Assume it does nothing since order is closed and trade is open
|
||||||
freqtrade.update_trades_without_assigned_fees()
|
freqtrade.update_trades_without_assigned_fees()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
@ -5848,7 +5861,7 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
|||||||
|
|
||||||
freqtrade.manage_open_orders()
|
freqtrade.manage_open_orders()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
@ -5884,7 +5897,7 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
|||||||
assert len(trades) == 1
|
assert len(trades) == 1
|
||||||
# Assert trade is as expected (averaged dca)
|
# Assert trade is as expected (averaged dca)
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
assert trade.amount == 50
|
assert trade.amount == 50
|
||||||
@ -5893,7 +5906,7 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
|||||||
assert pytest.approx(trade.realized_profit) == -152.375
|
assert pytest.approx(trade.realized_profit) == -152.375
|
||||||
assert pytest.approx(trade.close_profit_abs) == -152.375
|
assert pytest.approx(trade.close_profit_abs) == -152.375
|
||||||
|
|
||||||
orders = Order.query.all()
|
orders = Order.session.scalars(select(Order)).all()
|
||||||
assert orders
|
assert orders
|
||||||
assert len(orders) == 2
|
assert len(orders) == 2
|
||||||
# Make sure the closed order is found as the second order.
|
# Make sure the closed order is found as the second order.
|
||||||
@ -5926,7 +5939,7 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
|||||||
sub_trade_amt=amount)
|
sub_trade_amt=amount)
|
||||||
# Assert trade is as expected (averaged dca)
|
# Assert trade is as expected (averaged dca)
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
assert trade.amount == 50
|
assert trade.amount == 50
|
||||||
@ -5935,7 +5948,7 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
|||||||
# Trade fully realized
|
# Trade fully realized
|
||||||
assert pytest.approx(trade.realized_profit) == 94.25
|
assert pytest.approx(trade.realized_profit) == 94.25
|
||||||
assert pytest.approx(trade.close_profit_abs) == 94.25
|
assert pytest.approx(trade.close_profit_abs) == 94.25
|
||||||
orders = Order.query.all()
|
orders = Order.session.scalars(select(Order)).all()
|
||||||
assert orders
|
assert orders
|
||||||
assert len(orders) == 3
|
assert len(orders) == 3
|
||||||
|
|
||||||
@ -6020,11 +6033,11 @@ def test_position_adjust3(mocker, default_conf_usdt, fee, data) -> None:
|
|||||||
exit_check=ExitCheckTuple(exit_type=ExitType.PARTIAL_EXIT),
|
exit_check=ExitCheckTuple(exit_type=ExitType.PARTIAL_EXIT),
|
||||||
sub_trade_amt=amount)
|
sub_trade_amt=amount)
|
||||||
|
|
||||||
orders1 = Order.query.all()
|
orders1 = Order.session.scalars(select(Order)).all()
|
||||||
assert orders1
|
assert orders1
|
||||||
assert len(orders1) == idx + 1
|
assert len(orders1) == idx + 1
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
if idx < len(data) - 1:
|
if idx < len(data) - 1:
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
@ -6039,7 +6052,7 @@ def test_position_adjust3(mocker, default_conf_usdt, fee, data) -> None:
|
|||||||
order_obj = trade.select_order(order[0], False)
|
order_obj = trade.select_order(order[0], False)
|
||||||
assert order_obj.order_id == f'60{idx}'
|
assert order_obj.order_id == f'60{idx}'
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.session.scalars(select(Trade)).first()
|
||||||
assert trade
|
assert trade
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
assert trade.is_open is False
|
assert trade.is_open is False
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from sqlalchemy import select
|
||||||
|
|
||||||
from freqtrade.enums import ExitCheckTuple, ExitType, TradingMode
|
from freqtrade.enums import ExitCheckTuple, ExitType, TradingMode
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
@ -91,7 +92,7 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
|||||||
assert freqtrade.strategy.confirm_trade_exit.call_count == 0
|
assert freqtrade.strategy.confirm_trade_exit.call_count == 0
|
||||||
wallets_mock.reset_mock()
|
wallets_mock.reset_mock()
|
||||||
|
|
||||||
trades = Trade.query.all()
|
trades = Trade.session.scalars(select(Trade)).all()
|
||||||
# Make sure stoploss-order is open and trade is bought (since we mock update_trade_state)
|
# Make sure stoploss-order is open and trade is bought (since we mock update_trade_state)
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
stoploss_order_closed['id'] = '3'
|
stoploss_order_closed['id'] = '3'
|
||||||
@ -179,13 +180,13 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, mocker, balance_rati
|
|||||||
n = freqtrade.enter_positions()
|
n = freqtrade.enter_positions()
|
||||||
assert n == 4
|
assert n == 4
|
||||||
|
|
||||||
trades = Trade.query.all()
|
trades = Trade.session.scalars(select(Trade)).all()
|
||||||
assert len(trades) == 4
|
assert len(trades) == 4
|
||||||
assert freqtrade.wallets.get_trade_stake_amount('XRP/BTC') == result1
|
assert freqtrade.wallets.get_trade_stake_amount('XRP/BTC') == result1
|
||||||
|
|
||||||
rpc._rpc_force_entry('TKN/BTC', None)
|
rpc._rpc_force_entry('TKN/BTC', None)
|
||||||
|
|
||||||
trades = Trade.query.all()
|
trades = Trade.session.scalars(select(Trade)).all()
|
||||||
assert len(trades) == 5
|
assert len(trades) == 5
|
||||||
|
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
|
Loading…
Reference in New Issue
Block a user