Merge branch 'develop' into feature/fetch-public-trades

This commit is contained in:
TheJoeSchr 2024-02-02 15:14:04 +01:00 committed by GitHub
commit 5487e02ba2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1489 additions and 1405 deletions

View File

@ -22,9 +22,11 @@ from pandas import DataFrame, concat
from freqtrade.constants import (DEFAULT_TRADES_COLUMNS, DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES, BidAsk,
BuySell, Config, EntryExit, ExchangeConfig,
ListPairsWithTimeframes, MakerTaker, OBLiteral, PairWithTimeframe)
from freqtrade.data.converter import clean_duplicate_trades, clean_ohlcv_dataframe, ohlcv_to_dataframe, trades_dict_to_list, public_trades_to_dataframe
from freqtrade.data.converter.converter import _calculate_ohlcv_candle_start_and_end
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, PriceType, TradingMode
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, PriceType, RunMode, TradingMode
from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError,
InvalidOrderException, OperationalException, PricingError,
RetryableOrderError, TemporaryError)
@ -629,7 +631,11 @@ class Exchange:
raise OperationalException(
f"Invalid timeframe '{timeframe}'. This exchange supports: {self.timeframes}")
if timeframe and timeframe_to_minutes(timeframe) < 1:
if (
timeframe
and self._config['runmode'] != RunMode.UTIL_EXCHANGE
and timeframe_to_minutes(timeframe) < 1
):
raise OperationalException("Timeframes < 1m are currently not supported by Freqtrade.")
def validate_ordertypes(self, order_types: Dict) -> None:

View File

