mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
3.3 KiB
114 lines
3.3 KiB
import {
|
|
activityDummyData,
|
|
assetProfileDummyData
|
|
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
|
|
import * as commonHelper from '@ghostfolio/common/helper';
|
|
import { parseDate } from '@ghostfolio/common/helper';
|
|
import { Activity } from '@ghostfolio/common/interfaces';
|
|
|
|
import { AssetProfileSplit, DataSource } from '@prisma/client';
|
|
import { Big } from 'big.js';
|
|
|
|
import { adjustActivityBySplits } from './asset-profile-split.helper';
|
|
|
|
describe('adjustActivityBySplits', () => {
|
|
it('adjusts quantity and prices using the cumulative split factor', () => {
|
|
const activity = createActivity('2020-01-01');
|
|
const splits = [
|
|
createSplit('2021-01-01', 2, 1),
|
|
createSplit('2022-01-01', 1, 3)
|
|
];
|
|
|
|
const adjustedActivity = adjustActivityBySplits(activity, splits);
|
|
|
|
expect(adjustedActivity).not.toBe(activity);
|
|
expect(adjustedActivity).toMatchObject({
|
|
quantity: 20 / 3,
|
|
unitPrice: 150,
|
|
unitPriceInAssetProfileCurrency: 150,
|
|
value: 1000,
|
|
valueInBaseCurrency: 1000
|
|
});
|
|
expect(new Big(adjustedActivity.quantity).toFixed(15)).toBe(
|
|
new Big(20).div(3).toFixed(15)
|
|
);
|
|
expect(activity).toMatchObject({
|
|
quantity: 10,
|
|
unitPrice: 100,
|
|
unitPriceInAssetProfileCurrency: 100
|
|
});
|
|
});
|
|
|
|
it('only adjusts activities before the split calendar date', () => {
|
|
const split = createSplit('2024-06-15T00:00:00Z', 2, 1);
|
|
const activityOnSplitDate = createActivity('2024-06-15T18:00:00Z');
|
|
const activityAfterSplit = createActivity('2024-06-16T00:00:00Z');
|
|
|
|
const adjustedActivityOnSplitDate = adjustActivityBySplits(
|
|
activityOnSplitDate,
|
|
[split]
|
|
);
|
|
const adjustedActivityAfterSplit = adjustActivityBySplits(
|
|
activityAfterSplit,
|
|
[split]
|
|
);
|
|
|
|
expect(adjustedActivityOnSplitDate).toEqual(activityOnSplitDate);
|
|
expect(adjustedActivityOnSplitDate).toBe(activityOnSplitDate);
|
|
expect(adjustedActivityAfterSplit).toEqual(activityAfterSplit);
|
|
expect(adjustedActivityAfterSplit).toBe(activityAfterSplit);
|
|
});
|
|
|
|
it('compares stored UTC split dates without normalizing them locally', () => {
|
|
const resetHoursSpy = jest
|
|
.spyOn(commonHelper, 'resetHours')
|
|
.mockReturnValue(new Date('2024-06-14T00:00:00Z'));
|
|
|
|
try {
|
|
const activity = createActivity('2024-06-14T12:00:00Z');
|
|
const split = createSplit('2024-06-15T00:00:00Z', 2, 1);
|
|
|
|
const adjustedActivity = adjustActivityBySplits(activity, [split]);
|
|
|
|
expect(adjustedActivity.quantity).toBe(20);
|
|
} finally {
|
|
resetHoursSpy.mockRestore();
|
|
}
|
|
});
|
|
});
|
|
|
|
function createActivity(date: string): Activity {
|
|
return {
|
|
...activityDummyData,
|
|
assetProfile: {
|
|
...assetProfileDummyData,
|
|
dataSource: DataSource.YAHOO,
|
|
symbol: 'AAPL'
|
|
},
|
|
date: parseDate(date),
|
|
quantity: 10,
|
|
type: 'BUY',
|
|
unitPrice: 100,
|
|
unitPriceInAssetProfileCurrency: 100,
|
|
value: 1000,
|
|
valueInBaseCurrency: 1000
|
|
} as Activity;
|
|
}
|
|
|
|
function createSplit(
|
|
date: string,
|
|
numerator: number,
|
|
denominator: number
|
|
): AssetProfileSplit {
|
|
const splitDate = new Date(date);
|
|
|
|
return {
|
|
denominator,
|
|
numerator,
|
|
createdAt: splitDate,
|
|
date: splitDate,
|
|
id: `${date}-${numerator}-${denominator}`,
|
|
symbolProfileId: 'aapl-profile',
|
|
updatedAt: splitDate
|
|
};
|
|
}
|
|
|