From d746e19726c64585bd9f179fe87c90ad10a5e78a Mon Sep 17 00:00:00 2001 From: Ani Channarasappa Date: Sat, 18 Feb 2023 15:05:02 -0500 Subject: [PATCH] fix: holding total change amount and percent mixed currencies with cost basis conversion disabled --- internal/asset/asset.go | 18 +++++++++--------- internal/asset/asset_test.go | 26 +++++++++++++++++++++++++- internal/asset/currency.go | 19 ------------------- internal/currency/currency_test.go | 1 + 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/internal/asset/asset.go b/internal/asset/asset.go index ce38360..d8348ff 100644 --- a/internal/asset/asset.go +++ b/internal/asset/asset.go @@ -30,8 +30,7 @@ func GetAssets(ctx c.Context, assetGroupQuote c.AssetGroupQuote) ([]c.Asset, Hol currencyRateByUse := currency.GetCurrencyRateFromContext(ctx, assetQuote.Currency.FromCurrencyCode) - holding := getHoldingFromAssetQuote(assetQuote, holdingsBySymbol) - holding = convertAssetHoldingCurrency(currencyRateByUse, holding) + holding := getHoldingFromAssetQuote(assetQuote, holdingsBySymbol, currencyRateByUse) holdingSummary = addHoldingToHoldingSummary(holdingSummary, holding, currencyRateByUse) assets = append(assets, c.Asset{ @@ -102,21 +101,22 @@ func updateHoldingWeights(assets []c.Asset, holdingSummary HoldingSummary) []c.A } -func getHoldingFromAssetQuote(assetQuote c.AssetQuote, lotsBySymbol map[string]AggregatedLot) c.Holding { +func getHoldingFromAssetQuote(assetQuote c.AssetQuote, lotsBySymbol map[string]AggregatedLot, currencyRateByUse currency.CurrencyRateByUse) c.Holding { if aggregatedLot, ok := lotsBySymbol[assetQuote.Symbol]; ok { - value := aggregatedLot.Quantity * assetQuote.QuotePrice.Price - totalChangeAmount := value - aggregatedLot.Cost - totalChangePercent := (totalChangeAmount / aggregatedLot.Cost) * 100 + value := aggregatedLot.Quantity * assetQuote.QuotePrice.Price * currencyRateByUse.QuotePrice + cost := aggregatedLot.Cost * currencyRateByUse.PositionCost + totalChangeAmount := value - cost + totalChangePercent := (totalChangeAmount / cost) * 100 return c.Holding{ Value: value, - Cost: aggregatedLot.Cost, + Cost: cost, Quantity: aggregatedLot.Quantity, UnitValue: value / aggregatedLot.Quantity, - UnitCost: aggregatedLot.Cost / aggregatedLot.Quantity, + UnitCost: cost / aggregatedLot.Quantity, DayChange: c.HoldingChange{ - Amount: assetQuote.QuotePrice.Change * aggregatedLot.Quantity, + Amount: assetQuote.QuotePrice.Change * aggregatedLot.Quantity * currencyRateByUse.QuotePrice, Percent: assetQuote.QuotePrice.ChangePercent, }, TotalChange: c.HoldingChange{ diff --git a/internal/asset/asset_test.go b/internal/asset/asset_test.go index 6c31149..8cf4e87 100644 --- a/internal/asset/asset_test.go +++ b/internal/asset/asset_test.go @@ -164,7 +164,7 @@ var _ = Describe("Asset", func() { Expect(outputHoldingSummary.DayChange.Amount).To(Equal(190.0)) }) - When("and only the summary conversion only option is set", func() { + When("and the summary conversion only option is set", func() { inputContextSummaryConversion := inputContext inputContextSummaryConversion.Config = c.Config{ @@ -183,6 +183,30 @@ var _ = Describe("Asset", func() { }) + When("and the disable unit cost conversion option is set", func() { + + inputContextDisableUnitCostConversion := inputContext + inputContextDisableUnitCostConversion.Config = c.Config{ + Currency: "EUR", + CurrencyDisableUnitCostConversion: true, + } + + outputAssets, outputHoldingSummary := GetAssets(inputContextDisableUnitCostConversion, inputAssetGroupQuote) + + It("should not convert holding costs", func() { + Expect(outputAssets[0].Holding.Cost).To(Equal(1000.0)) // 1000 EUR unconverted since option is set + Expect(outputAssets[0].Holding.UnitCost).To(Equal(100.0)) + Expect(outputAssets[0].Holding.Value).To(Equal(1650.0)) // Conversion 10 shares @ 110 USD/share to EUR + Expect(outputAssets[0].Holding.TotalChange.Amount).To(Equal(650.0)) + Expect(outputAssets[0].Holding.TotalChange.Percent).To(Equal(65.0)) + Expect(outputHoldingSummary.Cost).To(Equal(1100.0)) // Sum of 1000 EUR + 100 EUR + Expect(outputHoldingSummary.DayChange.Percent).To(Equal(9.090909090909092)) + Expect(outputHoldingSummary.TotalChange.Amount).To(Equal(990.0)) + Expect(outputHoldingSummary.TotalChange.Percent).To(Equal(90.0)) + }) + + }) + }) When("there is no explicit currency conversion", func() { diff --git a/internal/asset/currency.go b/internal/asset/currency.go index ddda68c..18b072b 100644 --- a/internal/asset/currency.go +++ b/internal/asset/currency.go @@ -25,22 +25,3 @@ func convertAssetQuoteExtendedCurrency(currencyRateByUse currency.CurrencyRateBy Volume: quoteExtended.Volume, } } - -func convertAssetHoldingCurrency(currencyRateByUse currency.CurrencyRateByUse, holding c.Holding) c.Holding { - return c.Holding{ - Value: holding.Value * currencyRateByUse.QuotePrice, - Cost: holding.Cost * currencyRateByUse.PositionCost, - Quantity: holding.Quantity, - UnitValue: holding.UnitValue * currencyRateByUse.QuotePrice, - UnitCost: holding.UnitCost * currencyRateByUse.PositionCost, - DayChange: c.HoldingChange{ - Amount: holding.DayChange.Amount * currencyRateByUse.QuotePrice, - Percent: holding.DayChange.Percent, - }, - TotalChange: c.HoldingChange{ - Amount: holding.TotalChange.Amount * currencyRateByUse.QuotePrice, - Percent: holding.TotalChange.Percent, - }, - Weight: holding.Weight, - } -} diff --git a/internal/currency/currency_test.go b/internal/currency/currency_test.go index 6057260..534c238 100644 --- a/internal/currency/currency_test.go +++ b/internal/currency/currency_test.go @@ -152,6 +152,7 @@ var _ = Describe("Currency", func() { } outputCurrencyRateByUse := GetCurrencyRateFromContext(inputCtx, "USD") Expect(outputCurrencyRateByUse.SummaryCost).To(Equal(1.0)) + Expect(outputCurrencyRateByUse.PositionCost).To(Equal(1.0)) }) }) })