mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
Merge pull request #9750 from freqtrade/feat/order_tags
Add tags per order
This commit is contained in:
commit
f7472ca74c
|
@ -767,6 +767,7 @@ This callback is **not** called when there is an open order (either buy or sell)
|
|||
`adjust_trade_position()` is called very frequently for the duration of a trade, so you must keep your implementation as performant as possible.
|
||||
|
||||
Position adjustments will always be applied in the direction of the trade, so a positive value will always increase your position (negative values will decrease your position), no matter if it's a long or short trade.
|
||||
Adjustment orders can be assigned with a tag by returning a 2 element Tuple, with the first element being the adjustment amount, and the 2nd element the tag (e.g. `return 250, 'increase_favorable_conditions'`).
|
||||
|
||||
Modifications to leverage are not possible, and the stake-amount returned is assumed to be before applying leverage.
|
||||
|
||||
|
@ -833,7 +834,8 @@ class DigDeeperStrategy(IStrategy):
|
|||
min_stake: Optional[float], max_stake: float,
|
||||
current_entry_rate: float, current_exit_rate: float,
|
||||
current_entry_profit: float, current_exit_profit: float,
|
||||
**kwargs) -> Optional[float]:
|
||||
**kwargs
|
||||
) -> Union[Optional[float], Tuple[Optional[float], Optional[str]]]:
|
||||
"""
|
||||
Custom trade adjustment logic, returning the stake amount that a trade should be
|
||||
increased or decreased.
|
||||
|
@ -859,11 +861,12 @@ class DigDeeperStrategy(IStrategy):
|
|||
:return float: Stake amount to adjust your trade,
|
||||
Positive values to increase position, Negative values to decrease position.
|
||||
Return None for no action.
|
||||
Optionally, return a tuple with a 2nd element with an order reason
|
||||
"""
|
||||
|
||||
if current_profit > 0.05 and trade.nr_of_successful_exits == 0:
|
||||
# Take half of the profit at +5%
|
||||
return -(trade.stake_amount / 2)
|
||||
return -(trade.stake_amount / 2), 'half_profit_5%'
|
||||
|
||||
if current_profit > -0.05:
|
||||
return None
|
||||
|
@ -891,7 +894,7 @@ class DigDeeperStrategy(IStrategy):
|
|||
stake_amount = filled_entries[0].stake_amount
|
||||
# This then calculates current safety order size
|
||||
stake_amount = stake_amount * (1 + (count_of_entries * 0.25))
|
||||
return stake_amount
|
||||
return stake_amount, '1/3rd_increase'
|
||||
except Exception as exception:
|
||||
return None
|
||||
|
||||
|
|
|
@ -645,8 +645,7 @@ class FreqtradeBot(LoggingMixin):
|
|||
max_entry_stake = self.exchange.get_max_pair_stake_amount(trade.pair, current_entry_rate)
|
||||
stake_available = self.wallets.get_available_stake_amount()
|
||||
logger.debug(f"Calling adjust_trade_position for pair {trade.pair}")
|
||||
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
|
||||
default_retval=None, supress_error=True)(
|
||||
stake_amount, order_tag = self.strategy._adjust_trade_position_internal(
|
||||
trade=trade,
|
||||
current_time=datetime.now(timezone.utc), current_rate=current_entry_rate,
|
||||
current_profit=current_entry_profit, min_stake=min_entry_stake,
|
||||
|
@ -665,7 +664,8 @@ class FreqtradeBot(LoggingMixin):
|
|||
else:
|
||||
logger.debug("Max adjustment entries is set to unlimited.")
|
||||
self.execute_entry(trade.pair, stake_amount, price=current_entry_rate,
|
||||
trade=trade, is_short=trade.is_short, mode='pos_adjust')
|
||||
trade=trade, is_short=trade.is_short, mode='pos_adjust',
|
||||
enter_tag=order_tag)
|
||||
|
||||
if stake_amount is not None and stake_amount < 0.0:
|
||||
# We should decrease our position
|
||||
|
@ -684,7 +684,7 @@ class FreqtradeBot(LoggingMixin):
|
|||
return
|
||||
|
||||
self.execute_trade_exit(trade, current_exit_rate, exit_check=ExitCheckTuple(
|
||||
exit_type=ExitType.PARTIAL_EXIT), sub_trade_amt=amount)
|
||||
exit_type=ExitType.PARTIAL_EXIT), sub_trade_amt=amount, exit_tag=order_tag)
|
||||
|
||||
def _check_depth_of_market(self, pair: str, conf: Dict, side: SignalDirection) -> bool:
|
||||
"""
|
||||
|
@ -782,6 +782,7 @@ class FreqtradeBot(LoggingMixin):
|
|||
leverage=leverage
|
||||
)
|
||||
order_obj = Order.parse_from_ccxt_object(order, pair, side, amount, enter_limit_requested)
|
||||
order_obj.ft_order_tag = enter_tag
|
||||
order_id = order['id']
|
||||
order_status = order.get('status')
|
||||
logger.info(f"Order {order_id} was created for {pair} and status is {order_status}.")
|
||||
|
@ -1753,6 +1754,7 @@ class FreqtradeBot(LoggingMixin):
|
|||
return False
|
||||
|
||||
order_obj = Order.parse_from_ccxt_object(order, trade.pair, trade.exit_side, amount, limit)
|
||||
order_obj.ft_order_tag = exit_reason
|
||||
trade.orders.append(order_obj)
|
||||
|
||||
trade.exit_order_status = ''
|
||||
|
|
|
@ -537,14 +537,14 @@ class Backtesting:
|
|||
min_stake = self.exchange.get_min_pair_stake_amount(trade.pair, current_rate, -0.1)
|
||||
max_stake = self.exchange.get_max_pair_stake_amount(trade.pair, current_rate)
|
||||
stake_available = self.wallets.get_available_stake_amount()
|
||||
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
|
||||
default_retval=None, supress_error=True)(
|
||||
stake_amount, order_tag = self.strategy._adjust_trade_position_internal(
|
||||
trade=trade, # type: ignore[arg-type]
|
||||
current_time=current_time, current_rate=current_rate,
|
||||
current_profit=current_profit, min_stake=min_stake,
|
||||
max_stake=min(max_stake, stake_available),
|
||||
current_entry_rate=current_rate, current_exit_rate=current_rate,
|
||||
current_entry_profit=current_profit, current_exit_profit=current_profit)
|
||||
current_entry_profit=current_profit, current_exit_profit=current_profit
|
||||
)
|
||||
|
||||
# Check if we should increase our position
|
||||
if stake_amount is not None and stake_amount > 0.0:
|
||||
|
@ -554,7 +554,8 @@ class Backtesting:
|
|||
check_adjust_entry = (entry_count <= self.strategy.max_entry_position_adjustment)
|
||||
if check_adjust_entry:
|
||||
pos_trade = self._enter_trade(
|
||||
trade.pair, row, 'short' if trade.is_short else 'long', stake_amount, trade)
|
||||
trade.pair, row, 'short' if trade.is_short else 'long', stake_amount, trade,
|
||||
entry_tag1=order_tag)
|
||||
if pos_trade is not None:
|
||||
self.wallets.update()
|
||||
return pos_trade
|
||||
|
@ -569,7 +570,7 @@ class Backtesting:
|
|||
if min_stake and remaining != 0 and remaining < min_stake:
|
||||
# Remaining stake is too low to be sold.
|
||||
return trade
|
||||
exit_ = ExitCheckTuple(ExitType.PARTIAL_EXIT)
|
||||
exit_ = ExitCheckTuple(ExitType.PARTIAL_EXIT, order_tag)
|
||||
pos_trade = self._get_exit_for_signal(trade, row, exit_, current_time, amount)
|
||||
if pos_trade is not None:
|
||||
order = pos_trade.orders[-1]
|
||||
|
@ -681,11 +682,11 @@ class Backtesting:
|
|||
|
||||
trade.exit_reason = exit_reason
|
||||
|
||||
return self._exit_trade(trade, row, close_rate, amount_)
|
||||
return self._exit_trade(trade, row, close_rate, amount_, exit_reason)
|
||||
return None
|
||||
|
||||
def _exit_trade(self, trade: LocalTrade, sell_row: Tuple,
|
||||
close_rate: float, amount: Optional[float] = None) -> Optional[LocalTrade]:
|
||||
def _exit_trade(self, trade: LocalTrade, sell_row: Tuple, close_rate: float,
|
||||
amount: float, exit_reason: Optional[str]) -> Optional[LocalTrade]:
|
||||
self.order_id_counter += 1
|
||||
exit_candle_time = sell_row[DATE_IDX].to_pydatetime()
|
||||
order_type = self.strategy.order_types['exit']
|
||||
|
@ -712,6 +713,7 @@ class Backtesting:
|
|||
filled=0,
|
||||
remaining=amount,
|
||||
cost=amount * close_rate,
|
||||
ft_order_tag=exit_reason,
|
||||
)
|
||||
order._trade_bt = trade
|
||||
trade.orders.append(order)
|
||||
|
@ -835,7 +837,9 @@ class Backtesting:
|
|||
stake_amount: Optional[float] = None,
|
||||
trade: Optional[LocalTrade] = None,
|
||||
requested_rate: Optional[float] = None,
|
||||
requested_stake: Optional[float] = None) -> Optional[LocalTrade]:
|
||||
requested_stake: Optional[float] = None,
|
||||
entry_tag1: Optional[str] = None
|
||||
) -> Optional[LocalTrade]:
|
||||
"""
|
||||
:param trade: Trade to adjust - initial entry if None
|
||||
:param requested_rate: Adjusted entry rate
|
||||
|
@ -843,7 +847,7 @@ class Backtesting:
|
|||
"""
|
||||
|
||||
current_time = row[DATE_IDX].to_pydatetime()
|
||||
entry_tag = row[ENTER_TAG_IDX] if len(row) >= ENTER_TAG_IDX + 1 else None
|
||||
entry_tag = entry_tag1 or (row[ENTER_TAG_IDX] if len(row) >= ENTER_TAG_IDX + 1 else None)
|
||||
# let's call the custom entry price, using the open price as default price
|
||||
order_type = self.strategy.order_types['entry']
|
||||
pos_adjust = trade is not None and requested_rate is None
|
||||
|
@ -944,6 +948,7 @@ class Backtesting:
|
|||
filled=0,
|
||||
remaining=amount,
|
||||
cost=amount * propose_rate + trade.fee_open,
|
||||
ft_order_tag=entry_tag,
|
||||
)
|
||||
order._trade_bt = trade
|
||||
trade.orders.append(order)
|
||||
|
@ -963,7 +968,8 @@ class Backtesting:
|
|||
# Ignore trade if entry-order did not fill yet
|
||||
continue
|
||||
exit_row = data[pair][-1]
|
||||
self._exit_trade(trade, exit_row, exit_row[OPEN_IDX], trade.amount)
|
||||
self._exit_trade(trade, exit_row, exit_row[OPEN_IDX], trade.amount,
|
||||
ExitType.FORCE_EXIT.value)
|
||||
trade.orders[-1].close_bt_order(exit_row[DATE_IDX].to_pydatetime(), trade)
|
||||
|
||||
trade.close_date = exit_row[DATE_IDX].to_pydatetime()
|
||||
|
|
|
@ -223,6 +223,7 @@ def migrate_orders_table(engine, table_back_name: str, cols_order: List):
|
|||
ft_amount = get_column_def(cols_order, 'ft_amount', 'coalesce(amount, 0.0)')
|
||||
ft_price = get_column_def(cols_order, 'ft_price', 'coalesce(price, 0.0)')
|
||||
ft_cancel_reason = get_column_def(cols_order, 'ft_cancel_reason', 'null')
|
||||
ft_order_tag = get_column_def(cols_order, 'ft_order_tag', 'null')
|
||||
|
||||
# sqlite does not support literals for booleans
|
||||
with engine.begin() as connection:
|
||||
|
@ -230,13 +231,14 @@ def migrate_orders_table(engine, table_back_name: str, cols_order: List):
|
|||
insert into orders (id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id,
|
||||
status, symbol, order_type, side, price, amount, filled, average, remaining, cost,
|
||||
stop_price, order_date, order_filled_date, order_update_date, ft_fee_base, funding_fee,
|
||||
ft_amount, ft_price, ft_cancel_reason
|
||||
ft_amount, ft_price, ft_cancel_reason, ft_order_tag
|
||||
)
|
||||
select id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id,
|
||||
status, symbol, order_type, side, price, amount, filled, {average} average, remaining,
|
||||
cost, {stop_price} stop_price, order_date, order_filled_date,
|
||||
order_update_date, {ft_fee_base} ft_fee_base, {funding_fee} funding_fee,
|
||||
{ft_amount} ft_amount, {ft_price} ft_price, {ft_cancel_reason} ft_cancel_reason
|
||||
{ft_amount} ft_amount, {ft_price} ft_price, {ft_cancel_reason} ft_cancel_reason,
|
||||
{ft_order_tag} ft_order_tag
|
||||
from {table_back_name}
|
||||
"""))
|
||||
|
||||
|
@ -331,8 +333,8 @@ def check_migrate(engine, decl_base, previous_tables) -> None:
|
|||
# if ('orders' not in previous_tables
|
||||
# or not has_column(cols_orders, 'funding_fee')):
|
||||
migrating = False
|
||||
# if not has_column(cols_orders, 'ft_cancel_reason'):
|
||||
if not has_column(cols_trades, 'funding_fee_running'):
|
||||
# if not has_column(cols_trades, 'funding_fee_running'):
|
||||
if not has_column(cols_orders, 'ft_order_tag'):
|
||||
migrating = True
|
||||
logger.info(f"Running database migration for trades - "
|
||||
f"backup: {table_back_name}, {order_table_bak_name}")
|
||||
|
|
|
@ -89,6 +89,8 @@ class Order(ModelBase):
|
|||
funding_fee: Mapped[Optional[float]] = mapped_column(Float(), nullable=True)
|
||||
|
||||
ft_fee_base: Mapped[Optional[float]] = mapped_column(Float(), nullable=True)
|
||||
ft_order_tag: Mapped[Optional[str]] = mapped_column(String(CUSTOM_TAG_MAX_LENGTH),
|
||||
nullable=True)
|
||||
|
||||
@property
|
||||
def order_date_utc(self) -> datetime:
|
||||
|
@ -212,6 +214,10 @@ class Order(ModelBase):
|
|||
return order
|
||||
|
||||
def to_json(self, entry_side: str, minified: bool = False) -> Dict[str, Any]:
|
||||
"""
|
||||
:param minified: If True, only return a subset of the data is returned.
|
||||
Only used for backtesting.
|
||||
"""
|
||||
resp = {
|
||||
'amount': self.safe_amount,
|
||||
'safe_price': self.safe_price,
|
||||
|
@ -219,6 +225,7 @@ class Order(ModelBase):
|
|||
'order_filled_timestamp': int(self.order_filled_date.replace(
|
||||
tzinfo=timezone.utc).timestamp() * 1000) if self.order_filled_date else None,
|
||||
'ft_is_entry': self.ft_order_side == entry_side,
|
||||
'ft_order_tag': self.ft_order_tag,
|
||||
}
|
||||
if not minified:
|
||||
resp.update({
|
||||
|
@ -1405,6 +1412,7 @@ class LocalTrade:
|
|||
ft_price=order["price"],
|
||||
remaining=order["remaining"],
|
||||
funding_fee=order.get("funding_fee", None),
|
||||
ft_order_tag=order.get("ft_order_tag", None),
|
||||
)
|
||||
trade.orders.append(order_obj)
|
||||
|
||||
|
|
|
@ -261,6 +261,7 @@ class OrderSchema(BaseModel):
|
|||
order_timestamp: Optional[int] = None
|
||||
order_filled_timestamp: Optional[int] = None
|
||||
ft_fee_base: Optional[float] = None
|
||||
ft_order_tag: Optional[str] = None
|
||||
|
||||
|
||||
class TradeSchema(BaseModel):
|
||||
|
|
|
@ -511,7 +511,8 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||
min_stake: Optional[float], max_stake: float,
|
||||
current_entry_rate: float, current_exit_rate: float,
|
||||
current_entry_profit: float, current_exit_profit: float,
|
||||
**kwargs) -> Optional[float]:
|
||||
**kwargs
|
||||
) -> Union[Optional[float], Tuple[Optional[float], Optional[str]]]:
|
||||
"""
|
||||
Custom trade adjustment logic, returning the stake amount that a trade should be
|
||||
increased or decreased.
|
||||
|
@ -537,6 +538,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||
:return float: Stake amount to adjust your trade,
|
||||
Positive values to increase position, Negative values to decrease position.
|
||||
Return None for no action.
|
||||
Optionally, return a tuple with a 2nd element with an order reason
|
||||
"""
|
||||
return None
|
||||
|
||||
|
@ -725,6 +727,36 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||
|
||||
_ft_stop_uses_after_fill = False
|
||||
|
||||
def _adjust_trade_position_internal(
|
||||
self, trade: Trade, current_time: datetime,
|
||||
current_rate: float, current_profit: float,
|
||||
min_stake: Optional[float], max_stake: float,
|
||||
current_entry_rate: float, current_exit_rate: float,
|
||||
current_entry_profit: float, current_exit_profit: float,
|
||||
**kwargs
|
||||
) -> Tuple[Optional[float], str]:
|
||||
"""
|
||||
wrapper around adjust_trade_position to handle the return value
|
||||
"""
|
||||
resp = strategy_safe_wrapper(self.adjust_trade_position,
|
||||
default_retval=(None, ''), supress_error=True)(
|
||||
trade=trade, current_time=current_time,
|
||||
current_rate=current_rate, current_profit=current_profit,
|
||||
min_stake=min_stake, max_stake=max_stake,
|
||||
current_entry_rate=current_entry_rate, current_exit_rate=current_exit_rate,
|
||||
current_entry_profit=current_entry_profit, current_exit_profit=current_exit_profit,
|
||||
**kwargs
|
||||
)
|
||||
order_tag = ''
|
||||
if isinstance(resp, tuple):
|
||||
if len(resp) >= 1:
|
||||
stake_amount = resp[0]
|
||||
if len(resp) > 1:
|
||||
order_tag = resp[1] or ''
|
||||
else:
|
||||
stake_amount = resp
|
||||
return stake_amount, order_tag
|
||||
|
||||
def __informative_pairs_freqai(self) -> ListPairsWithTimeframes:
|
||||
"""
|
||||
Create informative-pairs needed for FreqAI
|
||||
|
|
|
@ -742,14 +742,18 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
|||
'orders': [
|
||||
[
|
||||
{'amount': 0.00957442, 'safe_price': 0.104445, 'ft_order_side': 'buy',
|
||||
'order_filled_timestamp': 1517251200000, 'ft_is_entry': True},
|
||||
'order_filled_timestamp': 1517251200000, 'ft_is_entry': True,
|
||||
'ft_order_tag': ''},
|
||||
{'amount': 0.00957442, 'safe_price': 0.10496853383458644, 'ft_order_side': 'sell',
|
||||
'order_filled_timestamp': 1517265300000, 'ft_is_entry': False}
|
||||
'order_filled_timestamp': 1517265300000, 'ft_is_entry': False,
|
||||
'ft_order_tag': 'roi'}
|
||||
], [
|
||||
{'amount': 0.0097064, 'safe_price': 0.10302485, 'ft_order_side': 'buy',
|
||||
'order_filled_timestamp': 1517283000000, 'ft_is_entry': True},
|
||||
'order_filled_timestamp': 1517283000000, 'ft_is_entry': True,
|
||||
'ft_order_tag': ''},
|
||||
{'amount': 0.0097064, 'safe_price': 0.10354126528822055, 'ft_order_side': 'sell',
|
||||
'order_filled_timestamp': 1517285400000, 'ft_is_entry': False}
|
||||
'order_filled_timestamp': 1517285400000, 'ft_is_entry': False,
|
||||
'ft_order_tag': 'roi'}
|
||||
]
|
||||
]
|
||||
})
|
||||
|
|
|
@ -148,7 +148,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera
|
|||
assert pytest.approx(trade.amount) == 47.61904762 * leverage
|
||||
assert len(trade.orders) == 1
|
||||
# Increase position by 100
|
||||
backtesting.strategy.adjust_trade_position = MagicMock(return_value=100)
|
||||
backtesting.strategy.adjust_trade_position = MagicMock(return_value=(100, 'PartIncrease'))
|
||||
|
||||
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row, current_time)
|
||||
|
||||
|
@ -156,6 +156,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera
|
|||
assert pytest.approx(trade.stake_amount) == 200.0
|
||||
assert pytest.approx(trade.amount) == 95.23809524 * leverage
|
||||
assert len(trade.orders) == 2
|
||||
assert trade.orders[-1].ft_order_tag == 'PartIncrease'
|
||||
assert pytest.approx(trade.liquidation_price) == (0.1038916 if leverage == 1 else 1.2127791)
|
||||
|
||||
# Reduce by more than amount - no change to trade.
|
||||
|
@ -171,13 +172,14 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera
|
|||
assert pytest.approx(trade.liquidation_price) == (0.1038916 if leverage == 1 else 1.2127791)
|
||||
|
||||
# Reduce position by 50
|
||||
backtesting.strategy.adjust_trade_position = MagicMock(return_value=-100)
|
||||
backtesting.strategy.adjust_trade_position = MagicMock(return_value=(-100, 'partDecrease'))
|
||||
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row, current_time)
|
||||
|
||||
assert trade
|
||||
assert pytest.approx(trade.stake_amount) == 100.0
|
||||
assert pytest.approx(trade.amount) == 47.61904762 * leverage
|
||||
assert len(trade.orders) == 3
|
||||
assert trade.orders[-1].ft_order_tag == 'partDecrease'
|
||||
assert trade.nr_of_successful_entries == 2
|
||||
assert trade.nr_of_successful_exits == 1
|
||||
assert pytest.approx(trade.liquidation_price) == (0.1038916 if leverage == 1 else 1.2127791)
|
||||
|
|
|
@ -99,7 +99,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||
'order_filled_timestamp': ANY, 'order_type': 'limit', 'price': 1.098e-05,
|
||||
'is_open': False, 'pair': 'ETH/BTC', 'order_id': ANY,
|
||||
'remaining': ANY, 'status': ANY, 'ft_is_entry': True, 'ft_fee_base': None,
|
||||
'funding_fee': ANY,
|
||||
'funding_fee': ANY, 'ft_order_tag': None,
|
||||
}],
|
||||
}
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
|
|
|
@ -6725,11 +6725,15 @@ def test_check_and_call_adjust_trade_position(mocker, default_conf_usdt, fee, ca
|
|||
)
|
||||
create_mock_trades(fee)
|
||||
caplog.set_level(logging.DEBUG)
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=10)
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=(10, 'aaaa'))
|
||||
freqtrade.process_open_trade_positions()
|
||||
assert log_has_re(r"Max adjustment entries for .* has been reached\.", caplog)
|
||||
assert freqtrade.strategy.adjust_trade_position.call_count == 1
|
||||
|
||||
caplog.clear()
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=-10)
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=(-10, 'partial_exit_c'))
|
||||
freqtrade.process_open_trade_positions()
|
||||
assert log_has_re(r"LIMIT_SELL has been fulfilled.*", caplog)
|
||||
assert freqtrade.strategy.adjust_trade_position.call_count == 1
|
||||
trade = Trade.get_trades(trade_filter=[Trade.id == 5]).first()
|
||||
assert trade.orders[-1].ft_order_tag == 'partial_exit_c'
|
||||
|
|
|
@ -536,7 +536,7 @@ def test_dca_order_adjust_entry_replace_fails(
|
|||
# Create DCA order for 2nd trade (so we have 2 open orders on 2 trades)
|
||||
# this 2nd order won't fill.
|
||||
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=20)
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=(20, 'PeNF'))
|
||||
|
||||
freqtrade.process()
|
||||
|
||||
|
@ -627,12 +627,13 @@ def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog, levera
|
|||
assert log_has_re(
|
||||
r"Remaining amount of \d\.\d+.* would be smaller than the minimum of 10.", caplog)
|
||||
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=-20)
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=(-20, 'PES'))
|
||||
|
||||
freqtrade.process()
|
||||
trade = Trade.get_trades().first()
|
||||
assert len(trade.orders) == 2
|
||||
assert trade.orders[-1].ft_order_side == 'sell'
|
||||
assert trade.orders[-1].ft_order_tag == 'PES'
|
||||
assert pytest.approx(trade.stake_amount) == 40.198
|
||||
assert pytest.approx(trade.amount) == 20.099 * leverage
|
||||
assert trade.open_rate == 2.0
|
||||
|
|
Loading…
Reference in New Issue
Block a user