From 80635fc0c0bed80517f6f54277ea7c2942009e6a Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Mon, 3 Nov 2025 15:45:11 +0100 Subject: [PATCH] feat: Add position scaling controls to settings UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **UI Updates (settings page):** Added new '📈 Position Scaling' section with: - Enable/disable toggle (defaults to OFF) - Min quality score slider (60-90, default 75) - Min profit to scale (0-2%, default 0.4%) - Scale size percent (10-100%, default 50%) - Max position multiplier (1-3x, default 2.0x) - Min ADX increase (0-15, default 5) - Max price position for scale (50-90%, default 70%) **Visual Feedback:** - Purple-themed section with warning banner - Real-time risk calculator showing: * Original position size (SOL example) * Scale addition amount * Total after 1 scale * Maximum possible position size - Dynamic descriptions explain each parameter - Warning: 'DISABLED by default' with red indicator **API Updates:** Extended /api/settings GET/POST to handle 7 new fields: - ENABLE_POSITION_SCALING - MIN_SCALE_QUALITY_SCORE - MIN_PROFIT_FOR_SCALE - MAX_SCALE_MULTIPLIER - SCALE_SIZE_PERCENT - MIN_ADX_INCREASE - MAX_PRICE_POSITION_FOR_SCALE **User Flow:** 1. Navigate to http://localhost:3001/settings 2. Scroll to '📈 Position Scaling' section 3. Toggle 'Enable Position Scaling' to 1 4. Adjust thresholds (defaults are conservative) 5. See live calculation of scaling impact 6. Click 'Save Settings' 7. Click 'Restart Bot' to apply **Safety:** - Feature OFF by default (requires explicit opt-in) - Warning banner explains scaling behavior - Risk calculator shows maximum exposure - Conservative defaults prevent aggressive scaling - All parameters adjustable via sliders **Example:** With defaults (SOL $210×10x = $2100): - Scale adds: $1050 (50% of $2100) - Total after 1 scale: $3150 - Max position (2x): $4200 User can now enable and configure position scaling without touching .env file! --- app/api/settings/route.ts | 32 +++++++++++ app/settings/page.tsx | 112 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/app/api/settings/route.ts b/app/api/settings/route.ts index cb54625..7771a88 100644 --- a/app/api/settings/route.ts +++ b/app/api/settings/route.ts @@ -87,6 +87,17 @@ export async function GET() { USE_TRAILING_STOP: env.USE_TRAILING_STOP === 'true' || env.USE_TRAILING_STOP === undefined, TRAILING_STOP_PERCENT: parseFloat(env.TRAILING_STOP_PERCENT || '0.3'), TRAILING_STOP_ACTIVATION: parseFloat(env.TRAILING_STOP_ACTIVATION || '0.5'), + + // Position Scaling + ENABLE_POSITION_SCALING: env.ENABLE_POSITION_SCALING === 'true', + MIN_SCALE_QUALITY_SCORE: parseInt(env.MIN_SCALE_QUALITY_SCORE || '75'), + MIN_PROFIT_FOR_SCALE: parseFloat(env.MIN_PROFIT_FOR_SCALE || '0.4'), + MAX_SCALE_MULTIPLIER: parseFloat(env.MAX_SCALE_MULTIPLIER || '2.0'), + SCALE_SIZE_PERCENT: parseFloat(env.SCALE_SIZE_PERCENT || '50'), + MIN_ADX_INCREASE: parseFloat(env.MIN_ADX_INCREASE || '5'), + MAX_PRICE_POSITION_FOR_SCALE: parseFloat(env.MAX_PRICE_POSITION_FOR_SCALE || '70'), + + // Safety MAX_DAILY_DRAWDOWN: parseFloat(env.MAX_DAILY_DRAWDOWN || '-50'), MAX_TRADES_PER_HOUR: parseInt(env.MAX_TRADES_PER_HOUR || '6'), MIN_TIME_BETWEEN_TRADES: parseInt(env.MIN_TIME_BETWEEN_TRADES || '600'), @@ -112,6 +123,16 @@ export async function POST(request: NextRequest) { const updates = { MAX_POSITION_SIZE_USD: settings.MAX_POSITION_SIZE_USD.toString(), LEVERAGE: settings.LEVERAGE.toString(), + + // Per-symbol settings + SOLANA_ENABLED: settings.SOLANA_ENABLED.toString(), + SOLANA_POSITION_SIZE: settings.SOLANA_POSITION_SIZE.toString(), + SOLANA_LEVERAGE: settings.SOLANA_LEVERAGE.toString(), + ETHEREUM_ENABLED: settings.ETHEREUM_ENABLED.toString(), + ETHEREUM_POSITION_SIZE: settings.ETHEREUM_POSITION_SIZE.toString(), + ETHEREUM_LEVERAGE: settings.ETHEREUM_LEVERAGE.toString(), + + // Risk management STOP_LOSS_PERCENT: settings.STOP_LOSS_PERCENT.toString(), TAKE_PROFIT_1_PERCENT: settings.TAKE_PROFIT_1_PERCENT.toString(), TAKE_PROFIT_1_SIZE_PERCENT: settings.TAKE_PROFIT_1_SIZE_PERCENT.toString(), @@ -124,6 +145,17 @@ export async function POST(request: NextRequest) { USE_TRAILING_STOP: settings.USE_TRAILING_STOP.toString(), TRAILING_STOP_PERCENT: settings.TRAILING_STOP_PERCENT.toString(), TRAILING_STOP_ACTIVATION: settings.TRAILING_STOP_ACTIVATION.toString(), + + // Position Scaling + ENABLE_POSITION_SCALING: settings.ENABLE_POSITION_SCALING.toString(), + MIN_SCALE_QUALITY_SCORE: settings.MIN_SCALE_QUALITY_SCORE.toString(), + MIN_PROFIT_FOR_SCALE: settings.MIN_PROFIT_FOR_SCALE.toString(), + MAX_SCALE_MULTIPLIER: settings.MAX_SCALE_MULTIPLIER.toString(), + SCALE_SIZE_PERCENT: settings.SCALE_SIZE_PERCENT.toString(), + MIN_ADX_INCREASE: settings.MIN_ADX_INCREASE.toString(), + MAX_PRICE_POSITION_FOR_SCALE: settings.MAX_PRICE_POSITION_FOR_SCALE.toString(), + + // Safety MAX_DAILY_DRAWDOWN: settings.MAX_DAILY_DRAWDOWN.toString(), MAX_TRADES_PER_HOUR: settings.MAX_TRADES_PER_HOUR.toString(), MIN_TIME_BETWEEN_TRADES: settings.MIN_TIME_BETWEEN_TRADES.toString(), diff --git a/app/settings/page.tsx b/app/settings/page.tsx index f76cea8..393d26e 100644 --- a/app/settings/page.tsx +++ b/app/settings/page.tsx @@ -34,6 +34,17 @@ interface TradingSettings { USE_TRAILING_STOP: boolean TRAILING_STOP_PERCENT: number TRAILING_STOP_ACTIVATION: number + + // Position Scaling + ENABLE_POSITION_SCALING: boolean + MIN_SCALE_QUALITY_SCORE: number + MIN_PROFIT_FOR_SCALE: number + MAX_SCALE_MULTIPLIER: number + SCALE_SIZE_PERCENT: number + MIN_ADX_INCREASE: number + MAX_PRICE_POSITION_FOR_SCALE: number + + // Safety MAX_DAILY_DRAWDOWN: number MAX_TRADES_PER_HOUR: number MIN_TIME_BETWEEN_TRADES: number @@ -520,6 +531,107 @@ export default function SettingsPage() { /> + {/* Position Scaling */} +
+
+

