diff --git a/frontend/positions.html b/frontend/positions.html
index 55d4f03..28937e9 100644
--- a/frontend/positions.html
+++ b/frontend/positions.html
@@ -195,10 +195,11 @@
-
-
-
-
Net P/L (after commissions)
+
+
+
+
+
@@ -227,10 +228,12 @@
| Sym | Strategy | Legs |
Entered $ |
- Current $ |
+ Current $ (mid) |
+ Current $ (nat) |
Comm. (RT) |
- Gross P/L |
- Net P/L |
+ Net P/L (mid) |
+ Net P/L (close now) |
+ Spread $ |
P/L % |
Opened |
Status |
@@ -245,10 +248,12 @@
|
|
|
+ |
|
- |
- |
- |
+ |
+ |
+ |
+ |
|
|
|
@@ -333,7 +338,7 @@
},
rowFor(o) {
- let entered = 0, value = 0;
+ let entered = 0, value = 0, valueNatural = 0;
for (const l of (o.legs||[])) {
const sign = l.side === 'short' ? -1 : 1;
entered += sign * l.qty * 100 * (l.entryPrice || 0);
@@ -341,15 +346,36 @@
const oo = m && m[Number(l.strike) + '@' + l.type];
const mid = oo ? Number(oo.midPrice ?? oo.mid ?? oo.bsPrice ?? 0) : 0;
value += sign * l.qty * 100 * mid;
+
+ // Natural mark = realistic close-now price:
+ // long legs sell at the BID (other side takes them off your hands)
+ // short legs buy back at the ASK (you pay to take them off)
+ // If quotes are missing (after-hours / illiquid), fall back to mid.
+ const bid = oo ? Number(oo.bid ?? 0) : 0;
+ const ask = oo ? Number(oo.ask ?? 0) : 0;
+ let natural;
+ if (l.side === 'long') natural = bid > 0 ? bid : mid;
+ else natural = ask > 0 ? ask : mid;
+ valueNatural += sign * l.qty * 100 * natural;
}
const baseEntered = (o.entry_cost != null) ? Number(o.entry_cost) : entered;
const grossPL = value - baseEntered;
const commission = SettingsStore.estimate(o.legs || []).roundTrip;
const netPL = grossPL - commission;
+
+ // Natural-mark P/L: what you'd actually capture closing right now at
+ // the worst end of each leg's spread, after commissions.
+ const grossPLNatural = valueNatural - baseEntered;
+ const netPLNatural = grossPLNatural - commission;
+
const denom = Math.max(Math.abs(baseEntered), 1);
return {
- o, entered: baseEntered, value, grossPL, commission, netPL,
+ o, entered: baseEntered, value, valueNatural,
+ grossPL, commission, netPL,
+ grossPLNatural, netPLNatural,
plPct: netPL / denom * 100,
+ plPctNatural: netPLNatural / denom * 100,
+ spreadCost: netPL - netPLNatural,
legsSummary: (o.legs||[]).map(l =>
(l.side === 'long' ? '+' : '-') + l.qty + ' ' + l.strike + (l.type === 'call' ? 'C' : 'P') + '·' + (l.expiry||'').slice(5)
).join(' / '),
@@ -364,12 +390,20 @@
get openCount() { return this.visible.filter(r => r.o.status === 'open').length; },
get totals() {
- let entered = 0, current = 0, comm = 0, gross = 0;
+ let entered = 0, current = 0, currentNat = 0, comm = 0, gross = 0, grossNat = 0;
for (const r of this.visible) {
if (r.o.status !== 'open') continue;
- entered += r.entered; current += r.value; comm += r.commission; gross += r.grossPL;
+ entered += r.entered;
+ current += r.value; currentNat += r.valueNatural;
+ comm += r.commission;
+ gross += r.grossPL; grossNat += r.grossPLNatural;
}
- return { entered, current, commission: comm, grossPL: gross, netPL: gross - comm };
+ return {
+ entered, current, currentNat,
+ commission: comm,
+ grossPL: gross, netPL: gross - comm,
+ grossPLNatural: grossNat, netPLNatural: grossNat - comm,
+ };
},
get commissionLabel() {