Skip to content

Commit bef4bfa

Browse files
committed
feat(econify): Add FX date transparency for SNP database integration (v0.2.7)
- Enhanced FXTable interface with optional dates field for rate timestamps - explain.fx.asOf field shows when each exchange rate was last updated - Perfect for SNP database integration - pass through FX update dates - Fully backward compatible - dates are optional - Added comprehensive tests and documentation - All 241 tests passing
1 parent f5e355b commit bef4bfa

6 files changed

Lines changed: 113 additions & 3 deletions

File tree

packages/econify/CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22

33
All notable changes to the econify package will be documented in this file.
44

5+
## [0.2.7] - 2025-01-16
6+
7+
### Enhanced
8+
9+
- **FX Date Transparency**: Added support for FX rate dates in explain metadata
10+
- **Date Tracking**: `FXTable.dates` field to specify when each rate was last
11+
updated
12+
- **Explain Integration**: `explain.fx.asOf` field shows rate timestamp in
13+
metadata
14+
- **SNP Database Ready**: Perfect for passing through dates from external FX
15+
sources
16+
- **Full Transparency**: Users can see exactly when each exchange rate was
17+
current
18+
19+
### Technical Improvements
20+
21+
- **Enhanced FXTable Interface**: Added optional `dates` field for rate
22+
timestamps
23+
- **Backward Compatible**: Existing code works unchanged - dates are optional
24+
- **Documentation Enhanced**: Complete examples showing FX date usage patterns
25+
526
## [0.2.6] - 2025-01-16
627

728
### Enhanced

packages/econify/README.md

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,11 @@ const result = await processEconomicData(economicData, {
134134
targetMagnitude: "billions",
135135
targetTimeScale: "month", // 🆕 Standardize time periods to monthly
136136

137-
// Provide exchange rates
137+
// Provide exchange rates with dates
138138
fxFallback: {
139139
base: "USD",
140140
rates: { EUR: 0.92 },
141+
dates: { EUR: "2024-01-15T10:30:00Z" }, // When each rate was last updated
141142
},
142143
});
143144

@@ -225,7 +226,9 @@ result.data.forEach((item) => {
225226
// Enhanced FX information
226227
if (item.explain?.fx) {
227228
console.log(
228-
` 💱 FX: ${item.explain.fx.currency} → ${item.explain.fx.base} (rate: ${item.explain.fx.rate})`,
229+
` 💱 FX: ${item.explain.fx.currency} → ${item.explain.fx.base} (rate: ${item.explain.fx.rate})${
230+
item.explain.fx.asOf ? ` as of ${item.explain.fx.asOf}` : ""
231+
}`,
229232
);
230233
}
231234
});
@@ -897,6 +900,7 @@ interface PipelineOptions {
897900
fxFallback?: {
898901
base: string;
899902
rates: Record<string, number>;
903+
dates?: Record<string, string>; // When each rate was last updated
900904
};
901905

902906
// Exemptions - skip normalization for specific indicators
@@ -1064,6 +1068,45 @@ const value = normalizeValue(100, "EUR Million", {
10641068
});
10651069
```
10661070
1071+
### FX Rate Dates & Transparency
1072+
1073+
Include when each exchange rate was last updated for full transparency:
1074+
1075+
```ts
1076+
// From your SNP database - include dates for each rate
1077+
const fxFallback = {
1078+
base: "USD",
1079+
rates: {
1080+
XOF: 558.16,
1081+
EUR: 0.92,
1082+
GBP: 0.79,
1083+
},
1084+
dates: {
1085+
XOF: "2024-01-15T10:30:00Z", // When XOF rate was last updated
1086+
EUR: "2024-01-15T09:45:00Z", // When EUR rate was last updated
1087+
GBP: "2024-01-15T11:15:00Z", // When GBP rate was last updated
1088+
},
1089+
};
1090+
1091+
const result = await processEconomicData(data, {
1092+
targetCurrency: "USD",
1093+
fxFallback,
1094+
explain: true,
1095+
});
1096+
1097+
// The explain metadata will include the date for each FX conversion:
1098+
// {
1099+
// "fx": {
1100+
// "currency": "XOF",
1101+
// "base": "USD",
1102+
// "rate": 558.16,
1103+
// "asOf": "2024-01-15T10:30:00Z", // ← Date included!
1104+
// "source": "fallback",
1105+
// "sourceId": "SNP"
1106+
// }
1107+
// }
1108+
```
1109+
10671110
### Data Quality Assessment
10681111
10691112
Assess and improve data quality:

packages/econify/deno.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tellimer/econify",
3-
"version": "0.2.6",
3+
"version": "0.2.7",
44
"tasks": {
55
"dev": "deno run --watch src/helpers/sample_usage.ts",
66
"test": "deno test",

packages/econify/src/normalization/explain.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export function buildExplainMetadata(
4444
currency: effectiveCurrency,
4545
base: "USD",
4646
rate: rate,
47+
// Include the date if available from FX table
48+
asOf: options.fx.dates?.[effectiveCurrency],
4749
source: "fallback", // We'll enhance this when we have live FX info
4850
sourceId: "SNP", // Default for fallback
4951
};

packages/econify/src/normalization/explain_test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,3 +551,45 @@ Deno.test("buildExplainMetadata - component fields with no original time scale",
551551
assertEquals(explain.timeScale.original, undefined);
552552
assertEquals(explain.timeScale.normalized, "month");
553553
});
554+
555+
Deno.test("buildExplainMetadata - FX with date information", () => {
556+
const fx: FXTable = {
557+
base: "USD",
558+
rates: { XOF: 558.16, EUR: 0.85 },
559+
dates: {
560+
XOF: "2024-01-15T10:30:00Z",
561+
EUR: "2024-01-15T10:30:00Z",
562+
},
563+
};
564+
565+
const explain = buildExplainMetadata(1000, "XOF billions", 1791.4, {
566+
toCurrency: "USD",
567+
toMagnitude: "millions",
568+
fx,
569+
});
570+
571+
// Should include FX date information
572+
assertEquals(explain.fx?.currency, "XOF");
573+
assertEquals(explain.fx?.rate, 558.16);
574+
assertEquals(explain.fx?.asOf, "2024-01-15T10:30:00Z");
575+
assertEquals(explain.fx?.source, "fallback");
576+
assertEquals(explain.fx?.sourceId, "SNP");
577+
});
578+
579+
Deno.test("buildExplainMetadata - FX without date information", () => {
580+
const fx: FXTable = {
581+
base: "USD",
582+
rates: { EUR: 0.85 },
583+
// No dates field
584+
};
585+
586+
const explain = buildExplainMetadata(100, "EUR millions", 85, {
587+
toCurrency: "USD",
588+
fx,
589+
});
590+
591+
// Should not include asOf field when no date available
592+
assertEquals(explain.fx?.currency, "EUR");
593+
assertEquals(explain.fx?.rate, 0.85);
594+
assertEquals(explain.fx?.asOf, undefined);
595+
});

packages/econify/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export interface Classification {
1818
export interface FXTable {
1919
base: string;
2020
rates: Record<string, number>;
21+
/** Optional dates for each rate - when each rate was last updated */
22+
dates?: Record<string, string>;
2123
}
2224

2325
export type Scale =

0 commit comments

Comments
 (0)