+ ⚠️ Advanced Feature: Scale into existing profitable positions when high-quality signals confirm trend strength. +

+

+ When enabled: Same-direction signals will ADD to position (not rejected) if quality ≥{settings?.MIN_SCALE_QUALITY_SCORE || 75}, + profit ≥{settings?.MIN_PROFIT_FOR_SCALE || 0.4}%, ADX increased ≥{settings?.MIN_ADX_INCREASE || 5}, and price position <{settings?.MAX_PRICE_POSITION_FOR_SCALE || 70}%. +

+
+ updateSetting('ENABLE_POSITION_SCALING', v === 1)} + min={0} + max={1} + step={1} + description="🔴 DISABLED by default. Enable to allow scaling into profitable positions. 0 = block duplicates (safe), 1 = allow scaling (aggressive)." + /> + updateSetting('MIN_SCALE_QUALITY_SCORE', v)} + min={60} + max={90} + step={5} + description="Scaling signal must score this high (0-100). Higher = more selective. Recommended: 75 (vs 60 for initial entry)." + /> + updateSetting('MIN_PROFIT_FOR_SCALE', v)} + min={0} + max={2} + step={0.1} + description="Position must be this profitable before scaling. Example: 0.4% = must be at/past TP1. NEVER scales into losing positions." + /> + updateSetting('SCALE_SIZE_PERCENT', v)} + min={10} + max={100} + step={10} + description="Add this % of original position size. Example: 50% = if original was $2100, scale adds $1050." + /> + updateSetting('MAX_SCALE_MULTIPLIER', v)} + min={1} + max={3} + step={0.5} + description="Max total position size after scaling. Example: 2.0 = can scale to 200% of original (original + 1 scale of 100%)." + /> + updateSetting('MIN_ADX_INCREASE', v)} + min={0} + max={15} + step={1} + description="ADX must increase by this much since entry. Example: 5 = if entered at ADX 15, scale requires ADX ≥20. Confirms trend strengthening." + /> + updateSetting('MAX_PRICE_POSITION_FOR_SCALE', v)} + min={50} + max={90} + step={5} + description="Don't scale if price is above this % of range. Example: 70% = blocks scaling near resistance. Prevents chasing." + /> + + {/* Risk Calculator for Scaling */} + {settings.ENABLE_POSITION_SCALING && ( +
+

📊 Scaling Impact (SOL Example)

+
+
+ Original Position: + ${settings.SOLANA_POSITION_SIZE * settings.SOLANA_LEVERAGE} +
+
+ Scale Addition ({settings.SCALE_SIZE_PERCENT}%): + +${((settings.SOLANA_POSITION_SIZE * settings.SOLANA_LEVERAGE) * (settings.SCALE_SIZE_PERCENT / 100)).toFixed(0)} +
+
+ Total After 1 Scale: + ${((settings.SOLANA_POSITION_SIZE * settings.SOLANA_LEVERAGE) * (1 + settings.SCALE_SIZE_PERCENT / 100)).toFixed(0)} +
+
+ Max Position ({settings.MAX_SCALE_MULTIPLIER}x): + ${((settings.SOLANA_POSITION_SIZE * settings.SOLANA_LEVERAGE) * settings.MAX_SCALE_MULTIPLIER).toFixed(0)} +
+
+
+ )} +
+ {/* Trade Limits */}