Merge pull request #4671 from freqtrade/fix/sqlalchemy

sqlalchemy database locked bug
This commit is contained in:
Matthias 2021-04-05 14:09:35 +02:00 committed by GitHub
commit 431f0a3db4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 69 additions and 80 deletions

View File

@ -187,7 +187,7 @@ class FreqtradeBot(LoggingMixin):
if self.get_free_open_trades(): if self.get_free_open_trades():
self.enter_positions() self.enter_positions()
Trade.session.flush() Trade.query.session.flush()
def process_stopped(self) -> None: def process_stopped(self) -> None:
""" """
@ -621,8 +621,8 @@ class FreqtradeBot(LoggingMixin):
if order_status == 'closed': if order_status == 'closed':
self.update_trade_state(trade, order_id, order) self.update_trade_state(trade, order_id, order)
Trade.session.add(trade) Trade.query.session.add(trade)
Trade.session.flush() Trade.query.session.flush()
# Updating wallets # Updating wallets
self.wallets.update() self.wallets.update()
@ -1205,7 +1205,7 @@ class FreqtradeBot(LoggingMixin):
# In case of market sell orders the order can be closed immediately # In case of market sell orders the order can be closed immediately
if order.get('status', 'unknown') == 'closed': if order.get('status', 'unknown') == 'closed':
self.update_trade_state(trade, trade.open_order_id, order) self.update_trade_state(trade, trade.open_order_id, order)
Trade.session.flush() Trade.query.session.flush()
# Lock pair for one candle to prevent immediate rebuys # Lock pair for one candle to prevent immediate rebuys
self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc), self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc),

View File

