bharosa 0.3.3 → 0.5.0

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.
@@ -0,0 +1,103 @@
1
+ import type { RequestOptions } from "./client-types.cjs";
2
+ export type AnalyticsPeriod = "1y" | "3y" | "5y" | "since_inception";
3
+ export type AnalyticsMetric = {
4
+ status: "available";
5
+ value: string;
6
+ reason: null;
7
+ } | {
8
+ status: "unavailable";
9
+ value: null;
10
+ reason: string;
11
+ };
12
+ export interface PerformanceSelection {
13
+ asOf: string;
14
+ periods?: AnalyticsPeriod[];
15
+ }
16
+ export interface AnalyticsEnvelope {
17
+ methodology_version: "ts-analytics-v1";
18
+ requested_as_of: string;
19
+ currency: "INR";
20
+ source_cutoff: string;
21
+ source_version: string;
22
+ warnings: string[];
23
+ }
24
+ export interface PerformancePeriod {
25
+ period: AnalyticsPeriod;
26
+ requested_start: string | null;
27
+ requested_end: string;
28
+ status: "available" | "unavailable";
29
+ reason: string | null;
30
+ total_return_pct: AnalyticsMetric;
31
+ cagr_pct: AnalyticsMetric;
32
+ }
33
+ export interface AnalyticsFreshnessPolicy {
34
+ nav_days: number;
35
+ aum_days: number;
36
+ ter_days: number;
37
+ category_min_funds: number;
38
+ category_min_coverage_pct: number;
39
+ }
40
+ export interface SchemePerformance extends AnalyticsEnvelope {
41
+ calculation_precision: "decimal_inputs_float64_returns";
42
+ freshness_policy: AnalyticsFreshnessPolicy;
43
+ available_history_start: string | null;
44
+ inception_basis: "verified_first_scheme_nav" | null;
45
+ scheme_nav_inception_date: string | null;
46
+ object: "scheme_performance";
47
+ scheme_id: string;
48
+ fund_id: string;
49
+ plan: string;
50
+ option: string;
51
+ basis: string;
52
+ periods: (PerformancePeriod & {
53
+ start_observation_date: string | null;
54
+ end_observation_date: string | null;
55
+ start_nav: string | null;
56
+ end_nav: string | null;
57
+ start_lag_days: number | null;
58
+ end_lag_days: number | null;
59
+ elapsed_days: number | null;
60
+ annualization_basis: "observed_nav_dates_365_25";
61
+ })[];
62
+ }
63
+ export interface CategorySelection {
64
+ asOf: string;
65
+ periods?: Exclude<AnalyticsPeriod, "since_inception">[];
66
+ metrics?: "return"[];
67
+ plan?: "direct" | "regular";
68
+ option?: "growth";
69
+ }
70
+ export interface CategoryStatistics extends AnalyticsEnvelope {
71
+ freshness_policy: AnalyticsFreshnessPolicy;
72
+ object: "category_statistics";
73
+ category: string;
74
+ cohort: {
75
+ policy: "current_classification_as_of_prices";
76
+ plan: "direct" | "regular";
77
+ option: "growth";
78
+ fund_count: number;
79
+ selected_scheme_count: number;
80
+ classification_date: string | null;
81
+ };
82
+ metrics: Record<string, AnalyticsMetric & {
83
+ method: "equal_weighted_arithmetic_mean";
84
+ return_type: "total_return" | "cagr" | null;
85
+ observation_date_min: string | null;
86
+ observation_date_max: string | null;
87
+ observed_elapsed_days_min: number | null;
88
+ observed_elapsed_days_max: number | null;
89
+ start_lag_days_max: number | null;
90
+ end_lag_days_max: number | null;
91
+ unit: string;
92
+ eligible_count: number;
93
+ valid_count: number;
94
+ missing_count: number;
95
+ stale_count: number;
96
+ invalid_count: number;
97
+ coverage_pct: string;
98
+ exclusions: Record<string, number>;
99
+ }>;
100
+ }
101
+ export interface AnalyticsResource<P, R> {
102
+ retrieve(id: string, params: P, options?: RequestOptions): Promise<R>;
103
+ }
@@ -0,0 +1,103 @@
1
+ import type { RequestOptions } from "./client-types.js";
2
+ export type AnalyticsPeriod = "1y" | "3y" | "5y" | "since_inception";
3
+ export type AnalyticsMetric = {
4
+ status: "available";
5
+ value: string;
6
+ reason: null;
7
+ } | {
8
+ status: "unavailable";
9
+ value: null;
10
+ reason: string;
11
+ };
12
+ export interface PerformanceSelection {
13
+ asOf: string;
14
+ periods?: AnalyticsPeriod[];
15
+ }
16
+ export interface AnalyticsEnvelope {
17
+ methodology_version: "ts-analytics-v1";
18
+ requested_as_of: string;
19
+ currency: "INR";
20
+ source_cutoff: string;
21
+ source_version: string;
22
+ warnings: string[];
23
+ }
24
+ export interface PerformancePeriod {
25
+ period: AnalyticsPeriod;
26
+ requested_start: string | null;
27
+ requested_end: string;
28
+ status: "available" | "unavailable";
29
+ reason: string | null;
30
+ total_return_pct: AnalyticsMetric;
31
+ cagr_pct: AnalyticsMetric;
32
+ }
33
+ export interface AnalyticsFreshnessPolicy {
34
+ nav_days: number;
35
+ aum_days: number;
36
+ ter_days: number;
37
+ category_min_funds: number;
38
+ category_min_coverage_pct: number;
39
+ }
40
+ export interface SchemePerformance extends AnalyticsEnvelope {
41
+ calculation_precision: "decimal_inputs_float64_returns";
42
+ freshness_policy: AnalyticsFreshnessPolicy;
43
+ available_history_start: string | null;
44
+ inception_basis: "verified_first_scheme_nav" | null;
45
+ scheme_nav_inception_date: string | null;
46
+ object: "scheme_performance";
47
+ scheme_id: string;
48
+ fund_id: string;
49
+ plan: string;
50
+ option: string;
51
+ basis: string;
52
+ periods: (PerformancePeriod & {
53
+ start_observation_date: string | null;
54
+ end_observation_date: string | null;
55
+ start_nav: string | null;
56
+ end_nav: string | null;
57
+ start_lag_days: number | null;
58
+ end_lag_days: number | null;
59
+ elapsed_days: number | null;
60
+ annualization_basis: "observed_nav_dates_365_25";
61
+ })[];
62
+ }
63
+ export interface CategorySelection {
64
+ asOf: string;
65
+ periods?: Exclude<AnalyticsPeriod, "since_inception">[];
66
+ metrics?: "return"[];
67
+ plan?: "direct" | "regular";
68
+ option?: "growth";
69
+ }
70
+ export interface CategoryStatistics extends AnalyticsEnvelope {
71
+ freshness_policy: AnalyticsFreshnessPolicy;
72
+ object: "category_statistics";
73
+ category: string;
74
+ cohort: {
75
+ policy: "current_classification_as_of_prices";
76
+ plan: "direct" | "regular";
77
+ option: "growth";
78
+ fund_count: number;
79
+ selected_scheme_count: number;
80
+ classification_date: string | null;
81
+ };
82
+ metrics: Record<string, AnalyticsMetric & {
83
+ method: "equal_weighted_arithmetic_mean";
84
+ return_type: "total_return" | "cagr" | null;
85
+ observation_date_min: string | null;
86
+ observation_date_max: string | null;
87
+ observed_elapsed_days_min: number | null;
88
+ observed_elapsed_days_max: number | null;
89
+ start_lag_days_max: number | null;
90
+ end_lag_days_max: number | null;
91
+ unit: string;
92
+ eligible_count: number;
93
+ valid_count: number;
94
+ missing_count: number;
95
+ stale_count: number;
96
+ invalid_count: number;
97
+ coverage_pct: string;
98
+ exclusions: Record<string, number>;
99
+ }>;
100
+ }
101
+ export interface AnalyticsResource<P, R> {
102
+ retrieve(id: string, params: P, options?: RequestOptions): Promise<R>;
103
+ }
@@ -1,3 +1,4 @@
1
+ import type * as A from "./analytics-types.cjs";
1
2
  import type { MonthlyMarketClient } from "./monthly-types.cjs";
