Skip to content

Commit 7e3ade7

Browse files
authored
Merge pull request #65 from heyitsStylez/extract-build-display-series
Extract buildDisplaySeries from rCpnlChart into 05b-pnl.js
2 parents 93383cd + 84e4d1f commit 7e3ade7

4 files changed

Lines changed: 88 additions & 36 deletions

File tree

docs/architecture.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ flowchart TD
6363
6464
subgraph PnL ["P&L — 05b-pnl.js"]
6565
CP["computePnl(trades, assetFilter, livePrices)\n→ { realised, unrealised, total,\n missingSpotAssets,\n realisedSeries, realisedByMonth }"]
66+
BDS["buildDisplaySeries(series, period, today)\n→ [{date, val}]"]
6667
end
6768
6869
subgraph Stats ["Stats — 05d-calc-stats.js"]

src/js/05b-pnl.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,39 @@ function computePnl(trades, assetFilter, livePrices) {
8080
return { realised, unrealised, total, missingSpotAssets, realisedSeries, realisedByMonth };
8181
}
8282

83+
function buildDisplaySeries(series, period, today) {
84+
if (!series.length) return [];
85+
86+
const allSeries = [{ date: series[0].date, val: 0 }, ...series];
87+
88+
let dispSeries;
89+
if (period === 'ALL') {
90+
dispSeries = allSeries;
91+
} else {
92+
const days = period === '1M' ? 30 : 90;
93+
const cutDate = new Date(today + 'T12:00:00');
94+
cutDate.setDate(cutDate.getDate() - days);
95+
const cutStr = cutDate.toISOString().slice(0, 10);
96+
97+
const lastBefore = allSeries.filter(p => p.date < cutStr);
98+
const baseline = lastBefore.length ? lastBefore[lastBefore.length - 1].val : 0;
99+
const inPeriod = allSeries.filter(p => p.date >= cutStr);
100+
101+
if (!inPeriod.length) {
102+
dispSeries = [{ date: cutStr, val: baseline }, { date: today, val: baseline }];
103+
} else {
104+
dispSeries = [{ date: cutStr, val: baseline }, ...inPeriod];
105+
}
106+
}
107+
108+
const last = dispSeries[dispSeries.length - 1];
109+
if (last.date < today) {
110+
dispSeries = [...dispSeries, { date: today, val: last.val }];
111+
}
112+
113+
return dispSeries;
114+
}
115+
83116
if (typeof module !== 'undefined' && module.exports) {
84-
module.exports = { computePnl };
117+
module.exports = { computePnl, buildDisplaySeries };
85118
}

src/js/07-render-charts.js

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -65,42 +65,9 @@ function rCpnlChart() {
6565
return;
6666
}
6767

68-
// Period filter cutoff
69-
const today = new Date();
70-
let cutoff = null;
71-
if (sCpnlPeriod === '1M') {
72-
cutoff = new Date(today); cutoff.setDate(cutoff.getDate() - 30);
73-
} else if (sCpnlPeriod === '3M') {
74-
cutoff = new Date(today); cutoff.setDate(cutoff.getDate() - 90);
75-
}
76-
77-
// Cumulative series: prepend a zero-baseline so the first point starts at 0.
78-
const allSeries = [{ date: realisedSeries[0].date, val: 0 }, ...realisedSeries];
68+
const todayStr = today();
7969
const totalPnl = realisedSeries[realisedSeries.length - 1].val;
80-
81-
// Filter series for display period
82-
let dispSeries;
83-
if (!cutoff) {
84-
dispSeries = allSeries;
85-
} else {
86-
const cutStr = cutoff.toISOString().slice(0, 10);
87-
let baseline = 0;
88-
let lastBefore = allSeries.filter(p => p.date < cutStr);
89-
if (lastBefore.length) baseline = lastBefore[lastBefore.length - 1].val;
90-
const inPeriod = allSeries.filter(p => p.date >= cutStr);
91-
if (!inPeriod.length) {
92-
dispSeries = [{ date: cutStr, val: totalPnl }, { date: today.toISOString().slice(0, 10), val: totalPnl }];
93-
} else {
94-
dispSeries = [{ date: cutStr, val: baseline }, ...inPeriod];
95-
}
96-
}
97-
98-
// Add today as the final point (carries last value forward)
99-
const todayStr = today.toISOString().slice(0, 10);
100-
const last = dispSeries[dispSeries.length - 1];
101-
if (last.date < todayStr) {
102-
dispSeries = [...dispSeries, { date: todayStr, val: last.val }];
103-
}
70+
const dispSeries = buildDisplaySeries(realisedSeries, sCpnlPeriod, todayStr);
10471

10572
// Period change
10673
const periodStart = dispSeries[0].val;

test/unit/pnl.test.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,54 @@ test('realisedSeries: CALLED event contributes premium AND capital gain at expir
242242
assert.strictEqual(last.date, '2026-02-15');
243243
assert.strictEqual(last.val, 550);
244244
});
245+
246+
// buildDisplaySeries tests
247+
const { buildDisplaySeries } = require('../../src/js/05b-pnl.js');
248+
249+
test('buildDisplaySeries: empty input returns empty', () => {
250+
assert.deepStrictEqual(buildDisplaySeries([], 'ALL', '2026-05-16'), []);
251+
});
252+
253+
test("buildDisplaySeries: ALL prepends zero-baseline and appends today", () => {
254+
const series = [
255+
{ date: '2026-01-15', val: 100 },
256+
{ date: '2026-03-01', val: 250 },
257+
];
258+
const result = buildDisplaySeries(series, 'ALL', '2026-05-16');
259+
assert.deepStrictEqual(result, [
260+
{ date: '2026-01-15', val: 0 },
261+
{ date: '2026-01-15', val: 100 },
262+
{ date: '2026-03-01', val: 250 },
263+
{ date: '2026-05-16', val: 250 },
264+
]);
265+
});
266+
267+
test("buildDisplaySeries: 1M with no in-range points returns flat [{cutoff,lastVal},{today,lastVal}]", () => {
268+
// All trades before the 30-day window
269+
const series = [{ date: '2026-01-15', val: 100 }];
270+
// today=2026-05-16 → cutoff=2026-04-16; series point is before cutoff
271+
const result = buildDisplaySeries(series, '1M', '2026-05-16');
272+
assert.strictEqual(result.length, 2);
273+
assert.strictEqual(result[0].date, '2026-04-16');
274+
assert.strictEqual(result[0].val, 100);
275+
assert.strictEqual(result[1].date, '2026-05-16');
276+
assert.strictEqual(result[1].val, 100);
277+
});
278+
279+
test("buildDisplaySeries: baseline carry-forward is last point before the cutoff", () => {
280+
// today=2026-05-16 → 1M cutoff=2026-04-16
281+
// Two points before cutoff; baseline should be 250 (the later one)
282+
// One point after cutoff at 300
283+
const series = [
284+
{ date: '2026-01-15', val: 100 },
285+
{ date: '2026-03-01', val: 250 },
286+
{ date: '2026-04-20', val: 300 },
287+
];
288+
const result = buildDisplaySeries(series, '1M', '2026-05-16');
289+
// dispSeries: [{cutoff, 250}, {2026-04-20, 300}], then today appended
290+
assert.deepStrictEqual(result, [
291+
{ date: '2026-04-16', val: 250 },
292+
{ date: '2026-04-20', val: 300 },
293+
{ date: '2026-05-16', val: 300 },
294+
]);
295+
});

0 commit comments

Comments
 (0)