@ -59,13 +59,10 @@ def init_db(db_url: str, clean_open_orders: bool = False) -> 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=True, autocommit=True)) Trade._session = scoped_session(sessionmaker(bind=engine, autoflush=True, autocommit=True))
Trade.query = Trade.session.query_property() Trade.query = Trade._session.query_property()
# Copy session attributes to order object too Order.query = Trade._session.query_property()
Order.session = Trade.session PairLock.query = Trade._session.query_property()
Order.query = Order.session.query_property()
PairLock.session = Trade.session
PairLock.query = PairLock.session.query_property()
previous_tables = inspect(engine).get_table_names() previous_tables = inspect(engine).get_table_names()
_DECL_BASE.metadata.create_all(engine) _DECL_BASE.metadata.create_all(engine)
@ -81,7 +78,7 @@ def cleanup_db() -> None:
Flushes all pending operations to disk. Flushes all pending operations to disk.
:return: None :return: None
""" """
Trade.session.flush() Trade.query.session.flush()
def clean_dry_run_db() -> None: def clean_dry_run_db() -> None:
@ -677,7 +674,7 @@ class LocalTrade():
in stake currency in stake currency
""" """
if Trade.use_db: if Trade.use_db:
total_open_stake_amount = Trade.session.query( total_open_stake_amount = Trade.query.with_entities(
func.sum(Trade.stake_amount)).filter(Trade.is_open.is_(True)).scalar() func.sum(Trade.stake_amount)).filter(Trade.is_open.is_(True)).scalar()
else: else:
total_open_stake_amount = sum( total_open_stake_amount = sum(
@ -689,7 +686,7 @@ class LocalTrade():
""" """
Returns List of dicts containing all Trades, including profit and trade count Returns List of dicts containing all Trades, including profit and trade count
""" """
pair_rates = Trade.session.query( pair_rates = Trade.query.with_entities(
Trade.pair, Trade.pair,
func.sum(Trade.close_profit).label('profit_sum'), func.sum(Trade.close_profit).label('profit_sum'),
func.count(Trade.pair).label('count') func.count(Trade.pair).label('count')
@ -712,7 +709,7 @@ class LocalTrade():
Get best pair with closed trade. Get best pair with closed trade.
:returns: Tuple containing (pair, profit_sum) :returns: Tuple containing (pair, profit_sum)
""" """
best_pair = Trade.session.query( best_pair = Trade.query.with_entities(
Trade.pair, func.sum(Trade.close_profit).label('profit_sum') Trade.pair, func.sum(Trade.close_profit).label('profit_sum')
).filter(Trade.is_open.is_(False)) \ ).filter(Trade.is_open.is_(False)) \
.group_by(Trade.pair) \ .group_by(Trade.pair) \
@ -805,10 +802,10 @@ class Trade(_DECL_BASE, LocalTrade):
def delete(self) -> None: def delete(self) -> None:
for order in self.orders: for order in self.orders:
Order.session.delete(order) Order.query.session.delete(order)
Trade.session.delete(self) Trade.query.session.delete(self)
Trade.session.flush() Trade.query.session.flush()
@staticmethod @staticmethod
def get_trades_proxy(*, pair: str = None, is_open: bool = None, def get_trades_proxy(*, pair: str = None, is_open: bool = None,

View File

@ -48,8 +48,8 @@ class PairLocks():
active=True active=True
) )
if PairLocks.use_db: if PairLocks.use_db:
PairLock.session.add(lock) PairLock.query.session.add(lock)
PairLock.session.flush() PairLock.query.session.flush()
else: else:
PairLocks.locks.append(lock) PairLocks.locks.append(lock)
@ -99,7 +99,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.session.flush() PairLock.query.session.flush()
@staticmethod @staticmethod
def is_global_lock(now: Optional[datetime] = None) -> bool: def is_global_lock(now: Optional[datetime] = None) -> bool:

View File

@ -558,7 +558,7 @@ class RPC:
# Execute sell for all open orders # Execute sell for all open orders
for trade in Trade.get_open_trades(): for trade in Trade.get_open_trades():
_exec_forcesell(trade) _exec_forcesell(trade)
Trade.session.flush() Trade.query.session.flush()
self._freqtrade.wallets.update() self._freqtrade.wallets.update()
return {'result': 'Created sell orders for all open trades.'} return {'result': 'Created sell orders for all open trades.'}
@ -571,7 +571,7 @@ class RPC:
raise RPCException('invalid argument') raise RPCException('invalid argument')
_exec_forcesell(trade) _exec_forcesell(trade)
Trade.session.flush() Trade.query.session.flush()
self._freqtrade.wallets.update() self._freqtrade.wallets.update()
return {'result': f'Created sell order for trade {trade_id}.'} return {'result': f'Created sell order for trade {trade_id}.'}
@ -696,7 +696,7 @@ class RPC:
lock.lock_end_time = datetime.now(timezone.utc) lock.lock_end_time = datetime.now(timezone.utc)
# session is always the same # session is always the same
PairLock.session.flush() PairLock.query.session.flush()
return self._rpc_locks() return self._rpc_locks()

View File

@ -197,7 +197,7 @@ def create_mock_trades(fee, use_db: bool = True):
""" """
def add_trade(trade): def add_trade(trade):
if use_db: if use_db:
Trade.session.add(trade) Trade.query.session.add(trade)
else: else:
LocalTrade.add_bt_trade(trade) LocalTrade.add_bt_trade(trade)

View File

@ -91,7 +91,7 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog):
assert not log_has_re(message, caplog) assert not log_has_re(message, caplog)
caplog.clear() caplog.clear()
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=200, min_ago_close=30, min_ago_open=200, min_ago_close=30,
)) ))
@ -100,12 +100,12 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog):
assert not log_has_re(message, caplog) assert not log_has_re(message, caplog)
caplog.clear() caplog.clear()
# This trade does not count, as it's closed too long ago # This trade does not count, as it's closed too long ago
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'BCH/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'BCH/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=250, min_ago_close=100, min_ago_open=250, min_ago_close=100,
)) ))
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'ETH/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'ETH/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=240, min_ago_close=30, min_ago_open=240, min_ago_close=30,
)) ))
@ -114,7 +114,7 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog):
assert not log_has_re(message, caplog) assert not log_has_re(message, caplog)
caplog.clear() caplog.clear()
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'LTC/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'LTC/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=180, min_ago_close=30, min_ago_open=180, min_ago_close=30,
)) ))
@ -148,7 +148,7 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
assert not log_has_re(message, caplog) assert not log_has_re(message, caplog)
caplog.clear() caplog.clear()
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
pair, fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, pair, fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=200, min_ago_close=30, profit_rate=0.9, min_ago_open=200, min_ago_close=30, profit_rate=0.9,
)) ))
@ -158,12 +158,12 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
assert not log_has_re(message, caplog) assert not log_has_re(message, caplog)
caplog.clear() caplog.clear()
# This trade does not count, as it's closed too long ago # This trade does not count, as it's closed too long ago
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
pair, fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, pair, fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=250, min_ago_close=100, profit_rate=0.9, min_ago_open=250, min_ago_close=100, profit_rate=0.9,
)) ))
# Trade does not count for per pair stop as it's the wrong pair. # Trade does not count for per pair stop as it's the wrong pair.
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'ETH/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'ETH/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=240, min_ago_close=30, profit_rate=0.9, min_ago_open=240, min_ago_close=30, profit_rate=0.9,
)) ))
@ -178,7 +178,7 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
caplog.clear() caplog.clear()
# 2nd Trade that counts with correct pair # 2nd Trade that counts with correct pair
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
pair, fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, pair, fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=180, min_ago_close=30, profit_rate=0.9, min_ago_open=180, min_ago_close=30, profit_rate=0.9,
)) ))
@ -203,7 +203,7 @@ def test_CooldownPeriod(mocker, default_conf, fee, caplog):
assert not log_has_re(message, caplog) assert not log_has_re(message, caplog)
caplog.clear() caplog.clear()
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=200, min_ago_close=30, min_ago_open=200, min_ago_close=30,
)) ))
@ -213,7 +213,7 @@ def test_CooldownPeriod(mocker, default_conf, fee, caplog):
assert PairLocks.is_pair_locked('XRP/BTC') assert PairLocks.is_pair_locked('XRP/BTC')
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'ETH/BTC', fee.return_value, False, sell_reason=SellType.ROI.value, 'ETH/BTC', fee.return_value, False, sell_reason=SellType.ROI.value,
min_ago_open=205, min_ago_close=35, min_ago_open=205, min_ago_close=35,
)) ))
@ -242,7 +242,7 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog):
assert not log_has_re(message, caplog) assert not log_has_re(message, caplog)
caplog.clear() caplog.clear()
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=800, min_ago_close=450, profit_rate=0.9, min_ago_open=800, min_ago_close=450, profit_rate=0.9,
)) ))
@ -253,7 +253,7 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog):
assert not PairLocks.is_pair_locked('XRP/BTC') assert not PairLocks.is_pair_locked('XRP/BTC')
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=200, min_ago_close=120, profit_rate=0.9, min_ago_open=200, min_ago_close=120, profit_rate=0.9,
)) ))
@ -265,14 +265,14 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog):
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
# Add positive trade # Add positive trade
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'XRP/BTC', fee.return_value, False, sell_reason=SellType.ROI.value, 'XRP/BTC', fee.return_value, False, sell_reason=SellType.ROI.value,
min_ago_open=20, min_ago_close=10, profit_rate=1.15, min_ago_open=20, min_ago_close=10, profit_rate=1.15,
)) ))
assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not freqtrade.protections.stop_per_pair('XRP/BTC')
assert not PairLocks.is_pair_locked('XRP/BTC') assert not PairLocks.is_pair_locked('XRP/BTC')
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=110, min_ago_close=20, profit_rate=0.8, min_ago_open=110, min_ago_close=20, profit_rate=0.8,
)) ))
@ -300,15 +300,15 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not freqtrade.protections.stop_per_pair('XRP/BTC')
caplog.clear() caplog.clear()
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=1000, min_ago_close=900, profit_rate=1.1, min_ago_open=1000, min_ago_close=900, profit_rate=1.1,
)) ))
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'ETH/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'ETH/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=1000, min_ago_close=900, profit_rate=1.1, min_ago_open=1000, min_ago_close=900, profit_rate=1.1,
)) ))
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'NEO/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'NEO/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=1000, min_ago_close=900, profit_rate=1.1, min_ago_open=1000, min_ago_close=900, profit_rate=1.1,
)) ))
@ -316,7 +316,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not freqtrade.protections.stop_per_pair('XRP/BTC')
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=500, min_ago_close=400, profit_rate=0.9, min_ago_open=500, min_ago_close=400, profit_rate=0.9,
)) ))
@ -326,7 +326,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
assert not PairLocks.is_pair_locked('XRP/BTC') assert not PairLocks.is_pair_locked('XRP/BTC')
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, 'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
min_ago_open=1200, min_ago_close=1100, profit_rate=0.5, min_ago_open=1200, min_ago_close=1100, profit_rate=0.5,
)) ))
@ -339,7 +339,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
assert not log_has_re(message, caplog) assert not log_has_re(message, caplog)
# Winning trade ... (should not lock, does not change drawdown!) # Winning trade ... (should not lock, does not change drawdown!)
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'XRP/BTC', fee.return_value, False, sell_reason=SellType.ROI.value, 'XRP/BTC', fee.return_value, False, sell_reason=SellType.ROI.value,
min_ago_open=320, min_ago_close=410, profit_rate=1.5, min_ago_open=320, min_ago_close=410, profit_rate=1.5,
)) ))
@ -349,7 +349,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
caplog.clear() caplog.clear()
# Add additional negative trade, causing a loss of > 15% # Add additional negative trade, causing a loss of > 15%
Trade.session.add(generate_mock_trade( Trade.query.session.add(generate_mock_trade(
'XRP/BTC', fee.return_value, False, sell_reason=SellType.ROI.value, 'XRP/BTC', fee.return_value, False, sell_reason=SellType.ROI.value,
min_ago_open=20, min_ago_close=10, profit_rate=0.8, min_ago_open=20, min_ago_close=10, profit_rate=0.8,
)) ))

