Browse Source

Page FXMacroData historical data by offset

pull/7865/head
Robert Tidball 11 hours ago
parent
commit
341103b6cb
  1. 34
      apps/api/src/services/data-provider/fxmacrodata/fxmacrodata.service.spec.ts
  2. 17
      apps/api/src/services/data-provider/fxmacrodata/fxmacrodata.service.ts
  3. 4
      apps/api/src/services/data-provider/fxmacrodata/interfaces/interfaces.ts

34
apps/api/src/services/data-provider/fxmacrodata/fxmacrodata.service.spec.ts

@ -207,6 +207,40 @@ describe('FXMacroDataService', () => {
});
});
it('follows the pagination offsets until the last page', async () => {
responses['offset=0'] = {
data: [
{ date: '2026-09-10', val: 1.16 },
{ date: '2026-09-09', val: 1.15 }
],
pagination: { has_more: true, next_offset: 2 }
};
responses['offset=2'] = {
data: [{ date: '2026-09-08', val: 1.14 }],
pagination: { has_more: false, next_offset: null }
};
const historical = await fxMacroDataService.getHistorical({
from: new Date('2026-09-08'),
symbol: 'EURUSD',
to: new Date('2026-09-10')
});
expect(historical).toEqual({
'2026-09-08': { marketPrice: 1.14 },
'2026-09-09': { marketPrice: 1.15 },
'2026-09-10': { marketPrice: 1.16 }
});
const urls = (fetchService.fetch as jest.Mock).mock.calls.map(([url]) => {
return url as string;
});
expect(urls).toHaveLength(2);
expect(urls[0]).toContain('limit=100');
expect(urls[1]).toContain('offset=2');
});
it('skips a date whose rate is null', async () => {
responses['forex/eur/usd'] = {
data: [

17
apps/api/src/services/data-provider/fxmacrodata/fxmacrodata.service.ts

@ -26,6 +26,9 @@ import {
FXMacroDataSourcesResponse
} from './interfaces/interfaces';
const PAGE_SIZE = 100;
const MAX_PAGES = 1000;
@Injectable()
export class FXMacroDataService implements DataProviderInterface {
private readonly baseUrl = 'https://api.fxmacrodata.com/v1';
@ -100,14 +103,16 @@ export class FXMacroDataService implements DataProviderInterface {
// The endpoint caps a page at 100 rows and orders most-recent-first, so
// longer ranges are walked page by page rather than silently truncated.
for (let page = 1; ; page++) {
const { data } = await this.get<FXMacroDataForexResponse>({
let offset = 0;
for (let page = 0; page < MAX_PAGES; page++) {
const { data, pagination } = await this.get<FXMacroDataForexResponse>({
requestTimeout,
path: `forex/${base.toLowerCase()}/${quote.toLowerCase()}`,
searchParams: {
end_date: format(to, DATE_FORMAT),
limit: '100',
page: page.toString(),
limit: PAGE_SIZE.toString(),
offset: offset.toString(),
start_date: format(from, DATE_FORMAT)
}
});
@ -124,9 +129,11 @@ export class FXMacroDataService implements DataProviderInterface {
}
}
if (data.length < 100) {
if (!pagination?.has_more) {
break;
}
offset = pagination.next_offset ?? offset + data.length;
}
return response;

4
apps/api/src/services/data-provider/fxmacrodata/interfaces/interfaces.ts

@ -5,6 +5,10 @@ export interface FXMacroDataForexResponse {
// The rate is documented as anyOf[number, null].
val: number | null;
}[];
pagination?: {
has_more: boolean;
next_offset: number | null;
};
quote: string;
}

Loading…
Cancel
Save