diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 997e90571f3..0bd0e154914 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -950,7 +950,8 @@ Returning any other price will cancel the existing order, and replace it with a The trade open-date (`trade.open_date_utc`) will remain at the time of the very first order placed. Please make sure to be aware of this - and eventually adjust your logic in other callbacks to account for this, and use the date of the first filled order instead. -If the cancellation of the original order fails, then the order will not be replaced - though the order will most likely have been canceled on exchange. Having this happen on initial entries will result in the deletion of the order, while on position adjustment orders, it'll result in the trade size remaining as is. +If the cancellation of the original order fails, then the order will not be replaced - though the order will most likely have been canceled on exchange. Having this happen on initial entries will result in the deletion of the order, while on position adjustment orders, it'll result in the trade size remaining as is. +If the order has been partially filled, the order will not be replaced. You can however use [`adjust_trade_position()`](#adjust-trade-position) to adjust the trade size to the full, expected position size, should this be necessary / desired. !!! Warning "Regular timeout" Entry `unfilledtimeout` mechanism (as well as `check_entry_timeout()`) takes precedence over this. diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 494949439b8..67c4cc70907 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -548,6 +548,7 @@ def market_is_tradable(self, market: dict[str, Any]) -> bool: and ( self.precisionMode != TICK_SIZE # Too low precision will falsify calculations + or market.get("precision", {}).get("price") is None or market.get("precision", {}).get("price") > 1e-11 ) and ( diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4327c80a53c..6212ad8550d 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1695,7 +1695,7 @@ def replace_order(self, order: CcxtOrder, order_obj: Order | None, trade: Trade) ) if not res: self.replace_order_failed( - trade, f"Could not cancel order for {trade}, therefore not replacing." + trade, f"Could not fully cancel order for {trade}, therefore not replacing." ) return if adjusted_entry_price: diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 73d3acfa957..77fe2a84a2a 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -1160,8 +1160,12 @@ def get_latest_candle( logger.warning(f"Empty candle (OHLCV) data for pair {pair}") return None, None - latest_date_pd = dataframe["date"].max() - latest = dataframe.loc[dataframe["date"] == latest_date_pd].iloc[-1] + try: + latest_date_pd = dataframe["date"].max() + latest = dataframe.loc[dataframe["date"] == latest_date_pd].iloc[-1] + except Exception as e: + logger.warning(f"Unable to get latest candle (OHLCV) data for pair {pair} - {e}") + return None, None # Explicitly convert to datetime object to ensure the below comparison does not fail latest_date: datetime = latest_date_pd.to_pydatetime() diff --git a/tests/freqtradebot/test_freqtradebot.py b/tests/freqtradebot/test_freqtradebot.py index 7a30713bc5d..5cfc5d70152 100644 --- a/tests/freqtradebot/test_freqtradebot.py +++ b/tests/freqtradebot/test_freqtradebot.py @@ -2053,7 +2053,7 @@ def test_adjust_entry_replace_fail( assert len(trades) == 0 assert len(Order.session.scalars(select(Order)).all()) == 0 assert fetch_order_mock.call_count == 4 - assert log_has_re(r"Could not cancel order.*, therefore not replacing\.", caplog) + assert log_has_re(r"Could not fully cancel order.*, therefore not replacing\.", caplog) # Entry adjustment is called assert freqtrade.strategy.adjust_entry_price.call_count == 1