@ -432,10 +432,6 @@ class FreqtradeBot(LoggingMixin):
try:
fo = self.exchange.fetch_order_or_stoploss_order(order.order_id, order.ft_pair,
order.ft_order_side == 'stoploss')
if order.ft_order_side == 'stoploss':
if fo and fo['status'] == 'open':
# Assume this as the open stoploss order
trade.stoploss_order_id = order.order_id
if fo:
logger.info(f"Found {order} for trade {trade}.")
self.update_trade_state(trade, order.order_id, fo,
@ -895,17 +891,15 @@ class FreqtradeBot(LoggingMixin):
def cancel_stoploss_on_exchange(self, trade: Trade) -> Trade:
# First cancelling stoploss on exchange ...
if trade.stoploss_order_id:
for oslo in trade.open_sl_orders:
try:
logger.info(f"Cancelling stoploss on exchange for {trade}")
logger.info(f"Cancelling stoploss on exchange for {trade} "
f"order: {oslo.order_id}")
co = self.exchange.cancel_stoploss_order_with_result(
trade.stoploss_order_id, trade.pair, trade.amount)
self.update_trade_state(trade, trade.stoploss_order_id, co, stoploss_order=True)
# Reset stoploss order id.
trade.stoploss_order_id = None
oslo.order_id, trade.pair, trade.amount)
self.update_trade_state(trade, oslo.order_id, co, stoploss_order=True)
except InvalidOrderException:
logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id} "
logger.exception(f"Could not cancel stoploss order {oslo.order_id} "
f"for pair {trade.pair}")
return trade
@ -1080,7 +1074,7 @@ class FreqtradeBot(LoggingMixin):
if (
not trade.has_open_orders
and not trade.stoploss_order_id
and not trade.has_open_sl_orders
and not self.wallets.check_exit_amount(trade)
):
logger.warning(
@ -1190,8 +1184,6 @@ class FreqtradeBot(LoggingMixin):
order_obj = Order.parse_from_ccxt_object(stoploss_order, trade.pair, 'stoploss',
trade.amount, stop_price)
trade.orders.append(order_obj)
trade.stoploss_order_id = str(stoploss_order['id'])
trade.stoploss_last_update = datetime.now(timezone.utc)
return True
except InsufficientFundsError as e:
logger.warning(f"Unable to place stoploss order {e}.")
@ -1199,13 +1191,11 @@ class FreqtradeBot(LoggingMixin):
self.handle_insufficient_funds(trade)
except InvalidOrderException as e:
trade.stoploss_order_id = None
logger.error(f'Unable to place a stoploss order on exchange. {e}')
logger.warning('Exiting the trade forcefully')
self.emergency_exit(trade, stop_price)
except ExchangeError:
trade.stoploss_order_id = None
logger.exception('Unable to place a stoploss order on exchange.')
return False
@ -1219,27 +1209,28 @@ class FreqtradeBot(LoggingMixin):
"""
logger.debug('Handling stoploss on exchange %s ...', trade)
stoploss_order = None
try:
# First we check if there is already a stoploss on exchange
stoploss_order = self.exchange.fetch_stoploss_order(
trade.stoploss_order_id, trade.pair) if trade.stoploss_order_id else None
except InvalidOrderException as exception:
logger.warning('Unable to fetch stoploss order: %s', exception)
stoploss_orders = []
for slo in trade.open_sl_orders:
stoploss_order = None
try:
# First we check if there is already a stoploss on exchange
stoploss_order = self.exchange.fetch_stoploss_order(
slo.order_id, trade.pair) if slo.order_id else None
except InvalidOrderException as exception:
logger.warning('Unable to fetch stoploss order: %s', exception)
if stoploss_order:
self.update_trade_state(trade, trade.stoploss_order_id, stoploss_order,
stoploss_order=True)
if stoploss_order:
stoploss_orders.append(stoploss_order)
self.update_trade_state(trade, slo.order_id, stoploss_order,
stoploss_order=True)
# We check if stoploss order is fulfilled
if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'):
trade.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value
self.update_trade_state(trade, trade.stoploss_order_id, stoploss_order,
stoploss_order=True)
self._notify_exit(trade, "stoploss", True)
self.handle_protections(trade.pair, trade.trade_direction)
return True
# We check if stoploss order is fulfilled
if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'):
trade.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value
self._notify_exit(trade, "stoploss", True)
self.handle_protections(trade.pair, trade.trade_direction)
return True
if trade.has_open_orders or not trade.is_open:
# Trade has an open Buy or Sell order, Stoploss-handling can't happen in this case
@ -1248,7 +1239,7 @@ class FreqtradeBot(LoggingMixin):
return False
# If enter order is fulfilled but there is no stoploss, we add a stoploss on exchange
if not stoploss_order:
if len(stoploss_orders) == 0:
stop_price = trade.stoploss_or_liquidation
if self.edge:
stoploss = self.edge.get_stoploss(pair=trade.pair)
@ -1262,27 +1253,7 @@ class FreqtradeBot(LoggingMixin):
# in which case the trade will be closed - which we must check below.
return False
# If stoploss order is canceled for some reason we add it again
if (trade.is_open
and stoploss_order
and stoploss_order['status'] in ('canceled', 'cancelled')):
if self.create_stoploss_order(trade=trade, stop_price=trade.stoploss_or_liquidation):
return False
else:
logger.warning('Stoploss order was cancelled, but unable to recreate one.')
# Finally we check if stoploss on exchange should be moved up because of trailing.
# Triggered Orders are now real orders - so don't replace stoploss anymore
if (
trade.is_open and stoploss_order
and stoploss_order.get('status_stop') != 'triggered'
and (self.config.get('trailing_stop', False)
or self.config.get('use_custom_stoploss', False))
):
# if trailing stoploss is enabled we check if stoploss value has changed
# in which case we cancel stoploss order and put another one with new
# value immediately
self.handle_trailing_stoploss_on_exchange(trade, stoploss_order)
self.manage_trade_stoploss_orders(trade, stoploss_orders)
return False
@ -1318,6 +1289,42 @@ class FreqtradeBot(LoggingMixin):
logger.warning(f"Could not create trailing stoploss order "
f"for pair {trade.pair}.")
def manage_trade_stoploss_orders(self, trade: Trade, stoploss_orders: List[Dict]):
"""
Perform required actions acording to existing stoploss orders of trade
:param trade: Corresponding Trade
:param stoploss_orders: Current on exchange stoploss orders
:return: None
"""
# If all stoploss orderd are canceled for some reason we add it again
canceled_sl_orders = [o for o in stoploss_orders
if o['status'] in ('canceled', 'cancelled')]
if (
trade.is_open and
len(stoploss_orders) > 0 and
len(stoploss_orders) == len(canceled_sl_orders)
):
if self.create_stoploss_order(trade=trade, stop_price=trade.stoploss_or_liquidation):
return False
else:
logger.warning('All Stoploss orders are cancelled, but unable to recreate one.')
active_sl_orders = [o for o in stoploss_orders if o not in canceled_sl_orders]
if len(active_sl_orders) > 0:
last_active_sl_order = active_sl_orders[-1]
# Finally we check if stoploss on exchange should be moved up because of trailing.
# Triggered Orders are now real orders - so don't replace stoploss anymore
if (trade.is_open and
last_active_sl_order.get('status_stop') != 'triggered' and
(self.config.get('trailing_stop', False) or
self.config.get('use_custom_stoploss', False))):
# if trailing stoploss is enabled we check if stoploss value has changed
# in which case we cancel stoploss order and put another one with new
# value immediately
self.handle_trailing_stoploss_on_exchange(trade, last_active_sl_order)
return
def manage_open_orders(self) -> None:
"""
Management of open orders on exchange. Unfilled orders might be cancelled if timeout

View File

@ -185,13 +185,14 @@ class Backtesting:
# Load detail timeframe if specified
self.timeframe_detail = str(self.config.get('timeframe_detail', ''))
if self.timeframe_detail:
self.timeframe_detail_min = timeframe_to_minutes(self.timeframe_detail)
if self.timeframe_min <= self.timeframe_detail_min:
timeframe_detail_min = timeframe_to_minutes(self.timeframe_detail)
self.timeframe_detail_td = timedelta(minutes=timeframe_detail_min)
if self.timeframe_min <= timeframe_detail_min:
raise OperationalException(
"Detail timeframe must be smaller than strategy timeframe.")
else:
self.timeframe_detail_min = 0
self.timeframe_detail_td = timedelta(seconds=0)
self.detail_data: Dict[str, DataFrame] = {}
self.futures_data: Dict[str, DataFrame] = {}
@ -1268,7 +1269,7 @@ class Backtesting:
open_trade_count_start = self.backtest_loop(
det_row, pair, current_time_det, end_date,
open_trade_count_start, trade_dir, is_first)
current_time_det += timedelta(minutes=self.timeframe_detail_min)
current_time_det += self.timeframe_detail_td
is_first = False
else:
self.dataprovider._set_dataframe_max_date(current_time)

View File

@ -1,7 +1,7 @@
import logging
from typing import List, Optional
from sqlalchemy import inspect, select, text, tuple_, update
from sqlalchemy import inspect, select, text, update
from freqtrade.exceptions import OperationalException
from freqtrade.persistence.trade_model import Order, Trade
@ -91,8 +91,6 @@ def migrate_trades_and_orders_table(
is_stop_loss_trailing = get_column_def(
cols, 'is_stop_loss_trailing',
f'coalesce({stop_loss_pct}, 0.0) <> coalesce({initial_stop_loss_pct}, 0.0)')
stoploss_order_id = get_column_def(cols, 'stoploss_order_id', 'null')
stoploss_last_update = get_column_def(cols, 'stoploss_last_update', 'null')
max_rate = get_column_def(cols, 'max_rate', '0.0')
min_rate = get_column_def(cols, 'min_rate', 'null')
exit_reason = get_column_def(cols, 'sell_reason', get_column_def(cols, 'exit_reason', 'null'))
@ -160,7 +158,7 @@ def migrate_trades_and_orders_table(
open_rate_requested, close_rate, close_rate_requested, close_profit,
stake_amount, amount, amount_requested, open_date, close_date,
stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct,
is_stop_loss_trailing, stoploss_order_id, stoploss_last_update,
is_stop_loss_trailing,
max_rate, min_rate, exit_reason, exit_order_status, strategy, enter_tag,
timeframe, open_trade_value, close_profit_abs,
trading_mode, leverage, liquidation_price, is_short,
@ -180,7 +178,6 @@ def migrate_trades_and_orders_table(
{initial_stop_loss} initial_stop_loss,
{initial_stop_loss_pct} initial_stop_loss_pct,
{is_stop_loss_trailing} is_stop_loss_trailing,
{stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update,
{max_rate} max_rate, {min_rate} min_rate,
case when {exit_reason} = 'sell_signal' then 'exit_signal'
when {exit_reason} = 'custom_sell' then 'custom_exit'
@ -279,6 +276,8 @@ def fix_old_dry_orders(engine):
with engine.begin() as connection:
# Update current dry-run Orders where
# - stoploss order is Open (will be replaced eventually)
# 2nd query:
# - current Order is open
# - current Trade is closed
# - current Order trade_id not equal to current Trade.id
@ -286,11 +285,6 @@ def fix_old_dry_orders(engine):
stmt = update(Order).where(
Order.ft_is_open.is_(True),
tuple_(Order.ft_trade_id, Order.order_id).not_in(
select(
Trade.id, Trade.stoploss_order_id
).where(Trade.stoploss_order_id.is_not(None))
),
Order.ft_order_side == 'stoploss',
Order.order_id.like('dry%'),

View File

@ -177,6 +177,8 @@ class Order(ModelBase):
order_date = safe_value_fallback(order, 'timestamp')
if order_date:
self.order_date = datetime.fromtimestamp(order_date / 1000, tz=timezone.utc)
elif not self.order_date:
self.order_date = dt_now()
self.ft_is_open = True
if self.status in NON_OPEN_EXCHANGE_STATES:
@ -376,10 +378,6 @@ class LocalTrade:
# percentage value of the initial stop loss
initial_stop_loss_pct: Optional[float] = None
is_stop_loss_trailing: bool = False
# stoploss order id which is on exchange
stoploss_order_id: Optional[str] = None
# last update time of the stoploss order on exchange
stoploss_last_update: Optional[datetime] = None
# absolute value of the highest reached price
max_rate: Optional[float] = None
# Lowest price reached
@ -469,8 +467,8 @@ class LocalTrade:
@property
def stoploss_last_update_utc(self):
if self.stoploss_last_update:
return self.stoploss_last_update.replace(tzinfo=timezone.utc)
if self.has_open_sl_orders:
return max(o.order_date_utc for o in self.open_sl_orders)
return None
@property
@ -526,7 +524,7 @@ class LocalTrade:
return [o for o in self.orders if o.ft_is_open and o.ft_order_side != 'stoploss']
@property
def has_open_orders(self) -> int:
def has_open_orders(self) -> bool:
"""
True if there are open orders for this trade excluding stoploss orders
"""
@ -536,6 +534,37 @@ class LocalTrade:
]
return len(open_orders_wo_sl) > 0
@property
def open_sl_orders(self) -> List[Order]:
"""
All open stoploss orders for this trade
"""
return [
o for o in self.orders
if o.ft_order_side in ['stoploss'] and o.ft_is_open
]
@property
def has_open_sl_orders(self) -> bool:
"""
True if there are open stoploss orders for this trade
"""
open_sl_orders = [
o for o in self.orders
if o.ft_order_side in ['stoploss'] and o.ft_is_open
]
return len(open_sl_orders) > 0
@property
def sl_orders(self) -> List[Order]:
"""
All stoploss orders for this trade
"""
return [
o for o in self.orders
if o.ft_order_side in ['stoploss']
]
@property
def open_orders_ids(self) -> List[str]:
open_orders_ids_wo_sl = [
@ -628,11 +657,10 @@ class LocalTrade:
'stop_loss_abs': self.stop_loss,
'stop_loss_ratio': self.stop_loss_pct if self.stop_loss_pct else None,
'stop_loss_pct': (self.stop_loss_pct * 100) if self.stop_loss_pct else None,
'stoploss_order_id': self.stoploss_order_id,
'stoploss_last_update': (self.stoploss_last_update.strftime(DATETIME_PRINT_FORMAT)
if self.stoploss_last_update else None),
'stoploss_last_update_timestamp': int(self.stoploss_last_update.replace(
tzinfo=timezone.utc).timestamp() * 1000) if self.stoploss_last_update else None,
'stoploss_last_update': (self.stoploss_last_update_utc.strftime(DATETIME_PRINT_FORMAT)
if self.stoploss_last_update_utc else None),
'stoploss_last_update_timestamp': int(self.stoploss_last_update_utc.timestamp() * 1000
) if self.stoploss_last_update_utc else None,
'initial_stop_loss_abs': self.initial_stop_loss,
'initial_stop_loss_ratio': (self.initial_stop_loss_pct
if self.initial_stop_loss_pct else None),
@ -793,7 +821,6 @@ class LocalTrade:
logger.info(f'{order.order_type.upper()}_{payment} has been fulfilled for {self}.')
elif order.ft_order_side == 'stoploss' and order.status not in ('open', ):
self.stoploss_order_id = None
self.close_rate_requested = self.stop_loss
self.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value
if self.is_open and order.safe_filled > 0:
@ -1370,11 +1397,6 @@ class LocalTrade:
exit_order_status=data["exit_order_status"],
stop_loss=data["stop_loss_abs"],
stop_loss_pct=data["stop_loss_ratio"],
stoploss_order_id=data["stoploss_order_id"],
stoploss_last_update=(
datetime.fromtimestamp(data["stoploss_last_update_timestamp"] // 1000,
tz=timezone.utc)
if data["stoploss_last_update_timestamp"] else None),
initial_stop_loss=data["initial_stop_loss_abs"],
initial_stop_loss_pct=data["initial_stop_loss_ratio"],
min_rate=data["min_rate"],
@ -1481,11 +1503,6 @@ class Trade(ModelBase, LocalTrade):
Float(), nullable=True) # type: ignore
is_stop_loss_trailing: Mapped[bool] = mapped_column(
nullable=False, default=False) # type: ignore
# stoploss order id which is on exchange
stoploss_order_id: Mapped[Optional[str]] = mapped_column(
String(255), nullable=True, index=True) # type: ignore
# last update time of the stoploss order on exchange
stoploss_last_update: Mapped[Optional[datetime]] = mapped_column(nullable=True) # type: ignore
# absolute value of the highest reached price
max_rate: Mapped[Optional[float]] = mapped_column(
Float(), nullable=True, default=0.0) # type: ignore

View File

@ -315,7 +315,6 @@ class TradeSchema(BaseModel):
stop_loss_abs: Optional[float] = None
stop_loss_ratio: Optional[float] = None
stop_loss_pct: Optional[float] = None
stoploss_order_id: Optional[str] = None
stoploss_last_update: Optional[str] = None
stoploss_last_update_timestamp: Optional[int] = None
initial_stop_loss_abs: Optional[float] = None

View File

@ -979,15 +979,16 @@ class RPC:
except (ExchangeError):
pass
# cancel stoploss on exchange ...
# cancel stoploss on exchange orders ...
if (self._freqtrade.strategy.order_types.get('stoploss_on_exchange')
and trade.stoploss_order_id):
try:
self._freqtrade.exchange.cancel_stoploss_order(trade.stoploss_order_id,
trade.pair)
c_count += 1
except (ExchangeError):
pass
and trade.has_open_sl_orders):
for oslo in trade.open_sl_orders:
try:
self._freqtrade.exchange.cancel_stoploss_order(oslo.order_id, trade.pair)
c_count += 1
except (ExchangeError):
pass
trade.delete()
self._freqtrade.wallets.update()

View File

@ -266,7 +266,6 @@ def mock_trade_5(fee, is_short: bool):
exchange='binance',
strategy='SampleStrategy',
enter_tag='TEST1',
stoploss_order_id=f'prod_stoploss_{direc(is_short)}_3455',
timeframe=5,
is_short=is_short,
stop_loss_pct=0.10,

View File

@ -282,7 +282,6 @@ def mock_trade_usdt_5(fee, is_short: bool):
open_rate=2.0,
exchange='binance',
strategy='SampleStrategy',
stoploss_order_id=f'prod_stoploss_3455_{direc(is_short)}',
timeframe=5,
is_short=is_short,
)

View File

@ -9,7 +9,7 @@ import ccxt
import pytest
from pandas import DataFrame
from freqtrade.enums import CandleType, MarginMode, TradingMode
from freqtrade.enums import CandleType, MarginMode, RunMode, TradingMode
from freqtrade.exceptions import (DDosProtection, DependencyException, ExchangeError,
InsufficientFundsError, InvalidOrderException,
OperationalException, PricingError, TemporaryError)
@ -796,7 +796,9 @@ def test_validate_timeframes_failed(default_conf, mocker):
mocker.patch(f'{EXMS}._init_ccxt', MagicMock(return_value=api_mock))
mocker.patch(f'{EXMS}._load_markets', MagicMock(return_value={}))
mocker.patch(f'{EXMS}.validate_pairs', MagicMock())
mocker.patch(f'{EXMS}.validate_pairs')
mocker.patch(f'{EXMS}.validate_stakecurrency')
mocker.patch(f'{EXMS}.validate_pricing')
with pytest.raises(OperationalException,
match=r"Invalid timeframe '3m'. This exchange supports.*"):
Exchange(default_conf)
@ -806,6 +808,10 @@ def test_validate_timeframes_failed(default_conf, mocker):
match=r"Timeframes < 1m are currently not supported by Freqtrade."):
Exchange(default_conf)
# Will not raise an exception in util mode.
default_conf['runmode'] = RunMode.UTIL_EXCHANGE
Exchange(default_conf)
def test_validate_timeframes_emulated_ohlcv_1(default_conf, mocker):
default_conf["timeframe"] = "3m"

View File

View File

@ -49,7 +49,7 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
stoploss_order_closed['filled'] = stoploss_order_closed['amount']
# Sell first trade based on stoploss, keep 2nd and 3rd trade open
stop_orders = [stoploss_order_closed, stoploss_order_open, stoploss_order_open]
stop_orders = [stoploss_order_closed, stoploss_order_open.copy(), stoploss_order_open.copy()]
stoploss_order_mock = MagicMock(
side_effect=stop_orders)
# Sell 3rd trade (not called for the first trade)
@ -100,9 +100,10 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
stop_order = stop_orders[idx]
stop_order['id'] = f"stop{idx}"
oobj = Order.parse_from_ccxt_object(stop_order, trade.pair, 'stoploss')
oobj.ft_is_open = True
trade.orders.append(oobj)
trade.stoploss_order_id = f"stop{idx}"
assert len(trade.open_sl_orders) == 1
n = freqtrade.exit_positions(trades)
assert n == 2
@ -113,6 +114,7 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
# Only order for 3rd trade needs to be cancelled
assert cancel_order_mock.call_count == 1
assert stoploss_order_mock.call_count == 3
# Wallets must be updated between stoploss cancellation and selling, and will be updated again
# during update_trade_state
assert wallets_mock.call_count == 4

File diff suppressed because it is too large Load Diff

View File

@ -74,7 +74,7 @@ def test_init_dryrun_db(default_conf, tmpdir):
assert Path(filename).is_file()
def test_migrate_new(mocker, default_conf, fee, caplog):
def test_migrate(mocker, default_conf, fee, caplog):
"""
Test Database migration (starting with new pairformat)
"""
@ -277,8 +277,6 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
assert trade.exit_reason is None
assert trade.strategy is None
assert trade.timeframe == '5m'
assert trade.stoploss_order_id == 'dry_stop_order_id222'
assert trade.stoploss_last_update is None
assert log_has("trying trades_bak1", caplog)
assert log_has("trying trades_bak2", caplog)
assert log_has("Running database migration for trades - backup: trades_bak2, orders_bak0",
@ -294,9 +292,10 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
assert orders[0].order_id == 'dry_buy_order'
assert orders[0].ft_order_side == 'buy'
# All dry-run stoploss orders will be closed
assert orders[-1].order_id == 'dry_stop_order_id222'
assert orders[-1].ft_order_side == 'stoploss'
assert orders[-1].ft_is_open is True
assert orders[-1].ft_is_open is False
assert orders[1].order_id == 'dry_buy_order22'
assert orders[1].ft_order_side == 'buy'

View File

@ -1432,7 +1432,6 @@ def test_to_json(fee):
'stop_loss_abs': None,
'stop_loss_ratio': None,
'stop_loss_pct': None,
'stoploss_order_id': None,
'stoploss_last_update': None,
'stoploss_last_update_timestamp': None,
'initial_stop_loss_abs': None,
@ -1500,7 +1499,6 @@ def test_to_json(fee):
'stop_loss_abs': None,
'stop_loss_pct': None,
'stop_loss_ratio': None,
'stoploss_order_id': None,
'stoploss_last_update': None,
'stoploss_last_update_timestamp': None,
'initial_stop_loss_abs': None,

View File

@ -54,7 +54,6 @@ def test_trade_fromjson():
"stop_loss_abs": 0.1981,
"stop_loss_ratio": -0.216,
"stop_loss_pct": -21.6,
"stoploss_order_id": null,
"stoploss_last_update": "2022-10-18 09:13:42",
"stoploss_last_update_timestamp": 1666077222000,
"initial_stop_loss_abs": 0.1981,

View File

@ -63,7 +63,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'stop_loss_abs': 9.89e-06,
'stop_loss_pct': -10.0,
'stop_loss_ratio': -0.1,
'stoploss_order_id': None,
'stoploss_last_update': ANY,
'stoploss_last_update_timestamp': ANY,
'initial_stop_loss_abs': 9.89e-06,
@ -355,7 +354,6 @@ def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog, is_short):
rpc._rpc_delete('200')
trades = Trade.session.scalars(select(Trade)).all()
trades[2].stoploss_order_id = '102'
trades[2].orders.append(
Order(
ft_order_side='stoploss',

View File

@ -1174,7 +1174,6 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short,
'stop_loss_abs': ANY,
'stop_loss_pct': ANY,
'stop_loss_ratio': ANY,
'stoploss_order_id': None,
'stoploss_last_update': ANY,
'stoploss_last_update_timestamp': ANY,
'initial_stop_loss_abs': 0.0,
@ -1378,7 +1377,6 @@ def test_api_force_entry(botclient, mocker, fee, endpoint):
'stop_loss_abs': None,
'stop_loss_pct': None,
'stop_loss_ratio': None,
'stoploss_order_id': None,
'stoploss_last_update': None,
'stoploss_last_update_timestamp': None,
'initial_stop_loss_abs': None,