Skip to content

Commit 7366d0d

Browse files
kevinypfanclaude
andcommitted
feat: add stock ownership institutionalTrades, directorHoldings & tdccDistribution REST endpoints
Wraps the v1.0 /stock/ownership/{institutional-trades,director-holdings,tdcc-distribution}/:symbol endpoints (fugle-realtime issues 623 / 642 / 641). Same from/to/sort query contract as etfHoldings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DSkx8KegwNXgz4EDgdEcQa
1 parent e9908f7 commit 7366d0d

5 files changed

Lines changed: 207 additions & 0 deletions

File tree

‎src/rest/stock/client.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import { RestStockCorporateActionsCapitalChangesParams, capitalChanges } from '.
1919
import { RestStockCorporateActionsDividendsParams, dividends } from './corporate-actions/dividends';
2020
import { RestStockCorporateActionsListingApplicantsParams, listingApplicants } from './corporate-actions/listing-applicants';
2121
import { RestStockOwnershipEtfHoldingsParams, etfHoldings } from './ownership/etf-holdings';
22+
import { RestStockOwnershipInstitutionalTradesParams, institutionalTrades } from './ownership/institutional-trades';
23+
import { RestStockOwnershipDirectorHoldingsParams, directorHoldings } from './ownership/director-holdings';
24+
import { RestStockOwnershipTdccDistributionParams, tdccDistribution } from './ownership/tdcc-distribution';
2225

