refine position-sizing UI: top-of-panel controls, segmented risk, no spinners
The account/risk inputs are global "set once" settings, so they're moved out of the panel body into a single compact line in the recommendation header. Replaced the number-input spinners: risk % is now a segmented preset selector (0.5/1/2/3), account size a clean text field with a $ prefix. Relabel "at risk" → "max loss". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -173,7 +173,7 @@ function SetupCard({ setup, action, currentPrice, risk, regime }: { setup?: Trad
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-mono text-sm text-gray-100">{formatPrice(sizing.dollarRisk)}</div>
|
||||
<div className="text-[10px] text-gray-500">at risk</div>
|
||||
<div className="text-[10px] text-gray-500">max loss</div>
|
||||
</div>
|
||||
</div>
|
||||
{sizing.exceedsAccount && (
|
||||
@@ -195,33 +195,44 @@ function SetupCard({ setup, action, currentPrice, risk, regime }: { setup?: Trad
|
||||
);
|
||||
}
|
||||
|
||||
function RiskSettingsBar({ risk, update }: { risk: RiskSettings; update: (p: Partial<RiskSettings>) => void }) {
|
||||
const RISK_PRESETS = [0.5, 1, 2, 3];
|
||||
|
||||
/** Compact, set-once sizing controls: a clean account field (no spinners) and a
|
||||
* segmented risk-% selector — risk is almost always one of a few values. */
|
||||
function RiskControls({ risk, update }: { risk: RiskSettings; update: (p: Partial<RiskSettings>) => void }) {
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-2 text-xs">
|
||||
<span className="text-gray-500">Risk settings:</span>
|
||||
<label className="flex items-center gap-1.5">
|
||||
<span className="text-gray-500">Account $</span>
|
||||
<div className="flex flex-wrap items-center gap-2 text-xs text-gray-500">
|
||||
<span>Sizing assumes a</span>
|
||||
<span className="inline-flex items-center rounded-md border border-white/[0.08] bg-white/[0.03] px-2 py-0.5">
|
||||
<span className="mr-0.5 text-gray-500">$</span>
|
||||
<input
|
||||
type="number"
|
||||
min={0}
|
||||
value={risk.accountSize}
|
||||
onChange={(e) => update({ accountSize: Number(e.target.value) })}
|
||||
className="w-28 input-glass px-2 py-1 font-mono"
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
value={risk.accountSize ? String(risk.accountSize) : ''}
|
||||
onChange={(e) => update({ accountSize: Number(e.target.value.replace(/[^0-9]/g, '')) || 0 })}
|
||||
placeholder="10000"
|
||||
aria-label="Account size"
|
||||
className="w-20 bg-transparent font-mono text-gray-100 outline-none"
|
||||
/>
|
||||
</label>
|
||||
<label className="flex items-center gap-1.5">
|
||||
<span className="text-gray-500">Risk %</span>
|
||||
<input
|
||||
type="number"
|
||||
min={0}
|
||||
max={100}
|
||||
step={0.25}
|
||||
value={risk.riskPct}
|
||||
onChange={(e) => update({ riskPct: Number(e.target.value) })}
|
||||
className="w-20 input-glass px-2 py-1 font-mono"
|
||||
/>
|
||||
</label>
|
||||
<span className="text-[10px] text-gray-600">saved in this browser</span>
|
||||
</span>
|
||||
<span>account, risking</span>
|
||||
<span className="inline-flex overflow-hidden rounded-md border border-white/[0.08]">
|
||||
{RISK_PRESETS.map((p) => (
|
||||
<button
|
||||
key={p}
|
||||
type="button"
|
||||
onClick={() => update({ riskPct: p })}
|
||||
className={`px-2 py-0.5 font-mono transition-colors ${
|
||||
risk.riskPct === p
|
||||
? 'bg-blue-400/15 text-blue-200'
|
||||
: 'text-gray-400 hover:bg-white/[0.05] hover:text-gray-200'
|
||||
}`}
|
||||
>
|
||||
{p}%
|
||||
</button>
|
||||
))}
|
||||
</span>
|
||||
<span>per trade.</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -263,6 +274,9 @@ export function RecommendationPanel({ symbol, longSetup, shortSetup, currentPric
|
||||
</span>
|
||||
<span className="text-sm text-gray-300">Composite: {summary?.composite_score?.toFixed(1) ?? '—'}</span>
|
||||
<span className="text-xs text-gray-500">{symbol.toUpperCase()}</span>
|
||||
<div className="ml-auto">
|
||||
<RiskControls risk={risk} update={updateRisk} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-gray-500">Recommended Action is the ticker-level bias. The preferred setup is shown first; the opposite side is available under Alternative scenario.</p>
|
||||
@@ -271,8 +285,6 @@ export function RecommendationPanel({ symbol, longSetup, shortSetup, currentPric
|
||||
<p className="text-sm text-gray-300">{summary.reasoning}</p>
|
||||
)}
|
||||
|
||||
<RiskSettingsBar risk={risk} update={updateRisk} />
|
||||
|
||||
{earningsDays != null && earningsDays >= 0 && (
|
||||
earningsDays <= EARNINGS_HORIZON_DAYS ? (
|
||||
<p className="rounded border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-[11px] text-amber-300">
|
||||
|
||||
Reference in New Issue
Block a user