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():
self.enter_positions()
Trade.session.flush()
Trade.query.session.flush()
def process_stopped(self) -> None:
"""
@ -621,8 +621,8 @@ class FreqtradeBot(LoggingMixin):
if order_status == 'closed':
self.update_trade_state(trade, order_id, order)
Trade.session.add(trade)
Trade.session.flush()
Trade.query.session.add(trade)
Trade.query.session.flush()
# Updating wallets
self.wallets.update()
@ -1205,7 +1205,7 @@ class FreqtradeBot(LoggingMixin):
# In case of market sell orders the order can be closed immediately
if order.get('status', 'unknown') == 'closed':
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
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
# Scoped sessions proxy requests to the appropriate thread-local session.
# We should use the scoped_session object - not a seperately initialized version
Trade.session = scoped_session(sessionmaker(bind=engine, autoflush=True, autocommit=True))
Trade.query = Trade.session.query_property()
# Copy session attributes to order object too
Order.session = Trade.session
Order.query = Order.session.query_property()
PairLock.session = Trade.session
PairLock.query = PairLock.session.query_property()
Trade._session = scoped_session(sessionmaker(bind=engine, autoflush=True, autocommit=True))
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()
_DECL_BASE.metadata.create_all(engine)
@ -81,7 +78,7 @@ def cleanup_db() -> None:
Flushes all pending operations to disk.
:return: None
"""
Trade.session.flush()
Trade.query.session.flush()
def clean_dry_run_db() -> None:
@ -677,7 +674,7 @@ class LocalTrade():
in stake currency
"""
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()
else:
total_open_stake_amount = sum(
@ -689,7 +686,7 @@ class LocalTrade():
"""
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,
func.sum(Trade.close_profit).label('profit_sum'),
func.count(Trade.pair).label('count')
@ -712,7 +709,7 @@ class LocalTrade():
Get best pair with closed trade.
: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')
).filter(Trade.is_open.is_(False)) \
.group_by(Trade.pair) \
@ -805,10 +802,10 @@ class Trade(_DECL_BASE, LocalTrade):
def delete(self) -> None:
for order in self.orders:
Order.session.delete(order)
Order.query.session.delete(order)
Trade.session.delete(self)
Trade.session.flush()
Trade.query.session.delete(self)
Trade.query.session.flush()
@staticmethod
def get_trades_proxy(*, pair: str = None, is_open: bool = None,

View File

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

View File

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

View File

@ -197,7 +197,7 @@ def create_mock_trades(fee, use_db: bool = True):
"""
def add_trade(trade):
if use_db:
Trade.session.add(trade)
Trade.query.session.add(trade)
else:
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)
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,
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)
caplog.clear()
# 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,
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,
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)
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,
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)
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,
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)
caplog.clear()
# 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,
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.session.add(generate_mock_trade(
Trade.query.session.add(generate_mock_trade(
'ETH/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value,
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()
# 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,
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)
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,
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 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,
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)
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,
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_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,
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()
# 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,
min_ago_open=20, min_ago_close=10, profit_rate=1.15,
))
assert not freqtrade.protections.stop_per_pair('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,
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')
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,
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,
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,
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.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,
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_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,
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)
# 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,
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()
# 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,
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
create_mock_trades(fee)
Trade.session.flush()
Trade.query.session.flush()
rc = client_get(client, f"{BASE_URI}/trades")
assert_response(rc)
@ -538,7 +538,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets):
assert_response(rc, 502)
create_mock_trades(fee)
Trade.session.flush()
Trade.query.session.flush()
ftbot.strategy.order_types['stoploss_on_exchange'] = True
trades = Trade.query.all()
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.session.add(trade)
Trade.query.session.add(trade)
trade = Trade(
pair='XRP/ETH',
@ -735,8 +735,8 @@ def test_api_performance(botclient, mocker, ticker, fee):
close_rate=0.391
)
trade.close_profit = trade.calc_profit_ratio()
Trade.session.add(trade)
Trade.session.flush()
Trade.query.session.add(trade)
Trade.query.session.flush()
rc = client_get(client, f"{BASE_URI}/performance")
assert_response(rc)
@ -764,7 +764,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
trades = Trade.get_open_trades()
trades[0].open_order_id = None
ftbot.exit_positions(trades)
Trade.session.flush()
Trade.query.session.flush()
rc = client_get(client, f"{BASE_URI}/status")
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']
# create open trade not in whitelist
Trade.session.add(Trade(
Trade.query.session.add(Trade(
pair=pair,
stake_amount=0.001,
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,
exchange='bittrex',
))
Trade.session.add(Trade(
Trade.query.session.add(Trade(
pair='ETH/BTC',
stake_amount=0.001,
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!!
mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError))
patch_exchange(mocker)
Trade.session = MagicMock()
amount = sum(x['amount'] for x in trades_for_order)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
trade = Trade(
@ -1805,7 +1804,6 @@ def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_
# fetch_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError))
patch_exchange(mocker)
Trade.session = MagicMock()
amount = sum(x['amount'] for x in trades_for_order)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
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)
patch_exchange(mocker)
Trade.session = MagicMock()
amount = limit_sell_order["amount"]
freqtrade = get_patched_freqtradebot(mocker, default_conf)
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)
Trade.session.add(open_trade)
Trade.query.session.add(open_trade)
# Ensure default is to return empty (so not mocked yet)
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)
Trade.session.add(open_trade)
Trade.query.session.add(open_trade)
freqtrade.strategy.check_buy_timeout = MagicMock(return_value=False)
# 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)
Trade.session.add(open_trade)
Trade.query.session.add(open_trade)
# check it does cancel buy orders over the time limit
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)
Trade.session.add(open_trade)
Trade.query.session.add(open_trade)
# check it does cancel buy orders over the time limit
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.is_open = False
Trade.session.add(open_trade)
Trade.query.session.add(open_trade)
# Ensure default is false
freqtrade.check_handle_timedout()
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.is_open = False
Trade.session.add(open_trade)
Trade.query.session.add(open_trade)
freqtrade.strategy.check_sell_timeout = MagicMock(return_value=False)
# 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.is_open = False
Trade.session.add(open_trade)
Trade.query.session.add(open_trade)
# check it does cancel sell orders over the time limit
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)
Trade.session.add(open_trade)
Trade.query.session.add(open_trade)
# check it does cancel buy orders over the time limit
# 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_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
# and apply fees if necessary.
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_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
# and apply fees if necessary.
freqtrade.check_handle_timedout()
@ -2463,7 +2460,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke
)
freqtrade = FreqtradeBot(default_conf)
Trade.session.add(open_trade)
Trade.query.session.add(open_trade)
freqtrade.check_handle_timedout()
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._notify_buy_cancel = MagicMock()
Trade.session = MagicMock()
trade = MagicMock()
trade.pair = 'LTC/ETH'
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')
freqtrade = FreqtradeBot(default_conf)
Trade.session = MagicMock()
reason = CANCEL_REASON['TIMEOUT']
trade = MagicMock()
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._notify_buy_cancel = MagicMock()
Trade.session = MagicMock()
trade = MagicMock()
trade.pair = 'LTC/ETH'
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()
trade = Trade.query.first()
Trade.session = MagicMock()
PairLock.session = MagicMock()
freqtrade.config['dry_run'] = False
@ -4422,7 +4415,7 @@ def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog):
open_rate=0.01,
exchange='bittrex',
)
Trade.session.add(trade)
Trade.query.session.add(trade)
freqtrade.reupdate_buy_order_fees(trade)
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()
assert freqtrade.strategy.confirm_trade_exit.call_count == 0
wallets_mock.reset_mock()
Trade.session = MagicMock()
trades = Trade.query.all()
# 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):
# Check if init create a session
init_db(default_conf['db_url'], default_conf['dry_run'])
assert hasattr(Trade, 'session')
assert 'scoped_session' in type(Trade.session).__name__
assert hasattr(Trade, '_session')
assert 'scoped_session' in type(Trade._session).__name__
def test_init_custom_db_url(default_conf, tmpdir):
@ -403,7 +403,7 @@ def test_clean_dry_run_db(default_conf, fee):
exchange='bittrex',
open_order_id='dry_run_buy_12345'
)
Trade.session.add(trade)
Trade.query.session.add(trade)
trade = Trade(
pair='ETC/BTC',
@ -415,7 +415,7 @@ def test_clean_dry_run_db(default_conf, fee):
exchange='bittrex',
open_order_id='dry_run_sell_12345'
)
Trade.session.add(trade)
Trade.query.session.add(trade)
# Simulate prod entry
trade = Trade(
@ -428,7 +428,7 @@ def test_clean_dry_run_db(default_conf, fee):
exchange='bittrex',
open_order_id='prod_buy_12345'
)
Trade.session.add(trade)
Trade.query.session.add(trade)
# We have 3 entries: 2 dry_run, 1 prod
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.initial_stop_loss == 0.95
assert trade.initial_stop_loss_pct == -0.05
Trade.session.add(trade)
Trade.query.session.add(trade)
# Lower stoploss
Trade.stoploss_reinitialization(0.06)