From 3078ab9790fd8510c6e801cb3c82f10ab74a4748 Mon Sep 17 00:00:00 2001 From: direkturcrypto Date: Fri, 27 Mar 2026 13:36:20 +0700 Subject: [PATCH] fix: tiered floor must start from MM_ADAPTIVE_MIN_COMBINED, not hardcoded $1.00 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Was using breakevenFloor (1.00 - filledPrice) which ignored the user's MM_ADAPTIVE_MIN_COMBINED setting. This caused sells below the configured minimum (e.g. selling at 28c when min combined was 1.01). Now: filled @ 70c, MM_ADAPTIVE_MIN_COMBINED=1.01 → floor starts at 31c Phase 1 (>180s): 31c Phase 2 (90-180s): 21c Phase 3 (30-90s): 11c Phase 4 (<30s): market sell Co-Authored-By: Claude Opus 4.6 --- src/services/mmExecutor.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/services/mmExecutor.js b/src/services/mmExecutor.js index 3b133fe..63ad08c 100644 --- a/src/services/mmExecutor.js +++ b/src/services/mmExecutor.js @@ -469,8 +469,7 @@ async function adaptiveLegCL(pos, unfilledKey) { const minAdaptivePrice = Math.max(0, config.mmAdaptiveMinCombined - filledLegPrice); // ── Tiered floors (5m markets): progressively lower floor over time ──── - // breakevenFloor: filledLeg + unfilledLeg = $1.00 → zero net P&L - const breakevenFloor = Math.max(0, 1.00 - filledLegPrice); + // Start from minAdaptivePrice (MM_ADAPTIVE_MIN_COMBINED - filledPrice), then drop per phase const floorDrop = config.mmDefensiveEnabled ? 0.10 : 0; const emergencyPrice = config.mmDefensiveWorstThreshold; // default 0.10 @@ -480,22 +479,22 @@ async function adaptiveLegCL(pos, unfilledKey) { * Get the current floor based on time remaining (5m markets only). * Other durations use the fixed mmAdaptiveMinCombined floor. * - * Phase 1 (> 180s left): breakevenFloor (e.g. 0.40 for 60c fill) - * Phase 2 (90–180s): breakevenFloor - 0.10 (e.g. 0.30) - * Phase 3 (30–90s): breakevenFloor - 0.20 (e.g. 0.20) + * Phase 1 (> 180s left): minAdaptivePrice (from MM_ADAPTIVE_MIN_COMBINED) + * Phase 2 (90–180s): minAdaptivePrice - 0.10 + * Phase 3 (30–90s): minAdaptivePrice - 0.20 * Phase 4 (< 30s): market sell */ function getTieredFloor(msLeft) { if (!is5m) return minAdaptivePrice; // non-5m: use fixed floor - if (msLeft > 180_000) return breakevenFloor; - if (msLeft > 90_000) return Math.max(0.01, breakevenFloor - floorDrop); - if (msLeft > 30_000) return Math.max(0.01, breakevenFloor - floorDrop * 2); + if (msLeft > 180_000) return minAdaptivePrice; + if (msLeft > 90_000) return Math.max(0.01, minAdaptivePrice - floorDrop); + if (msLeft > 30_000) return Math.max(0.01, minAdaptivePrice - floorDrop * 2); return 0; // phase 4: market sell } logger.warn(`MM: one leg filled — starting adaptive CL for ${unfilledKey.toUpperCase()} | ${label}`); if (is5m) { - logger.info(`MM adaptive CL: filled @ $${filledLegPrice.toFixed(3)} | breakeven floor: $${breakevenFloor.toFixed(3)} | tiered: $${breakevenFloor.toFixed(2)} → $${Math.max(0.01, breakevenFloor - floorDrop).toFixed(2)} → $${Math.max(0.01, breakevenFloor - floorDrop * 2).toFixed(2)}`); + logger.info(`MM adaptive CL: filled @ $${filledLegPrice.toFixed(3)} | floor (minCombined $${config.mmAdaptiveMinCombined.toFixed(2)}): $${minAdaptivePrice.toFixed(3)} | tiered: $${minAdaptivePrice.toFixed(2)} → $${Math.max(0.01, minAdaptivePrice - floorDrop).toFixed(2)} → $${Math.max(0.01, minAdaptivePrice - floorDrop * 2).toFixed(2)}`); } else { logger.info(`MM adaptive CL: filled leg @ $${filledLegPrice.toFixed(3)} | min floor for combined ≥ $${config.mmAdaptiveMinCombined.toFixed(2)}: $${minAdaptivePrice.toFixed(3)}`); }