get_distance,
get_label_defaults,
get_zl_ma_fn,
+ midpoint,
non_zero_diff,
price_retracement_percent,
smooth_extrema,
INTERFACE_VERSION = 3
def version(self) -> str:
- return "3.3.165"
+ return "3.3.166"
timeframe = "5m"
)
return trade_take_profit_price_history
+ @staticmethod
+ def _interp_partial_stake_amount(
+ stake_amount: float,
+ percent: float,
+ min_percent: float,
+ max_percent: float,
+ min_stake: float,
+ max_stake: float,
+ ) -> float:
+ max_stake = max_stake if max_stake > 0 else stake_amount
+ if max_stake < min_stake:
+ max_stake = min_stake
+ if max_percent <= min_percent or not np.isfinite(max_percent - min_percent):
+ return float(midpoint(min_stake, max_stake))
+ interp_partial_stake_amount = float(
+ np.interp(percent, [min_percent, max_percent], [min_stake, max_stake])
+ )
+ if interp_partial_stake_amount < min_stake:
+ return float(min_stake)
+ return (
+ float(max_stake)
+ if interp_partial_stake_amount > max_stake
+ else interp_partial_stake_amount
+ )
+
+ @staticmethod
+ @lru_cache(maxsize=8)
+ def get_min_stake_multiplier(
+ percent: float,
+ min_percent: float,
+ max_percent: float,
+ min_multiplier: float = 1.15,
+ max_multiplier: float = 1.40,
+ ) -> float:
+ if not (
+ isinstance(percent, (int, float))
+ and isinstance(min_percent, (int, float))
+ and isinstance(max_percent, (int, float))
+ ):
+ return min_multiplier
+ if max_percent <= min_percent or not np.isfinite(max_percent - min_percent):
+ return min_multiplier
+ normalized_percent = (percent - min_percent) / (max_percent - min_percent)
+ if not np.isfinite(normalized_percent):
+ return min_multiplier
+ if normalized_percent < 0.0:
+ normalized_percent = 0.0
+ elif normalized_percent > 1.0:
+ normalized_percent = 1.0
+ return min_multiplier + (max_multiplier - min_multiplier) * normalized_percent
+
def adjust_trade_position(
self,
trade: Trade,
),
)
if trade_partial_exit:
- trade_stake_percent = self.partial_exit_stages[trade_exit_stage][1]
- trade_partial_stake_amount = trade.stake_amount * trade_stake_percent
- if min_stake and trade_partial_stake_amount < min_stake:
+ if min_stake is None:
+ min_stake = np.finfo(float).eps
+ percent = self.partial_exit_stages[trade_exit_stage][1]
+ percent_values = [
+ v[1]
+ for v in self.partial_exit_stages.values()
+ if isinstance(v[1], (int, float))
+ ]
+ min_percent = min(percent_values)
+ max_percent = max(percent_values)
+ trade_partial_stake_amount = QuickAdapterV3._interp_partial_stake_amount(
+ stake_amount=trade.stake_amount,
+ percent=percent,
+ min_percent=min_percent,
+ max_percent=max_percent,
+ min_stake=min_stake,
+ max_stake=max_stake,
+ )
+ safe_min_stake = min_stake * QuickAdapterV3.get_min_stake_multiplier(
+ percent=percent,
+ min_percent=min_percent,
+ max_percent=max_percent,
+ )
+ remaining_stake_amount = trade.stake_amount - trade_partial_stake_amount
+ if remaining_stake_amount < safe_min_stake:
logger.info(
- f"Trade {trade.trade_direction} {trade.pair} stage {trade_exit_stage} | "
- f"Stake amount {format_number(trade_partial_stake_amount)} < min_stake {format_number(min_stake)}, "
- f"clamped to {format_number(min_stake)}"
+ f"Trade {trade.trade_direction} {trade.pair} stage {trade_exit_stage} | Remaining stake amount {format_number(remaining_stake_amount)} < safe_min_stake {format_number(safe_min_stake)}, closing position"
)
- trade_partial_stake_amount = min_stake
- elif max_stake and trade_partial_stake_amount > max_stake:
- logger.info(
- f"Trade {trade.trade_direction} {trade.pair} stage {trade_exit_stage} | "
- f"Stake amount {format_number(trade_partial_stake_amount)} > max_stake {format_number(max_stake)}, "
- f"clamped to {format_number(max_stake)}"
+ last_exit_stage = max(self.partial_exit_stages.keys()) + 1
+ return (
+ -trade.stake_amount,
+ f"take_profit_{trade.trade_direction}_{last_exit_stage}",
)
- trade_partial_stake_amount = max_stake
return (
-trade_partial_stake_amount,
f"take_profit_{trade.trade_direction}_{trade_exit_stage}",