Risk-based sizing on each setup card: shares = floor((account × risk%) / |entry − stop|), with position value and dollars-at-risk. Account size and per-trade risk % are editable inline and persisted in localStorage. Flags when a position would exceed the account (needs margin). Frontend-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
38 lines
1.1 KiB
TypeScript
38 lines
1.1 KiB
TypeScript
export interface PositionSize {
|
||
shares: number;
|
||
riskPerShare: number;
|
||
dollarRisk: number;
|
||
positionValue: number;
|
||
/** Position value exceeds the account → needs margin / not affordable in cash. */
|
||
exceedsAccount: boolean;
|
||
}
|
||
|
||
/**
|
||
* Risk-based position sizing. Risk a fixed % of the account per trade; the stop
|
||
* distance sets how many shares that budget buys:
|
||
* shares = floor((account × risk%) / |entry − stop|)
|
||
* Returns null when inputs are unusable (no account, no risk, zero stop width).
|
||
*/
|
||
export function positionSize(
|
||
accountSize: number,
|
||
riskPct: number,
|
||
entry: number,
|
||
stop: number,
|
||
): PositionSize | null {
|
||
const riskPerShare = Math.abs(entry - stop);
|
||
if (!(accountSize > 0) || !(riskPct > 0) || !(riskPerShare > 0) || !(entry > 0)) {
|
||
return null;
|
||
}
|
||
const budget = accountSize * (riskPct / 100);
|
||
const shares = Math.floor(budget / riskPerShare);
|
||
const dollarRisk = shares * riskPerShare;
|
||
const positionValue = shares * entry;
|
||
return {
|
||
shares,
|
||
riskPerShare,
|
||
dollarRisk,
|
||
positionValue,
|
||
exceedsAccount: positionValue > accountSize,
|
||
};
|
||
}
|