mirror of https://github.com/ghostfolio/ghostfolio
Browse Source
* Clean up initial (original) values from X-Ray * Refactor current to valueInBaseCurrency * Update changelogpull/1915/head
Thomas Kaul
2 years ago
committed by
GitHub
12 changed files with 30 additions and 282 deletions
@ -1,88 +0,0 @@ |
|||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; |
|
||||
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; |
|
||||
import { |
|
||||
PortfolioDetails, |
|
||||
PortfolioPosition, |
|
||||
UserSettings |
|
||||
} from '@ghostfolio/common/interfaces'; |
|
||||
|
|
||||
import { Rule } from '../../rule'; |
|
||||
|
|
||||
export class AccountClusterRiskInitialInvestment extends Rule<Settings> { |
|
||||
public constructor( |
|
||||
protected exchangeRateDataService: ExchangeRateDataService, |
|
||||
private accounts: PortfolioDetails['accounts'] |
|
||||
) { |
|
||||
super(exchangeRateDataService, { |
|
||||
name: 'Initial Investment' |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public evaluate(ruleSettings?: Settings) { |
|
||||
const accounts: { |
|
||||
[symbol: string]: Pick<PortfolioPosition, 'name'> & { |
|
||||
investment: number; |
|
||||
}; |
|
||||
} = {}; |
|
||||
|
|
||||
for (const [accountId, account] of Object.entries(this.accounts)) { |
|
||||
accounts[accountId] = { |
|
||||
name: account.name, |
|
||||
investment: account.original |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
let maxItem; |
|
||||
let totalInvestment = 0; |
|
||||
|
|
||||
for (const account of Object.values(accounts)) { |
|
||||
if (!maxItem) { |
|
||||
maxItem = account; |
|
||||
} |
|
||||
|
|
||||
// Calculate total investment
|
|
||||
totalInvestment += account.investment; |
|
||||
|
|
||||
// Find maximum
|
|
||||
if (account.investment > maxItem?.investment) { |
|
||||
maxItem = account; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const maxInvestmentRatio = maxItem.investment / totalInvestment; |
|
||||
|
|
||||
if (maxInvestmentRatio > ruleSettings.threshold) { |
|
||||
return { |
|
||||
evaluation: `Over ${ |
|
||||
ruleSettings.threshold * 100 |
|
||||
}% of your initial investment is at ${maxItem.name} (${( |
|
||||
maxInvestmentRatio * 100 |
|
||||
).toPrecision(3)}%)`,
|
|
||||
value: false |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
return { |
|
||||
evaluation: `The major part of your initial investment is at ${ |
|
||||
maxItem.name |
|
||||
} (${(maxInvestmentRatio * 100).toPrecision(3)}%) and does not exceed ${ |
|
||||
ruleSettings.threshold * 100 |
|
||||
}%`,
|
|
||||
value: true |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
public getSettings(aUserSettings: UserSettings): Settings { |
|
||||
return { |
|
||||
baseCurrency: aUserSettings.baseCurrency, |
|
||||
isActive: true, |
|
||||
threshold: 0.5 |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
interface Settings extends RuleSettings { |
|
||||
baseCurrency: string; |
|
||||
isActive: boolean; |
|
||||
threshold: number; |
|
||||
} |
|
@ -1,71 +0,0 @@ |
|||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; |
|
||||
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; |
|
||||
import { TimelinePosition, UserSettings } from '@ghostfolio/common/interfaces'; |
|
||||
|
|
||||
import { Rule } from '../../rule'; |
|
||||
|
|
||||
export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule<Settings> { |
|
||||
public constructor( |
|
||||
protected exchangeRateDataService: ExchangeRateDataService, |
|
||||
private positions: TimelinePosition[] |
|
||||
) { |
|
||||
super(exchangeRateDataService, { |
|
||||
name: 'Initial Investment: Base Currency' |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public evaluate(ruleSettings: Settings) { |
|
||||
const positionsGroupedByCurrency = this.groupCurrentPositionsByAttribute( |
|
||||
this.positions, |
|
||||
'currency', |
|
||||
ruleSettings.baseCurrency |
|
||||
); |
|
||||
|
|
||||
let maxItem = positionsGroupedByCurrency[0]; |
|
||||
let totalInvestment = 0; |
|
||||
|
|
||||
positionsGroupedByCurrency.forEach((groupItem) => { |
|
||||
// Calculate total investment
|
|
||||
totalInvestment += groupItem.investment; |
|
||||
|
|
||||
// Find maximum
|
|
||||
if (groupItem.investment > maxItem.investment) { |
|
||||
maxItem = groupItem; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
const baseCurrencyItem = positionsGroupedByCurrency.find((item) => { |
|
||||
return item.groupKey === ruleSettings.baseCurrency; |
|
||||
}); |
|
||||
|
|
||||
const baseCurrencyInvestmentRatio = |
|
||||
baseCurrencyItem?.investment / totalInvestment || 0; |
|
||||
|
|
||||
if (maxItem.groupKey !== ruleSettings.baseCurrency) { |
|
||||
return { |
|
||||
evaluation: `The major part of your initial investment is not in your base currency (${( |
|
||||
baseCurrencyInvestmentRatio * 100 |
|
||||
).toPrecision(3)}% in ${ruleSettings.baseCurrency})`,
|
|
||||
value: false |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
return { |
|
||||
evaluation: `The major part of your initial investment is in your base currency (${( |
|
||||
baseCurrencyInvestmentRatio * 100 |
|
||||
).toPrecision(3)}% in ${ruleSettings.baseCurrency})`,
|
|
||||
value: true |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
public getSettings(aUserSettings: UserSettings): Settings { |
|
||||
return { |
|
||||
baseCurrency: aUserSettings.baseCurrency, |
|
||||
isActive: true |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
interface Settings extends RuleSettings { |
|
||||
baseCurrency: string; |
|
||||
} |
|
@ -1,72 +0,0 @@ |
|||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; |
|
||||
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; |
|
||||
import { TimelinePosition, UserSettings } from '@ghostfolio/common/interfaces'; |
|
||||
|
|
||||
import { Rule } from '../../rule'; |
|
||||
|
|
||||
export class CurrencyClusterRiskInitialInvestment extends Rule<Settings> { |
|
||||
public constructor( |
|
||||
protected exchangeRateDataService: ExchangeRateDataService, |
|
||||
private positions: TimelinePosition[] |
|
||||
) { |
|
||||
super(exchangeRateDataService, { |
|
||||
name: 'Initial Investment' |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public evaluate(ruleSettings: Settings) { |
|
||||
const positionsGroupedByCurrency = this.groupCurrentPositionsByAttribute( |
|
||||
this.positions, |
|
||||
'currency', |
|
||||
ruleSettings.baseCurrency |
|
||||
); |
|
||||
|
|
||||
let maxItem = positionsGroupedByCurrency[0]; |
|
||||
let totalInvestment = 0; |
|
||||
|
|
||||
positionsGroupedByCurrency.forEach((groupItem) => { |
|
||||
// Calculate total investment
|
|
||||
totalInvestment += groupItem.investment; |
|
||||
|
|
||||
// Find maximum
|
|
||||
if (groupItem.investment > maxItem.investment) { |
|
||||
maxItem = groupItem; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
const maxInvestmentRatio = maxItem.investment / totalInvestment; |
|
||||
|
|
||||
if (maxInvestmentRatio > ruleSettings.threshold) { |
|
||||
return { |
|
||||
evaluation: `Over ${ |
|
||||
ruleSettings.threshold * 100 |
|
||||
}% of your initial investment is in ${maxItem.groupKey} (${( |
|
||||
maxInvestmentRatio * 100 |
|
||||
).toPrecision(3)}%)`,
|
|
||||
value: false |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
return { |
|
||||
evaluation: `The major part of your initial investment is in ${ |
|
||||
maxItem.groupKey |
|
||||
} (${(maxInvestmentRatio * 100).toPrecision(3)}%) and does not exceed ${ |
|
||||
ruleSettings.threshold * 100 |
|
||||
}%`,
|
|
||||
value: true |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
public getSettings(aUserSettings: UserSettings): Settings { |
|
||||
return { |
|
||||
baseCurrency: aUserSettings.baseCurrency, |
|
||||
isActive: true, |
|
||||
threshold: 0.5 |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
interface Settings extends RuleSettings { |
|
||||
baseCurrency: string; |
|
||||
threshold: number; |
|
||||
} |
|
Loading…
Reference in new issue