View File

@ -510,7 +510,7 @@ def test_api_trades(botclient, mocker, fee, markets):
assert rc.json()['trades_count'] == 0 assert rc.json()['trades_count'] == 0
create_mock_trades(fee) create_mock_trades(fee)
Trade.session.flush() Trade.query.session.flush()
rc = client_get(client, f"{BASE_URI}/trades") rc = client_get(client, f"{BASE_URI}/trades")
assert_response(rc) assert_response(rc)
@ -538,7 +538,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets):
assert_response(rc, 502) assert_response(rc, 502)
create_mock_trades(fee) create_mock_trades(fee)
Trade.session.flush() Trade.query.session.flush()
ftbot.strategy.order_types['stoploss_on_exchange'] = True ftbot.strategy.order_types['stoploss_on_exchange'] = True
trades = Trade.query.all() trades = Trade.query.all()
trades[1].stoploss_order_id = '1234' trades[1].stoploss_order_id = '1234'
@ -720,7 +720,7 @@ def test_api_performance(botclient, mocker, ticker, fee):
) )
trade.close_profit = trade.calc_profit_ratio() trade.close_profit = trade.calc_profit_ratio()
Trade.session.add(trade) Trade.query.session.add(trade)
trade = Trade( trade = Trade(
pair='XRP/ETH', pair='XRP/ETH',
@ -735,8 +735,8 @@ def test_api_performance(botclient, mocker, ticker, fee):
close_rate=0.391 close_rate=0.391
) )
trade.close_profit = trade.calc_profit_ratio() trade.close_profit = trade.calc_profit_ratio()
Trade.session.add(trade) Trade.query.session.add(trade)
Trade.session.flush() Trade.query.session.flush()
rc = client_get(client, f"{BASE_URI}/performance") rc = client_get(client, f"{BASE_URI}/performance")
assert_response(rc) assert_response(rc)
@ -764,7 +764,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
trades = Trade.get_open_trades() trades = Trade.get_open_trades()
trades[0].open_order_id = None trades[0].open_order_id = None
ftbot.exit_positions(trades) ftbot.exit_positions(trades)
Trade.session.flush() Trade.query.session.flush()
rc = client_get(client, f"{BASE_URI}/status") rc = client_get(client, f"{BASE_URI}/status")
assert_response(rc) assert_response(rc)

