@cranberry-money/shared-utils 8.1.0 → 8.3.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.
- package/dist/dashboard.d.ts +72 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +121 -0
- package/dist/holdings.d.ts +79 -0
- package/dist/holdings.d.ts.map +1 -0
- package/dist/holdings.js +139 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { AssetAllocation, AssetHolding } from '@cranberry-money/shared-types';
|
|
2
|
+
/**
|
|
3
|
+
* Display item interface for allocation data
|
|
4
|
+
*/
|
|
5
|
+
export interface AllocationDisplayItem {
|
|
6
|
+
readonly instrumentUuid: string;
|
|
7
|
+
readonly symbol: string;
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly targetPercentage?: number;
|
|
10
|
+
readonly actualPercentage?: number;
|
|
11
|
+
readonly quantity?: number;
|
|
12
|
+
readonly value?: number;
|
|
13
|
+
readonly currentPrice?: number;
|
|
14
|
+
readonly color: string;
|
|
15
|
+
readonly hasTarget: boolean;
|
|
16
|
+
readonly hasActual: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Processes allocation and holding data for dashboard display
|
|
20
|
+
* Merges target allocations with actual holdings data
|
|
21
|
+
*
|
|
22
|
+
* @param allocations - Target asset allocations
|
|
23
|
+
* @param holdings - Actual asset holdings
|
|
24
|
+
* @returns Array of display items sorted by target/actual percentage
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const allocations = [{ instrument: '123', percentage: '50.00', ... }];
|
|
28
|
+
* const holdings = [{ instrument: { uuid: '123' }, quantity: 100, ... }];
|
|
29
|
+
* processAllocationData(allocations, holdings);
|
|
30
|
+
*/
|
|
31
|
+
export declare const processAllocationData: (allocations: readonly AssetAllocation[], holdings: readonly AssetHolding[]) => AllocationDisplayItem[];
|
|
32
|
+
/**
|
|
33
|
+
* Calculates totals from allocation display items
|
|
34
|
+
*
|
|
35
|
+
* @param items - Array of allocation display items
|
|
36
|
+
* @returns Object with total calculations
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const items = [{ targetPercentage: 50, actualPercentage: 45, value: 1000, quantity: 10 }];
|
|
40
|
+
* calculateTotals(items);
|
|
41
|
+
* // returns { totalTarget: 50, totalActual: 45, totalValue: 1000, totalQuantity: 10 }
|
|
42
|
+
*/
|
|
43
|
+
export declare const calculateTotals: (items: readonly AllocationDisplayItem[]) => {
|
|
44
|
+
totalTarget: number;
|
|
45
|
+
totalActual: number;
|
|
46
|
+
totalValue: number;
|
|
47
|
+
totalQuantity: number;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Formats a percentage value for display
|
|
51
|
+
*
|
|
52
|
+
* @param value - The percentage value to format
|
|
53
|
+
* @returns Formatted percentage string
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* formatPercentage(45.678); // returns "45.7%"
|
|
57
|
+
* formatPercentage(undefined); // returns "0.0%"
|
|
58
|
+
*/
|
|
59
|
+
export declare const formatPercentage: (value?: number) => string;
|
|
60
|
+
/**
|
|
61
|
+
* Formats currency value for dashboard display
|
|
62
|
+
*
|
|
63
|
+
* @param value - The numeric value to format
|
|
64
|
+
* @param currency - Currency code (defaults to DEFAULT_CURRENCY)
|
|
65
|
+
* @returns Formatted currency string or "—" for undefined values
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* formatDashboardCurrency(1234.56); // returns "$1,234.56"
|
|
69
|
+
* formatDashboardCurrency(undefined); // returns "—"
|
|
70
|
+
*/
|
|
71
|
+
export declare const formatDashboardCurrency: (value?: number, currency?: string) => string;
|
|
72
|
+
//# sourceMappingURL=dashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAMnF;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GAChC,aAAa,SAAS,eAAe,EAAE,EACvC,UAAU,SAAS,YAAY,EAAE,KAChC,qBAAqB,EAwEvB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,SAAS,qBAAqB,EAAE;;;;;CAOtE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,KAAG,MAGjD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,GAAI,QAAQ,MAAM,EAAE,WAAU,MAAyB,KAAG,MAG7F,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { DEFAULT_CURRENCY } from '@cranberry-money/shared-constants';
|
|
2
|
+
import { getChartColor } from '@cranberry-money/shared-constants';
|
|
3
|
+
import { formatCurrencyWithCode } from './currency';
|
|
4
|
+
import { calculateHoldingAllocations } from './holdings';
|
|
5
|
+
/**
|
|
6
|
+
* Processes allocation and holding data for dashboard display
|
|
7
|
+
* Merges target allocations with actual holdings data
|
|
8
|
+
*
|
|
9
|
+
* @param allocations - Target asset allocations
|
|
10
|
+
* @param holdings - Actual asset holdings
|
|
11
|
+
* @returns Array of display items sorted by target/actual percentage
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const allocations = [{ instrument: '123', percentage: '50.00', ... }];
|
|
15
|
+
* const holdings = [{ instrument: { uuid: '123' }, quantity: 100, ... }];
|
|
16
|
+
* processAllocationData(allocations, holdings);
|
|
17
|
+
*/
|
|
18
|
+
export const processAllocationData = (allocations, holdings) => {
|
|
19
|
+
const actualAllocations = calculateHoldingAllocations(holdings);
|
|
20
|
+
const instrumentMap = new Map();
|
|
21
|
+
// Process target allocations
|
|
22
|
+
allocations.forEach((allocation) => {
|
|
23
|
+
instrumentMap.set(allocation.instrument, {
|
|
24
|
+
symbol: allocation.instrumentSymbol,
|
|
25
|
+
name: allocation.instrumentName,
|
|
26
|
+
targetPercentage: parseFloat(allocation.percentage),
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
// Process actual holdings
|
|
30
|
+
holdings.forEach((holding) => {
|
|
31
|
+
const existing = instrumentMap.get(holding.instrument.uuid);
|
|
32
|
+
const currentPrice = parseFloat(holding.instrument.currentPrice || '0');
|
|
33
|
+
const value = holding.quantity * currentPrice;
|
|
34
|
+
instrumentMap.set(holding.instrument.uuid, {
|
|
35
|
+
symbol: holding.instrumentSymbol,
|
|
36
|
+
name: holding.instrumentName,
|
|
37
|
+
targetPercentage: existing?.targetPercentage,
|
|
38
|
+
actualPercentage: actualAllocations[holding.instrument.uuid] || 0,
|
|
39
|
+
quantity: holding.quantity,
|
|
40
|
+
currentPrice,
|
|
41
|
+
value,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
// Convert to display items
|
|
45
|
+
const items = [];
|
|
46
|
+
let colorIndex = 0;
|
|
47
|
+
instrumentMap.forEach((data, instrumentUuid) => {
|
|
48
|
+
items.push({
|
|
49
|
+
instrumentUuid,
|
|
50
|
+
symbol: data.symbol,
|
|
51
|
+
name: data.name,
|
|
52
|
+
targetPercentage: data.targetPercentage,
|
|
53
|
+
actualPercentage: data.actualPercentage,
|
|
54
|
+
quantity: data.quantity,
|
|
55
|
+
currentPrice: data.currentPrice,
|
|
56
|
+
value: data.value,
|
|
57
|
+
color: getChartColor(colorIndex++),
|
|
58
|
+
hasTarget: data.targetPercentage !== undefined,
|
|
59
|
+
hasActual: data.actualPercentage !== undefined && data.actualPercentage > 0,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
// Sort by target percentage (highest first), then by actual percentage
|
|
63
|
+
return items.sort((a, b) => {
|
|
64
|
+
const aTarget = a.targetPercentage || 0;
|
|
65
|
+
const bTarget = b.targetPercentage || 0;
|
|
66
|
+
if (aTarget !== bTarget)
|
|
67
|
+
return bTarget - aTarget;
|
|
68
|
+
const aActual = a.actualPercentage || 0;
|
|
69
|
+
const bActual = b.actualPercentage || 0;
|
|
70
|
+
return bActual - aActual;
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Calculates totals from allocation display items
|
|
75
|
+
*
|
|
76
|
+
* @param items - Array of allocation display items
|
|
77
|
+
* @returns Object with total calculations
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* const items = [{ targetPercentage: 50, actualPercentage: 45, value: 1000, quantity: 10 }];
|
|
81
|
+
* calculateTotals(items);
|
|
82
|
+
* // returns { totalTarget: 50, totalActual: 45, totalValue: 1000, totalQuantity: 10 }
|
|
83
|
+
*/
|
|
84
|
+
export const calculateTotals = (items) => {
|
|
85
|
+
const totalTarget = items.reduce((sum, item) => sum + (item.targetPercentage || 0), 0);
|
|
86
|
+
const totalActual = items.reduce((sum, item) => sum + (item.actualPercentage || 0), 0);
|
|
87
|
+
const totalValue = items.reduce((sum, item) => sum + (item.value || 0), 0);
|
|
88
|
+
const totalQuantity = items.reduce((sum, item) => sum + (item.quantity || 0), 0);
|
|
89
|
+
return { totalTarget, totalActual, totalValue, totalQuantity };
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Formats a percentage value for display
|
|
93
|
+
*
|
|
94
|
+
* @param value - The percentage value to format
|
|
95
|
+
* @returns Formatted percentage string
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* formatPercentage(45.678); // returns "45.7%"
|
|
99
|
+
* formatPercentage(undefined); // returns "0.0%"
|
|
100
|
+
*/
|
|
101
|
+
export const formatPercentage = (value) => {
|
|
102
|
+
if (value === undefined)
|
|
103
|
+
return '0.0%';
|
|
104
|
+
return `${value.toFixed(1)}%`;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Formats currency value for dashboard display
|
|
108
|
+
*
|
|
109
|
+
* @param value - The numeric value to format
|
|
110
|
+
* @param currency - Currency code (defaults to DEFAULT_CURRENCY)
|
|
111
|
+
* @returns Formatted currency string or "—" for undefined values
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* formatDashboardCurrency(1234.56); // returns "$1,234.56"
|
|
115
|
+
* formatDashboardCurrency(undefined); // returns "—"
|
|
116
|
+
*/
|
|
117
|
+
export const formatDashboardCurrency = (value, currency = DEFAULT_CURRENCY) => {
|
|
118
|
+
if (value === undefined || value === null)
|
|
119
|
+
return '—';
|
|
120
|
+
return formatCurrencyWithCode(value, currency, 'en-AU', 2, 2);
|
|
121
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { AssetHolding } from '@cranberry-money/shared-types';
|
|
2
|
+
import { type SortDirection } from '@cranberry-money/shared-constants';
|
|
3
|
+
/**
|
|
4
|
+
* Calculates the total portfolio value based on holdings and instrument prices
|
|
5
|
+
*
|
|
6
|
+
* @param holdings - Array of asset holdings
|
|
7
|
+
* @param prices - Map of instrument UUIDs to their current prices
|
|
8
|
+
* @returns Total portfolio value
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const holdings = [
|
|
12
|
+
* { instrument: { uuid: '123' }, quantity: 100 },
|
|
13
|
+
* { instrument: { uuid: '456' }, quantity: 50 }
|
|
14
|
+
* ];
|
|
15
|
+
* const prices = { '123': 10.50, '456': 25.00 };
|
|
16
|
+
* calculatePortfolioValue(holdings, prices); // returns 2300
|
|
17
|
+
*/
|
|
18
|
+
export declare const calculatePortfolioValue: (holdings: readonly AssetHolding[], prices: Record<string, number>) => number;
|
|
19
|
+
/**
|
|
20
|
+
* Calculates holding allocation percentages within a portfolio
|
|
21
|
+
*
|
|
22
|
+
* @param holdings - Array of asset holdings
|
|
23
|
+
* @returns Map of instrument UUIDs to their percentage allocations
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const holdings = [
|
|
27
|
+
* { instrument: { uuid: '123' }, quantity: 100 },
|
|
28
|
+
* { instrument: { uuid: '456' }, quantity: 100 }
|
|
29
|
+
* ];
|
|
30
|
+
* calculateHoldingAllocations(holdings); // returns { '123': 50, '456': 50 }
|
|
31
|
+
*/
|
|
32
|
+
export declare const calculateHoldingAllocations: (holdings: readonly AssetHolding[]) => Record<string, number>;
|
|
33
|
+
/**
|
|
34
|
+
* Groups holdings by instrument UUID for aggregated view
|
|
35
|
+
*
|
|
36
|
+
* @param holdings - Array of asset holdings to group
|
|
37
|
+
* @returns Map of instrument UUIDs to their holdings arrays
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* const holdings = [
|
|
41
|
+
* { instrument: { uuid: '123' }, quantity: 100 },
|
|
42
|
+
* { instrument: { uuid: '123' }, quantity: 50 },
|
|
43
|
+
* { instrument: { uuid: '456' }, quantity: 200 }
|
|
44
|
+
* ];
|
|
45
|
+
* groupHoldingsByInstrument(holdings);
|
|
46
|
+
* // returns { '123': [holding1, holding2], '456': [holding3] }
|
|
47
|
+
*/
|
|
48
|
+
export declare const groupHoldingsByInstrument: (holdings: readonly AssetHolding[]) => Record<string, AssetHolding[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Filters holdings with quantities above a threshold
|
|
51
|
+
*
|
|
52
|
+
* @param holdings - Array of asset holdings
|
|
53
|
+
* @param minQuantity - Minimum quantity threshold
|
|
54
|
+
* @returns Filtered array of holdings
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* const holdings = [
|
|
58
|
+
* { quantity: 100 },
|
|
59
|
+
* { quantity: 50 },
|
|
60
|
+
* { quantity: 200 }
|
|
61
|
+
* ];
|
|
62
|
+
* filterHoldingsByMinQuantity(holdings, 75); // returns holdings with quantity >= 75
|
|
63
|
+
*/
|
|
64
|
+
export declare const filterHoldingsByMinQuantity: (holdings: readonly AssetHolding[], minQuantity: number) => AssetHolding[];
|
|
65
|
+
/**
|
|
66
|
+
* Sorts holdings by various criteria
|
|
67
|
+
*
|
|
68
|
+
* @param holdings - Array of asset holdings to sort
|
|
69
|
+
* @param sortBy - Field to sort by
|
|
70
|
+
* @param direction - Sort direction (ascending or descending)
|
|
71
|
+
* @returns Sorted array of holdings
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* const holdings = [...];
|
|
75
|
+
* sortHoldings(holdings, 'quantity', 'desc'); // Sort by quantity descending
|
|
76
|
+
* sortHoldings(holdings, 'symbol', 'asc'); // Sort by symbol ascending
|
|
77
|
+
*/
|
|
78
|
+
export declare const sortHoldings: (holdings: readonly AssetHolding[], sortBy: "quantity" | "symbol" | "name" | "createdAt", direction?: SortDirection) => AssetHolding[];
|
|
79
|
+
//# sourceMappingURL=holdings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"holdings.d.ts","sourceRoot":"","sources":["../src/holdings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAiE,KAAK,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAGtI;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,GAAI,UAAU,SAAS,YAAY,EAAE,EAAE,QAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,MAK3G,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,2BAA2B,GAAI,UAAU,SAAS,YAAY,EAAE,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAWpG,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,yBAAyB,GAAI,UAAU,SAAS,YAAY,EAAE,KAAG,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,CAS1G,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,2BAA2B,GAAI,UAAU,SAAS,YAAY,EAAE,EAAE,aAAa,MAAM,KAAG,YAAY,EAEhH,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,GACvB,UAAU,SAAS,YAAY,EAAE,EACjC,QAAQ,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,EACpD,YAAW,aAAmC,KAC7C,YAAY,EA0Cd,CAAC"}
|
package/dist/holdings.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { DEFAULT_NUMERIC_ZERO, SORT_DIRECTION_ASC, SORT_DIRECTION_DESC } from '@cranberry-money/shared-constants';
|
|
2
|
+
import { sortByStringField } from './collections';
|
|
3
|
+
/**
|
|
4
|
+
* Calculates the total portfolio value based on holdings and instrument prices
|
|
5
|
+
*
|
|
6
|
+
* @param holdings - Array of asset holdings
|
|
7
|
+
* @param prices - Map of instrument UUIDs to their current prices
|
|
8
|
+
* @returns Total portfolio value
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const holdings = [
|
|
12
|
+
* { instrument: { uuid: '123' }, quantity: 100 },
|
|
13
|
+
* { instrument: { uuid: '456' }, quantity: 50 }
|
|
14
|
+
* ];
|
|
15
|
+
* const prices = { '123': 10.50, '456': 25.00 };
|
|
16
|
+
* calculatePortfolioValue(holdings, prices); // returns 2300
|
|
17
|
+
*/
|
|
18
|
+
export const calculatePortfolioValue = (holdings, prices) => {
|
|
19
|
+
return holdings.reduce((total, holding) => {
|
|
20
|
+
const price = prices[holding.instrument.uuid] || DEFAULT_NUMERIC_ZERO;
|
|
21
|
+
return total + holding.quantity * price;
|
|
22
|
+
}, 0);
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Calculates holding allocation percentages within a portfolio
|
|
26
|
+
*
|
|
27
|
+
* @param holdings - Array of asset holdings
|
|
28
|
+
* @returns Map of instrument UUIDs to their percentage allocations
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const holdings = [
|
|
32
|
+
* { instrument: { uuid: '123' }, quantity: 100 },
|
|
33
|
+
* { instrument: { uuid: '456' }, quantity: 100 }
|
|
34
|
+
* ];
|
|
35
|
+
* calculateHoldingAllocations(holdings); // returns { '123': 50, '456': 50 }
|
|
36
|
+
*/
|
|
37
|
+
export const calculateHoldingAllocations = (holdings) => {
|
|
38
|
+
const totalQuantity = holdings.reduce((sum, holding) => sum + holding.quantity, 0);
|
|
39
|
+
if (totalQuantity === 0)
|
|
40
|
+
return {};
|
|
41
|
+
const allocations = {};
|
|
42
|
+
holdings.forEach((holding) => {
|
|
43
|
+
allocations[holding.instrument.uuid] = (holding.quantity / totalQuantity) * 100;
|
|
44
|
+
});
|
|
45
|
+
return allocations;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Groups holdings by instrument UUID for aggregated view
|
|
49
|
+
*
|
|
50
|
+
* @param holdings - Array of asset holdings to group
|
|
51
|
+
* @returns Map of instrument UUIDs to their holdings arrays
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* const holdings = [
|
|
55
|
+
* { instrument: { uuid: '123' }, quantity: 100 },
|
|
56
|
+
* { instrument: { uuid: '123' }, quantity: 50 },
|
|
57
|
+
* { instrument: { uuid: '456' }, quantity: 200 }
|
|
58
|
+
* ];
|
|
59
|
+
* groupHoldingsByInstrument(holdings);
|
|
60
|
+
* // returns { '123': [holding1, holding2], '456': [holding3] }
|
|
61
|
+
*/
|
|
62
|
+
export const groupHoldingsByInstrument = (holdings) => {
|
|
63
|
+
return holdings.reduce((groups, holding) => {
|
|
64
|
+
const instrumentUuid = holding.instrument.uuid;
|
|
65
|
+
if (!groups[instrumentUuid]) {
|
|
66
|
+
groups[instrumentUuid] = [];
|
|
67
|
+
}
|
|
68
|
+
groups[instrumentUuid].push(holding);
|
|
69
|
+
return groups;
|
|
70
|
+
}, {});
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Filters holdings with quantities above a threshold
|
|
74
|
+
*
|
|
75
|
+
* @param holdings - Array of asset holdings
|
|
76
|
+
* @param minQuantity - Minimum quantity threshold
|
|
77
|
+
* @returns Filtered array of holdings
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* const holdings = [
|
|
81
|
+
* { quantity: 100 },
|
|
82
|
+
* { quantity: 50 },
|
|
83
|
+
* { quantity: 200 }
|
|
84
|
+
* ];
|
|
85
|
+
* filterHoldingsByMinQuantity(holdings, 75); // returns holdings with quantity >= 75
|
|
86
|
+
*/
|
|
87
|
+
export const filterHoldingsByMinQuantity = (holdings, minQuantity) => {
|
|
88
|
+
return holdings.filter((holding) => holding.quantity >= minQuantity);
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Sorts holdings by various criteria
|
|
92
|
+
*
|
|
93
|
+
* @param holdings - Array of asset holdings to sort
|
|
94
|
+
* @param sortBy - Field to sort by
|
|
95
|
+
* @param direction - Sort direction (ascending or descending)
|
|
96
|
+
* @returns Sorted array of holdings
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* const holdings = [...];
|
|
100
|
+
* sortHoldings(holdings, 'quantity', 'desc'); // Sort by quantity descending
|
|
101
|
+
* sortHoldings(holdings, 'symbol', 'asc'); // Sort by symbol ascending
|
|
102
|
+
*/
|
|
103
|
+
export const sortHoldings = (holdings, sortBy, direction = SORT_DIRECTION_DESC) => {
|
|
104
|
+
// Use shared utility for string field sorting
|
|
105
|
+
if (sortBy === 'symbol') {
|
|
106
|
+
const sorted = sortByStringField([...holdings], 'instrumentSymbol');
|
|
107
|
+
return direction === SORT_DIRECTION_ASC ? sorted : sorted.reverse();
|
|
108
|
+
}
|
|
109
|
+
if (sortBy === 'name') {
|
|
110
|
+
const sorted = sortByStringField([...holdings], 'instrumentName');
|
|
111
|
+
return direction === SORT_DIRECTION_ASC ? sorted : sorted.reverse();
|
|
112
|
+
}
|
|
113
|
+
// Custom logic for non-string fields
|
|
114
|
+
return [...holdings].sort((a, b) => {
|
|
115
|
+
let valueA;
|
|
116
|
+
let valueB;
|
|
117
|
+
switch (sortBy) {
|
|
118
|
+
case 'quantity':
|
|
119
|
+
valueA = a.quantity;
|
|
120
|
+
valueB = b.quantity;
|
|
121
|
+
break;
|
|
122
|
+
case 'createdAt':
|
|
123
|
+
valueA = a.createdAt;
|
|
124
|
+
valueB = b.createdAt;
|
|
125
|
+
break;
|
|
126
|
+
default:
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
if (typeof valueA === 'string' && typeof valueB === 'string') {
|
|
130
|
+
const comparison = valueA.localeCompare(valueB);
|
|
131
|
+
return direction === SORT_DIRECTION_ASC ? comparison : -comparison;
|
|
132
|
+
}
|
|
133
|
+
if (typeof valueA === 'number' && typeof valueB === 'number') {
|
|
134
|
+
const comparison = valueA - valueB;
|
|
135
|
+
return direction === SORT_DIRECTION_ASC ? comparison : -comparison;
|
|
136
|
+
}
|
|
137
|
+
return 0;
|
|
138
|
+
});
|
|
139
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -20,4 +20,6 @@ export { downloadBlob, downloadTextFile, downloadJsonFile, generateTimestampedFi
|
|
|
20
20
|
export { capitalizeFirstLetter, formatStatus, formatRiskLevel, capitalizeFirst, formatLabel, camelToTitle, } from './formatting';
|
|
21
21
|
export { formatQuantityWithSuffix, formatLargeNumber, formatAsPercentage, formatWithSeparators, roundToDecimals, clampNumber, } from './numbers';
|
|
22
22
|
export { validateAllocations } from './allocations';
|
|
23
|
+
export { calculatePortfolioValue, calculateHoldingAllocations, groupHoldingsByInstrument, filterHoldingsByMinQuantity, sortHoldings, } from './holdings';
|
|
24
|
+
export { processAllocationData, calculateTotals, formatPercentage, formatDashboardCurrency, type AllocationDisplayItem, } from './dashboard';
|
|
23
25
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAGjF,OAAO,EACL,8BAA8B,EAC9B,qCAAqC,EACrC,cAAc,EACd,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,YAAY,GACb,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGnE,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGhH,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,4BAA4B,EAC5B,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EACzB,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,QAAQ,EACR,wBAAwB,EACxB,kBAAkB,EAClB,kBAAkB,EAClB,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,mBAAmB,EACnB,UAAU,EAEV,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,qBAAqB,EAErB,0BAA0B,EAC1B,4BAA4B,EAC5B,qBAAqB,EACrB,uBAAuB,EACvB,2BAA2B,EAC3B,6BAA6B,EAC7B,2BAA2B,EAC3B,6BAA6B,GAC9B,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,QAAQ,CAAC;AAGhB,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,YAAY,EACZ,aAAa,GACd,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,2BAA2B,EAC3B,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,qBAAqB,EACrB,YAAY,EACZ,eAAe,EACf,eAAe,EACf,WAAW,EACX,YAAY,GACb,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,WAAW,GACZ,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -40,3 +40,7 @@ export { capitalizeFirstLetter, formatStatus, formatRiskLevel, capitalizeFirst,
|
|
|
40
40
|
export { formatQuantityWithSuffix, formatLargeNumber, formatAsPercentage, formatWithSeparators, roundToDecimals, clampNumber, } from './numbers';
|
|
41
41
|
// Allocation utilities
|
|
42
42
|
export { validateAllocations } from './allocations';
|
|
43
|
+
// Holdings utilities
|
|
44
|
+
export { calculatePortfolioValue, calculateHoldingAllocations, groupHoldingsByInstrument, filterHoldingsByMinQuantity, sortHoldings, } from './holdings';
|
|
45
|
+
// Dashboard utilities
|
|
46
|
+
export { processAllocationData, calculateTotals, formatPercentage, formatDashboardCurrency, } from './dashboard';
|