@sybilion/uilib 1.3.62 → 1.3.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/widgets/DriversComparisonChart/DriversComparisonChart.js +2 -2
- package/dist/esm/components/widgets/DriversComparisonChart/driversComparisonChart.helpers.js +21 -5
- package/dist/esm/types/src/components/widgets/DriversComparisonChart/driversComparisonChart.helpers.d.ts +3 -1
- package/package.json +1 -1
- package/src/components/widgets/DriversComparisonChart/AGENT.md +3 -3
- package/src/components/widgets/DriversComparisonChart/DriversComparisonChart.tsx +2 -2
- package/src/components/widgets/DriversComparisonChart/driversComparisonChart.helpers.test.ts +65 -45
- package/src/components/widgets/DriversComparisonChart/driversComparisonChart.helpers.ts +22 -4
|
@@ -83,8 +83,8 @@ function DriversComparisonChart({ payload, datasetHistorical = [], loading = fal
|
|
|
83
83
|
const mergedChartData = useMemo(() => mergeBacktestsChartData(payloadForView), [payloadForView]);
|
|
84
84
|
const mergedWithHistorical = useMemo(() => mergeDatasetHistoricalWithBacktestsChartData(datasetHistorical, mergedChartData), [datasetHistorical, mergedChartData]);
|
|
85
85
|
const historicalWindowFloor = useMemo(() => {
|
|
86
|
-
const
|
|
87
|
-
const merged = mergeDatasetHistoricalWithBacktestsChartData(datasetHistorical,
|
|
86
|
+
const laggedMerged = mergeBacktestsChartData(payload);
|
|
87
|
+
const merged = mergeDatasetHistoricalWithBacktestsChartData(datasetHistorical, laggedMerged);
|
|
88
88
|
const sortedDrivers = [...(payload?.drivers ?? [])]
|
|
89
89
|
.filter(d => d.normalized_series &&
|
|
90
90
|
Object.keys(d.normalized_series).some(key => d.normalized_series[key] != null))
|
package/dist/esm/components/widgets/DriversComparisonChart/driversComparisonChart.helpers.js
CHANGED
|
@@ -57,7 +57,7 @@ function formatLagMonthsLabel(months) {
|
|
|
57
57
|
return `${months} month(s)`;
|
|
58
58
|
}
|
|
59
59
|
function getLagDisplayForView(lag, tab) {
|
|
60
|
-
if (tab === '
|
|
60
|
+
if (tab === 'lagged') {
|
|
61
61
|
return lag?.trim() ? lag : '—';
|
|
62
62
|
}
|
|
63
63
|
const months = parseLagMonthsFromLabel(lag);
|
|
@@ -78,8 +78,24 @@ function shiftNormalizedSeriesForward(series, lagMonths) {
|
|
|
78
78
|
}
|
|
79
79
|
return out;
|
|
80
80
|
}
|
|
81
|
+
/** Shift driver points left (earlier months) by lag — overlapped alignment vs target. */
|
|
82
|
+
function shiftNormalizedSeriesBackward(series, lagMonths) {
|
|
83
|
+
if (lagMonths <= 0)
|
|
84
|
+
return { ...series };
|
|
85
|
+
const out = {};
|
|
86
|
+
for (const [dateStr, val] of Object.entries(series)) {
|
|
87
|
+
if (val === null || val === undefined)
|
|
88
|
+
continue;
|
|
89
|
+
let shifted = normalizeToMonthStart(dateStr);
|
|
90
|
+
for (let i = 0; i < lagMonths; i++) {
|
|
91
|
+
shifted = getPreviousMonth(shifted);
|
|
92
|
+
}
|
|
93
|
+
out[shifted] = val;
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
81
97
|
function applyDriversComparisonViewToPayload(payload, tab) {
|
|
82
|
-
if (!payload || tab === '
|
|
98
|
+
if (!payload || tab === 'lagged')
|
|
83
99
|
return payload;
|
|
84
100
|
return {
|
|
85
101
|
target: {
|
|
@@ -90,7 +106,7 @@ function applyDriversComparisonViewToPayload(payload, tab) {
|
|
|
90
106
|
const lagMonths = parseLagMonthsFromLabel(resolveDriverLagLabel(driver));
|
|
91
107
|
const series = driver.normalized_series ?? {};
|
|
92
108
|
const normalized_series = lagMonths != null && lagMonths > 0
|
|
93
|
-
?
|
|
109
|
+
? shiftNormalizedSeriesBackward(series, lagMonths)
|
|
94
110
|
: { ...series };
|
|
95
111
|
return {
|
|
96
112
|
...driver,
|
|
@@ -231,7 +247,7 @@ function prependHistoricalLeadFromDataset(points, datasetHistorical, xMin) {
|
|
|
231
247
|
}
|
|
232
248
|
return [...lead, ...points];
|
|
233
249
|
}
|
|
234
|
-
/**
|
|
250
|
+
/** Lagged (unshifted) anchor minus lead months — stable xMin across lagged/overlapped tabs. */
|
|
235
251
|
function computeDriversComparisonHistoricalWindowFloor(mergedWithHistorical, forecastIds) {
|
|
236
252
|
if (mergedWithHistorical.length === 0 || forecastIds.length === 0)
|
|
237
253
|
return null;
|
|
@@ -253,4 +269,4 @@ function buildDriversComparisonChartData(mergedWithHistorical, datasetHistorical
|
|
|
253
269
|
return withLead.filter(p => normalizeToMonthStart(p.date) >= xMin);
|
|
254
270
|
}
|
|
255
271
|
|
|
256
|
-
export { DRIVER_COMPARISON_CHART_LEAD_MONTHS, DRIVER_FORECAST_ID_BASE, INITIAL_VISIBLE_SERIES_COUNT, applyDriversComparisonViewToPayload, buildDriversComparisonChartData, computeDriversComparisonHistoricalWindowFloor, formatLagMonthsLabel, formatSeriesImportance, getLagDisplayForView, getZoomAnchorMonthFromDriverMaterialStarts, mergeBacktestsChartData, mergeDatasetHistoricalWithBacktestsChartData, parseLagMonthsFromLabel, prependHistoricalLeadFromDataset, resolveDriverLagLabel, shiftNormalizedSeriesForward, subtractMonthsFromMonthStart };
|
|
272
|
+
export { DRIVER_COMPARISON_CHART_LEAD_MONTHS, DRIVER_FORECAST_ID_BASE, INITIAL_VISIBLE_SERIES_COUNT, applyDriversComparisonViewToPayload, buildDriversComparisonChartData, computeDriversComparisonHistoricalWindowFloor, formatLagMonthsLabel, formatSeriesImportance, getLagDisplayForView, getZoomAnchorMonthFromDriverMaterialStarts, mergeBacktestsChartData, mergeDatasetHistoricalWithBacktestsChartData, parseLagMonthsFromLabel, prependHistoricalLeadFromDataset, resolveDriverLagLabel, shiftNormalizedSeriesBackward, shiftNormalizedSeriesForward, subtractMonthsFromMonthStart };
|
|
@@ -16,6 +16,8 @@ export declare function parseLagMonthsFromLabel(lag: string | null | undefined):
|
|
|
16
16
|
export declare function formatLagMonthsLabel(months: number): string;
|
|
17
17
|
export declare function getLagDisplayForView(lag: string | null | undefined, tab: DriversComparisonViewTab): string;
|
|
18
18
|
export declare function shiftNormalizedSeriesForward(series: Record<string, number | null>, lagMonths: number): Record<string, number | null>;
|
|
19
|
+
/** Shift driver points left (earlier months) by lag — overlapped alignment vs target. */
|
|
20
|
+
export declare function shiftNormalizedSeriesBackward(series: Record<string, number | null>, lagMonths: number): Record<string, number | null>;
|
|
19
21
|
export declare function applyDriversComparisonViewToPayload(payload: BacktestsComponentPayload | null, tab: DriversComparisonViewTab): BacktestsComponentPayload | null;
|
|
20
22
|
export declare function mergeBacktestsChartData(payload: BacktestsComponentPayload | null): ChartDataPoint[];
|
|
21
23
|
/** While drivers comparison is not loaded: dataset historical only. After load: normalized historical from target.normalized_series (see mergeBacktestsChartData) replaces it. */
|
|
@@ -35,6 +37,6 @@ export declare function subtractMonthsFromMonthStart(date: string, count: number
|
|
|
35
37
|
* (scaled to the first normalized point) so the lead-in shows the target line alone.
|
|
36
38
|
*/
|
|
37
39
|
export declare function prependHistoricalLeadFromDataset(points: ChartDataPoint[], datasetHistorical: ChartDataPoint[], xMin: string): ChartDataPoint[];
|
|
38
|
-
/**
|
|
40
|
+
/** Lagged (unshifted) anchor minus lead months — stable xMin across lagged/overlapped tabs. */
|
|
39
41
|
export declare function computeDriversComparisonHistoricalWindowFloor(mergedWithHistorical: ChartDataPoint[], forecastIds: number[]): string | null;
|
|
40
42
|
export declare function buildDriversComparisonChartData(mergedWithHistorical: ChartDataPoint[], datasetHistorical: ChartDataPoint[], forecastIds: number[], historicalWindowFloor?: string | null): ChartDataPoint[];
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ Host provides:
|
|
|
10
10
|
- `payload`: BacktestsComponentPayload from platform SDK (host fetch per analysis)
|
|
11
11
|
- Optional `datasetHistorical` overlay
|
|
12
12
|
- `seriesInitKey` when selected analysis changes
|
|
13
|
-
- `viewTab` / `onViewTabChange`: `
|
|
13
|
+
- `viewTab` / `onViewTabChange`: `lagged` (calendar-aligned, raw dates) or `overlapped` (driver series shifted backward by parsed lag months)
|
|
14
14
|
|
|
15
15
|
View tabs: host should render uilib `Tabs variant="button"` with **Lagged** / **Overlapped** in the toolbar (analysis selector left, tabs right). Chart applies `applyDriversComparisonViewToPayload` internally.
|
|
16
16
|
|
|
@@ -18,8 +18,8 @@ Report tile: `drivers_comparison_chart` — host loads normalized backtests payl
|
|
|
18
18
|
|
|
19
19
|
Requires: `payload` — target + driver normalized_series; `loading` / `chartLoading` — spinners; `seriesInitKey` — reset visible series on analysis or view tab change; `runAnalysisHint` / `statusHint` — empty/error text.
|
|
20
20
|
|
|
21
|
-
Lag column: **
|
|
21
|
+
Lag column: **Lagged** shows API `lag` string (may be a range). **Overlapped** shows single `N month(s)` from `parseLagMonthsFromLabel` (range uses max month).
|
|
22
22
|
|
|
23
|
-
Historical window: lead-in is anchored to the **
|
|
23
|
+
Historical window: lead-in is anchored to the **lagged** (unshifted) view (`computeDriversComparisonHistoricalWindowFloor`); overlapped tab shifts driver lines backward only — switching tabs does not change historical extent when floor is pinned.
|
|
24
24
|
|
|
25
25
|
Empty/loading: loading props shimmer chart; null `payload` with `runAnalysisHint` prompts to run analysis.
|
|
@@ -160,10 +160,10 @@ export function DriversComparisonChart({
|
|
|
160
160
|
);
|
|
161
161
|
|
|
162
162
|
const historicalWindowFloor = useMemo(() => {
|
|
163
|
-
const
|
|
163
|
+
const laggedMerged = mergeBacktestsChartData(payload);
|
|
164
164
|
const merged = mergeDatasetHistoricalWithBacktestsChartData(
|
|
165
165
|
datasetHistorical,
|
|
166
|
-
|
|
166
|
+
laggedMerged,
|
|
167
167
|
);
|
|
168
168
|
const sortedDrivers = [...(payload?.drivers ?? [])]
|
|
169
169
|
.filter(
|
package/src/components/widgets/DriversComparisonChart/driversComparisonChart.helpers.test.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
mergeDatasetHistoricalWithBacktestsChartData,
|
|
13
13
|
parseLagMonthsFromLabel,
|
|
14
14
|
resolveDriverLagLabel,
|
|
15
|
+
shiftNormalizedSeriesBackward,
|
|
15
16
|
shiftNormalizedSeriesForward,
|
|
16
17
|
subtractMonthsFromMonthStart,
|
|
17
18
|
} from './driversComparisonChart.helpers';
|
|
@@ -58,17 +59,17 @@ describe('formatLagMonthsLabel', () => {
|
|
|
58
59
|
});
|
|
59
60
|
|
|
60
61
|
describe('getLagDisplayForView', () => {
|
|
61
|
-
it('shows API lag on
|
|
62
|
-
expect(getLagDisplayForView('9 to 12 month(s)', '
|
|
62
|
+
it('shows API lag on lagged tab', () => {
|
|
63
|
+
expect(getLagDisplayForView('9 to 12 month(s)', 'lagged')).toBe(
|
|
63
64
|
'9 to 12 month(s)',
|
|
64
65
|
);
|
|
65
66
|
});
|
|
66
67
|
|
|
67
|
-
it('shows parsed month on
|
|
68
|
-
expect(getLagDisplayForView('9 to 12 month(s)', '
|
|
68
|
+
it('shows parsed month on overlapped tab', () => {
|
|
69
|
+
expect(getLagDisplayForView('9 to 12 month(s)', 'overlapped')).toBe(
|
|
69
70
|
'12 month(s)',
|
|
70
71
|
);
|
|
71
|
-
expect(getLagDisplayForView('Unknown', '
|
|
72
|
+
expect(getLagDisplayForView('Unknown', 'overlapped')).toBe('—');
|
|
72
73
|
});
|
|
73
74
|
});
|
|
74
75
|
|
|
@@ -90,6 +91,24 @@ describe('shiftNormalizedSeriesForward', () => {
|
|
|
90
91
|
});
|
|
91
92
|
});
|
|
92
93
|
|
|
94
|
+
describe('shiftNormalizedSeriesBackward', () => {
|
|
95
|
+
it('moves points backward by lag months', () => {
|
|
96
|
+
const shifted = shiftNormalizedSeriesBackward(
|
|
97
|
+
{ '2015-03-01': 1.2, '2015-04-01': 1.4 },
|
|
98
|
+
2,
|
|
99
|
+
);
|
|
100
|
+
expect(shifted['2015-01-01']).toBe(1.2);
|
|
101
|
+
expect(shifted['2015-02-01']).toBe(1.4);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('returns a shallow copy when lag is zero', () => {
|
|
105
|
+
const series = { '2015-01-01': 1 };
|
|
106
|
+
const shifted = shiftNormalizedSeriesBackward(series, 0);
|
|
107
|
+
expect(shifted).toEqual(series);
|
|
108
|
+
expect(shifted).not.toBe(series);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
93
112
|
describe('applyDriversComparisonViewToPayload', () => {
|
|
94
113
|
const payload = {
|
|
95
114
|
target: {
|
|
@@ -107,18 +126,19 @@ describe('applyDriversComparisonViewToPayload', () => {
|
|
|
107
126
|
],
|
|
108
127
|
};
|
|
109
128
|
|
|
110
|
-
it('returns original payload for
|
|
111
|
-
expect(applyDriversComparisonViewToPayload(payload, '
|
|
112
|
-
payload,
|
|
113
|
-
);
|
|
129
|
+
it('returns original payload for lagged tab', () => {
|
|
130
|
+
expect(applyDriversComparisonViewToPayload(payload, 'lagged')).toBe(payload);
|
|
114
131
|
});
|
|
115
132
|
|
|
116
|
-
it('shifts driver series for
|
|
117
|
-
const
|
|
118
|
-
|
|
133
|
+
it('shifts driver series backward for overlapped tab without mutating source', () => {
|
|
134
|
+
const overlapped = applyDriversComparisonViewToPayload(
|
|
135
|
+
payload,
|
|
136
|
+
'overlapped',
|
|
137
|
+
);
|
|
138
|
+
expect(overlapped).not.toBe(payload);
|
|
119
139
|
expect(payload.drivers[0].normalized_series['2015-01-01']).toBe(0.5);
|
|
120
|
-
expect(
|
|
121
|
-
expect(
|
|
140
|
+
expect(overlapped?.drivers[0].normalized_series['2014-11-01']).toBe(0.5);
|
|
141
|
+
expect(overlapped?.target.normalized_series).toEqual(
|
|
122
142
|
payload.target.normalized_series,
|
|
123
143
|
);
|
|
124
144
|
});
|
|
@@ -135,11 +155,11 @@ describe('applyDriversComparisonViewToPayload', () => {
|
|
|
135
155
|
},
|
|
136
156
|
],
|
|
137
157
|
};
|
|
138
|
-
const
|
|
158
|
+
const overlapped = applyDriversComparisonViewToPayload(
|
|
139
159
|
withOverallLag,
|
|
140
|
-
'
|
|
160
|
+
'overlapped',
|
|
141
161
|
);
|
|
142
|
-
expect(
|
|
162
|
+
expect(overlapped?.drivers[0].normalized_series['2015-05-01']).toBe(0.8);
|
|
143
163
|
});
|
|
144
164
|
});
|
|
145
165
|
|
|
@@ -184,7 +204,7 @@ describe('computeDriversComparisonHistoricalWindowFloor', () => {
|
|
|
184
204
|
|
|
185
205
|
const forecastIds = [DRIVER_FORECAST_ID_BASE];
|
|
186
206
|
|
|
187
|
-
it('returns anchor minus lead months from
|
|
207
|
+
it('returns anchor minus lead months from lagged (unshifted) merged data', () => {
|
|
188
208
|
const merged = mergeDatasetHistoricalWithBacktestsChartData(
|
|
189
209
|
datasetHistorical,
|
|
190
210
|
mergeBacktestsChartData(laggedDriverPayload),
|
|
@@ -245,70 +265,70 @@ describe('buildDriversComparisonChartData historical window floor', () => {
|
|
|
245
265
|
const forecastIds = [DRIVER_FORECAST_ID_BASE];
|
|
246
266
|
|
|
247
267
|
it('keeps same xMin for lagged and overlapped when floor is pinned', () => {
|
|
248
|
-
const
|
|
268
|
+
const laggedMerged = mergeDatasetHistoricalWithBacktestsChartData(
|
|
249
269
|
datasetHistorical,
|
|
250
270
|
mergeBacktestsChartData(laggedDriverPayload),
|
|
251
271
|
);
|
|
252
|
-
const
|
|
272
|
+
const overlappedPayload = applyDriversComparisonViewToPayload(
|
|
253
273
|
laggedDriverPayload,
|
|
254
|
-
'
|
|
274
|
+
'overlapped',
|
|
255
275
|
);
|
|
256
|
-
const
|
|
276
|
+
const overlappedMerged = mergeDatasetHistoricalWithBacktestsChartData(
|
|
257
277
|
datasetHistorical,
|
|
258
|
-
mergeBacktestsChartData(
|
|
278
|
+
mergeBacktestsChartData(overlappedPayload),
|
|
259
279
|
);
|
|
260
280
|
const floor = computeDriversComparisonHistoricalWindowFloor(
|
|
261
|
-
|
|
281
|
+
laggedMerged,
|
|
262
282
|
forecastIds,
|
|
263
283
|
)!;
|
|
264
284
|
|
|
265
|
-
const
|
|
266
|
-
|
|
285
|
+
const laggedChart = buildDriversComparisonChartData(
|
|
286
|
+
laggedMerged,
|
|
267
287
|
datasetHistorical,
|
|
268
288
|
forecastIds,
|
|
269
289
|
floor,
|
|
270
290
|
);
|
|
271
|
-
const
|
|
272
|
-
|
|
291
|
+
const overlappedChart = buildDriversComparisonChartData(
|
|
292
|
+
overlappedMerged,
|
|
273
293
|
datasetHistorical,
|
|
274
294
|
forecastIds,
|
|
275
295
|
floor,
|
|
276
296
|
);
|
|
277
297
|
|
|
278
|
-
expect(overlappedChart[0]?.date).toBe('2014-10-01');
|
|
279
298
|
expect(laggedChart[0]?.date).toBe('2014-10-01');
|
|
280
|
-
expect(
|
|
299
|
+
expect(overlappedChart[0]?.date).toBe('2014-10-01');
|
|
300
|
+
expect(overlappedChart[0]?.date).toBe(laggedChart[0]?.date);
|
|
281
301
|
});
|
|
282
302
|
|
|
283
|
-
it('without floor override
|
|
284
|
-
const
|
|
303
|
+
it('without floor override overlapped chart starts earlier than lagged', () => {
|
|
304
|
+
const laggedMerged = mergeDatasetHistoricalWithBacktestsChartData(
|
|
285
305
|
datasetHistorical,
|
|
286
306
|
mergeBacktestsChartData(laggedDriverPayload),
|
|
287
307
|
);
|
|
288
|
-
const
|
|
308
|
+
const overlappedPayload = applyDriversComparisonViewToPayload(
|
|
289
309
|
laggedDriverPayload,
|
|
290
|
-
'
|
|
310
|
+
'overlapped',
|
|
291
311
|
);
|
|
292
|
-
const
|
|
312
|
+
const overlappedMerged = mergeDatasetHistoricalWithBacktestsChartData(
|
|
293
313
|
datasetHistorical,
|
|
294
|
-
mergeBacktestsChartData(
|
|
314
|
+
mergeBacktestsChartData(overlappedPayload),
|
|
295
315
|
);
|
|
296
316
|
|
|
297
|
-
const
|
|
298
|
-
|
|
317
|
+
const laggedChart = buildDriversComparisonChartData(
|
|
318
|
+
laggedMerged,
|
|
299
319
|
datasetHistorical,
|
|
300
320
|
forecastIds,
|
|
301
321
|
);
|
|
302
|
-
const
|
|
303
|
-
|
|
322
|
+
const overlappedChart = buildDriversComparisonChartData(
|
|
323
|
+
overlappedMerged,
|
|
304
324
|
datasetHistorical,
|
|
305
325
|
forecastIds,
|
|
306
326
|
);
|
|
307
327
|
|
|
308
|
-
expect(
|
|
309
|
-
expect(
|
|
310
|
-
expect(
|
|
311
|
-
|
|
312
|
-
);
|
|
328
|
+
expect(laggedChart[0]?.date).toBe('2014-10-01');
|
|
329
|
+
expect(overlappedChart[0]?.date).toBe('2014-07-01');
|
|
330
|
+
expect(
|
|
331
|
+
overlappedChart[0]?.date!.localeCompare(laggedChart[0]?.date!),
|
|
332
|
+
).toBe(-1);
|
|
313
333
|
});
|
|
314
334
|
});
|
|
@@ -78,7 +78,7 @@ export function getLagDisplayForView(
|
|
|
78
78
|
lag: string | null | undefined,
|
|
79
79
|
tab: DriversComparisonViewTab,
|
|
80
80
|
): string {
|
|
81
|
-
if (tab === '
|
|
81
|
+
if (tab === 'lagged') {
|
|
82
82
|
return lag?.trim() ? lag : '—';
|
|
83
83
|
}
|
|
84
84
|
const months = parseLagMonthsFromLabel(lag);
|
|
@@ -102,11 +102,29 @@ export function shiftNormalizedSeriesForward(
|
|
|
102
102
|
return out;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/** Shift driver points left (earlier months) by lag — overlapped alignment vs target. */
|
|
106
|
+
export function shiftNormalizedSeriesBackward(
|
|
107
|
+
series: Record<string, number | null>,
|
|
108
|
+
lagMonths: number,
|
|
109
|
+
): Record<string, number | null> {
|
|
110
|
+
if (lagMonths <= 0) return { ...series };
|
|
111
|
+
const out: Record<string, number | null> = {};
|
|
112
|
+
for (const [dateStr, val] of Object.entries(series)) {
|
|
113
|
+
if (val === null || val === undefined) continue;
|
|
114
|
+
let shifted = normalizeToMonthStart(dateStr);
|
|
115
|
+
for (let i = 0; i < lagMonths; i++) {
|
|
116
|
+
shifted = getPreviousMonth(shifted);
|
|
117
|
+
}
|
|
118
|
+
out[shifted] = val;
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
|
|
105
123
|
export function applyDriversComparisonViewToPayload(
|
|
106
124
|
payload: BacktestsComponentPayload | null,
|
|
107
125
|
tab: DriversComparisonViewTab,
|
|
108
126
|
): BacktestsComponentPayload | null {
|
|
109
|
-
if (!payload || tab === '
|
|
127
|
+
if (!payload || tab === 'lagged') return payload;
|
|
110
128
|
|
|
111
129
|
return {
|
|
112
130
|
target: {
|
|
@@ -118,7 +136,7 @@ export function applyDriversComparisonViewToPayload(
|
|
|
118
136
|
const series = driver.normalized_series ?? {};
|
|
119
137
|
const normalized_series =
|
|
120
138
|
lagMonths != null && lagMonths > 0
|
|
121
|
-
?
|
|
139
|
+
? shiftNormalizedSeriesBackward(series, lagMonths)
|
|
122
140
|
: { ...series };
|
|
123
141
|
return {
|
|
124
142
|
...driver,
|
|
@@ -290,7 +308,7 @@ export function prependHistoricalLeadFromDataset(
|
|
|
290
308
|
return [...lead, ...points];
|
|
291
309
|
}
|
|
292
310
|
|
|
293
|
-
/**
|
|
311
|
+
/** Lagged (unshifted) anchor minus lead months — stable xMin across lagged/overlapped tabs. */
|
|
294
312
|
export function computeDriversComparisonHistoricalWindowFloor(
|
|
295
313
|
mergedWithHistorical: ChartDataPoint[],
|
|
296
314
|
forecastIds: number[],
|