View Javadoc
1   /*
2    * MoneyWise: Finance Application
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets;
18  
19  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20  import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
21  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
22  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusDecimal;
23  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
24  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
25  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
26  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusUnits;
27  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
28  import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
29  import io.github.tonywasher.joceanus.metis.data.MetisDataFieldValue;
30  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataList;
31  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
32  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldTableItem;
33  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
34  import io.github.tonywasher.joceanus.metis.list.MetisListIndexed;
35  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.base.MoneyWiseXAnalysisEvent;
36  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.base.MoneyWiseXAnalysisHistory;
37  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisInterfaces.MoneyWiseXAnalysisBucketPriced;
38  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisInterfaces.MoneyWiseXAnalysisCursor;
39  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.values.MoneyWiseXAnalysisSecurityAttr;
40  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.values.MoneyWiseXAnalysisSecurityValues;
41  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicResource;
42  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio;
43  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
44  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding;
45  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency;
46  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseSecurityClass;
47  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseSecurityType;
48  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseStaticDataType;
49  import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseDataException;
50  
51  import java.util.Currency;
52  import java.util.Iterator;
53  import java.util.List;
54  
55  /**
56   * The Security Bucket class.
57   */
58  public final class MoneyWiseXAnalysisSecurityBucket
59          implements MetisFieldTableItem, MoneyWiseXAnalysisBucketPriced {
60      /**
61       * Local Report fields.
62       */
63      private static final MetisFieldSet<MoneyWiseXAnalysisSecurityBucket> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseXAnalysisSecurityBucket.class);
64  
65      /*
66       * Declare Fields.
67       */
68      static {
69          FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.ANALYSIS_NAME, MoneyWiseXAnalysisSecurityBucket::getAnalysis);
70          FIELD_DEFS.declareLocalField(MoneyWiseBasicResource.ASSETTYPE_SECURITYHOLDING, MoneyWiseXAnalysisSecurityBucket::getSecurityHolding);
71          FIELD_DEFS.declareLocalField(MoneyWiseStaticDataType.SECURITYTYPE, MoneyWiseXAnalysisSecurityBucket::getSecurityType);
72          FIELD_DEFS.declareLocalField(MoneyWiseStaticDataType.CURRENCY, MoneyWiseXAnalysisSecurityBucket::getCurrency);
73          FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.BUCKET_BASEVALUES, MoneyWiseXAnalysisSecurityBucket::getBaseValues);
74          FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.BUCKET_HISTORY, MoneyWiseXAnalysisSecurityBucket::getHistoryMap);
75          FIELD_DEFS.declareLocalFieldsForEnum(MoneyWiseXAnalysisSecurityAttr.class, MoneyWiseXAnalysisSecurityBucket::getAttributeValue);
76      }
77  
78      /**
79       * The analysis.
80       */
81      private final MoneyWiseXAnalysis theAnalysis;
82  
83      /**
84       * The security holding.
85       */
86      private final MoneyWiseSecurityHolding theHolding;
87  
88      /**
89       * The security.
90       */
91      private final MoneyWiseSecurity theSecurity;
92  
93      /**
94       * The portfolio.
95       */
96      private final MoneyWisePortfolio thePortfolio;
97  
98      /**
99       * The currency.
100      */
101     private final MoneyWiseCurrency theCurrency;
102 
103     /**
104      * Is this a foreign currency?
105      */
106     private final boolean isForeignCurrency;
107 
108     /**
109      * Is this a stock option?
110      */
111     private final boolean isStockOption;
112 
113     /**
114      * The security type.
115      */
116     private final MoneyWiseSecurityType theCategory;
117 
118     /**
119      * Values.
120      */
121     private final MoneyWiseXAnalysisSecurityValues theValues;
122 
123     /**
124      * The base values.
125      */
126     private final MoneyWiseXAnalysisSecurityValues theBaseValues;
127 
128     /**
129      * History Map.
130      */
131     private final MoneyWiseXAnalysisHistory<MoneyWiseXAnalysisSecurityValues, MoneyWiseXAnalysisSecurityAttr> theHistory;
132 
133     /**
134      * Constructor.
135      *
136      * @param pAnalysis the analysis
137      * @param pHolding  the security holding
138      */
139     MoneyWiseXAnalysisSecurityBucket(final MoneyWiseXAnalysis pAnalysis,
140                                      final MoneyWiseSecurityHolding pHolding) {
141         /* Store the details */
142         theHolding = pHolding;
143         theCurrency = pHolding.getAssetCurrency();
144         theSecurity = pHolding.getSecurity();
145         thePortfolio = pHolding.getPortfolio();
146         theAnalysis = pAnalysis;
147 
148         /* Obtain category */
149         theCategory = theSecurity.getCategory();
150 
151         /* Determine currency */
152         final MoneyWiseCurrency myReportingCurrency = pAnalysis.getCurrency();
153         final MoneyWiseCurrency myHoldingCurrency = pHolding.getAssetCurrency();
154 
155         /* Determine whether we are a foreign currency */
156         isForeignCurrency = !MetisDataDifference.isEqual(myReportingCurrency, myHoldingCurrency);
157         final Currency myCurrency = MoneyWiseXAnalysisAccountBucket.deriveCurrency(myHoldingCurrency);
158         final Currency myRepCurrency = MoneyWiseXAnalysisAccountBucket.deriveCurrency(myReportingCurrency);
159 
160         /* Note stockOption */
161         isStockOption = theSecurity.getUnderlyingStock() != null;
162 
163         /* Create the history map */
164         final MoneyWiseXAnalysisSecurityValues myValues = new MoneyWiseXAnalysisSecurityValues(myCurrency, myRepCurrency);
165         theHistory = new MoneyWiseXAnalysisHistory<>(myValues);
166 
167         /* Access the key value maps */
168         theValues = theHistory.getValues();
169         theBaseValues = theHistory.getBaseValues();
170 
171         /* Register for price Updates */
172         final MoneyWiseXAnalysisCursor myCursor = theAnalysis.getCursor();
173         myCursor.registerForPriceUpdates(this);
174 
175         /* Store the security price */
176         recordSecurityPrice();
177 
178         /* If this is a foreign currency account */
179         if (isForeignCurrency) {
180             /* Register for xchangeRate Updates */
181             myCursor.registerForXchgRateUpdates(this);
182 
183             /* Record the exchangeRate and copy to base */
184             recordExchangeRate();
185             theBaseValues.setValue(MoneyWiseXAnalysisSecurityAttr.EXCHANGERATE, getValue(MoneyWiseXAnalysisSecurityAttr.EXCHANGERATE));
186 
187             /* Create a new reportedValuation */
188             final OceanusMoney myReported = new OceanusMoney(theAnalysis.getCurrency().getCurrency());
189             theValues.setValue(MoneyWiseXAnalysisSecurityAttr.VALUATION, myReported);
190             theBaseValues.setValue(MoneyWiseXAnalysisSecurityAttr.VALUATION, myReported);
191         }
192     }
193 
194     /**
195      * Constructor.
196      *
197      * @param pAnalysis the analysis
198      * @param pBase     the underlying bucket
199      * @param pDate     the date for the bucket
200      */
201     private MoneyWiseXAnalysisSecurityBucket(final MoneyWiseXAnalysis pAnalysis,
202                                              final MoneyWiseXAnalysisSecurityBucket pBase,
203                                              final OceanusDate pDate) {
204         /* Copy details from base */
205         theHolding = pBase.getSecurityHolding();
206         theCurrency = pBase.getCurrency();
207         theSecurity = pBase.getSecurity();
208         thePortfolio = pBase.getPortfolio();
209         theCategory = pBase.getSecurityType();
210         theAnalysis = pAnalysis;
211         isForeignCurrency = pBase.isForeignCurrency();
212         isStockOption = pBase.isStockOption();
213 
214         /* Access the relevant history */
215         theHistory = new MoneyWiseXAnalysisHistory<>(pBase.getHistoryMap(), pDate);
216 
217         /* Access the key value maps */
218         theValues = theHistory.getValues();
219         theBaseValues = theHistory.getBaseValues();
220     }
221 
222     /**
223      * Constructor.
224      *
225      * @param pAnalysis the analysis
226      * @param pBase     the underlying bucket
227      * @param pRange    the range for the bucket
228      */
229     private MoneyWiseXAnalysisSecurityBucket(final MoneyWiseXAnalysis pAnalysis,
230                                              final MoneyWiseXAnalysisSecurityBucket pBase,
231                                              final OceanusDateRange pRange) {
232         /* Copy details from base */
233         theHolding = pBase.getSecurityHolding();
234         theCurrency = pBase.getCurrency();
235         theSecurity = pBase.getSecurity();
236         thePortfolio = pBase.getPortfolio();
237         theCategory = pBase.getSecurityType();
238         theAnalysis = pAnalysis;
239         isForeignCurrency = pBase.isForeignCurrency();
240         isStockOption = pBase.isStockOption();
241 
242         /* Access the relevant history */
243         theHistory = new MoneyWiseXAnalysisHistory<>(pBase.getHistoryMap(), pRange);
244 
245         /* Access the key value maps */
246         theValues = theHistory.getValues();
247         theBaseValues = theHistory.getBaseValues();
248     }
249 
250     @Override
251     public MetisFieldSet<MoneyWiseXAnalysisSecurityBucket> getDataFieldSet() {
252         return FIELD_DEFS;
253     }
254 
255     @Override
256     public String formatObject(final OceanusDataFormatter pFormatter) {
257         return toString();
258     }
259 
260     @Override
261     public Long getBucketId() {
262         return theHolding.getExternalId();
263     }
264 
265     @Override
266     public String toString() {
267         return getDecoratedName() + " " + theValues;
268     }
269 
270     /**
271      * Obtain the name.
272      *
273      * @return the name
274      */
275     public String getSecurityName() {
276         return theSecurity.getName();
277     }
278 
279     /**
280      * Obtain the decorated name.
281      *
282      * @return the decorated name
283      */
284     public String getDecoratedName() {
285         return theHolding.getName();
286     }
287 
288     /**
289      * Obtain the holding.
290      *
291      * @return the holding
292      */
293     public MoneyWiseSecurityHolding getSecurityHolding() {
294         return theHolding;
295     }
296 
297     @Override
298     public MoneyWiseSecurity getSecurity() {
299         return theSecurity;
300     }
301 
302     /**
303      * Obtain the portfolio.
304      *
305      * @return the portfolio
306      */
307     public MoneyWisePortfolio getPortfolio() {
308         return thePortfolio;
309     }
310 
311     @Override
312     public MoneyWiseCurrency getCurrency() {
313         return theCurrency;
314     }
315 
316     /**
317      * Is this a foreign currency?
318      *
319      * @return true/false
320      */
321     public boolean isForeignCurrency() {
322         return isForeignCurrency;
323     }
324 
325     @Override
326     public boolean isStockOption() {
327         return isStockOption;
328     }
329 
330     @Override
331     public Integer getIndexedId() {
332         return theSecurity.getIndexedId();
333     }
334 
335     /**
336      * Obtain the security type.
337      *
338      * @return the security type
339      */
340     public MoneyWiseSecurityType getSecurityType() {
341         return theCategory;
342     }
343 
344     /**
345      * Is this bucket idle?
346      *
347      * @return true/false
348      */
349     public boolean isIdle() {
350         return theHistory.isIdle();
351     }
352 
353     /**
354      * Obtain the analysis.
355      *
356      * @return the analysis
357      */
358     MoneyWiseXAnalysis getAnalysis() {
359         return theAnalysis;
360     }
361 
362     /**
363      * Obtain date range.
364      *
365      * @return the range
366      */
367     public OceanusDateRange getDateRange() {
368         return theAnalysis.getDateRange();
369     }
370 
371     /**
372      * Obtain the value map.
373      *
374      * @return the value map
375      */
376     public MoneyWiseXAnalysisSecurityValues getValues() {
377         return theValues;
378     }
379 
380     /**
381      * Obtain the base value map.
382      *
383      * @return the base value map
384      */
385     public MoneyWiseXAnalysisSecurityValues getBaseValues() {
386         return theBaseValues;
387     }
388 
389     /**
390      * Obtain values for event.
391      *
392      * @param pEvent the event
393      * @return the values (or null)
394      */
395     public MoneyWiseXAnalysisSecurityValues getValuesForEvent(final MoneyWiseXAnalysisEvent pEvent) {
396         return theHistory.getValuesForEvent(pEvent);
397     }
398 
399     /**
400      * Obtain previous values for event.
401      *
402      * @param pEvent the event
403      * @return the values (or null)
404      */
405     public MoneyWiseXAnalysisSecurityValues getPreviousValuesForEvent(final MoneyWiseXAnalysisEvent pEvent) {
406         return theHistory.getPreviousValuesForEvent(pEvent);
407     }
408 
409     /**
410      * Obtain delta for event.
411      *
412      * @param pEvent the event
413      * @param pAttr  the attribute
414      * @return the delta (or null)
415      */
416     public OceanusDecimal getDeltaForEvent(final MoneyWiseXAnalysisEvent pEvent,
417                                            final MoneyWiseXAnalysisSecurityAttr pAttr) {
418         /* Obtain delta for transaction */
419         return theHistory.getDeltaValue(pEvent, pAttr);
420     }
421 
422     /**
423      * Obtain money delta for event.
424      *
425      * @param pEvent the event
426      * @param pAttr  the attribute
427      * @return the delta (or null)
428      */
429     public OceanusMoney getMoneyDeltaForEvent(final MoneyWiseXAnalysisEvent pEvent,
430                                               final MoneyWiseXAnalysisSecurityAttr pAttr) {
431         /* Obtain delta for transaction */
432         return theHistory.getDeltaMoneyValue(pEvent, pAttr);
433     }
434 
435     /**
436      * Obtain units delta for event.
437      *
438      * @param pEvent the event
439      * @param pAttr  the attribute
440      * @return the delta (or null)
441      */
442     public OceanusUnits getUnitsDeltaForEvent(final MoneyWiseXAnalysisEvent pEvent,
443                                               final MoneyWiseXAnalysisSecurityAttr pAttr) {
444         /* Obtain delta for transaction */
445         return theHistory.getDeltaUnitsValue(pEvent, pAttr);
446     }
447 
448     /**
449      * Obtain the history map.
450      *
451      * @return the history map
452      */
453     private MoneyWiseXAnalysisHistory<MoneyWiseXAnalysisSecurityValues, MoneyWiseXAnalysisSecurityAttr> getHistoryMap() {
454         return theHistory;
455     }
456 
457     /**
458      * Set Attribute.
459      *
460      * @param pAttr  the attribute
461      * @param pValue the value of the attribute
462      */
463     public void setValue(final MoneyWiseXAnalysisSecurityAttr pAttr,
464                          final Object pValue) {
465         /* Set the value into the list */
466         theValues.setValue(pAttr, pValue);
467     }
468 
469     /**
470      * Get an attribute value.
471      *
472      * @param pAttr the attribute
473      * @return the value to set
474      */
475     private Object getAttributeValue(final MoneyWiseXAnalysisSecurityAttr pAttr) {
476         /* Access value of object */
477         final Object myValue = getValue(pAttr);
478 
479         /* Return the value */
480         return myValue != null
481                 ? myValue
482                 : MetisDataFieldValue.SKIP;
483     }
484 
485     /**
486      * Obtain an attribute value.
487      *
488      * @param pAttr the attribute
489      * @return the value of the attribute or null
490      */
491     private Object getValue(final MoneyWiseXAnalysisSecurityAttr pAttr) {
492         /* Obtain the value */
493         return theValues.getValue(pAttr);
494     }
495 
496     /**
497      * Adjust Units.
498      *
499      * @param pDelta the delta
500      */
501     public void adjustUnits(final OceanusUnits pDelta) {
502         OceanusUnits myValue = theValues.getUnitsValue(MoneyWiseXAnalysisSecurityAttr.UNITS);
503         myValue = new OceanusUnits(myValue);
504         myValue.addUnits(pDelta);
505         setValue(MoneyWiseXAnalysisSecurityAttr.UNITS, myValue);
506     }
507 
508     /**
509      * Adjust ResidualCost.
510      *
511      * @param pDelta the delta
512      */
513     public void adjustResidualCost(final OceanusMoney pDelta) {
514         OceanusMoney myValue = theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.RESIDUALCOST);
515         myValue = new OceanusMoney(myValue);
516         myValue.addAmount(pDelta);
517         setValue(MoneyWiseXAnalysisSecurityAttr.RESIDUALCOST, myValue);
518     }
519 
520     /**
521      * Adjust RealisedGains.
522      *
523      * @param pDelta the delta
524      */
525     public void adjustRealisedGains(final OceanusMoney pDelta) {
526         OceanusMoney myValue = theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.REALISEDGAINS);
527         myValue = new OceanusMoney(myValue);
528         myValue.addAmount(pDelta);
529         setValue(MoneyWiseXAnalysisSecurityAttr.REALISEDGAINS, myValue);
530     }
531 
532     /**
533      * Adjust Dividends.
534      *
535      * @param pDelta the delta
536      */
537     public void adjustDividend(final OceanusMoney pDelta) {
538         OceanusMoney myValue = theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.DIVIDEND);
539         myValue = new OceanusMoney(myValue);
540         myValue.addAmount(pDelta);
541         setValue(MoneyWiseXAnalysisSecurityAttr.DIVIDEND, myValue);
542     }
543 
544     /**
545      * Adjust Funded.
546      *
547      * @param pDelta the delta
548      */
549     public void adjustFunded(final OceanusMoney pDelta) {
550         OceanusMoney myValue = theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.FUNDED);
551         myValue = new OceanusMoney(myValue);
552         myValue.addAmount(pDelta);
553         setValue(MoneyWiseXAnalysisSecurityAttr.FUNDED, myValue);
554     }
555 
556     /**
557      * Ensure that start date is set.
558      *
559      * @param pDate the startDate
560      */
561     public void ensureStartDate(final OceanusDate pDate) {
562         if (theValues.getValue(MoneyWiseXAnalysisSecurityAttr.STARTDATE) == null) {
563             setValue(MoneyWiseXAnalysisSecurityAttr.STARTDATE, pDate);
564         }
565     }
566 
567     @Override
568     public void calculateUnrealisedGains() {
569         /* Unrealised gains is VALUATION - RESIDUALCOST */
570         OceanusMoney myValue = theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
571         myValue = new OceanusMoney(myValue);
572         myValue.subtractAmount(theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.RESIDUALCOST));
573         setValue(MoneyWiseXAnalysisSecurityAttr.UNREALISEDGAINS, myValue);
574     }
575 
576     @Override
577     public void recordSecurityPrice() {
578         /* Access the current price */
579         final MoneyWiseXAnalysisCursor myCursor = theAnalysis.getCursor();
580         OceanusPrice myPrice = myCursor.getCurrentPrice(getSecurity());
581 
582         /* If this is a stockOption */
583         if (isStockOption) {
584             /* Price is any positive difference between stockPrice and optioonPrice */
585             myPrice = new OceanusPrice(myPrice);
586             myPrice.subtractPrice(getSecurity().getOptionPrice());
587             if (!myPrice.isPositive()) {
588                 myPrice.setZero();
589             }
590         }
591 
592         /* If we are funded */
593         final OceanusMoney myFunded = new OceanusMoney(theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.FUNDED));
594         if (myFunded.isNonZero()) {
595             /* Set funded to zero */
596             theValues.setZeroMoney(MoneyWiseXAnalysisSecurityAttr.FUNDED);
597 
598             /* If we have zero units, honour autoUnits */
599             final MoneyWiseSecurityClass mySecClass = getSecurity().getCategoryClass();
600             final OceanusUnits myUnits = theValues.getUnitsValue(MoneyWiseXAnalysisSecurityAttr.UNITS);
601             if (myUnits.isZero() && mySecClass.isAutoUnits()) {
602                 final OceanusUnits myAutoUnits = OceanusUnits.getWholeUnits(mySecClass.getAutoUnits());
603                 theValues.setValue(MoneyWiseXAnalysisSecurityAttr.UNITS, myAutoUnits);
604             }
605         }
606 
607         /* Store the price */
608         theValues.setValue(MoneyWiseXAnalysisSecurityAttr.PRICE, myPrice);
609     }
610 
611     @Override
612     public void recordExchangeRate() {
613         final MoneyWiseXAnalysisCursor myCursor = theAnalysis.getCursor();
614         final OceanusRatio myRate = myCursor.getCurrentXchgRate(getSecurity().getAssetCurrency());
615         theValues.setValue(MoneyWiseXAnalysisSecurityAttr.EXCHANGERATE, myRate);
616     }
617 
618     @Override
619     public void registerEvent(final MoneyWiseXAnalysisEvent pEvent) {
620         /* Register the event in the history */
621         theHistory.registerEvent(pEvent, theValues);
622     }
623 
624     @Override
625     public void valueAsset() {
626         /* Access units and price */
627         final OceanusUnits myUnits = theValues.getUnitsValue(MoneyWiseXAnalysisSecurityAttr.UNITS);
628         final OceanusPrice myPrice = theValues.getPriceValue(MoneyWiseXAnalysisSecurityAttr.PRICE);
629 
630         /* Calculate the value */
631         final OceanusMoney myLocalValue = myUnits.valueAtPrice(myPrice);
632         setValue(MoneyWiseXAnalysisSecurityAttr.VALUE, myLocalValue);
633 
634         /* Adjust the valuation */
635         adjustValuation();
636     }
637 
638     @Override
639     public void adjustValuation() {
640         /* Calculate the value of the asset */
641         OceanusMoney myBalance = theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUE);
642 
643         /* If this is a foreign asset */
644         if (isForeignCurrency) {
645             /* Calculate the value in the local currency */
646             final OceanusRatio myRate = theValues.getRatioValue(MoneyWiseXAnalysisSecurityAttr.EXCHANGERATE);
647             myBalance = myBalance.convertCurrency(theAnalysis.getCurrency().getCurrency(), myRate);
648         }
649 
650         /* If we have a funded value */
651         final OceanusMoney myFunded = theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.FUNDED);
652         if (myFunded.isNonZero()) {
653             /* Add to valuation */
654             myBalance.addAmount(myFunded);
655         }
656 
657         /* Record the valuation */
658         theValues.setValue(MoneyWiseXAnalysisSecurityAttr.VALUATION, myBalance);
659     }
660 
661     @Override
662     public OceanusMoney getDeltaValuation() {
663         /* Determine the delta */
664         final OceanusMoney myDelta = new OceanusMoney(theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION));
665         myDelta.subtractAmount(theHistory.getLastValues().getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION));
666         return myDelta;
667     }
668 
669     /**
670      * Obtain the delta of unrealisedGains.
671      *
672      * @return the delta
673      */
674     public OceanusMoney getDeltaUnrealisedGains() {
675         /* Determine the delta */
676         final OceanusMoney myDelta = new OceanusMoney(theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.UNREALISEDGAINS));
677         myDelta.subtractAmount(theHistory.getLastValues().getMoneyValue(MoneyWiseXAnalysisSecurityAttr.UNREALISEDGAINS));
678         return myDelta;
679     }
680 
681     /**
682      * calculate the deltas for a priced asset.
683      */
684     private void calculateDeltas() {
685         /* Obtain a copy of the value */
686         OceanusMoney myValue = theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
687         myValue = new OceanusMoney(myValue);
688 
689         /* Subtract any base value */
690         myValue.subtractAmount(theBaseValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION));
691 
692         /* Set the delta */
693         setValue(MoneyWiseXAnalysisSecurityAttr.VALUEDELTA, myValue);
694     }
695 
696     /**
697      * Adjust to base.
698      */
699     void adjustToBase() {
700         /* Adjust to base values */
701         theValues.adjustToBaseValues(theBaseValues);
702         theBaseValues.resetBaseValues();
703     }
704 
705     /**
706      * Is the bucket active?
707      *
708      * @return true/false
709      */
710     public boolean isActive() {
711         return theValues.isActive();
712     }
713 
714     /**
715      * SecurityBucket list class.
716      */
717     public static final class MoneyWiseXAnalysisSecurityBucketList
718             implements MetisFieldItem, MetisDataList<MoneyWiseXAnalysisSecurityBucket> {
719         /**
720          * Local Report fields.
721          */
722         private static final MetisFieldSet<MoneyWiseXAnalysisSecurityBucketList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseXAnalysisSecurityBucketList.class);
723 
724         /*
725          * Declare Fields.
726          */
727         static {
728             FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.ANALYSIS_NAME, MoneyWiseXAnalysisSecurityBucketList::getAnalysis);
729         }
730 
731         /**
732          * The analysis.
733          */
734         private final MoneyWiseXAnalysis theAnalysis;
735 
736         /**
737          * The list.
738          */
739         private final MetisListIndexed<MoneyWiseXAnalysisSecurityBucket> theList;
740 
741         /**
742          * Construct a top-level List.
743          *
744          * @param pAnalysis the analysis
745          */
746         MoneyWiseXAnalysisSecurityBucketList(final MoneyWiseXAnalysis pAnalysis) {
747             theAnalysis = pAnalysis;
748             theList = new MetisListIndexed<>();
749             theList.setComparator((l, r) -> l.getSecurity().compareTo(r.getSecurity()));
750         }
751 
752         /**
753          * Construct a dated List.
754          *
755          * @param pAnalysis the analysis
756          * @param pBase     the base list
757          * @param pDate     the Date
758          */
759         MoneyWiseXAnalysisSecurityBucketList(final MoneyWiseXAnalysis pAnalysis,
760                                              final MoneyWiseXAnalysisSecurityBucketList pBase,
761                                              final OceanusDate pDate) {
762             /* Initialise class */
763             this(pAnalysis);
764 
765             /* Loop through the buckets */
766             final Iterator<MoneyWiseXAnalysisSecurityBucket> myIterator = pBase.iterator();
767             while (myIterator.hasNext()) {
768                 final MoneyWiseXAnalysisSecurityBucket myCurr = myIterator.next();
769 
770                 /* Access the bucket for this date */
771                 final MoneyWiseXAnalysisSecurityBucket myBucket = new MoneyWiseXAnalysisSecurityBucket(pAnalysis, myCurr, pDate);
772 
773                 /*
774                  * Ignore idle securities. Note that we must include securities that have been
775                  * closed in order to adjust Market Growth.
776                  */
777                 if (!myBucket.isIdle()) {
778                     /* Add to the list */
779                     theList.add(myBucket);
780                 }
781             }
782         }
783 
784         /**
785          * Construct a ranged List.
786          *
787          * @param pAnalysis the analysis
788          * @param pBase     the base list
789          * @param pRange    the Date Range
790          */
791         MoneyWiseXAnalysisSecurityBucketList(final MoneyWiseXAnalysis pAnalysis,
792                                              final MoneyWiseXAnalysisSecurityBucketList pBase,
793                                              final OceanusDateRange pRange) {
794             /* Initialise class */
795             this(pAnalysis);
796 
797             /* Loop through the buckets */
798             final Iterator<MoneyWiseXAnalysisSecurityBucket> myIterator = pBase.iterator();
799             while (myIterator.hasNext()) {
800                 final MoneyWiseXAnalysisSecurityBucket myCurr = myIterator.next();
801 
802                 /* Access the bucket for this range */
803                 final MoneyWiseXAnalysisSecurityBucket myBucket = new MoneyWiseXAnalysisSecurityBucket(pAnalysis, myCurr, pRange);
804 
805                 /* If the bucket is non-idle or active */
806                 if (myBucket.isActive() || !myBucket.isIdle()) {
807                     /* Adjust to base and add to the list */
808                     myBucket.adjustToBase();
809                     theList.add(myBucket);
810                 }
811             }
812         }
813 
814         @Override
815         public MetisFieldSet<MoneyWiseXAnalysisSecurityBucketList> getDataFieldSet() {
816             return FIELD_DEFS;
817         }
818 
819         @Override
820         public List<MoneyWiseXAnalysisSecurityBucket> getUnderlyingList() {
821             return theList.getUnderlyingList();
822         }
823 
824         @Override
825         public String formatObject(final OceanusDataFormatter pFormatter) {
826             return getDataFieldSet().getName();
827         }
828 
829         /**
830          * Obtain the analysis.
831          *
832          * @return the analysis
833          */
834         MoneyWiseXAnalysis getAnalysis() {
835             return theAnalysis;
836         }
837 
838         /**
839          * Obtain item by id.
840          *
841          * @param pId the id to lookup
842          * @return the item (or null if not present)
843          */
844         public MoneyWiseXAnalysisSecurityBucket findItemById(final Integer pId) {
845             /* Return results */
846             return theList.getItemById(pId);
847         }
848 
849         /**
850          * SortBuckets.
851          */
852         void sortBuckets() {
853             theList.sortList();
854         }
855 
856         /**
857          * Obtain the SecurityBucket for a given security holding.
858          *
859          * @param pHolding the security holding
860          * @return the bucket
861          */
862         public MoneyWiseXAnalysisSecurityBucket getBucket(final MoneyWiseSecurityHolding pHolding) {
863             /* Locate the bucket in the list */
864             final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
865             MoneyWiseXAnalysisSecurityBucket myItem = findItemById(mySecurity.getIndexedId());
866 
867             /* If the item does not yet exist */
868             if (myItem == null) {
869                 /* Create the new bucket */
870                 myItem = new MoneyWiseXAnalysisSecurityBucket(theAnalysis, pHolding);
871 
872                 /* Add to the list */
873                 theList.add(myItem);
874             }
875 
876             /* Return the bucket */
877             return myItem;
878         }
879 
880         /**
881          * Mark active securities.
882          *
883          * @return true/false are there active securities?
884          * @throws OceanusException on error
885          */
886         boolean markActiveSecurities() throws OceanusException {
887             /* Loop through the buckets */
888             boolean areActive = false;
889             final Iterator<MoneyWiseXAnalysisSecurityBucket> myIterator = iterator();
890             while (myIterator.hasNext()) {
891                 final MoneyWiseXAnalysisSecurityBucket myCurr = myIterator.next();
892                 final MoneyWiseSecurity mySecurity = myCurr.getSecurity();
893 
894                 /* If we are active */
895                 if (myCurr.isActive()) {
896                     /* Set the security as relevant */
897                     mySecurity.setRelevant();
898                     areActive = true;
899                 }
900 
901                 /* If we are closed */
902                 if (Boolean.TRUE.equals(mySecurity.isClosed())) {
903                     /* Ensure that we have correct closed dates */
904                     mySecurity.adjustClosed();
905 
906                     /* If we are Relevant */
907                     if (mySecurity.isRelevant()
908                             && theAnalysis.getData().checkClosedAccounts()) {
909                         /* throw exception */
910                         throw new MoneyWiseDataException(myCurr, "Illegally closed security");
911                     }
912                 }
913             }
914 
915             /* Return active indication */
916             return areActive;
917         }
918     }
919 }