View File

@ -768,7 +768,7 @@ def test_process_trade_no_whitelist_pair(default_conf, ticker, limit_buy_order,
assert pair not in default_conf['exchange']['pair_whitelist'] assert pair not in default_conf['exchange']['pair_whitelist']
# create open trade not in whitelist # create open trade not in whitelist
Trade.session.add(Trade( Trade.query.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,
@ -778,7 +778,7 @@ def test_process_trade_no_whitelist_pair(default_conf, ticker, limit_buy_order,
open_rate=0.01, open_rate=0.01,
exchange='bittrex', exchange='bittrex',
)) ))
Trade.session.add(Trade( Trade.query.session.add(Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee_open=fee.return_value, fee_open=fee.return_value,
@ -1779,7 +1779,6 @@ def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_
# fetch_order should not be called!! # fetch_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError)) mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError))
patch_exchange(mocker) patch_exchange(mocker)
Trade.session = MagicMock()
amount = sum(x['amount'] for x in trades_for_order) amount = sum(x['amount'] for x in trades_for_order)
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
trade = Trade( trade = Trade(
@ -1805,7 +1804,6 @@ def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_
# fetch_order should not be called!! # fetch_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError)) mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError))
patch_exchange(mocker) patch_exchange(mocker)
Trade.session = MagicMock()
amount = sum(x['amount'] for x in trades_for_order) amount = sum(x['amount'] for x in trades_for_order)
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
trade = Trade( trade = Trade(
@ -1868,7 +1866,6 @@ def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_orde
mocker.patch('freqtrade.wallets.Wallets.update', wallet_mock) mocker.patch('freqtrade.wallets.Wallets.update', wallet_mock)
patch_exchange(mocker) patch_exchange(mocker)
Trade.session = MagicMock()
amount = limit_sell_order["amount"] amount = limit_sell_order["amount"]
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
wallet_mock.reset_mock() wallet_mock.reset_mock()
@ -2110,7 +2107,7 @@ def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_or
) )
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
Trade.session.add(open_trade) Trade.query.session.add(open_trade)
# Ensure default is to return empty (so not mocked yet) # Ensure default is to return empty (so not mocked yet)
freqtrade.check_handle_timedout() freqtrade.check_handle_timedout()
@ -2161,7 +2158,7 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, op
) )
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
Trade.session.add(open_trade) Trade.query.session.add(open_trade)
freqtrade.strategy.check_buy_timeout = MagicMock(return_value=False) freqtrade.strategy.check_buy_timeout = MagicMock(return_value=False)
# check it does cancel buy orders over the time limit # check it does cancel buy orders over the time limit
@ -2191,7 +2188,7 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, o
) )
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
Trade.session.add(open_trade) Trade.query.session.add(open_trade)
# check it does cancel buy orders over the time limit # check it does cancel buy orders over the time limit
freqtrade.check_handle_timedout() freqtrade.check_handle_timedout()
@ -2218,7 +2215,7 @@ def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_ord
) )
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
Trade.session.add(open_trade) Trade.query.session.add(open_trade)
# check it does cancel buy orders over the time limit # check it does cancel buy orders over the time limit
freqtrade.check_handle_timedout() freqtrade.check_handle_timedout()
@ -2248,7 +2245,7 @@ def test_check_handle_timedout_sell_usercustom(default_conf, ticker, limit_sell_
open_trade.close_profit_abs = 0.001 open_trade.close_profit_abs = 0.001
open_trade.is_open = False open_trade.is_open = False
Trade.session.add(open_trade) Trade.query.session.add(open_trade)
# Ensure default is false # Ensure default is false
freqtrade.check_handle_timedout() freqtrade.check_handle_timedout()
assert cancel_order_mock.call_count == 0 assert cancel_order_mock.call_count == 0
@ -2296,7 +2293,7 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old,
open_trade.close_profit_abs = 0.001 open_trade.close_profit_abs = 0.001
open_trade.is_open = False open_trade.is_open = False
Trade.session.add(open_trade) Trade.query.session.add(open_trade)
freqtrade.strategy.check_sell_timeout = MagicMock(return_value=False) freqtrade.strategy.check_sell_timeout = MagicMock(return_value=False)
# check it does cancel sell orders over the time limit # check it does cancel sell orders over the time limit
@ -2327,7 +2324,7 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old,
open_trade.close_date = arrow.utcnow().shift(minutes=-601).datetime open_trade.close_date = arrow.utcnow().shift(minutes=-601).datetime
open_trade.is_open = False open_trade.is_open = False
Trade.session.add(open_trade) Trade.query.session.add(open_trade)
# check it does cancel sell orders over the time limit # check it does cancel sell orders over the time limit
freqtrade.check_handle_timedout() freqtrade.check_handle_timedout()
@ -2353,7 +2350,7 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old
) )
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
Trade.session.add(open_trade) Trade.query.session.add(open_trade)
# check it does cancel buy orders over the time limit # check it does cancel buy orders over the time limit
# note this is for a partially-complete buy order # note this is for a partially-complete buy order
@ -2386,7 +2383,7 @@ def test_check_handle_timedout_partial_fee(default_conf, ticker, open_trade, cap
open_trade.fee_open = fee() open_trade.fee_open = fee()
open_trade.fee_close = fee() open_trade.fee_close = fee()
Trade.session.add(open_trade) Trade.query.session.add(open_trade)
# 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.
freqtrade.check_handle_timedout() freqtrade.check_handle_timedout()
@ -2426,7 +2423,7 @@ def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade,
open_trade.fee_open = fee() open_trade.fee_open = fee()
open_trade.fee_close = fee() open_trade.fee_close = fee()
Trade.session.add(open_trade) Trade.query.session.add(open_trade)
# 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.
freqtrade.check_handle_timedout() freqtrade.check_handle_timedout()
@ -2463,7 +2460,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke
) )
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
Trade.session.add(open_trade) Trade.query.session.add(open_trade)
freqtrade.check_handle_timedout() freqtrade.check_handle_timedout()
assert log_has_re(r"Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, " assert log_has_re(r"Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, "
@ -2486,7 +2483,6 @@ def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> Non
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
freqtrade._notify_buy_cancel = MagicMock() freqtrade._notify_buy_cancel = MagicMock()
Trade.session = MagicMock()
trade = MagicMock() trade = MagicMock()
trade.pair = 'LTC/ETH' trade.pair = 'LTC/ETH'
limit_buy_order['filled'] = 0.0 limit_buy_order['filled'] = 0.0
@ -2520,7 +2516,6 @@ def test_handle_cancel_buy_exchanges(mocker, caplog, default_conf,
nofiy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot._notify_buy_cancel') nofiy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot._notify_buy_cancel')
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
Trade.session = MagicMock()
reason = CANCEL_REASON['TIMEOUT'] reason = CANCEL_REASON['TIMEOUT']
trade = MagicMock() trade = MagicMock()
trade.pair = 'LTC/ETH' trade.pair = 'LTC/ETH'
@ -2549,7 +2544,6 @@ def test_handle_cancel_buy_corder_empty(mocker, default_conf, limit_buy_order,
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
freqtrade._notify_buy_cancel = MagicMock() freqtrade._notify_buy_cancel = MagicMock()
Trade.session = MagicMock()
trade = MagicMock() trade = MagicMock()
trade.pair = 'LTC/ETH' trade.pair = 'LTC/ETH'
limit_buy_order['filled'] = 0.0 limit_buy_order['filled'] = 0.0
@ -2812,7 +2806,6 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, c
freqtrade.enter_positions() freqtrade.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
Trade.session = MagicMock()
PairLock.session = MagicMock() PairLock.session = MagicMock()
freqtrade.config['dry_run'] = False freqtrade.config['dry_run'] = False
@ -4422,7 +4415,7 @@ def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog):
open_rate=0.01, open_rate=0.01,
exchange='bittrex', exchange='bittrex',
) )
Trade.session.add(trade) Trade.query.session.add(trade)
freqtrade.reupdate_buy_order_fees(trade) freqtrade.reupdate_buy_order_fees(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)