2326
export class RestStockClient extends RestClient {
2427
get intraday() {
@@ -74,6 +77,9 @@ export class RestStockClient extends RestClient {
7477
const request = this.request;
7578
return {
7679
etfHoldings: (params: RestStockOwnershipEtfHoldingsParams) => etfHoldings(request, params),
80+
institutionalTrades: (params: RestStockOwnershipInstitutionalTradesParams) => institutionalTrades(request, params),
81+
directorHoldings: (params: RestStockOwnershipDirectorHoldingsParams) => directorHoldings(request, params),
82+
tdccDistribution: (params: RestStockOwnershipTdccDistributionParams) => tdccDistribution(request, params),
7783
};
7884
}
7985
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { RestClientRequest } from '../../client';
2+
3+
export interface RestStockOwnershipDirectorHoldingsParams {
4+
symbol: string;
5+
from?: string;
6+
to?: string;
7+
sort?: 'asc' | 'desc';
8+
}
9+
10+
export interface RestStockOwnershipDirectorHoldingsDirector {
11+
order: number;
12+
title: string;
13+
name: string;
14+
electedShares: number;
15+
heldShares: number;
16+
pledgedShares: number;
17+
pledgeRatio: number;
18+
relatedHeldShares: number;
19+
relatedPledgedShares: number;
20+
relatedPledgeRatio: number;
21+
}
22+
23+
export interface RestStockOwnershipDirectorHoldingsResponse {
24+
type?: string;
25+
exchange?: string;
26+
market?: string;
27+
symbol: string;
28+
data: Array<{
29+
/** yyyy-MM (monthly) */
30+
date: string;
31+
directors: RestStockOwnershipDirectorHoldingsDirector[];
32+
}>;
33+
}
34+
35+
export const directorHoldings = (request: RestClientRequest, params: RestStockOwnershipDirectorHoldingsParams) => {
36+
const { symbol, ...options } = params;
37+
return request(`ownership/director-holdings/${symbol}`, options) as Promise<RestStockOwnershipDirectorHoldingsResponse>;
38+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { RestClientRequest } from '../../client';
2+
3+
export interface RestStockOwnershipInstitutionalTradesParams {
4+
symbol: string;
5+
from?: string;
6+
to?: string;
7+
sort?: 'asc' | 'desc';
8+
}
9+
10+
export interface RestStockOwnershipInstitutionalTradesInvestor {
11+
buy: number;
12+
sell: number;
13+
net: number;
14+
}
15+
16+
export interface RestStockOwnershipInstitutionalTradesResponse {
17+
type?: string;
18+
exchange?: string;
19+
market?: string;
20+
symbol: string;
21+
data: Array<{
22+
date: string;
23+
foreign: RestStockOwnershipInstitutionalTradesInvestor;
24+
trust: RestStockOwnershipInstitutionalTradesInvestor;
25+
dealer: RestStockOwnershipInstitutionalTradesInvestor;
26+
total: number;
27+
}>;
28+
}
29+
30+
export const institutionalTrades = (request: RestClientRequest, params: RestStockOwnershipInstitutionalTradesParams) => {
31+
const { symbol, ...options } = params;
32+
return request(`ownership/institutional-trades/${symbol}`, options) as Promise<RestStockOwnershipInstitutionalTradesResponse>;
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { RestClientRequest } from '../../client';
2+
3+
export interface RestStockOwnershipTdccDistributionParams {
4+
symbol: string;
5+
from?: string;
6+
to?: string;
7+
sort?: 'asc' | 'desc';
8+
}
9+
10+
export interface RestStockOwnershipTdccDistributionLevel {
11+
range: string;
12+
holders: number;
13+
shares: number;
14+
proportion: number;
15+
}
16+
17+
export interface RestStockOwnershipTdccDistributionResponse {
18+
type?: string;
19+
exchange?: string;
20+
market?: string;
21+
symbol: string;
22+
data: Array<{
23+
date: string;
24+
distributions: RestStockOwnershipTdccDistributionLevel[];
25+
}>;
26+
}
27+
28+
export const tdccDistribution = (request: RestClientRequest, params: RestStockOwnershipTdccDistributionParams) => {
29+
const { symbol, ...options } = params;
30+
return request(`ownership/tdcc-distribution/${symbol}`, options) as Promise<RestStockOwnershipTdccDistributionResponse>;
31+
}

‎test/rest-client.spec.ts‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,9 @@ describe('RestClient', () => {
312312
const stock = client.stock as RestStockClient;
313313
expect(stock.ownership).toBeDefined();
314314
expect(stock.ownership).toHaveProperty('etfHoldings');
315+
expect(stock.ownership).toHaveProperty('institutionalTrades');
316+
expect(stock.ownership).toHaveProperty('directorHoldings');
317+
expect(stock.ownership).toHaveProperty('tdccDistribution');
315318
});
316319

317320
describe('.etfHoldings()', () => {
@@ -345,6 +348,102 @@ describe('RestClient', () => {
345348
);
346349
});
347350
});
351+
352+
describe('.institutionalTrades()', () => {
353+
it('should request with api key', async () => {
354+
const client = new RestClient({ apiKey: 'api-key' });
355+
const stock = client.stock as RestStockClient;
356+
await stock.ownership.institutionalTrades({ symbol: '2330' });
357+
expect(fetch).toBeCalledWith(
358+
'https://api.fugle.tw/marketdata/v1.0/stock/ownership/institutional-trades/2330',
359+
{ headers: { 'X-API-KEY': 'api-key' } },
360+
);
361+
});
362+
363+
it('should request with bearer token', async () => {
364+
const client = new RestClient({ bearerToken: 'bearer-token' });
365+
const stock = client.stock as RestStockClient;
366+
await stock.ownership.institutionalTrades({ symbol: '2330' });
367+
expect(fetch).toBeCalledWith(
368+
'https://api.fugle.tw/marketdata/v1.0/stock/ownership/institutional-trades/2330',
369+
{ headers: { 'Authorization': 'Bearer bearer-token' } },
370+
);
371+
});
372+
373+
it('should request with query params', async () => {
374+
const client = new RestClient({ apiKey: 'api-key' });
375+
const stock = client.stock as RestStockClient;
376+
await stock.ownership.institutionalTrades({ symbol: '2330', from: '2026-07-01', to: '2026-07-31', sort: 'desc' });
377+
expect(fetch).toBeCalledWith(
378+
'https://api.fugle.tw/marketdata/v1.0/stock/ownership/institutional-trades/2330?from=2026-07-01&sort=desc&to=2026-07-31',
379+
{ headers: { 'X-API-KEY': 'api-key' } },
380+
);
381+
});
382+
});
383+
384+
describe('.directorHoldings()', () => {
385+
it('should request with api key', async () => {
386+
const client = new RestClient({ apiKey: 'api-key' });
387+
const stock = client.stock as RestStockClient;
388+
await stock.ownership.directorHoldings({ symbol: '2330' });
389+
expect(fetch).toBeCalledWith(
390+
'https://api.fugle.tw/marketdata/v1.0/stock/ownership/director-holdings/2330',
391+
{ headers: { 'X-API-KEY': 'api-key' } },
392+
);
393+
});
394+
395+
it('should request with bearer token', async () => {
396+
const client = new RestClient({ bearerToken: 'bearer-token' });
397+
const stock = client.stock as RestStockClient;
398+
await stock.ownership.directorHoldings({ symbol: '2330' });
399+
expect(fetch).toBeCalledWith(
400+
'https://api.fugle.tw/marketdata/v1.0/stock/ownership/director-holdings/2330',
401+
{ headers: { 'Authorization': 'Bearer bearer-token' } },
402+
);
403+
});
404+
405+
it('should request with query params', async () => {
406+
const client = new RestClient({ apiKey: 'api-key' });
407+
const stock = client.stock as RestStockClient;
408+
await stock.ownership.directorHoldings({ symbol: '2330', from: '2026-01-01', to: '2026-05-31', sort: 'desc' });
409+
expect(fetch).toBeCalledWith(
410+
'https://api.fugle.tw/marketdata/v1.0/stock/ownership/director-holdings/2330?from=2026-01-01&sort=desc&to=2026-05-31',
411+
{ headers: { 'X-API-KEY': 'api-key' } },
412+
);
413+
});
414+
});
415+
416+
describe('.tdccDistribution()', () => {
417+
it('should request with api key', async () => {
418+
const client = new RestClient({ apiKey: 'api-key' });
419+
const stock = client.stock as RestStockClient;
420+
await stock.ownership.tdccDistribution({ symbol: '2330' });
421+
expect(fetch).toBeCalledWith(
422+
'https://api.fugle.tw/marketdata/v1.0/stock/ownership/tdcc-distribution/2330',
423+
{ headers: { 'X-API-KEY': 'api-key' } },
424+
);
425+
});
426+
427+
it('should request with bearer token', async () => {
428+
const client = new RestClient({ bearerToken: 'bearer-token' });
429+
const stock = client.stock as RestStockClient;
430+
await stock.ownership.tdccDistribution({ symbol: '2330' });
431+
expect(fetch).toBeCalledWith(
432+
'https://api.fugle.tw/marketdata/v1.0/stock/ownership/tdcc-distribution/2330',
433+
{ headers: { 'Authorization': 'Bearer bearer-token' } },
434+
);
435+
});
436+
437+
it('should request with query params', async () => {
438+
const client = new RestClient({ apiKey: 'api-key' });
439+
const stock = client.stock as RestStockClient;
440+
await stock.ownership.tdccDistribution({ symbol: '2330', from: '2026-06-01', to: '2026-07-03', sort: 'desc' });
441+
expect(fetch).toBeCalledWith(
442+
'https://api.fugle.tw/marketdata/v1.0/stock/ownership/tdcc-distribution/2330?from=2026-06-01&sort=desc&to=2026-07-03',
443+
{ headers: { 'X-API-KEY': 'api-key' } },
444+
);
445+
});
446+
});
348447
});
349448

350449
describe('.snapshot', () => {

0 commit comments

Comments
 (0)