Skip to content

Commit

Permalink
Added Widget for performance-based all time high (and clarify that th…
Browse files Browse the repository at this point in the history
…e existing one works based on prices).
  • Loading branch information
RomanLangrehr committed Nov 12, 2022
1 parent 6f2c960 commit 0308f48
Show file tree
Hide file tree
Showing 17 changed files with 404 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package name.abuchen.portfolio.math;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;

import java.time.LocalDate;
import java.util.Optional;
import java.util.OptionalDouble;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import name.abuchen.portfolio.snapshot.PerformanceIndex;

public class AllTimeHighPerformanceTest
{
private PerformanceIndex emptyIndex;
private PerformanceIndex oneEntryIndex;
private PerformanceIndex athAtTheEndIndex;
private PerformanceIndex athAtTheBeginnigIndex;
private PerformanceIndex athInTheMiddle;
private PerformanceIndex bankruptcy;
private PerformanceIndex worseThanBankruptcy; // Asset that looses more than 100%

@Before
public void setUp()
{
emptyIndex = Mockito.mock(PerformanceIndex.class);
when(emptyIndex.getAccumulatedPercentage()).thenReturn(new double[] {});
when(emptyIndex.getDates()).thenReturn(new LocalDate[] {});

oneEntryIndex = Mockito.mock(PerformanceIndex.class);
when(oneEntryIndex.getAccumulatedPercentage()).thenReturn(new double[] { 0d });
when(oneEntryIndex.getDates()).thenReturn(new LocalDate[] { LocalDate.of(2022, 11, 10) });

athAtTheEndIndex = Mockito.mock(PerformanceIndex.class);
when(athAtTheEndIndex.getAccumulatedPercentage()).thenReturn(new double[] { 0d, -.1d, -.1d, -.05d, .1d, .15d});
when(athAtTheEndIndex.getDates()).thenReturn(new LocalDate[] { LocalDate.of(2022, 11, 2),
LocalDate.of(2022, 11, 3), LocalDate.of(2022, 11, 4), LocalDate.of(2022, 11, 6),
LocalDate.of(2022, 11, 8), LocalDate.of(2022, 11, 11) });

athAtTheBeginnigIndex = Mockito.mock(PerformanceIndex.class);
when(athAtTheBeginnigIndex.getAccumulatedPercentage()).thenReturn(new double[] { 0d, -.1d, -.1d, -.05d, -.2d, -.15d});
when(athAtTheBeginnigIndex.getDates()).thenReturn(new LocalDate[] { LocalDate.of(2022, 11, 2),
LocalDate.of(2022, 11, 3), LocalDate.of(2022, 11, 4), LocalDate.of(2022, 11, 6),
LocalDate.of(2022, 11, 8), LocalDate.of(2022, 11, 11) });

athInTheMiddle = Mockito.mock(PerformanceIndex.class);
when(athInTheMiddle.getAccumulatedPercentage()).thenReturn(new double[] { 0d, -.1d, -.2d, 1d, .5d, 0d});
when(athInTheMiddle.getDates()).thenReturn(new LocalDate[] { LocalDate.of(2022, 11, 2),
LocalDate.of(2022, 11, 3), LocalDate.of(2022, 11, 4), LocalDate.of(2022, 11, 6),
LocalDate.of(2022, 11, 8), LocalDate.of(2022, 11, 11) });

bankruptcy = Mockito.mock(PerformanceIndex.class);
when(bankruptcy.getAccumulatedPercentage()).thenReturn(new double[] { 0d, .1d, .23d, -.9d, -1d, -1d});
when(bankruptcy.getDates()).thenReturn(new LocalDate[] { LocalDate.of(2022, 11, 2),
LocalDate.of(2022, 11, 3), LocalDate.of(2022, 11, 4), LocalDate.of(2022, 11, 6),
LocalDate.of(2022, 11, 8), LocalDate.of(2022, 11, 11) });

worseThanBankruptcy = Mockito.mock(PerformanceIndex.class);
when(worseThanBankruptcy.getAccumulatedPercentage()).thenReturn(new double[] { 0d, -.1d, -.23d, -.9d, -1d, -1.5d});
when(worseThanBankruptcy.getDates()).thenReturn(new LocalDate[] { LocalDate.of(2022, 11, 2),
LocalDate.of(2022, 11, 3), LocalDate.of(2022, 11, 4), LocalDate.of(2022, 11, 6),
LocalDate.of(2022, 11, 8), LocalDate.of(2022, 11, 11) });
}

@Test
public void testEmptyIndex()
{
AllTimeHighPerformance ath = new AllTimeHighPerformance(emptyIndex);

assertTrue(ath.getDistance().isEmpty());
assertTrue(ath.getDate().isEmpty());
}

@Test
public void testOneEntryIndex()
{
AllTimeHighPerformance ath = new AllTimeHighPerformance(oneEntryIndex);

assertApproximatelyEquals(OptionalDouble.of(0d), ath.getDistance());
assertEquals(Optional.of(LocalDate.of(2022, 11, 10)), ath.getDate());
}

@Test
public void testATHAtTheEndIndex()
{
AllTimeHighPerformance ath = new AllTimeHighPerformance(athAtTheEndIndex);

assertApproximatelyEquals(OptionalDouble.of(0d), ath.getDistance());
assertEquals(Optional.of(LocalDate.of(2022, 11, 11)), ath.getDate());
}

@Test
public void testATHAtTheBeginningIndex()
{
AllTimeHighPerformance ath = new AllTimeHighPerformance(athAtTheBeginnigIndex);

assertApproximatelyEquals(OptionalDouble.of(-.15d), ath.getDistance());
assertEquals(Optional.of(LocalDate.of(2022, 11, 2)), ath.getDate());
}

@Test
public void testATHInTheMiddleIndex()
{
AllTimeHighPerformance ath = new AllTimeHighPerformance(athInTheMiddle);

assertApproximatelyEquals(OptionalDouble.of(-.5d), ath.getDistance());
assertEquals(Optional.of(LocalDate.of(2022, 11, 6)), ath.getDate());
}

@Test
public void testBankruptcyIndex()
{
AllTimeHighPerformance ath = new AllTimeHighPerformance(bankruptcy);

assertApproximatelyEquals(OptionalDouble.of(-1d), ath.getDistance());
assertEquals(Optional.of(LocalDate.of(2022, 11, 4)), ath.getDate());
}

@Test
public void testWorseThanBankruptcyIndex()
{
AllTimeHighPerformance ath = new AllTimeHighPerformance(worseThanBankruptcy);

assertApproximatelyEquals(OptionalDouble.of(-1.5d), ath.getDistance());
assertEquals(Optional.of(LocalDate.of(2022, 11, 2)), ath.getDate());
}

private static void assertApproximatelyEquals(OptionalDouble double1, OptionalDouble double2)
{
assertTrue(double1.isPresent() == double2.isPresent());
assertEquals(double1.getAsDouble(), double2.getAsDouble(), 0.00001d);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import name.abuchen.portfolio.util.Interval;

@SuppressWarnings("nls")
public class AllTimeHighTest
public class AllTimeHighPriceTest
{
private Security securityOnePrice;
private Security securityTenPrices;
Expand Down Expand Up @@ -57,7 +57,7 @@ private void buildPrices(Security security, Long[] prices)
public void testSecurityIsNull()
{
Interval interval = Interval.of(securityOnePrice.getPrices().get(0).getDate(), LocalDate.now());
AllTimeHigh ath = new AllTimeHigh(null, interval);
AllTimeHighPrice ath = new AllTimeHighPrice(null, interval);

assertNull(ath.getDistance());
assertNull(ath.getValue());
Expand All @@ -68,7 +68,7 @@ public void testSecurityIsNull()
public void testSecurityHasOnlyOnePrice()
{
Interval interval = Interval.of(securityOnePrice.getPrices().get(0).getDate(), LocalDate.now());
AllTimeHigh ath = new AllTimeHigh(this.securityOnePrice, interval);
AllTimeHighPrice ath = new AllTimeHighPrice(this.securityOnePrice, interval);

assertNull(ath.getValue());
assertNull(ath.getDistance());
Expand All @@ -79,7 +79,7 @@ public void testSecurityHasOnlyOnePrice()
public void testAthValueFor10Prices()
{
Interval interval = Interval.of(securityTenPrices.getPrices().get(0).getDate(), LocalDate.now());
AllTimeHigh ath = new AllTimeHigh(this.securityTenPrices, interval);
AllTimeHighPrice ath = new AllTimeHighPrice(this.securityTenPrices, interval);

assertEquals(Long.valueOf(2133L), ath.getValue());
assertEquals(-0.47444, ath.getDistance(), 0.00001);
Expand All @@ -90,7 +90,7 @@ public void testAthValueFor10Prices()
public void testAthValueFor7PricesWithDateGaps()
{
Interval interval = Interval.of(this.securitySevenPricesWithGaps.getPrices().get(0).getDate(), LocalDate.now());
AllTimeHigh ath = new AllTimeHigh(this.securitySevenPricesWithGaps, interval);
AllTimeHighPrice ath = new AllTimeHighPrice(this.securitySevenPricesWithGaps, interval);

assertEquals(Long.valueOf(2062L), ath.getValue());
assertEquals(-0.24005, ath.getDistance(), 0.00001);
Expand All @@ -101,7 +101,7 @@ public void testAthValueFor7PricesWithDateGaps()
public void testAthValueFor10PricesForLargerInterval()
{
Interval interval = Interval.of(LocalDate.of(2021, 1, 1), LocalDate.of(2023, 12, 31));
AllTimeHigh ath = new AllTimeHigh(this.securityTenPrices, interval);
AllTimeHighPrice ath = new AllTimeHighPrice(this.securityTenPrices, interval);

assertEquals(Long.valueOf(2133L), ath.getValue());
assertEquals(-0.47444, ath.getDistance(), 0.00001);
Expand All @@ -112,7 +112,7 @@ public void testAthValueFor10PricesForLargerInterval()
public void testAthValueFor10PricesForLast2DaysInterval()
{
Interval interval = Interval.of(LocalDate.of(2022, 6, 10), LocalDate.of(2022, 6, 11));
AllTimeHigh ath = new AllTimeHigh(this.securityTenPrices, interval);
AllTimeHighPrice ath = new AllTimeHighPrice(this.securityTenPrices, interval);

assertEquals(Long.valueOf(1121L), ath.getValue());
assertEquals(0, ath.getDistance(), 0.00001);
Expand All @@ -123,7 +123,7 @@ public void testAthValueFor10PricesForLast2DaysInterval()
public void testAthValueFor10PricesFor5DaysInterval()
{
Interval interval = Interval.of(LocalDate.of(2022, 6, 7), LocalDate.of(2022, 6, 10));
AllTimeHigh ath = new AllTimeHigh(this.securityTenPrices, interval);
AllTimeHighPrice ath = new AllTimeHighPrice(this.securityTenPrices, interval);

assertEquals(Long.valueOf(1500L), ath.getValue());
assertEquals(-0.4, ath.getDistance(), 0.00001);
Expand All @@ -134,7 +134,7 @@ public void testAthValueFor10PricesFor5DaysInterval()
public void testAthValueFor10PricesForFirst3DaysInterval()
{
Interval interval = Interval.of(LocalDate.of(2022, 1, 31), LocalDate.of(2022, 6, 4));
AllTimeHigh ath = new AllTimeHigh(this.securityTenPrices, interval);
AllTimeHighPrice ath = new AllTimeHighPrice(this.securityTenPrices, interval);

assertEquals(Long.valueOf(699L), ath.getValue());
assertEquals(0, ath.getDistance(), 0.00001);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,8 @@ public class Messages extends NLS
public static String LabelDefaultReferenceAccountName;
public static String LabelDelta;
public static String LabelDescription;
public static String LabelDistanceFromATHPrice;
public static String LabelDistanceFromATHPerformance;
public static String LabelDoImport;
public static String LabelDoNotImport;
public static String LabelEarnings;
Expand Down Expand Up @@ -1089,7 +1091,6 @@ public class Messages extends NLS
public static String SecurityFilterSharesHeldNotZero;
public static String SecurityListFilter;
public static String SecurityListFilterDateReached;
public static String SecurityListFilterDistanceFromAth;
public static String SecurityListFilterHideInactive;
public static String SecurityListFilterLimitPriceExceeded;
public static String SecurityListFilterOnlyExchangeRates;
Expand Down Expand Up @@ -1186,7 +1187,8 @@ public class Messages extends NLS
public static String TabAccountBalanceChart;
public static String TabTransactions;
public static String TitlePasswordDialog;
public static String TooltipAllTimeHigh;
public static String TooltipAllTimeHighPrice;
public static String TooltipAllTimeHighPerformance;
public static String TooltipAverageHoldingPeriod;
public static String TooltipDateOfExchangeRate;
public static String TooltipHintPressAlt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -594,9 +594,9 @@ ColumnQuoteChange_Description = Percentage change of the quote within the given

ColumnQuoteChange_Option = \u0394 {0}

ColumnQuoteDistanceFromAthPercent = Distance from ATH
ColumnQuoteDistanceFromAthPercent = Distance from ATH (price)

ColumnQuoteDistanceFromAthPercent_Description = Percentage distance from the last All Time High (ATH) in the chosen period.
ColumnQuoteDistanceFromAthPercent_Description = Percentage distance from the last price-based All Time High (ATH) in the chosen period.

ColumnQuoteDistanceFromAthPercent_Option = \u0394 ATH {0} %

Expand Down Expand Up @@ -1202,6 +1202,10 @@ LabelDelta = Delta (for reporting period)

LabelDescription = Description

LabelDistanceFromATHPrice = Security: Distance from ATH (Price)

LabelDistanceFromATHPerformance = Distance from ATH (Performance)

LabelDividendPerShare = dividend payment per shares

LabelDividends = Dividends
Expand Down Expand Up @@ -2206,8 +2210,6 @@ SecurityListFilter = Filter securities

SecurityListFilterDateReached = Securities: Date reached

SecurityListFilterDistanceFromAth = Security: Distance from ATH

SecurityListFilterHideInactive = Only active instruments

SecurityListFilterLimitPriceExceeded = Securities: Limit price exceeded
Expand Down Expand Up @@ -2394,7 +2396,9 @@ TabTransactions = Transactions

TitlePasswordDialog = Assign password

TooltipAllTimeHigh = All-Time High (ATH) in the last {0} days: {2} ({1})\nCurrent security price: {3} ({4})
TooltipAllTimeHighPrice = Price-based All-Time High (ATH) in the last {0} days: {3} ({1} days ago, on {2})\nCurrent security price: {4} ({5})

TooltipAllTimeHighPerformance = Performance-based All-Time High (ATH) in the last {0} days: {1} days ago, on {2}

TooltipAverageHoldingPeriod = The average holding period is calculated as follows:\n\n* All trades are included that were in the portfolio at some point within the selected reporting period.\n\n* The holding period of each security results from the time of purchase and sale. Immediate sales are assumed for positions currently held.\n\n* The position weight is calculated from the purchase price of the position relative to the total number of all purchase prices.\n\n* Result "average holding period of all positions" is the sum of the products "holding period * percentage position weight" across all positions.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -584,9 +584,9 @@ ColumnQuoteChange_Description = Procentu\u00E1ln\u00ED zm\u011Bna kotace v dan\u

ColumnQuoteChange_Option = \u0394 {0}

ColumnQuoteDistanceFromAthPercent = Vzd\u00E1lenost od ATH
ColumnQuoteDistanceFromAthPercent = Vzd\u00E1lenost od ATH (cena)

ColumnQuoteDistanceFromAthPercent_Description = Procentu\u00E1ln\u00ED vzd\u00E1lenost od posledn\u00EDho All Time High (ATH) ve zvolen\u00E9m obdob\u00ED.
ColumnQuoteDistanceFromAthPercent_Description = Procentu\u00E1ln\u00ED vzd\u00E1lenost od posledn\u00EDho cenov\u00E9ho maxima (ATH) ve zvolen\u00E9m obdob\u00ED.

ColumnQuoteDistanceFromAthPercent_Option = \u0394 ATH {0} %

Expand Down Expand Up @@ -1188,6 +1188,10 @@ LabelDelta = Delta (za sledovan\u00E9 obdob\u00ED)

LabelDescription = Popis

LabelDistanceFromATHPrice = Zabezpe\u010Den\u00ED: Vzd\u00E1lenost od ATH (Cena)

LabelDistanceFromATHPerformance = Vzd\u00E1lenost od ATH (v\u00FDkon)

LabelDividendPerShare = v\u00FDplata dividendy na akcii

LabelDividends = Dividendy
Expand Down Expand Up @@ -2190,8 +2194,6 @@ SecurityListFilter = Filtrovat cenn\u00E9 pap\u00EDry

SecurityListFilterDateReached = Cenn\u00E9 pap\u00EDry: dosa\u017Een\u00FD datum

SecurityListFilterDistanceFromAth = Zabezpe\u010Den\u00ED: Vzd\u00E1lenost od ATH

SecurityListFilterHideInactive = Pouze aktivn\u00ED cenn\u00E9 pap\u00EDry

SecurityListFilterLimitPriceExceeded = P\u0159ekro\u010Den\u00ED limitn\u00ED ceny
Expand Down Expand Up @@ -2378,7 +2380,9 @@ TabTransactions = Transakce

TitlePasswordDialog = P\u0159i\u0159adit heslo

TooltipAllTimeHigh = All-Time High (ATH) za posledn\u00EDch {0} dn\u00ED: {2} ({1})\nAktu\u00E1ln\u00ED cena cenn\u00E9ho pap\u00EDru: {3} ({4})
TooltipAllTimeHighPrice = Cenov\u00E9 maximum (ATH) za posledn\u00EDch {0} dn\u00ED: {3} (p\u0159ed {1} dny, dne {2})\nAktu\u00E1ln\u00ED cena cenn\u00E9ho pap\u00EDru: {4} ({5})

TooltipAllTimeHighPerformance = V\u00FDkonnostn\u00ED maximum (ATH) za posledn\u00EDch {0} dn\u00ED: p\u0159ed {1} dny, dne {2}.

TooltipAverageHoldingPeriod = Pr\u016Fm\u011Brn\u00E1 doba dr\u017Een\u00ED se vypo\u010D\u00EDt\u00E1 takto:\n\n* Zahrnuty jsou v\u0161echny obchody, kter\u00E9 byly v portfoliu v ur\u010Dit\u00E9m okam\u017Eiku v r\u00E1mci zvolen\u00E9ho vykazovan\u00E9ho obdob\u00ED.\n\n* Doba dr\u017Eby ka\u017Ed\u00E9ho cenn\u00E9ho pap\u00EDru vypl\u00FDv\u00E1 z okam\u017Eiku n\u00E1kupu a prodeje. U aktu\u00E1ln\u011B dr\u017Een\u00FDch pozic se p\u0159edpokl\u00E1d\u00E1 okam\u017Eit\u00FD prodej.\n\n* V\u00E1ha pozice se vypo\u010D\u00EDt\u00E1 z n\u00E1kupn\u00ED ceny pozice vzhledem k celkov\u00E9mu po\u010Dtu v\u0161ech n\u00E1kupn\u00EDch cen.\n\n* V\u00FDsledek "pr\u016Fm\u011Brn\u00E1 doba dr\u017Een\u00ED v\u0161ech pozic" je sou\u010Dtem sou\u010Din\u016F "doba dr\u017Een\u00ED * procentn\u00ED v\u00E1ha pozice" ve v\u0161ech pozic\u00EDch.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,9 +581,9 @@ ColumnQuoteChange_Description = Prozentuale Kurs\u00E4nderung bezogen auf den Ze

ColumnQuoteChange_Option = \u0394 {0}

ColumnQuoteDistanceFromAthPercent = Abstand vom ATH
ColumnQuoteDistanceFromAthPercent = Abstand vom ATH (Preis)

ColumnQuoteDistanceFromAthPercent_Description = Prozentualer Abstand zum letzten All Time High (ATH) in dem gew\u00E4hlten Zeitraum.
ColumnQuoteDistanceFromAthPercent_Description = Prozentualer Abstand zum letzten preis-basierten All Time High (ATH) in dem gew\u00E4hlten Zeitraum.

ColumnQuoteDistanceFromAthPercent_Option = \u0394 ATH {0} %

Expand Down Expand Up @@ -1187,6 +1187,10 @@ LabelDelta = Delta (im Berichtszeitraum)

LabelDescription = Beschreibung

LabelDistanceFromATHPrice = Wertpapier: Abstand vom ATH (Preis)

LabelDistanceFromATHPerformance = Abstand vom ATH (Performance)

LabelDividendPerShare = Dividende pro St\u00FCck

LabelDividends = Dividenden
Expand Down Expand Up @@ -2189,8 +2193,6 @@ SecurityListFilter = Wertpapiere filtern

SecurityListFilterDateReached = Wertpapiere: Termin erreicht

SecurityListFilterDistanceFromAth = Wertpapier: Abstand vom ATH

SecurityListFilterHideInactive = Nur aktive Instrumente

SecurityListFilterLimitPriceExceeded = Wertpapiere: Limit \u00FCberschritten
Expand Down Expand Up @@ -2377,7 +2379,9 @@ TabTransactions = Ums\u00E4tze

TitlePasswordDialog = Passwort vergeben

TooltipAllTimeHigh = All-Time-High (ATH) in den letzten {0} Tagen: {2} ({1})\nAktueller Kurs: {3} ({4})
TooltipAllTimeHighPrice = Preis-basiertes All-Time-High (ATH) in den letzten {0} Tagen: {3} (vor {1} Tagen, am {2})\nAktueller Kurs: {4} ({5})

TooltipAllTimeHighPerformance = Performance-basiertes All-Time-High (ATH) in den letzten {0} Tagen: Vor {1} Tagen, am {2}

TooltipAverageHoldingPeriod = Die mittlere Haltedauer errechnet sich wie folgt:\n\n* Es werden alle Trades einbezogen die irgendwann innerhalb des gew\u00E4hlten Berichtszeitraums im Depot waren.\n\n* Die Haltedauer jedes Wertpapiers ergibt sich aus Kauf- und Verkaufszeitpunkt. F\u00FCr aktuell gehaltene Positionen wird ein sofortiger Verkauf unterstellt.\n\n* Das Positionsgewicht errechnet sich aus Kaufpreis der Position relativ zu der Gesamtsumme aller Kaufpreise.\n\n* Ergebnis "mittlere Haltedauer aller Positionen" ergibt sich als Summe der Produkte "Haltedauer * prozentuales Positionsgewicht" \u00FCber alle Positionen.

Expand Down
Loading

0 comments on commit 0308f48

Please sign in to comment.