rank Top Setups by expected value, badge the top pick
Deploy / lint (push) Successful in 5s
Deploy / test (push) Successful in 37s
Deploy / deploy (push) Successful in 24s

The dashboard Top Setups list showed raw fields in arbitrary order with no
indication of why a ticker was listed or which was best. Now sort by expected
value (R) — probability-weighted payoff per unit risk — so the strongest
opportunity is row 1, badged "Top pick", with a new Exp. Value column that
folds R:R and target probability into one "is this worth taking" number.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 14:09:33 +02:00
parent da83f027e1
commit d892c46fbb
2 changed files with 73 additions and 29 deletions
+15
View File
@@ -13,6 +13,21 @@ export function primaryTargetProbability(setup: TradeSetup): number | null {
return setup.targets?.length ? bestTargetProbability(setup) : null;
}
/**
* Expected value per unit of risk, in R. Probability-weighted payoff:
* EV = p·(R:R) (1 p)
* where p is the primary target's hit probability. This is the single "is this
* worth taking" number — it rewards both a good payoff ratio and a likely
* target, so a fat-but-improbable target can't outrank a solid, probable one.
* Returns null when no target probability is known.
*/
export function expectedValueR(setup: TradeSetup): number | null {
const prob = primaryTargetProbability(setup);
if (prob == null) return null;
const p = prob / 100;
return p * setup.rr_ratio - (1 - p);
}
/** R:R recomputed from the current price (0 if no reward/risk left). */
export function liveRiskReward(setup: TradeSetup, currentPrice: number): number {
const reward = setup.direction === 'long' ? setup.target - currentPrice : currentPrice - setup.target;