View File

@ -89,7 +89,6 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
freqtrade.strategy.confirm_trade_entry.reset_mock() freqtrade.strategy.confirm_trade_entry.reset_mock()
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()
Trade.session = MagicMock()
trades = Trade.query.all() trades = Trade.query.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)

View File

@ -18,8 +18,8 @@ from tests.conftest import create_mock_trades, log_has, log_has_re
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'], default_conf['dry_run']) init_db(default_conf['db_url'], default_conf['dry_run'])
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):
@ -403,7 +403,7 @@ def test_clean_dry_run_db(default_conf, fee):
exchange='bittrex', exchange='bittrex',
open_order_id='dry_run_buy_12345' open_order_id='dry_run_buy_12345'
) )
Trade.session.add(trade) Trade.query.session.add(trade)
trade = Trade( trade = Trade(
pair='ETC/BTC', pair='ETC/BTC',
@ -415,7 +415,7 @@ def test_clean_dry_run_db(default_conf, fee):
exchange='bittrex', exchange='bittrex',
open_order_id='dry_run_sell_12345' open_order_id='dry_run_sell_12345'
) )
Trade.session.add(trade) Trade.query.session.add(trade)
# Simulate prod entry # Simulate prod entry
trade = Trade( trade = Trade(
@ -428,7 +428,7 @@ def test_clean_dry_run_db(default_conf, fee):
exchange='bittrex', exchange='bittrex',
open_order_id='prod_buy_12345' open_order_id='prod_buy_12345'
) )
Trade.session.add(trade) Trade.query.session.add(trade)
# We have 3 entries: 2 dry_run, 1 prod # We have 3 entries: 2 dry_run, 1 prod
assert len(Trade.query.filter(Trade.open_order_id.isnot(None)).all()) == 3 assert len(Trade.query.filter(Trade.open_order_id.isnot(None)).all()) == 3
@ -933,7 +933,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.session.add(trade) Trade.query.session.add(trade)
# Lower stoploss # Lower stoploss
Trade.stoploss_reinitialization(0.06) Trade.stoploss_reinitialization(0.06)