Feature/move wealth item and liability calculations to portfolio calculator (#3272)
* Move (wealth) item calculations to portfolio calculator * Move liability calculations to portfolio calculator * Update changelog
This commit is contained in:
parent
6c57609db8
commit
5d4e2fba8c
@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Moved the dividend calculations into the portfolio calculator
|
- Moved the dividend calculations into the portfolio calculator
|
||||||
- Moved the fee calculations into the portfolio calculator
|
- Moved the fee calculations into the portfolio calculator
|
||||||
- Moved the interest calculations into the portfolio calculator
|
- Moved the interest calculations into the portfolio calculator
|
||||||
|
- Moved the liability calculations into the portfolio calculator
|
||||||
|
- Moved the (wealth) item calculations into the portfolio calculator
|
||||||
|
|
||||||
## 2.72.0 - 2024-04-13
|
## 2.72.0 - 2024-04-13
|
||||||
|
|
||||||
|
@ -140,7 +140,9 @@ export abstract class PortfolioCalculator {
|
|||||||
totalFeesWithCurrencyEffect: new Big(0),
|
totalFeesWithCurrencyEffect: new Big(0),
|
||||||
totalInterestWithCurrencyEffect: new Big(0),
|
totalInterestWithCurrencyEffect: new Big(0),
|
||||||
totalInvestment: new Big(0),
|
totalInvestment: new Big(0),
|
||||||
totalInvestmentWithCurrencyEffect: new Big(0)
|
totalInvestmentWithCurrencyEffect: new Big(0),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big(0),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big(0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +151,9 @@ export abstract class PortfolioCalculator {
|
|||||||
let dates: Date[] = [];
|
let dates: Date[] = [];
|
||||||
let firstIndex = transactionPoints.length;
|
let firstIndex = transactionPoints.length;
|
||||||
let firstTransactionPoint: TransactionPoint = null;
|
let firstTransactionPoint: TransactionPoint = null;
|
||||||
|
let totalInterestWithCurrencyEffect = new Big(0);
|
||||||
|
let totalLiabilitiesWithCurrencyEffect = new Big(0);
|
||||||
|
let totalValuablesWithCurrencyEffect = new Big(0);
|
||||||
|
|
||||||
dates.push(resetHours(start));
|
dates.push(resetHours(start));
|
||||||
|
|
||||||
@ -274,8 +279,11 @@ export abstract class PortfolioCalculator {
|
|||||||
timeWeightedInvestmentWithCurrencyEffect,
|
timeWeightedInvestmentWithCurrencyEffect,
|
||||||
totalDividend,
|
totalDividend,
|
||||||
totalDividendInBaseCurrency,
|
totalDividendInBaseCurrency,
|
||||||
|
totalInterestInBaseCurrency,
|
||||||
totalInvestment,
|
totalInvestment,
|
||||||
totalInvestmentWithCurrencyEffect
|
totalInvestmentWithCurrencyEffect,
|
||||||
|
totalLiabilitiesInBaseCurrency,
|
||||||
|
totalValuablesInBaseCurrency
|
||||||
} = this.getSymbolMetrics({
|
} = this.getSymbolMetrics({
|
||||||
marketSymbolMap,
|
marketSymbolMap,
|
||||||
start,
|
start,
|
||||||
@ -333,6 +341,17 @@ export abstract class PortfolioCalculator {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
totalInterestWithCurrencyEffect = totalInterestWithCurrencyEffect.plus(
|
||||||
|
totalInterestInBaseCurrency
|
||||||
|
);
|
||||||
|
|
||||||
|
totalLiabilitiesWithCurrencyEffect =
|
||||||
|
totalLiabilitiesWithCurrencyEffect.plus(totalLiabilitiesInBaseCurrency);
|
||||||
|
|
||||||
|
totalValuablesWithCurrencyEffect = totalValuablesWithCurrencyEffect.plus(
|
||||||
|
totalValuablesInBaseCurrency
|
||||||
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(hasErrors ||
|
(hasErrors ||
|
||||||
currentRateErrors.find(({ dataSource, symbol }) => {
|
currentRateErrors.find(({ dataSource, symbol }) => {
|
||||||
@ -350,8 +369,10 @@ export abstract class PortfolioCalculator {
|
|||||||
...overall,
|
...overall,
|
||||||
errors,
|
errors,
|
||||||
positions,
|
positions,
|
||||||
hasErrors: hasAnySymbolMetricsErrors || overall.hasErrors,
|
totalInterestWithCurrencyEffect,
|
||||||
totalInterestWithCurrencyEffect: lastTransactionPoint.interest
|
totalLiabilitiesWithCurrencyEffect,
|
||||||
|
totalValuablesWithCurrencyEffect,
|
||||||
|
hasErrors: hasAnySymbolMetricsErrors || overall.hasErrors
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -715,6 +736,12 @@ export abstract class PortfolioCalculator {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getLiabilitiesInBaseCurrency() {
|
||||||
|
await this.snapshotPromise;
|
||||||
|
|
||||||
|
return this.snapshot.totalLiabilitiesWithCurrencyEffect;
|
||||||
|
}
|
||||||
|
|
||||||
public async getSnapshot() {
|
public async getSnapshot() {
|
||||||
await this.snapshotPromise;
|
await this.snapshotPromise;
|
||||||
|
|
||||||
@ -751,6 +778,12 @@ export abstract class PortfolioCalculator {
|
|||||||
return this.transactionPoints;
|
return this.transactionPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getValuablesInBaseCurrency() {
|
||||||
|
await this.snapshotPromise;
|
||||||
|
|
||||||
|
return this.snapshot.totalValuablesWithCurrencyEffect;
|
||||||
|
}
|
||||||
|
|
||||||
private computeTransactionPoints() {
|
private computeTransactionPoints() {
|
||||||
this.transactionPoints = [];
|
this.transactionPoints = [];
|
||||||
const symbols: { [symbol: string]: TransactionPointSymbol } = {};
|
const symbols: { [symbol: string]: TransactionPointSymbol } = {};
|
||||||
@ -767,13 +800,6 @@ export abstract class PortfolioCalculator {
|
|||||||
type,
|
type,
|
||||||
unitPrice
|
unitPrice
|
||||||
} of this.orders) {
|
} of this.orders) {
|
||||||
if (
|
|
||||||
// TODO
|
|
||||||
['ITEM', 'LIABILITY'].includes(type)
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let currentTransactionPointItem: TransactionPointSymbol;
|
let currentTransactionPointItem: TransactionPointSymbol;
|
||||||
const oldAccumulatedSymbol = symbols[SymbolProfile.symbol];
|
const oldAccumulatedSymbol = symbols[SymbolProfile.symbol];
|
||||||
|
|
||||||
@ -858,11 +884,25 @@ export abstract class PortfolioCalculator {
|
|||||||
interest = quantity.mul(unitPrice);
|
interest = quantity.mul(unitPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let liabilities = new Big(0);
|
||||||
|
|
||||||
|
if (type === 'LIABILITY') {
|
||||||
|
liabilities = quantity.mul(unitPrice);
|
||||||
|
}
|
||||||
|
|
||||||
|
let valuables = new Big(0);
|
||||||
|
|
||||||
|
if (type === 'ITEM') {
|
||||||
|
valuables = quantity.mul(unitPrice);
|
||||||
|
}
|
||||||
|
|
||||||
if (lastDate !== date || lastTransactionPoint === null) {
|
if (lastDate !== date || lastTransactionPoint === null) {
|
||||||
lastTransactionPoint = {
|
lastTransactionPoint = {
|
||||||
date,
|
date,
|
||||||
fees,
|
fees,
|
||||||
interest,
|
interest,
|
||||||
|
liabilities,
|
||||||
|
valuables,
|
||||||
items: newItems
|
items: newItems
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -872,6 +912,10 @@ export abstract class PortfolioCalculator {
|
|||||||
lastTransactionPoint.interest =
|
lastTransactionPoint.interest =
|
||||||
lastTransactionPoint.interest.plus(interest);
|
lastTransactionPoint.interest.plus(interest);
|
||||||
lastTransactionPoint.items = newItems;
|
lastTransactionPoint.items = newItems;
|
||||||
|
lastTransactionPoint.liabilities =
|
||||||
|
lastTransactionPoint.liabilities.plus(liabilities);
|
||||||
|
lastTransactionPoint.valuables =
|
||||||
|
lastTransactionPoint.valuables.plus(valuables);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastDate = date;
|
lastDate = date;
|
||||||
|
@ -176,7 +176,9 @@ describe('PortfolioCalculator', () => {
|
|||||||
totalFeesWithCurrencyEffect: new Big('3.2'),
|
totalFeesWithCurrencyEffect: new Big('3.2'),
|
||||||
totalInterestWithCurrencyEffect: new Big('0'),
|
totalInterestWithCurrencyEffect: new Big('0'),
|
||||||
totalInvestment: new Big('0'),
|
totalInvestment: new Big('0'),
|
||||||
totalInvestmentWithCurrencyEffect: new Big('0')
|
totalInvestmentWithCurrencyEffect: new Big('0'),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big('0')
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(investments).toEqual([
|
expect(investments).toEqual([
|
||||||
|
@ -159,7 +159,9 @@ describe('PortfolioCalculator', () => {
|
|||||||
totalFeesWithCurrencyEffect: new Big('3.2'),
|
totalFeesWithCurrencyEffect: new Big('3.2'),
|
||||||
totalInterestWithCurrencyEffect: new Big('0'),
|
totalInterestWithCurrencyEffect: new Big('0'),
|
||||||
totalInvestment: new Big('0'),
|
totalInvestment: new Big('0'),
|
||||||
totalInvestmentWithCurrencyEffect: new Big('0')
|
totalInvestmentWithCurrencyEffect: new Big('0'),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big('0')
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(investments).toEqual([
|
expect(investments).toEqual([
|
||||||
|
@ -144,7 +144,9 @@ describe('PortfolioCalculator', () => {
|
|||||||
totalFeesWithCurrencyEffect: new Big('1.55'),
|
totalFeesWithCurrencyEffect: new Big('1.55'),
|
||||||
totalInterestWithCurrencyEffect: new Big('0'),
|
totalInterestWithCurrencyEffect: new Big('0'),
|
||||||
totalInvestment: new Big('273.2'),
|
totalInvestment: new Big('273.2'),
|
||||||
totalInvestmentWithCurrencyEffect: new Big('273.2')
|
totalInvestmentWithCurrencyEffect: new Big('273.2'),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big('0')
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(investments).toEqual([
|
expect(investments).toEqual([
|
||||||
|
@ -178,7 +178,9 @@ describe('PortfolioCalculator', () => {
|
|||||||
totalFeesWithCurrencyEffect: new Big('0'),
|
totalFeesWithCurrencyEffect: new Big('0'),
|
||||||
totalInterestWithCurrencyEffect: new Big('0'),
|
totalInterestWithCurrencyEffect: new Big('0'),
|
||||||
totalInvestment: new Big('320.43'),
|
totalInvestment: new Big('320.43'),
|
||||||
totalInvestmentWithCurrencyEffect: new Big('318.542667299999967957')
|
totalInvestmentWithCurrencyEffect: new Big('318.542667299999967957'),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big('0')
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(investments).toEqual([
|
expect(investments).toEqual([
|
||||||
|
@ -125,7 +125,9 @@ describe('PortfolioCalculator', () => {
|
|||||||
totalFeesWithCurrencyEffect: new Big('49'),
|
totalFeesWithCurrencyEffect: new Big('49'),
|
||||||
totalInterestWithCurrencyEffect: new Big('0'),
|
totalInterestWithCurrencyEffect: new Big('0'),
|
||||||
totalInvestment: new Big('0'),
|
totalInvestment: new Big('0'),
|
||||||
totalInvestmentWithCurrencyEffect: new Big('0')
|
totalInvestmentWithCurrencyEffect: new Big('0'),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big('0')
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -157,7 +157,9 @@ describe('PortfolioCalculator', () => {
|
|||||||
totalFeesWithCurrencyEffect: new Big('1'),
|
totalFeesWithCurrencyEffect: new Big('1'),
|
||||||
totalInterestWithCurrencyEffect: new Big('0'),
|
totalInterestWithCurrencyEffect: new Big('0'),
|
||||||
totalInvestment: new Big('89.12'),
|
totalInvestment: new Big('89.12'),
|
||||||
totalInvestmentWithCurrencyEffect: new Big('82.329056')
|
totalInvestmentWithCurrencyEffect: new Big('82.329056'),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big('0')
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(investments).toEqual([
|
expect(investments).toEqual([
|
||||||
|
@ -0,0 +1,134 @@
|
|||||||
|
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
|
||||||
|
import {
|
||||||
|
activityDummyData,
|
||||||
|
symbolProfileDummyData
|
||||||
|
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
|
||||||
|
import {
|
||||||
|
PortfolioCalculatorFactory,
|
||||||
|
PerformanceCalculationType
|
||||||
|
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
|
||||||
|
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
|
||||||
|
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
|
||||||
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
|
||||||
|
import { parseDate } from '@ghostfolio/common/helper';
|
||||||
|
|
||||||
|
import { Big } from 'big.js';
|
||||||
|
|
||||||
|
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
CurrentRateService: jest.fn().mockImplementation(() => {
|
||||||
|
return CurrentRateServiceMock;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PortfolioCalculator', () => {
|
||||||
|
let currentRateService: CurrentRateService;
|
||||||
|
let exchangeRateDataService: ExchangeRateDataService;
|
||||||
|
let factory: PortfolioCalculatorFactory;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
currentRateService = new CurrentRateService(null, null, null, null);
|
||||||
|
|
||||||
|
exchangeRateDataService = new ExchangeRateDataService(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
factory = new PortfolioCalculatorFactory(
|
||||||
|
currentRateService,
|
||||||
|
exchangeRateDataService
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('compute portfolio snapshot', () => {
|
||||||
|
it.only('with item activity', async () => {
|
||||||
|
const spy = jest
|
||||||
|
.spyOn(Date, 'now')
|
||||||
|
.mockImplementation(() => parseDate('2022-01-31').getTime());
|
||||||
|
|
||||||
|
const activities: Activity[] = [
|
||||||
|
{
|
||||||
|
...activityDummyData,
|
||||||
|
date: new Date('2022-01-01'),
|
||||||
|
fee: 0,
|
||||||
|
quantity: 1,
|
||||||
|
SymbolProfile: {
|
||||||
|
...symbolProfileDummyData,
|
||||||
|
currency: 'USD',
|
||||||
|
dataSource: 'MANUAL',
|
||||||
|
name: 'Penthouse Apartment',
|
||||||
|
symbol: 'dac95060-d4f2-4653-a253-2c45e6fb5cde'
|
||||||
|
},
|
||||||
|
type: 'ITEM',
|
||||||
|
unitPrice: 500000
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const portfolioCalculator = factory.createCalculator({
|
||||||
|
activities,
|
||||||
|
calculationType: PerformanceCalculationType.TWR,
|
||||||
|
currency: 'USD'
|
||||||
|
});
|
||||||
|
|
||||||
|
const portfolioSnapshot = await portfolioCalculator.computeSnapshot(
|
||||||
|
parseDate('2022-01-01')
|
||||||
|
);
|
||||||
|
|
||||||
|
spy.mockRestore();
|
||||||
|
|
||||||
|
expect(portfolioSnapshot).toEqual({
|
||||||
|
currentValueInBaseCurrency: new Big('0'),
|
||||||
|
errors: [],
|
||||||
|
grossPerformance: new Big('0'),
|
||||||
|
grossPerformancePercentage: new Big('0'),
|
||||||
|
grossPerformancePercentageWithCurrencyEffect: new Big('0'),
|
||||||
|
grossPerformanceWithCurrencyEffect: new Big('0'),
|
||||||
|
hasErrors: true,
|
||||||
|
netPerformance: new Big('0'),
|
||||||
|
netPerformancePercentage: new Big('0'),
|
||||||
|
netPerformancePercentageWithCurrencyEffect: new Big('0'),
|
||||||
|
netPerformanceWithCurrencyEffect: new Big('0'),
|
||||||
|
positions: [
|
||||||
|
{
|
||||||
|
averagePrice: new Big('500000'),
|
||||||
|
currency: 'USD',
|
||||||
|
dataSource: 'MANUAL',
|
||||||
|
dividend: new Big('0'),
|
||||||
|
dividendInBaseCurrency: new Big('0'),
|
||||||
|
fee: new Big('0'),
|
||||||
|
firstBuyDate: '2022-01-01',
|
||||||
|
grossPerformance: null,
|
||||||
|
grossPerformancePercentage: null,
|
||||||
|
grossPerformancePercentageWithCurrencyEffect: null,
|
||||||
|
grossPerformanceWithCurrencyEffect: null,
|
||||||
|
investment: new Big('0'),
|
||||||
|
investmentWithCurrencyEffect: new Big('0'),
|
||||||
|
marketPrice: null,
|
||||||
|
marketPriceInBaseCurrency: 500000,
|
||||||
|
netPerformance: null,
|
||||||
|
netPerformancePercentage: null,
|
||||||
|
netPerformancePercentageWithCurrencyEffect: null,
|
||||||
|
netPerformanceWithCurrencyEffect: null,
|
||||||
|
quantity: new Big('0'),
|
||||||
|
symbol: 'dac95060-d4f2-4653-a253-2c45e6fb5cde',
|
||||||
|
tags: [],
|
||||||
|
timeWeightedInvestment: new Big('0'),
|
||||||
|
timeWeightedInvestmentWithCurrencyEffect: new Big('0'),
|
||||||
|
transactionCount: 1,
|
||||||
|
valueInBaseCurrency: new Big('0')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
totalFeesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalInterestWithCurrencyEffect: new Big('0'),
|
||||||
|
totalInvestment: new Big('0'),
|
||||||
|
totalInvestmentWithCurrencyEffect: new Big('0'),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big('0')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,134 @@
|
|||||||
|
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
|
||||||
|
import {
|
||||||
|
activityDummyData,
|
||||||
|
symbolProfileDummyData
|
||||||
|
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
|
||||||
|
import {
|
||||||
|
PortfolioCalculatorFactory,
|
||||||
|
PerformanceCalculationType
|
||||||
|
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
|
||||||
|
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
|
||||||
|
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
|
||||||
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
|
||||||
|
import { parseDate } from '@ghostfolio/common/helper';
|
||||||
|
|
||||||
|
import { Big } from 'big.js';
|
||||||
|
|
||||||
|
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
CurrentRateService: jest.fn().mockImplementation(() => {
|
||||||
|
return CurrentRateServiceMock;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PortfolioCalculator', () => {
|
||||||
|
let currentRateService: CurrentRateService;
|
||||||
|
let exchangeRateDataService: ExchangeRateDataService;
|
||||||
|
let factory: PortfolioCalculatorFactory;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
currentRateService = new CurrentRateService(null, null, null, null);
|
||||||
|
|
||||||
|
exchangeRateDataService = new ExchangeRateDataService(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
factory = new PortfolioCalculatorFactory(
|
||||||
|
currentRateService,
|
||||||
|
exchangeRateDataService
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('compute portfolio snapshot', () => {
|
||||||
|
it.only('with liability activity', async () => {
|
||||||
|
const spy = jest
|
||||||
|
.spyOn(Date, 'now')
|
||||||
|
.mockImplementation(() => parseDate('2022-01-31').getTime());
|
||||||
|
|
||||||
|
const activities: Activity[] = [
|
||||||
|
{
|
||||||
|
...activityDummyData,
|
||||||
|
date: new Date('2022-01-01'),
|
||||||
|
fee: 0,
|
||||||
|
quantity: 1,
|
||||||
|
SymbolProfile: {
|
||||||
|
...symbolProfileDummyData,
|
||||||
|
currency: 'USD',
|
||||||
|
dataSource: 'MANUAL',
|
||||||
|
name: 'Loan',
|
||||||
|
symbol: '55196015-1365-4560-aa60-8751ae6d18f8'
|
||||||
|
},
|
||||||
|
type: 'LIABILITY',
|
||||||
|
unitPrice: 3000
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const portfolioCalculator = factory.createCalculator({
|
||||||
|
activities,
|
||||||
|
calculationType: PerformanceCalculationType.TWR,
|
||||||
|
currency: 'USD'
|
||||||
|
});
|
||||||
|
|
||||||
|
const portfolioSnapshot = await portfolioCalculator.computeSnapshot(
|
||||||
|
parseDate('2022-01-01')
|
||||||
|
);
|
||||||
|
|
||||||
|
spy.mockRestore();
|
||||||
|
|
||||||
|
expect(portfolioSnapshot).toEqual({
|
||||||
|
currentValueInBaseCurrency: new Big('0'),
|
||||||
|
errors: [],
|
||||||
|
grossPerformance: new Big('0'),
|
||||||
|
grossPerformancePercentage: new Big('0'),
|
||||||
|
grossPerformancePercentageWithCurrencyEffect: new Big('0'),
|
||||||
|
grossPerformanceWithCurrencyEffect: new Big('0'),
|
||||||
|
hasErrors: true,
|
||||||
|
netPerformance: new Big('0'),
|
||||||
|
netPerformancePercentage: new Big('0'),
|
||||||
|
netPerformancePercentageWithCurrencyEffect: new Big('0'),
|
||||||
|
netPerformanceWithCurrencyEffect: new Big('0'),
|
||||||
|
positions: [
|
||||||
|
{
|
||||||
|
averagePrice: new Big('3000'),
|
||||||
|
currency: 'USD',
|
||||||
|
dataSource: 'MANUAL',
|
||||||
|
dividend: new Big('0'),
|
||||||
|
dividendInBaseCurrency: new Big('0'),
|
||||||
|
fee: new Big('0'),
|
||||||
|
firstBuyDate: '2022-01-01',
|
||||||
|
grossPerformance: null,
|
||||||
|
grossPerformancePercentage: null,
|
||||||
|
grossPerformancePercentageWithCurrencyEffect: null,
|
||||||
|
grossPerformanceWithCurrencyEffect: null,
|
||||||
|
investment: new Big('0'),
|
||||||
|
investmentWithCurrencyEffect: new Big('0'),
|
||||||
|
marketPrice: null,
|
||||||
|
marketPriceInBaseCurrency: 3000,
|
||||||
|
netPerformance: null,
|
||||||
|
netPerformancePercentage: null,
|
||||||
|
netPerformancePercentageWithCurrencyEffect: null,
|
||||||
|
netPerformanceWithCurrencyEffect: null,
|
||||||
|
quantity: new Big('0'),
|
||||||
|
symbol: '55196015-1365-4560-aa60-8751ae6d18f8',
|
||||||
|
tags: [],
|
||||||
|
timeWeightedInvestment: new Big('0'),
|
||||||
|
timeWeightedInvestmentWithCurrencyEffect: new Big('0'),
|
||||||
|
transactionCount: 1,
|
||||||
|
valueInBaseCurrency: new Big('0')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
totalFeesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalInterestWithCurrencyEffect: new Big('0'),
|
||||||
|
totalInvestment: new Big('0'),
|
||||||
|
totalInvestmentWithCurrencyEffect: new Big('0'),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big('0')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -133,7 +133,9 @@ describe('PortfolioCalculator', () => {
|
|||||||
totalFeesWithCurrencyEffect: new Big('19'),
|
totalFeesWithCurrencyEffect: new Big('19'),
|
||||||
totalInterestWithCurrencyEffect: new Big('0'),
|
totalInterestWithCurrencyEffect: new Big('0'),
|
||||||
totalInvestment: new Big('298.58'),
|
totalInvestment: new Big('298.58'),
|
||||||
totalInvestmentWithCurrencyEffect: new Big('298.58')
|
totalInvestmentWithCurrencyEffect: new Big('298.58'),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big('0')
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -83,7 +83,9 @@ describe('PortfolioCalculator', () => {
|
|||||||
totalFeesWithCurrencyEffect: new Big('0'),
|
totalFeesWithCurrencyEffect: new Big('0'),
|
||||||
totalInterestWithCurrencyEffect: new Big('0'),
|
totalInterestWithCurrencyEffect: new Big('0'),
|
||||||
totalInvestment: new Big(0),
|
totalInvestment: new Big(0),
|
||||||
totalInvestmentWithCurrencyEffect: new Big(0)
|
totalInvestmentWithCurrencyEffect: new Big(0),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big('0')
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(investments).toEqual([]);
|
expect(investments).toEqual([]);
|
||||||
|
@ -161,7 +161,9 @@ describe('PortfolioCalculator', () => {
|
|||||||
totalFeesWithCurrencyEffect: new Big('4.25'),
|
totalFeesWithCurrencyEffect: new Big('4.25'),
|
||||||
totalInterestWithCurrencyEffect: new Big('0'),
|
totalInterestWithCurrencyEffect: new Big('0'),
|
||||||
totalInvestment: new Big('75.80'),
|
totalInvestment: new Big('75.80'),
|
||||||
totalInvestmentWithCurrencyEffect: new Big('75.80')
|
totalInvestmentWithCurrencyEffect: new Big('75.80'),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big('0')
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(investments).toEqual([
|
expect(investments).toEqual([
|
||||||
|
@ -185,7 +185,9 @@ describe('PortfolioCalculator', () => {
|
|||||||
totalFeesWithCurrencyEffect: new Big('0'),
|
totalFeesWithCurrencyEffect: new Big('0'),
|
||||||
totalInterestWithCurrencyEffect: new Big('0'),
|
totalInterestWithCurrencyEffect: new Big('0'),
|
||||||
totalInvestment: new Big('0'),
|
totalInvestment: new Big('0'),
|
||||||
totalInvestmentWithCurrencyEffect: new Big('0')
|
totalInvestmentWithCurrencyEffect: new Big('0'),
|
||||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big('0')
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(investments).toEqual([
|
expect(investments).toEqual([
|
||||||
|
@ -109,6 +109,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
hasErrors,
|
hasErrors,
|
||||||
netPerformance,
|
netPerformance,
|
||||||
netPerformanceWithCurrencyEffect,
|
netPerformanceWithCurrencyEffect,
|
||||||
|
positions,
|
||||||
totalFeesWithCurrencyEffect,
|
totalFeesWithCurrencyEffect,
|
||||||
totalInterestWithCurrencyEffect,
|
totalInterestWithCurrencyEffect,
|
||||||
totalInvestment,
|
totalInvestment,
|
||||||
@ -131,7 +132,8 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
: grossPerformanceWithCurrencyEffect.div(
|
: grossPerformanceWithCurrencyEffect.div(
|
||||||
totalTimeWeightedInvestmentWithCurrencyEffect
|
totalTimeWeightedInvestmentWithCurrencyEffect
|
||||||
),
|
),
|
||||||
positions
|
totalLiabilitiesWithCurrencyEffect: new Big(0),
|
||||||
|
totalValuablesWithCurrencyEffect: new Big(0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,8 +196,12 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
let totalInvestmentFromBuyTransactions = new Big(0);
|
let totalInvestmentFromBuyTransactions = new Big(0);
|
||||||
let totalInvestmentFromBuyTransactionsWithCurrencyEffect = new Big(0);
|
let totalInvestmentFromBuyTransactionsWithCurrencyEffect = new Big(0);
|
||||||
let totalInvestmentWithCurrencyEffect = new Big(0);
|
let totalInvestmentWithCurrencyEffect = new Big(0);
|
||||||
|
let totalLiabilities = new Big(0);
|
||||||
|
let totalLiabilitiesInBaseCurrency = new Big(0);
|
||||||
let totalQuantityFromBuyTransactions = new Big(0);
|
let totalQuantityFromBuyTransactions = new Big(0);
|
||||||
let totalUnits = new Big(0);
|
let totalUnits = new Big(0);
|
||||||
|
let totalValuables = new Big(0);
|
||||||
|
let totalValuablesInBaseCurrency = new Big(0);
|
||||||
let valueAtStartDate: Big;
|
let valueAtStartDate: Big;
|
||||||
let valueAtStartDateWithCurrencyEffect: Big;
|
let valueAtStartDateWithCurrencyEffect: Big;
|
||||||
|
|
||||||
@ -236,7 +242,11 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
totalInterest: new Big(0),
|
totalInterest: new Big(0),
|
||||||
totalInterestInBaseCurrency: new Big(0),
|
totalInterestInBaseCurrency: new Big(0),
|
||||||
totalInvestment: new Big(0),
|
totalInvestment: new Big(0),
|
||||||
totalInvestmentWithCurrencyEffect: new Big(0)
|
totalInvestmentWithCurrencyEffect: new Big(0),
|
||||||
|
totalLiabilities: new Big(0),
|
||||||
|
totalLiabilitiesInBaseCurrency: new Big(0),
|
||||||
|
totalValuables: new Big(0),
|
||||||
|
totalValuablesInBaseCurrency: new Big(0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +291,11 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
totalInterest: new Big(0),
|
totalInterest: new Big(0),
|
||||||
totalInterestInBaseCurrency: new Big(0),
|
totalInterestInBaseCurrency: new Big(0),
|
||||||
totalInvestment: new Big(0),
|
totalInvestment: new Big(0),
|
||||||
totalInvestmentWithCurrencyEffect: new Big(0)
|
totalInvestmentWithCurrencyEffect: new Big(0),
|
||||||
|
totalLiabilities: new Big(0),
|
||||||
|
totalLiabilitiesInBaseCurrency: new Big(0),
|
||||||
|
totalValuables: new Big(0),
|
||||||
|
totalValuablesInBaseCurrency: new Big(0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,6 +550,20 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
totalInterestInBaseCurrency = totalInterestInBaseCurrency.plus(
|
totalInterestInBaseCurrency = totalInterestInBaseCurrency.plus(
|
||||||
interest.mul(exchangeRateAtOrderDate ?? 1)
|
interest.mul(exchangeRateAtOrderDate ?? 1)
|
||||||
);
|
);
|
||||||
|
} else if (order.type === 'ITEM') {
|
||||||
|
const valuables = order.quantity.mul(order.unitPrice);
|
||||||
|
|
||||||
|
totalValuables = totalValuables.plus(valuables);
|
||||||
|
totalValuablesInBaseCurrency = totalValuablesInBaseCurrency.plus(
|
||||||
|
valuables.mul(exchangeRateAtOrderDate ?? 1)
|
||||||
|
);
|
||||||
|
} else if (order.type === 'LIABILITY') {
|
||||||
|
const liabilities = order.quantity.mul(order.unitPrice);
|
||||||
|
|
||||||
|
totalLiabilities = totalLiabilities.plus(liabilities);
|
||||||
|
totalLiabilitiesInBaseCurrency = totalLiabilitiesInBaseCurrency.plus(
|
||||||
|
liabilities.mul(exchangeRateAtOrderDate ?? 1)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const valueOfInvestment = totalUnits.mul(order.unitPriceInBaseCurrency);
|
const valueOfInvestment = totalUnits.mul(order.unitPriceInBaseCurrency);
|
||||||
@ -853,6 +881,10 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
totalInterestInBaseCurrency,
|
totalInterestInBaseCurrency,
|
||||||
totalInvestment,
|
totalInvestment,
|
||||||
totalInvestmentWithCurrencyEffect,
|
totalInvestmentWithCurrencyEffect,
|
||||||
|
totalLiabilities,
|
||||||
|
totalLiabilitiesInBaseCurrency,
|
||||||
|
totalValuables,
|
||||||
|
totalValuablesInBaseCurrency,
|
||||||
grossPerformance: totalGrossPerformance,
|
grossPerformance: totalGrossPerformance,
|
||||||
grossPerformanceWithCurrencyEffect:
|
grossPerformanceWithCurrencyEffect:
|
||||||
totalGrossPerformanceWithCurrencyEffect,
|
totalGrossPerformanceWithCurrencyEffect,
|
||||||
|
@ -19,4 +19,6 @@ export interface PortfolioSnapshot extends ResponseError {
|
|||||||
totalInterestWithCurrencyEffect: Big;
|
totalInterestWithCurrencyEffect: Big;
|
||||||
totalInvestment: Big;
|
totalInvestment: Big;
|
||||||
totalInvestmentWithCurrencyEffect: Big;
|
totalInvestmentWithCurrencyEffect: Big;
|
||||||
|
totalLiabilitiesWithCurrencyEffect: Big;
|
||||||
|
totalValuablesWithCurrencyEffect: Big;
|
||||||
}
|
}
|
||||||
|
@ -7,4 +7,6 @@ export interface TransactionPoint {
|
|||||||
fees: Big;
|
fees: Big;
|
||||||
interest: Big;
|
interest: Big;
|
||||||
items: TransactionPointSymbol[];
|
items: TransactionPointSymbol[];
|
||||||
|
liabilities: Big;
|
||||||
|
valuables: Big;
|
||||||
}
|
}
|
||||||
|
@ -78,10 +78,8 @@ export class PortfolioController {
|
|||||||
@Query('assetClasses') filterByAssetClasses?: string,
|
@Query('assetClasses') filterByAssetClasses?: string,
|
||||||
@Query('range') dateRange: DateRange = 'max',
|
@Query('range') dateRange: DateRange = 'max',
|
||||||
@Query('tags') filterByTags?: string,
|
@Query('tags') filterByTags?: string,
|
||||||
@Query('withLiabilities') withLiabilitiesParam = 'false',
|
|
||||||
@Query('withMarkets') withMarketsParam = 'false'
|
@Query('withMarkets') withMarketsParam = 'false'
|
||||||
): Promise<PortfolioDetails & { hasError: boolean }> {
|
): Promise<PortfolioDetails & { hasError: boolean }> {
|
||||||
const withLiabilities = withLiabilitiesParam === 'true';
|
|
||||||
const withMarkets = withMarketsParam === 'true';
|
const withMarkets = withMarketsParam === 'true';
|
||||||
|
|
||||||
let hasDetails = true;
|
let hasDetails = true;
|
||||||
@ -107,8 +105,6 @@ export class PortfolioController {
|
|||||||
dateRange,
|
dateRange,
|
||||||
filters,
|
filters,
|
||||||
impersonationId,
|
impersonationId,
|
||||||
// TODO
|
|
||||||
// withLiabilities,
|
|
||||||
withMarkets,
|
withMarkets,
|
||||||
userId: this.request.user.id,
|
userId: this.request.user.id,
|
||||||
withSummary: true
|
withSummary: true
|
||||||
|
@ -60,7 +60,6 @@ import {
|
|||||||
Prisma
|
Prisma
|
||||||
} from '@prisma/client';
|
} from '@prisma/client';
|
||||||
import { Big } from 'big.js';
|
import { Big } from 'big.js';
|
||||||
import { isUUID } from 'class-validator';
|
|
||||||
import {
|
import {
|
||||||
differenceInDays,
|
differenceInDays,
|
||||||
format,
|
format,
|
||||||
@ -324,7 +323,6 @@ export class PortfolioService {
|
|||||||
impersonationId,
|
impersonationId,
|
||||||
userId,
|
userId,
|
||||||
withExcludedAccounts = false,
|
withExcludedAccounts = false,
|
||||||
withLiabilities = false,
|
|
||||||
withMarkets = false,
|
withMarkets = false,
|
||||||
withSummary = false
|
withSummary = false
|
||||||
}: {
|
}: {
|
||||||
@ -333,7 +331,6 @@ export class PortfolioService {
|
|||||||
impersonationId: string;
|
impersonationId: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
withExcludedAccounts?: boolean;
|
withExcludedAccounts?: boolean;
|
||||||
withLiabilities?: boolean;
|
|
||||||
withMarkets?: boolean;
|
withMarkets?: boolean;
|
||||||
withSummary?: boolean;
|
withSummary?: boolean;
|
||||||
}): Promise<PortfolioDetails & { hasErrors: boolean }> {
|
}): Promise<PortfolioDetails & { hasErrors: boolean }> {
|
||||||
@ -1623,35 +1620,10 @@ export class PortfolioService {
|
|||||||
|
|
||||||
const interest = await portfolioCalculator.getInterestInBaseCurrency();
|
const interest = await portfolioCalculator.getInterestInBaseCurrency();
|
||||||
|
|
||||||
// TODO: Move to portfolio calculator
|
const liabilities =
|
||||||
const items = getSum(
|
await portfolioCalculator.getLiabilitiesInBaseCurrency();
|
||||||
Object.keys(holdings)
|
|
||||||
.filter((symbol) => {
|
|
||||||
return (
|
|
||||||
isUUID(symbol) &&
|
|
||||||
holdings[symbol].dataSource === 'MANUAL' &&
|
|
||||||
holdings[symbol].valueInBaseCurrency > 0
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.map((symbol) => {
|
|
||||||
return new Big(holdings[symbol].valueInBaseCurrency).abs();
|
|
||||||
})
|
|
||||||
).toNumber();
|
|
||||||
|
|
||||||
// TODO: Move to portfolio calculator
|
const valuables = await portfolioCalculator.getValuablesInBaseCurrency();
|
||||||
const liabilities = getSum(
|
|
||||||
Object.keys(holdings)
|
|
||||||
.filter((symbol) => {
|
|
||||||
return (
|
|
||||||
isUUID(symbol) &&
|
|
||||||
holdings[symbol].dataSource === 'MANUAL' &&
|
|
||||||
holdings[symbol].valueInBaseCurrency < 0
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.map((symbol) => {
|
|
||||||
return new Big(holdings[symbol].valueInBaseCurrency).abs();
|
|
||||||
})
|
|
||||||
).toNumber();
|
|
||||||
|
|
||||||
const totalBuy = this.getSumOfActivityType({
|
const totalBuy = this.getSumOfActivityType({
|
||||||
userCurrency,
|
userCurrency,
|
||||||
@ -1701,7 +1673,7 @@ export class PortfolioService {
|
|||||||
|
|
||||||
const netWorth = new Big(balanceInBaseCurrency)
|
const netWorth = new Big(balanceInBaseCurrency)
|
||||||
.plus(performanceInformation.performance.currentValue)
|
.plus(performanceInformation.performance.currentValue)
|
||||||
.plus(items)
|
.plus(valuables)
|
||||||
.plus(excludedAccountsAndActivities)
|
.plus(excludedAccountsAndActivities)
|
||||||
.minus(liabilities)
|
.minus(liabilities)
|
||||||
.toNumber();
|
.toNumber();
|
||||||
@ -1730,8 +1702,6 @@ export class PortfolioService {
|
|||||||
cash,
|
cash,
|
||||||
excludedAccountsAndActivities,
|
excludedAccountsAndActivities,
|
||||||
firstOrderDate,
|
firstOrderDate,
|
||||||
items,
|
|
||||||
liabilities,
|
|
||||||
totalBuy,
|
totalBuy,
|
||||||
totalSell,
|
totalSell,
|
||||||
committedFunds: committedFunds.toNumber(),
|
committedFunds: committedFunds.toNumber(),
|
||||||
@ -1752,6 +1722,8 @@ export class PortfolioService {
|
|||||||
.minus(emergencyFundPositionsValueInBaseCurrency)
|
.minus(emergencyFundPositionsValueInBaseCurrency)
|
||||||
.toNumber(),
|
.toNumber(),
|
||||||
interest: interest.toNumber(),
|
interest: interest.toNumber(),
|
||||||
|
items: valuables.toNumber(),
|
||||||
|
liabilities: liabilities.toNumber(),
|
||||||
ordersCount: activities.filter(({ type }) => {
|
ordersCount: activities.filter(({ type }) => {
|
||||||
return type === 'BUY' || type === 'SELL';
|
return type === 'BUY' || type === 'SELL';
|
||||||
}).length,
|
}).length,
|
||||||
|
@ -18,10 +18,8 @@ export function getFactor(activityType: ActivityType) {
|
|||||||
|
|
||||||
switch (activityType) {
|
switch (activityType) {
|
||||||
case 'BUY':
|
case 'BUY':
|
||||||
case 'ITEM':
|
|
||||||
factor = 1;
|
factor = 1;
|
||||||
break;
|
break;
|
||||||
case 'LIABILITY':
|
|
||||||
case 'SELL':
|
case 'SELL':
|
||||||
factor = -1;
|
factor = -1;
|
||||||
break;
|
break;
|
||||||
|
@ -102,7 +102,7 @@ export class HomeSummaryComponent implements OnDestroy, OnInit {
|
|||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
|
||||||
this.dataService
|
this.dataService
|
||||||
.fetchPortfolioDetails({ withLiabilities: true })
|
.fetchPortfolioDetails()
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe(({ summary }) => {
|
.subscribe(({ summary }) => {
|
||||||
this.summary = summary;
|
this.summary = summary;
|
||||||
|
@ -411,19 +411,13 @@ export class DataService {
|
|||||||
|
|
||||||
public fetchPortfolioDetails({
|
public fetchPortfolioDetails({
|
||||||
filters,
|
filters,
|
||||||
withLiabilities = false,
|
|
||||||
withMarkets = false
|
withMarkets = false
|
||||||
}: {
|
}: {
|
||||||
filters?: Filter[];
|
filters?: Filter[];
|
||||||
withLiabilities?: boolean;
|
|
||||||
withMarkets?: boolean;
|
withMarkets?: boolean;
|
||||||
} = {}): Observable<PortfolioDetails> {
|
} = {}): Observable<PortfolioDetails> {
|
||||||
let params = this.buildFiltersAsQueryParams({ filters });
|
let params = this.buildFiltersAsQueryParams({ filters });
|
||||||
|
|
||||||
if (withLiabilities) {
|
|
||||||
params = params.append('withLiabilities', withLiabilities);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (withMarkets) {
|
if (withMarkets) {
|
||||||
params = params.append('withMarkets', withMarkets);
|
params = params.append('withMarkets', withMarkets);
|
||||||
}
|
}
|
||||||
|
@ -46,4 +46,8 @@ export interface SymbolMetrics {
|
|||||||
totalInterestInBaseCurrency: Big;
|
totalInterestInBaseCurrency: Big;
|
||||||
totalInvestment: Big;
|
totalInvestment: Big;
|
||||||
totalInvestmentWithCurrencyEffect: Big;
|
totalInvestmentWithCurrencyEffect: Big;
|
||||||
|
totalLiabilities: Big;
|
||||||
|
totalLiabilitiesInBaseCurrency: Big;
|
||||||
|
totalValuables: Big;
|
||||||
|
totalValuablesInBaseCurrency: Big;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user