2
3
  import type { MarketDataClient, CurrentHoldingsInput, CurrentHoldingsResult } from "./market-types.cjs";
3
4
  export type * from "./market-types.cjs";
@@ -23,7 +24,7 @@ interface BharosaBaseConfig {
23
24
  readonly allowLocalhostHttp?: boolean;
24
25
  readonly maxResponseBytes?: number;
25
26
  }
26
- export type BharosaConfig = BharosaBaseConfig & ({
27
+ type LegacyBharosaConfig = BharosaBaseConfig & ({
27
28
  readonly auth: Auth;
28
29
  readonly fetch?: typeof globalThis.fetch;
29
30
  readonly transport?: never;
@@ -32,6 +33,17 @@ export type BharosaConfig = BharosaBaseConfig & ({
32
33
  readonly auth?: never;
33
34
  readonly fetch?: never;
34
35
  });
36
+ /** Simple server-side API-key configuration. Account access remains server-authorized. */
37
+ export type BharosaConfig = LegacyBharosaConfig | {
38
+ readonly apiKey: string;
39
+ readonly account?: string;
40
+ readonly baseUrl?: string;
41
+ readonly allowLocalhostHttp?: boolean;
42
+ readonly maxResponseBytes?: number;
43
+ readonly fetch?: typeof globalThis.fetch;
44
+ readonly auth?: never;
45
+ readonly transport?: never;
46
+ };
35
47
  export interface RequestOptions {
36
48
  idempotencyKey?: string;
37
49
  signal?: AbortSignal;
@@ -175,7 +187,11 @@ export type FundObjectReferences = Omit<Fields<FundReferences>, "data"> & {
175
187
  };
176
188
  export interface ClientSurface {
177
189
  readonly marketSnapshots: MonthlyMarketClient;
190
+ readonly fundCategories: {
191
+ readonly statistics: A.AnalyticsResource<A.CategorySelection, A.CategoryStatistics>;
192
+ };
178
193
  readonly mutualFundSchemes: {
194
+ readonly performance: A.AnalyticsResource<A.PerformanceSelection, A.SchemePerformance>;
179
195
  readonly navs: {
180
196
  readonly retrieve: MonthlyMarketClient["schemes"]["nav"];
181
197
  };
@@ -1,3 +1,4 @@
1
+ import type * as A from "./analytics-types.js";
1
2
  import type { MonthlyMarketClient } from "./monthly-types.js";
2
3
  import type { MarketDataClient, CurrentHoldingsInput, CurrentHoldingsResult } from "./market-types.js";
3
4
  export type * from "./market-types.js";
@@ -23,7 +24,7 @@ interface BharosaBaseConfig {
23
24
  readonly allowLocalhostHttp?: boolean;
24
25
  readonly maxResponseBytes?: number;
25
26
  }
26
- export type BharosaConfig = BharosaBaseConfig & ({
27
+ type LegacyBharosaConfig = BharosaBaseConfig & ({
27
28
  readonly auth: Auth;
28
29
  readonly fetch?: typeof globalThis.fetch;
29
30
  readonly transport?: never;
@@ -32,6 +33,17 @@ export type BharosaConfig = BharosaBaseConfig & ({
32
33
  readonly auth?: never;
33
34
  readonly fetch?: never;
34
35
  });
36
+ /** Simple server-side API-key configuration. Account access remains server-authorized. */
37
+ export type BharosaConfig = LegacyBharosaConfig | {
38
+ readonly apiKey: string;
39
+ readonly account?: string;
40
+ readonly baseUrl?: string;
41
+ readonly allowLocalhostHttp?: boolean;
42
+ readonly maxResponseBytes?: number;
43
+ readonly fetch?: typeof globalThis.fetch;
44
+ readonly auth?: never;
45
+ readonly transport?: never;
46
+ };
35
47
  export interface RequestOptions {
36
48
  idempotencyKey?: string;
37
49
  signal?: AbortSignal;
@@ -175,7 +187,11 @@ export type FundObjectReferences = Omit<Fields<FundReferences>, "data"> & {
175
187
  };
176
188
  export interface ClientSurface {
177
189
  readonly marketSnapshots: MonthlyMarketClient;
190
+ readonly fundCategories: {
191
+ readonly statistics: A.AnalyticsResource<A.CategorySelection, A.CategoryStatistics>;
192
+ };
178
193
  readonly mutualFundSchemes: {
194
+ readonly performance: A.AnalyticsResource<A.PerformanceSelection, A.SchemePerformance>;
179
195
  readonly navs: {
180
196
  readonly retrieve: MonthlyMarketClient["schemes"]["nav"];
181
197
  };
package/dist/index.cjs CHANGED
@@ -42,12 +42,52 @@ class Bharosa {
42
42
  clients;
43
43
  investors;
44
44
  portfolios;
45
+ fundCategories;
45
46
  mutualFunds;
46
47
  mutualFundSchemes;
48
+ markets;
47
49
  marketSnapshots;
48
50
  marketData;
49
51
  constructor(config) {
52
+ this.markets = baselineMarkets((0, transport_cjs_1.createTransport)(config, true));
50
53
  const call = (0, transport_cjs_1.createTransport)(config);
54
+ const analyticsQuery = (q, extra = []) => {
55
+ (0, transport_cjs_1.keys)(q, ["asOf", "periods", ...extra]);
56
+ checkedDate(q.asOf);
57
+ (0, transport_cjs_1.check)(q.asOf.endsWith("-01"));
58
+ if (q.periods !== undefined)
59
+ (0, transport_cjs_1.check)(Array.isArray(q.periods) &&
60
+ q.periods.length > 0 &&
61
+ q.periods.length <= 4 &&
62
+ new Set(q.periods).size === q.periods.length &&
63
+ q.periods.every((p) => ["1y", "3y", "5y", "since_inception"].includes(p)));
64
+ return {
65
+ as_of: q.asOf,
66
+ ...(q.periods === undefined ? {} : { periods: q.periods.join(",") }),
67
+ };
68
+ };
69
+ this.fundCategories = Object.freeze({
70
+ statistics: Object.freeze({
71
+ retrieve: (id, q, o) => {
72
+ const query = analyticsQuery(q, ["metrics", "plan", "option"]);
73
+ if (q.metrics !== undefined)
74
+ (0, transport_cjs_1.check)(Array.isArray(q.metrics) &&
75
+ q.metrics.length > 0 &&
76
+ q.metrics.length <= 3 &&
77
+ new Set(q.metrics).size === q.metrics.length &&
78
+ q.metrics.every((m) => m === "return"));
79
+ (0, transport_cjs_1.check)(!q.periods?.includes("since_inception"));
80
+ (0, transport_cjs_1.check)(!(q.metrics && !q.metrics.includes("return") && q.periods));
81
+ return call("analytics_category", {
82
+ id,
83
+ ...query,
84
+ ...(q.metrics ? { metrics: q.metrics.join(",") } : {}),
85
+ ...(q.plan ? { plan: q.plan } : {}),
86
+ ...(q.option ? { option: q.option } : {}),
87
+ }, undefined, o);
88
+ },
89
+ }),
90
+ });
51
91
  const list = (p) => {
52
92
  (0, transport_cjs_1.keys)(p, ["limit", "startingAfter", "client"]);
53
93
  return Object.fromEntries(Object.entries(p)
@@ -276,7 +316,10 @@ class Bharosa {
276
316
  };
277
317
  this.marketSnapshots = {
278
318
  schemes: {
279
- nav: (id, q, o) => call("monthly_nav", { id, ...monthlyQuery(q) }, undefined, o),
319
+ nav: (id, q = {}, o) => {
320
+ (0, transport_cjs_1.keys)(q, ["asOf"]);
321
+ return call("monthly_nav", { id, ...(q.asOf === undefined ? {} : { as_of: q.asOf }) }, undefined, o);
322
+ },
280
323
  expenseRatio: (id, q, o) => call("monthly_expense", { id, ...monthlyQuery(q) }, undefined, o),
281
324
  valuation: (id, input = {}, o) => {
282
325
  (0, transport_cjs_1.keys)(input, ["units", "asOf", "positionDate", "observedValue"]);
@@ -299,6 +342,9 @@ class Bharosa {
299
342
  },
300
343
  };
301
344
  this.mutualFundSchemes = Object.freeze({
345
+ performance: Object.freeze({
346
+ retrieve: (id, q, o) => call("analytics_scheme", { id, ...analyticsQuery(q) }, undefined, o),
347
+ }),
302
348
  navs: Object.freeze({ retrieve: this.marketSnapshots.schemes.nav }),
303
349
  expenseRatios: Object.freeze({
304
350
  retrieve: this.marketSnapshots.schemes.expenseRatio,
@@ -367,24 +413,33 @@ class BharosaMarket {
367
413
  ]);
368
414
  const { apiKey, ...rest } = config;
369
415
  const call = (0, transport_cjs_1.createTransport)({ ...rest, account: "me", auth: { type: "account_key", apiKey } }, true);
370
- const query = (q, paging = false) => {
371
- (0, transport_cjs_1.keys)(q, paging ? ["asOf", "limit", "startingAfter"] : ["asOf"]);
372
- return {
373
- as_of: q.asOf,
374
- ...(q.limit === undefined ? {} : { limit: q.limit }),
375
- ...(q.startingAfter === undefined
376
- ? {}
377
- : { starting_after: q.startingAfter }),
378
- };
379
- };
380
- this.schemes = {
381
- nav: (id, q, o) => call("monthly_nav", { id, ...query(q) }, undefined, o),
382
- expenseRatio: (id, q, o) => call("monthly_expense", { id, ...query(q) }, undefined, o),
383
- };
384
- this.funds = {
385
- portfolio: (id, q, o) => call("monthly_portfolio", { id, ...query(q) }, undefined, o),
386
- holdings: (id, q, o) => call("monthly_holdings", { id, ...query(q, true) }, undefined, o),
387
- };
416
+ const resources = baselineMarkets(call);
417
+ this.schemes = resources.schemes;
418
+ this.funds = resources.funds;
388
419
  }
389
420
  }
390
421
  exports.BharosaMarket = BharosaMarket;
422
+ function baselineMarkets(call) {
423
+ const query = (q, paging = false) => {
424
+ (0, transport_cjs_1.keys)(q, paging ? ["asOf", "limit", "startingAfter"] : ["asOf"]);
425
+ return {
426
+ as_of: q.asOf,
427
+ ...(q.limit === undefined ? {} : { limit: q.limit }),
428
+ ...(q.startingAfter === undefined
429
+ ? {}
430
+ : { starting_after: q.startingAfter }),
431
+ };
432
+ };
433
+ const schemes = {
434
+ nav: (id, q = {}, o) => {
435
+ (0, transport_cjs_1.keys)(q, ["asOf"]);
436
+ return call("monthly_nav", { id, ...(q.asOf === undefined ? {} : { as_of: q.asOf }) }, undefined, o);
437
+ },
438
+ expenseRatio: (id, q, o) => call("monthly_expense", { id, ...query(q) }, undefined, o),
439
+ };
440
+ const funds = {
441
+ portfolio: (id, q, o) => call("monthly_portfolio", { id, ...query(q) }, undefined, o),
442
+ holdings: (id, q, o) => call("monthly_holdings", { id, ...query(q, true) }, undefined, o),
443
+ };
444
+ return Object.freeze({ schemes: Object.freeze(schemes), funds: Object.freeze(funds) });
445
+ }
package/dist/index.d.cts CHANGED
@@ -1,3 +1,4 @@
1
+ export type * from "./analytics-types.cjs";
1
2
  import type { MonthlyMarketClient } from "./monthly-types.cjs";
2
3
  export type * from "./monthly-types.cjs";
3
4
  import type * as T from "./client-types.cjs";
@@ -9,8 +10,10 @@ export declare class Bharosa implements T.ClientSurface {
9
10
  readonly clients: T.ClientSurface["clients"];
10
11
  readonly investors: T.ClientSurface["investors"];
11
12
  readonly portfolios: T.ClientSurface["portfolios"];
13
+ readonly fundCategories: T.ClientSurface["fundCategories"];
12
14
  readonly mutualFunds: T.ClientSurface["mutualFunds"];
13
15
  readonly mutualFundSchemes: T.ClientSurface["mutualFundSchemes"];
16
+ readonly markets: BaselineMarketClient;
14
17
  readonly marketSnapshots: MonthlyMarketClient;
15
18
  readonly marketData: D.MarketDataClient;
16
19
  constructor(config: T.BharosaConfig);
@@ -25,7 +28,16 @@ export interface BharosaMarketConfig {
25
28
  }
26
29
  /** Server-only registered-app monthly market access. No account or user token. */
27
30
  export declare class BharosaMarket {
28
- readonly schemes: Pick<MonthlyMarketClient["schemes"], "nav" | "expenseRatio">;
31
+ readonly schemes: BaselineMarketClient["schemes"];
29
32
  readonly funds: MonthlyMarketClient["funds"];
30
33
  constructor(config: BharosaMarketConfig);
31
34
  }
35
+ export interface BaselineMarketClient {
36
+ readonly schemes: {
37
+ nav(id: string, selection?: {
38
+ asOf?: string;
39
+ }, options?: T.RequestOptions): ReturnType<MonthlyMarketClient["schemes"]["nav"]>;
40
+ expenseRatio: MonthlyMarketClient["schemes"]["expenseRatio"];
41
+ };
42
+ readonly funds: MonthlyMarketClient["funds"];
43
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export type * from "./analytics-types.js";
1
2
  import type { MonthlyMarketClient } from "./monthly-types.js";
2
3
  export type * from "./monthly-types.js";
3
4
  import type * as T from "./client-types.js";
@@ -9,8 +10,10 @@ export declare class Bharosa implements T.ClientSurface {
9
10
  readonly clients: T.ClientSurface["clients"];
10
11
  readonly investors: T.ClientSurface["investors"];
11
12
  readonly portfolios: T.ClientSurface["portfolios"];
13
+ readonly fundCategories: T.ClientSurface["fundCategories"];
12
14
  readonly mutualFunds: T.ClientSurface["mutualFunds"];
13
15
  readonly mutualFundSchemes: T.ClientSurface["mutualFundSchemes"];
16
+ readonly markets: BaselineMarketClient;
14
17
  readonly marketSnapshots: MonthlyMarketClient;
15
18
  readonly marketData: D.MarketDataClient;
16
19
  constructor(config: T.BharosaConfig);
@@ -25,7 +28,16 @@ export interface BharosaMarketConfig {
25
28
  }
26
29
  /** Server-only registered-app monthly market access. No account or user token. */
27
30
  export declare class BharosaMarket {
28
- readonly schemes: Pick<MonthlyMarketClient["schemes"], "nav" | "expenseRatio">;
31
+ readonly schemes: BaselineMarketClient["schemes"];
29
32
  readonly funds: MonthlyMarketClient["funds"];
30
33
  constructor(config: BharosaMarketConfig);
31
34
  }
35
+ export interface BaselineMarketClient {
36
+ readonly schemes: {
37
+ nav(id: string, selection?: {
38
+ asOf?: string;
39
+ }, options?: T.RequestOptions): ReturnType<MonthlyMarketClient["schemes"]["nav"]>;
40
+ expenseRatio: MonthlyMarketClient["schemes"]["expenseRatio"];
41
+ };
42
+ readonly funds: MonthlyMarketClient["funds"];
43
+ }
@@ -29,12 +29,13 @@ module.exports = {
29
29
  {
30
30
  "name": "as_of",
31
31
  "in": "query",
32
- "required": true,
32
+ "required": false,
33
33
  "schema": {
34
34
  "type": "string",
35
35
  "format": "date",
36
36
  "pattern": "^[0-9]{4}-[0-9]{2}-01$"
37
- }
37
+ },
38
+ "description": "Omit for latest NAV on or before today in Asia/Kolkata. When supplied, must be the first of a month and not in the future. Actual observation date is returned separately."
38
39
  }
39
40
  ],
40
41
  "response": {
@@ -21,7 +21,7 @@ export interface MonthSelection {
21
21
  }
22
22
  export interface MonthlyMarketClient {
23
23
  schemes: {
24
- nav(id: string, query: MonthSelection, options?: RequestOptions): Promise<MarketSnapshot<D.NavObservation>>;
24
+ nav(id: string, query?: Partial<MonthSelection>, options?: RequestOptions): Promise<MarketSnapshot<D.NavObservation>>;
25
25
  expenseRatio(id: string, query: MonthSelection, options?: RequestOptions): Promise<MarketSnapshot<D.ExpenseObservation>>;
26
26
  valuation(id: string, input?: D.ValuationInput, options?: RequestOptions): Promise<D.LatestNavValuation>;
27
27
  };
@@ -21,7 +21,7 @@ export interface MonthSelection {
21
21
  }
22
22
  export interface MonthlyMarketClient {
23
23
  schemes: {
24
- nav(id: string, query: MonthSelection, options?: RequestOptions): Promise<MarketSnapshot<D.NavObservation>>;
24
+ nav(id: string, query?: Partial<MonthSelection>, options?: RequestOptions): Promise<MarketSnapshot<D.NavObservation>>;
25
25
  expenseRatio(id: string, query: MonthSelection, options?: RequestOptions): Promise<MarketSnapshot<D.ExpenseObservation>>;
26
26
  valuation(id: string, input?: D.ValuationInput, options?: RequestOptions): Promise<D.LatestNavValuation>;
27
27
  };
@@ -1,9 +1,12 @@
1
+ import type * as A from "./analytics-types.cjs";
1
2
  import type * as S from "./monthly-types.cjs";
2
3
  import type * as M from "./models.cjs";
3
4
  import type * as D from "./market-types.cjs";
4
5
  import type { CurrentMixEvidence } from "./client-types.cjs";
5
6
  /** Each entry is the decoded response of the corresponding generated operation. */
6
7
  export interface OperationResults {
8
+ analytics_scheme: A.SchemePerformance;
9
+ analytics_category: A.CategoryStatistics;
7
10
  monthly_nav: S.MarketSnapshot<D.NavObservation>;
8
11
  monthly_expense: S.MarketSnapshot<D.ExpenseObservation>;
9
12
  monthly_portfolio: S.MarketSnapshot<D.FundPortfolio>;
@@ -1,9 +1,12 @@
1
+ import type * as A from "./analytics-types.js";
1
2
  import type * as S from "./monthly-types.js";
2
3
  import type * as M from "./models.js";
3
4
  import type * as D from "./market-types.js";
4
5
  import type { CurrentMixEvidence } from "./client-types.js";
5
6
  /** Each entry is the decoded response of the corresponding generated operation. */
6
7
  export interface OperationResults {
8
+ analytics_scheme: A.SchemePerformance;
9
+ analytics_category: A.CategoryStatistics;
7
10
  monthly_nav: S.MarketSnapshot<D.NavObservation>;
8
11
  monthly_expense: S.MarketSnapshot<D.ExpenseObservation>;
9
12
  monthly_portfolio: S.MarketSnapshot<D.FundPortfolio>;
@@ -121,9 +121,76 @@ function responseMatches(id, op, data, params, body, account) {
121
121
  b("valuation_date") !== undefined &&
122
122
  d("valuation_date") !== b("valuation_date")))
123
123
  return false;
124
+ if (id.startsWith("analytics_")) {
125
+ if (id === "analytics_aum") {
126
+ if (d("requested_date") !== params.as_of ||
127
+ (d("observation_date") !== null &&
128
+ String(d("observation_date")) > String(params.as_of)))
129
+ return false;
130
+ if (d("status") === "available" &&
131
+ field(d("data"), "basis") === "reported_fund_total" &&
132
+ field(field(d("data"), "source"), "date") !== d("observation_date"))
133
+ return false;
134
+ if (d("status") === "available" &&
135
+ (field(d("data"), "fund_id") !== params.id ||
136
+ field(d("data"), "date") !== d("observation_date") ||
137
+ String(d("observation_date")) > String(params.as_of)))
138
+ return false;
139
+ }
140
+ else {
141
+ if (d("requested_as_of") !== params.as_of)
142
+ return false;
143
+ const identity = id === "analytics_scheme"
144
+ ? "scheme_id"
145
+ : id === "analytics_portfolio"
146
+ ? "portfolio_id"
147
+ : "category";
148
+ if (d(identity) !== params.id)
149
+ return false;
150
+ if (id === "analytics_category") {
151
+ if (d("cohort", "plan") !== (params.plan ?? "direct") ||
152
+ d("cohort", "option") !== (params.option ?? "growth"))
153
+ return false;
154
+ const requested = String(params.metrics ?? "return").split(",");
155
+ const expected = requested.flatMap((m) => m === "return"
156
+ ? String(params.periods ?? "1y,3y,5y")
157
+ .split(",")
158
+ .map((p) => "return_" + p)
159
+ : [m]);
160
+ for (const key of expected) {
161
+ const metric = d("metrics", key);
162
+ if (!(0, schema_cjs_1.record)(metric))
163
+ return false;
164
+ const eligible = Number(field(metric, "eligible_count")), valid = Number(field(metric, "valid_count"));
165
+ const excluded = [
166
+ "missing_count",
167
+ "stale_count",
168
+ "invalid_count",
169
+ ].reduce((n, k) => n + Number(field(metric, k)), 0);
170
+ if (eligible !== d("cohort", "fund_count") ||
171
+ valid + excluded !== eligible)
172
+ return false;
173
+ if (field(metric, "status") === "available" &&
174
+ (valid < 3 || valid * 100 < eligible * 80))
175
+ return false;
176
+ }
177
+ }
178
+ if (id !== "analytics_category") {
179
+ const expected = String(params.periods ?? "1y,3y,5y").split(",");
180
+ const got = rows(d("periods"));
181
+ if (got.length !== expected.length ||
182
+ got.some((p, i) => field(p, "period") !== expected[i] ||
183
+ field(p, "requested_end") !== params.as_of ||
184
+ (id === "analytics_scheme" &&
185
+ (later(field(p, "start_observation_date"), field(p, "requested_start")) ||
186
+ later(field(p, "end_observation_date"), params.as_of)))))
187
+ return false;
188
+ }
189
+ }
190
+ }
124
191
  if (id.startsWith("monthly_") && id !== "monthly_valuation") {
125
192
  const observation = d("observation_date"), requested = d("requested_date"), data = d("data");
126
- if (requested !== params.as_of)
193
+ if (!(id === "monthly_nav" && params.as_of === undefined) && requested !== params.as_of)
127
194
  return false;
128
195
  if (d("status") === "available") {
129
196
  if (typeof observation !== "string" ||