/* ============================================================
Black-Scholes calculator — functional
============================================================ */
function CalcBlackScholesPage() {
const [type, setType] = useState('call');
const [S, setS] = useState(100);
const [K, setK] = useState(100);
const [days, setDays] = useState(60);
const [r, setR] = useState(13.75);
const [q, setQ] = useState(0);
const [sigma, setSigma] = useState(28);
const T = days / 365;
const res = IBDMath.bs(+S, +K, T, +r / 100, +q / 100, +sigma / 100, type);
// Sensitivity: price vs spot
const sensitivity = useMemo(() => {
const sMin = +S * 0.7, sMax = +S * 1.3;
const pts = [];
for (let i = 0; i <= 60; i++) {
const s = sMin + (sMax - sMin) * (i / 60);
const r1 = IBDMath.bs(s, +K, T, +r / 100, +q / 100, +sigma / 100, type);
pts.push({ s, p: r1.price });
}
return pts;
}, [S, K, days, r, q, sigma, type]);
return (
{/* Result header */}
Resultado · {type === 'call' ? 'Call' : 'Put'} europeia
{IBDMath.fmtBRL(res.price)}
Prêmio teórico · σ {sigma}% · T {days} dias · r {r}%
moneyness
{(((+S - +K) / +K) * 100).toLocaleString('pt-BR', {maximumFractionDigits: 2})}%
S/K = {(+S / +K).toFixed(4)}
{/* Greeks */}
{/* Sensitivity chart */}
Sensibilidade do prêmio ao preço à vista
S ∈ [{(S*0.7).toFixed(2)} … {(S*1.3).toFixed(2)}]
{/* Decomposition */}
Decomposição do prêmio
{(() => {
const intrinsic = type === 'call' ? Math.max(+S - +K, 0) : Math.max(+K - +S, 0);
const time = res.price - intrinsic;
return (
<>
Valor intrínseco
{IBDMath.fmtBRL(intrinsic)}
max({type === 'call' ? 'S−K' : 'K−S'}, 0)
Valor extrínseco
{IBDMath.fmtBRL(time)}
prêmio − intrínseco
Volatilidade implícita usada
{(+sigma).toFixed(2)}%
ann. · base 252 / 365
>
);
})()}
{/* Formula */}
Fórmula utilizada
d₁ = [ ln(S/K) + (r − q + σ²/2)·T ] / (σ·√T) = {IBDMath.fmtNum(res.d1, 4)}
d₂ = d₁ − σ·√T = {IBDMath.fmtNum(res.d2, 4)}
{type === 'call'
? 'C = S·e^(−qT)·N(d₁) − K·e^(−rT)·N(d₂)'
: 'P = K·e^(−rT)·N(−d₂) − S·e^(−qT)·N(−d₁)'}
);
}
function NumField({ label, value, setValue, unit, step, hint }) {
return (
setValue(e.target.value === '' ? '' : +e.target.value)}
className="mono"
style={{padding: 12, border: 'none', background: 'var(--bg-elev)', fontSize: 15, color: 'var(--fg)', outline: 'none', width: '100%'}}
/>
{unit}
{hint &&
{hint}
}
);
}
function Greek({ name, label, value, sub }) {
return (
{name}
{value}
{label} · {sub}
);
}
function SensitivityChart({ points, S, K, type }) {
const w = 720, h = 280, pad = { l: 50, r: 16, t: 16, b: 36 };
const iw = w - pad.l - pad.r, ih = h - pad.t - pad.b;
const xs = points.map(p => p.s);
const ys = points.map(p => p.p);
const xMin = Math.min(...xs), xMax = Math.max(...xs);
const yMin = 0, yMax = Math.max(...ys) * 1.05;
const x = s => pad.l + ((s - xMin) / (xMax - xMin)) * iw;
const y = v => pad.t + ih - ((v - yMin) / (yMax - yMin)) * ih;
const path = points.map((p, i) => (i === 0 ? 'M' : 'L') + x(p.s).toFixed(2) + ',' + y(p.p).toFixed(2)).join(' ');
// intrinsic line
const intrPath = points.map((p, i) => {
const v = type === 'call' ? Math.max(p.s - K, 0) : Math.max(K - p.s, 0);
return (i === 0 ? 'M' : 'L') + x(p.s).toFixed(2) + ',' + y(v).toFixed(2);
}).join(' ');
const xTicks = 6, yTicks = 5;
return (
Prêmio teórico
Valor intrínseco
S atual
);
}
window.CalcBlackScholesPage = CalcBlackScholesPage;
window.NumField = NumField;