Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions src/js/core/06-render-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,22 +166,35 @@ function _openRow(r) {
// "↻ rolled" tag on a CLOSED leg that was rolled into a new option (Wheeler
// roll: the replacement carries rolledFrom = this id). Derived, so deleting the
// replacement drops the tag.
function _isRolled(r) {
return r.outcome === 'CLOSED' && trades.some(t => t.rolledFrom === r.id);
}

function _rolledTag(r) {
if (r.outcome !== 'CLOSED' || !trades.some(t => t.rolledFrom === r.id)) return '';
if (!_isRolled(r)) return '';
return '<span class="rolled-tag" title="Rolled into a new position">\u21bb rolled</span>';
}

// "% of premium captured" badge for buy-to-close (CLOSED) rows — how much of the
// collected premium you kept by closing early. Hidden when there is no premium
// to capture. Graded green/orange/red by how much was kept.
function _captureBadge(r) {
if (r.outcome !== 'CLOSED') return '';
if (r.outcome !== 'CLOSED' || _isRolled(r)) return '';
const pct = capturePct(r.premium, r.closeCost);
if (pct === null) return '';
const col = pct >= 70 ? 'var(--green)' : pct >= 40 ? 'var(--orange)' : 'var(--red)';
return '<span class="cap-badge" style="color:' + col + '">' + Math.round(pct) + '% kept</span>';
}

// Net premium of a CLOSED leg (premium − closeCost). A buy-back above the
// premium (common on a roll) goes negative: show "−$X" in red, not "+$-X".
function _netPremHtml(r) {
const net = r.premium - (r.closeCost || 0);
return net < 0
? '<span style="color:var(--red)">\u2212$' + fmt(-net) + '</span>'
: '+$' + fmt(net);
}

// "days early" tag for a buy-to-close — days still on the clock at close
// (expiry − closeDate). Completes the close-vs-hold story next to % kept.
function _daysEarlyTag(r) {
Expand Down Expand Up @@ -211,7 +224,7 @@ function _histRow(r) {
+ '<td>$' + fmt(r.strike) + '</td>'
+ '<td class="mu">' + fmtSize(r.size, r.asset) + '</td>'
+ '<td class="cr">' + (r.outcome === 'CLOSED'
? '+$' + fmt(r.premium - (r.closeCost || 0)) + '<br><span style="font-size:.6rem;color:var(--mu)">-$' + fmt(r.closeCost || 0) + ' to close</span>'
? _netPremHtml(r) + '<br><span style="font-size:.6rem;color:var(--mu)">-$' + fmt(r.closeCost || 0) + ' to close</span>'
: '+$' + fmt(r.premium)) + '</td>'
+ '<td>' + aprStr + '</td>'
+ '<td><span class="badge ' + outcomeBadge(r.outcome) + '">' + outcomeLabel(r) + '</span>'
Expand Down Expand Up @@ -267,7 +280,7 @@ function _histCard(r) {
const platBadge = r.platform === 'HSFC' ? '<span class="bplat bplat-hsfc">HSFC</span>' : '<span class="bplat bplat-rysk">RYSK</span>';
const outBadge = '<span class="badge ' + outcomeBadge(r.outcome) + '">' + outcomeLabel(r) + '</span>' + _captureBadge(r) + _rolledTag(r);
const premHtml = r.outcome === 'CLOSED'
? '+$' + fmt(r.premium - (r.closeCost || 0))
? _netPremHtml(r)
: '+$' + fmt(r.premium);
const corrBtn = r.outcome === 'EXPIRED' && r.type === 'CALL'
? '<button class="btn-qa btn-qa-cal" onclick="quickOutcome(' + r.id + ',\'CALLED\')">Called ↑</button>'
Expand Down
5 changes: 5 additions & 0 deletions test/integration/wheeler-app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -515,5 +515,10 @@ test('Roll button rolls an open PUT: old CLOSED, new OPEN linked', async (t) =>

// History shows the closed leg tagged as rolled; the open table has the new leg.
assert.match(doc.getElementById('ttbody-hist').innerHTML, /↻ rolled/);
// Buy-back > premium: net shows as −$70 (not "+$-70"), and no "% kept" badge on a rolled leg.
const hist = doc.getElementById('ttbody-hist').innerHTML;
assert.match(hist, /\u2212\$70/);
assert.doesNotMatch(hist, /\+\$-/);
assert.doesNotMatch(hist, /% kept/);
assert.match(doc.getElementById('ttbody-open').innerHTML, /195/);
});
Loading