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.OceanusMoney;
23  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
24  import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
25  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
26  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataList;
27  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
28  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldTableItem;
29  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
30  import io.github.tonywasher.joceanus.metis.list.MetisListIndexed;
31  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisSecurityBucket.MoneyWiseXAnalysisSecurityBucketList;
32  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.values.MoneyWiseXAnalysisAccountAttr;
33  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.values.MoneyWiseXAnalysisAccountValues;
34  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.values.MoneyWiseXAnalysisSecurityAttr;
35  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.values.MoneyWiseXAnalysisSecurityValues;
36  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
37  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio;
38  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
39  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding;
40  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency;
41  import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseDataException;
42  
43  import java.util.Currency;
44  import java.util.Iterator;
45  import java.util.List;
46  
47  /**
48   * Portfolio Bucket.
49   */
50  public final class MoneyWiseXAnalysisPortfolioBucket
51          implements MetisFieldTableItem {
52      /**
53       * Local Report fields.
54       */
55      private static final MetisFieldSet<MoneyWiseXAnalysisPortfolioBucket> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseXAnalysisPortfolioBucket.class);
56  
57      /*
58       * Declare Fields.
59       */
60      static {
61          FIELD_DEFS.declareLocalField(MoneyWiseBasicDataType.PORTFOLIO, MoneyWiseXAnalysisPortfolioBucket::getPortfolio);
62          FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.BUCKET_BASEVALUES, MoneyWiseXAnalysisPortfolioBucket::getPortfolioCash);
63          FIELD_DEFS.declareLocalField(MoneyWiseBasicDataType.CASH, MoneyWiseXAnalysisPortfolioBucket::getBaseValues);
64          FIELD_DEFS.declareLocalField(MoneyWiseBasicDataType.SECURITY.getListId(), MoneyWiseXAnalysisPortfolioBucket::getSecurities);
65          FIELD_DEFS.declareLocalFieldsForEnum(MoneyWiseXAnalysisSecurityAttr.class, MoneyWiseXAnalysisPortfolioBucket::getValue);
66      }
67  
68      /**
69       * Totals bucket name.
70       */
71      private static final MetisDataFieldId NAME_TOTALS = MoneyWiseXAnalysisBucketResource.ANALYSIS_TOTALS;
72  
73      /**
74       * The portfolio.
75       */
76      private final MoneyWisePortfolio thePortfolio;
77  
78      /**
79       * The reporting currency.
80       */
81      private final MoneyWiseCurrency theCurrency;
82  
83      /**
84       * The cash bucket.
85       */
86      private final MoneyWiseXAnalysisPortfolioCashBucket theCash;
87  
88      /**
89       * The security bucket list.
90       */
91      private final MoneyWiseXAnalysisSecurityBucketList theSecurities;
92  
93      /**
94       * Values.
95       */
96      private final MoneyWiseXAnalysisSecurityValues theValues;
97  
98      /**
99       * The base values.
100      */
101     private final MoneyWiseXAnalysisSecurityValues theBaseValues;
102 
103     /**
104      * Is this a foreign currency?
105      */
106     private final Boolean isForeignCurrency;
107 
108     /**
109      * Does this portfolio have foreign currency values?
110      */
111     private boolean hasForeignCurrency;
112 
113     /**
114      * Constructor.
115      *
116      * @param pAnalysis  the analysis
117      * @param pPortfolio the portfolio account
118      */
119     private MoneyWiseXAnalysisPortfolioBucket(final MoneyWiseXAnalysis pAnalysis,
120                                               final MoneyWisePortfolio pPortfolio) {
121         /* Store the portfolio */
122         thePortfolio = pPortfolio;
123         theCurrency = pAnalysis.getCurrency();
124 
125         /* Create the cash bucket */
126         theCash = new MoneyWiseXAnalysisPortfolioCashBucket(pAnalysis, pPortfolio);
127 
128         /* Create the securities list */
129         theSecurities = pPortfolio != null
130                 ? new MoneyWiseXAnalysisSecurityBucketList(pAnalysis)
131                 : null;
132 
133         /* Create the value maps and initialise them */
134         final Currency myCurrency = theCurrency == null
135                 ? MoneyWiseXAnalysisAccountBucket.DEFAULT_CURRENCY
136                 : theCurrency.getCurrency();
137         theValues = new MoneyWiseXAnalysisSecurityValues(myCurrency);
138         theBaseValues = new MoneyWiseXAnalysisSecurityValues(myCurrency);
139 
140         /* Create the valuation value */
141         theValues.setValue(MoneyWiseXAnalysisSecurityAttr.VALUATION, new OceanusMoney(myCurrency));
142         theBaseValues.setValue(MoneyWiseXAnalysisSecurityAttr.VALUATION, new OceanusMoney(myCurrency));
143 
144         /* Determine whether the portfolio is a foreign currency */
145         isForeignCurrency = !MetisDataDifference.isEqual(pAnalysis.getCurrency(), theCurrency);
146         hasForeignCurrency = isForeignCurrency;
147     }
148 
149     /**
150      * Constructor.
151      *
152      * @param pAnalysis the analysis
153      * @param pBase     the underlying bucket
154      * @param pDate     the date for the bucket
155      */
156     private MoneyWiseXAnalysisPortfolioBucket(final MoneyWiseXAnalysis pAnalysis,
157                                               final MoneyWiseXAnalysisPortfolioBucket pBase,
158                                               final OceanusDate pDate) {
159         /* Copy details from base */
160         thePortfolio = pBase.getPortfolio();
161         theCurrency = pBase.getCurrency();
162         isForeignCurrency = pBase.isForeignCurrency();
163 
164         /* Create the cash bucket */
165         theCash = new MoneyWiseXAnalysisPortfolioCashBucket(pAnalysis, pBase.getPortfolioCash(), pDate);
166 
167         /* Create the securities list */
168         theSecurities = thePortfolio != null
169                 ? new MoneyWiseXAnalysisSecurityBucketList(pAnalysis, pBase.getSecurities(), pDate)
170                 : null;
171 
172         /* Create the value maps and initialise them */
173         final Currency myCurrency = theCurrency.getCurrency();
174         theValues = new MoneyWiseXAnalysisSecurityValues(myCurrency);
175         theBaseValues = new MoneyWiseXAnalysisSecurityValues(myCurrency);
176     }
177 
178     /**
179      * Constructor.
180      *
181      * @param pAnalysis the analysis
182      * @param pBase     the underlying bucket
183      * @param pRange    the date range for the bucket
184      */
185     private MoneyWiseXAnalysisPortfolioBucket(final MoneyWiseXAnalysis pAnalysis,
186                                               final MoneyWiseXAnalysisPortfolioBucket pBase,
187                                               final OceanusDateRange pRange) {
188         /* Copy details from base */
189         thePortfolio = pBase.getPortfolio();
190         theCurrency = pBase.getCurrency();
191         isForeignCurrency = pBase.isForeignCurrency();
192 
193         /* Create the cash bucket */
194         theCash = new MoneyWiseXAnalysisPortfolioCashBucket(pAnalysis, pBase.getPortfolioCash(), pRange);
195 
196         /* Create the securities list */
197         theSecurities = thePortfolio != null
198                 ? new MoneyWiseXAnalysisSecurityBucketList(pAnalysis, pBase.getSecurities(), pRange)
199                 : null;
200 
201         /* Create the value maps and initialise them */
202         final Currency myCurrency = theCurrency.getCurrency();
203         theValues = new MoneyWiseXAnalysisSecurityValues(myCurrency);
204         theBaseValues = new MoneyWiseXAnalysisSecurityValues(myCurrency);
205     }
206 
207     @Override
208     public MetisFieldSet<MoneyWiseXAnalysisPortfolioBucket> getDataFieldSet() {
209         return FIELD_DEFS;
210     }
211 
212     @Override
213     public String formatObject(final OceanusDataFormatter pFormatter) {
214         return toString();
215     }
216 
217     @Override
218     public String toString() {
219         return getName() + theValues;
220     }
221 
222     /**
223      * Is this a foreign currency?
224      *
225      * @return true/false
226      */
227     public Boolean isForeignCurrency() {
228         return isForeignCurrency;
229     }
230 
231     /**
232      * Does this portfolio hold foreign currency accounts/securities?
233      *
234      * @return true/false
235      */
236     public Boolean hasForeignCurrency() {
237         return hasForeignCurrency;
238     }
239 
240     /**
241      * Obtain the name.
242      *
243      * @return the name
244      */
245     public String getName() {
246         return thePortfolio == null
247                 ? NAME_TOTALS.getId()
248                 : thePortfolio.getName();
249     }
250 
251     @Override
252     public Integer getIndexedId() {
253         return thePortfolio.getIndexedId();
254     }
255 
256     /**
257      * Obtain the portfolio.
258      *
259      * @return the portfolio
260      */
261     public MoneyWisePortfolio getPortfolio() {
262         return thePortfolio;
263     }
264 
265     /**
266      * Obtain the currency.
267      *
268      * @return the currency
269      */
270     public MoneyWiseCurrency getCurrency() {
271         return theCurrency;
272     }
273 
274     /**
275      * Obtain the portfolio cash bucket.
276      *
277      * @return the bucket
278      */
279     public MoneyWiseXAnalysisPortfolioCashBucket getPortfolioCash() {
280         return theCash;
281     }
282 
283     /**
284      * Obtain the security buckets.
285      *
286      * @return the buckets
287      */
288     public MoneyWiseXAnalysisSecurityBucketList getSecurities() {
289         return theSecurities;
290     }
291 
292     /**
293      * Obtain the security bucket iterator.
294      *
295      * @return the iterator
296      */
297     public Iterator<MoneyWiseXAnalysisSecurityBucket> securityIterator() {
298         return theSecurities.iterator();
299     }
300 
301     /**
302      * Obtain the values.
303      *
304      * @return the values
305      */
306     public MoneyWiseXAnalysisSecurityValues getValues() {
307         return theValues;
308     }
309 
310     /**
311      * Obtain the base values.
312      *
313      * @return the base values
314      */
315     public MoneyWiseXAnalysisSecurityValues getBaseValues() {
316         return theBaseValues;
317     }
318 
319     /**
320      * Set Value.
321      *
322      * @param pAttr  the attribute
323      * @param pValue the value of the attribute
324      */
325     void setValue(final MoneyWiseXAnalysisSecurityAttr pAttr,
326                   final OceanusMoney pValue) {
327         /* Set the value into the list */
328         theValues.setValue(pAttr, pValue);
329     }
330 
331     /**
332      * Obtain an attribute value.
333      *
334      * @param pAttr the attribute
335      * @return the value of the attribute or null
336      */
337     private Object getValue(final MoneyWiseXAnalysisSecurityAttr pAttr) {
338         /* Obtain the attribute */
339         return theValues.getValue(pAttr);
340     }
341 
342     /**
343      * Obtain the SecurityBucket from this portfolio for a security holding.
344      *
345      * @param pHolding the security holding
346      * @return the bucket
347      */
348     public MoneyWiseXAnalysisSecurityBucket getSecurityBucket(final MoneyWiseSecurityHolding pHolding) {
349         /* Return the security bucket for the portfolio's list */
350         return theSecurities.getBucket(pHolding);
351     }
352 
353     /**
354      * Obtain the SecurityBucket from this portfolio for a security.
355      *
356      * @param pSecurity the security
357      * @return the bucket
358      */
359     public MoneyWiseXAnalysisSecurityBucket findSecurityBucket(final MoneyWiseSecurity pSecurity) {
360         /* Return the security bucket for the portfolio's list */
361         return theSecurities.findItemById(pSecurity.getIndexedId());
362     }
363 
364     @Override
365     public boolean equals(final Object pThat) {
366         /* Handle the trivial cases */
367         if (this == pThat) {
368             return true;
369         }
370         if (pThat == null) {
371             return false;
372         }
373         if (!(pThat instanceof MoneyWiseXAnalysisPortfolioBucket)) {
374             return false;
375         }
376 
377         /* Compare the Portfolios */
378         final MoneyWiseXAnalysisPortfolioBucket myThat = (MoneyWiseXAnalysisPortfolioBucket) pThat;
379         return getPortfolio().equals(myThat.getPortfolio());
380     }
381 
382     @Override
383     public int hashCode() {
384         return getPortfolio().hashCode();
385     }
386 
387     /**
388      * Calculate delta.
389      */
390     void calculateDelta() {
391         /* Obtain a copy of the value */
392         OceanusMoney myValue = theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
393         myValue = new OceanusMoney(myValue);
394 
395         /* Subtract any base value */
396         final OceanusMoney myBase = theBaseValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
397         myValue.subtractAmount(myBase);
398 
399         /* Set the delta */
400         setValue(MoneyWiseXAnalysisSecurityAttr.VALUEDELTA, myValue);
401     }
402 
403     /**
404      * Add bucket to totals.
405      *
406      * @param pBucket the underlying bucket
407      */
408     void addValues(final MoneyWiseXAnalysisSecurityBucket pBucket) {
409         /* Add values */
410         addValues(theValues, pBucket.getValues());
411 
412         /* Add base values */
413         addValues(theBaseValues, pBucket.getBaseValues());
414 
415         /* Adjust foreign currency indication */
416         hasForeignCurrency |= pBucket.isForeignCurrency();
417     }
418 
419     /**
420      * Add bucket to totals.
421      *
422      * @param pBucket the underlying bucket
423      */
424     void addValues(final MoneyWiseXAnalysisPortfolioCashBucket pBucket) {
425         /* Add values */
426         addValues(theValues, pBucket.getValues());
427 
428         /* Add base values */
429         addValues(theBaseValues, pBucket.getBaseValues());
430     }
431 
432     /**
433      * Add bucket to totals.
434      *
435      * @param pTotals the totals
436      * @param pSource the values to add
437      */
438     private static void addValues(final MoneyWiseXAnalysisSecurityValues pTotals,
439                                   final MoneyWiseXAnalysisAccountValues pSource) {
440         /* Add valuation values */
441         final OceanusMoney myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
442         final OceanusMoney mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisAccountAttr.VALUATION);
443         myValue.addAmount(mySrcValue);
444     }
445 
446     /**
447      * Add bucket to totals.
448      *
449      * @param pTotals the totals
450      * @param pSource the values to add
451      */
452     private static void addValues(final MoneyWiseXAnalysisSecurityValues pTotals,
453                                   final MoneyWiseXAnalysisSecurityValues pSource) {
454         /* Add valuation values */
455         OceanusMoney myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
456         OceanusMoney mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
457         myValue.addAmount(mySrcValue);
458 
459         /* Add cost values */
460         myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.RESIDUALCOST);
461         mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.RESIDUALCOST);
462         myValue.addAmount(mySrcValue);
463 
464         /* Add gains values */
465         myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.REALISEDGAINS);
466         mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.REALISEDGAINS);
467         myValue.addAmount(mySrcValue);
468 
469         /* Add profit adjustment values */
470         myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.GAINSADJUST);
471         mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.GAINSADJUST);
472         myValue.addAmount(mySrcValue);
473 
474         /* Add dividends values */
475         myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.DIVIDEND);
476         mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.DIVIDEND);
477         myValue.addAmount(mySrcValue);
478 
479         /* Add market profit values */
480         myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.MARKETPROFIT);
481         mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.MARKETPROFIT);
482         if (mySrcValue != null) {
483             myValue.addAmount(mySrcValue);
484         }
485 
486         /* Add profit values */
487         myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.PROFIT);
488         mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.PROFIT);
489         if (mySrcValue != null) {
490             myValue.addAmount(mySrcValue);
491         }
492     }
493 
494     /**
495      * Is the portfolio bucket active?
496      *
497      * @return true/false
498      */
499     public boolean isActive() {
500         /* Look for active cash */
501         if (theCash.isActive()) {
502             return true;
503         }
504 
505         /* Loop through securities */
506         final Iterator<MoneyWiseXAnalysisSecurityBucket> myIterator = securityIterator();
507         while (myIterator.hasNext()) {
508             final MoneyWiseXAnalysisSecurityBucket mySecurity = myIterator.next();
509 
510             /* Look for active security */
511             if (mySecurity.isActive()) {
512                 return true;
513             }
514         }
515 
516         /* Inactive */
517         return false;
518     }
519 
520     /**
521      * Is the portfolio bucket idle?
522      *
523      * @return true/false
524      */
525     public boolean isIdle() {
526         /* Look for non-idle cash */
527         if (!theCash.isIdle()) {
528             return false;
529         }
530 
531         /* Loop through securities */
532         final Iterator<MoneyWiseXAnalysisSecurityBucket> myIterator = securityIterator();
533         while (myIterator.hasNext()) {
534             final MoneyWiseXAnalysisSecurityBucket mySecurity = myIterator.next();
535 
536             /* Look for active security */
537             if (!mySecurity.isIdle()) {
538                 return false;
539             }
540         }
541 
542         /* Idle */
543         return true;
544     }
545 
546     /**
547      * Obtain cash valuation.
548      *
549      * @param pBase get base valuation - true/false
550      * @return the valuation minus the cash value
551      */
552     public OceanusMoney getCashValue(final boolean pBase) {
553         /* Obtain the cash valuation */
554         final MoneyWiseXAnalysisAccountValues myCashValues = pBase
555                 ? theCash.getBaseValues()
556                 : theCash.getValues();
557         return new OceanusMoney(myCashValues.getMoneyValue(MoneyWiseXAnalysisAccountAttr.VALUATION));
558     }
559 
560     /**
561      * Obtain non-cash valuation.
562      *
563      * @param pBase get base valuation - true/false
564      * @return the valuation minus the cash value
565      */
566     public OceanusMoney getNonCashValue(final boolean pBase) {
567         /* Handle valuation by subtracting the cash valuation */
568         final MoneyWiseXAnalysisSecurityValues myValues = pBase
569                 ? theBaseValues
570                 : theValues;
571         final OceanusMoney myValue = new OceanusMoney(myValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION));
572         myValue.subtractAmount(getCashValue(pBase));
573         return myValue;
574     }
575 
576     /**
577      * PortfolioBucket list class.
578      */
579     public static final class MoneyWiseXAnalysisPortfolioBucketList
580             implements MetisFieldItem, MetisDataList<MoneyWiseXAnalysisPortfolioBucket> {
581         /**
582          * Local Report fields.
583          */
584         private static final MetisFieldSet<MoneyWiseXAnalysisPortfolioBucketList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseXAnalysisPortfolioBucketList.class);
585 
586         /*
587          * Declare Fields.
588          */
589         static {
590             FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.ANALYSIS_NAME, MoneyWiseXAnalysisPortfolioBucketList::getAnalysis);
591             FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.ANALYSIS_TOTALS, MoneyWiseXAnalysisPortfolioBucketList::getTotals);
592         }
593 
594         /**
595          * The analysis.
596          */
597         private final MoneyWiseXAnalysis theAnalysis;
598 
599         /**
600          * The list.
601          */
602         private final MetisListIndexed<MoneyWiseXAnalysisPortfolioBucket> theList;
603 
604         /**
605          * The totals.
606          */
607         private final MoneyWiseXAnalysisPortfolioBucket theTotals;
608 
609         /**
610          * Do we have a foreign portfolio account?
611          */
612         private Boolean haveForeignCurrency = Boolean.FALSE;
613 
614         /**
615          * Do we have active securities?
616          */
617         private Boolean haveActiveSecurities = Boolean.FALSE;
618 
619         /**
620          * Construct a top-level List.
621          *
622          * @param pAnalysis the analysis
623          */
624         MoneyWiseXAnalysisPortfolioBucketList(final MoneyWiseXAnalysis pAnalysis) {
625             /* Initialise class */
626             theAnalysis = pAnalysis;
627             theTotals = allocateTotalsBucket();
628             theList = new MetisListIndexed<>();
629             theList.setComparator((l, r) -> l.getPortfolio().compareTo(r.getPortfolio()));
630         }
631 
632         /**
633          * Construct a dated List.
634          *
635          * @param pAnalysis the analysis
636          * @param pBase     the base list
637          * @param pDate     the Date
638          */
639         MoneyWiseXAnalysisPortfolioBucketList(final MoneyWiseXAnalysis pAnalysis,
640                                               final MoneyWiseXAnalysisPortfolioBucketList pBase,
641                                               final OceanusDate pDate) {
642             /* Initialise class */
643             this(pAnalysis);
644 
645             /* Loop through the buckets */
646             final Iterator<MoneyWiseXAnalysisPortfolioBucket> myIterator = pBase.iterator();
647             while (myIterator.hasNext()) {
648                 final MoneyWiseXAnalysisPortfolioBucket myCurr = myIterator.next();
649 
650                 /* Access the bucket for this date */
651                 final MoneyWiseXAnalysisPortfolioBucket myBucket = new MoneyWiseXAnalysisPortfolioBucket(pAnalysis, myCurr, pDate);
652 
653                 /* Ignore if portfolio is idle */
654                 if (!myBucket.isIdle()) {
655                     /* Add to the list */
656                     theList.add(myBucket);
657                 }
658             }
659         }
660 
661         /**
662          * Construct a ranged List.
663          *
664          * @param pAnalysis the analysis
665          * @param pBase     the base list
666          * @param pRange    the Date Range
667          */
668         MoneyWiseXAnalysisPortfolioBucketList(final MoneyWiseXAnalysis pAnalysis,
669                                               final MoneyWiseXAnalysisPortfolioBucketList pBase,
670                                               final OceanusDateRange pRange) {
671             /* Initialise class */
672             this(pAnalysis);
673 
674             /* Loop through the buckets */
675             final Iterator<MoneyWiseXAnalysisPortfolioBucket> myIterator = pBase.iterator();
676             while (myIterator.hasNext()) {
677                 final MoneyWiseXAnalysisPortfolioBucket myCurr = myIterator.next();
678 
679                 /* Access the bucket for this range */
680                 final MoneyWiseXAnalysisPortfolioBucket myBucket = new MoneyWiseXAnalysisPortfolioBucket(pAnalysis, myCurr, pRange);
681 
682                 /* If the bucket is non-idle or active */
683                 if (myBucket.isActive() || !myBucket.isIdle()) {
684                     /* Add to the list */
685                     theList.add(myBucket);
686                 }
687             }
688         }
689 
690         @Override
691         public MetisFieldSet<MoneyWiseXAnalysisPortfolioBucketList> getDataFieldSet() {
692             return FIELD_DEFS;
693         }
694 
695         @Override
696         public List<MoneyWiseXAnalysisPortfolioBucket> getUnderlyingList() {
697             return theList.getUnderlyingList();
698         }
699 
700         @Override
701         public String formatObject(final OceanusDataFormatter pFormatter) {
702             return getDataFieldSet().getName();
703         }
704 
705         /**
706          * Obtain the analysis.
707          *
708          * @return the analysis
709          */
710         MoneyWiseXAnalysis getAnalysis() {
711             return theAnalysis;
712         }
713 
714         /**
715          * Obtain the Totals.
716          *
717          * @return the totals
718          */
719         public MoneyWiseXAnalysisPortfolioBucket getTotals() {
720             return theTotals;
721         }
722 
723         /**
724          * Do we have a foreign currency?
725          *
726          * @return true/false
727          */
728         public Boolean haveForeignCurrency() {
729             return haveForeignCurrency;
730         }
731 
732         /**
733          * Do we have active securities?
734          *
735          * @return true/false
736          */
737         public Boolean haveActiveSecurities() {
738             return haveActiveSecurities;
739         }
740 
741         /**
742          * Obtain item by id.
743          *
744          * @param pId the id to lookup
745          * @return the item (or null if not present)
746          */
747         public MoneyWiseXAnalysisPortfolioBucket findItemById(final Integer pId) {
748             /* Return results */
749             return theList.getItemById(pId);
750         }
751 
752         /**
753          * Obtain the PortfolioBucket for a given portfolio.
754          *
755          * @param pPortfolio the portfolio
756          * @return the bucket
757          */
758         public MoneyWiseXAnalysisPortfolioBucket getBucket(final MoneyWisePortfolio pPortfolio) {
759             /* Locate the bucket in the list */
760             MoneyWiseXAnalysisPortfolioBucket myItem = findItemById(pPortfolio.getIndexedId());
761 
762             /* If the item does not yet exist */
763             if (myItem == null) {
764                 /* Create the new bucket */
765                 myItem = new MoneyWiseXAnalysisPortfolioBucket(theAnalysis, pPortfolio);
766 
767                 /* Add to the list */
768                 theList.add(myItem);
769             }
770 
771             /* Return the bucket */
772             return myItem;
773         }
774 
775         /**
776          * Obtain the PortfolioBucket for a given portfolio.
777          *
778          * @param pPortfolio the portfolio
779          * @return the bucket
780          */
781         public MoneyWiseXAnalysisPortfolioCashBucket getCashBucket(final MoneyWisePortfolio pPortfolio) {
782             /* Locate the bucket in the list */
783             final MoneyWiseXAnalysisPortfolioBucket myItem = getBucket(pPortfolio);
784 
785             /* Return the bucket */
786             return myItem.getPortfolioCash();
787         }
788 
789         /**
790          * Obtain the SecurityBucket for a given security holding.
791          *
792          * @param pHolding the holding
793          * @return the bucket
794          */
795         public MoneyWiseXAnalysisSecurityBucket getBucket(final MoneyWiseSecurityHolding pHolding) {
796             /* Locate the portfolio bucket in the list */
797             final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
798             final MoneyWiseXAnalysisPortfolioBucket myBucket = getBucket(myPortfolio);
799 
800             /* Return the security bucket for the portfolio's list */
801             return myBucket.getSecurityBucket(pHolding);
802         }
803 
804         /**
805          * Obtain the matching PortfolioBucket.
806          *
807          * @param pPortfolio the portfolio
808          * @return the matching bucket
809          */
810         public MoneyWiseXAnalysisPortfolioBucket getMatchingPortfolio(final MoneyWisePortfolio pPortfolio) {
811             /* Return the matching portfolio if it exists else an orphan bucket */
812             final MoneyWiseXAnalysisPortfolioBucket myPortfolio = findItemById(pPortfolio.getIndexedId());
813             return myPortfolio != null
814                     ? myPortfolio
815                     : new MoneyWiseXAnalysisPortfolioBucket(theAnalysis, pPortfolio);
816         }
817 
818         /**
819          * Obtain the matching SecurityBucket.
820          *
821          * @param pSecurity the security
822          * @return the matching bucket
823          */
824         public MoneyWiseXAnalysisSecurityBucket getMatchingSecurityHolding(final MoneyWiseSecurityHolding pSecurity) {
825             /* Find the portfolio and holding */
826             final MoneyWiseXAnalysisPortfolioBucket myPortfolio = findItemById(pSecurity.getPortfolio().getIndexedId());
827             final MoneyWiseXAnalysisSecurityBucket mySecurity = myPortfolio == null
828                     ? null
829                     : myPortfolio.findSecurityBucket(pSecurity.getSecurity());
830             return mySecurity != null
831                     ? mySecurity
832                     : new MoneyWiseXAnalysisSecurityBucket(theAnalysis, pSecurity);
833         }
834 
835         /**
836          * Obtain the default PortfolioBucket.
837          *
838          * @return the default bucket
839          */
840         public MoneyWiseXAnalysisPortfolioBucket getDefaultPortfolio() {
841             /* Return the first portfolio in the list if it exists */
842             return isEmpty()
843                     ? null
844                     : theList.getUnderlyingList().get(0);
845         }
846 
847         /**
848          * Obtain the default SecurityBucket.
849          *
850          * @return the default bucket
851          */
852         public MoneyWiseXAnalysisSecurityBucket getDefaultSecurityHolding() {
853             /* Loop through the portfolio buckets */
854             final Iterator<MoneyWiseXAnalysisPortfolioBucket> myIterator = iterator();
855             while (myIterator.hasNext()) {
856                 final MoneyWiseXAnalysisPortfolioBucket myPortfolio = myIterator.next();
857 
858                 /* Loop through the security buckets */
859                 final Iterator<MoneyWiseXAnalysisSecurityBucket> mySecIterator = myPortfolio.securityIterator();
860                 if (mySecIterator.hasNext()) {
861                     /* Access bucket and category */
862                     return mySecIterator.next();
863                 }
864             }
865 
866             /* No security bucket found */
867             return null;
868         }
869 
870         /**
871          * Allocate the Totals PortfolioBucket.
872          *
873          * @return the bucket
874          */
875         private MoneyWiseXAnalysisPortfolioBucket allocateTotalsBucket() {
876             /* Create the totals portfolio */
877             return new MoneyWiseXAnalysisPortfolioBucket(theAnalysis, null);
878         }
879 
880         /**
881          * Analyse securities.
882          */
883         public void analyseSecurities() {
884             /* Access details */
885             final MoneyWiseXAnalysisPortfolioCashBucket myCashTotals = theTotals.getPortfolioCash();
886 
887             /* Loop through the portfolio buckets */
888             final Iterator<MoneyWiseXAnalysisPortfolioBucket> myPortIterator = iterator();
889             while (myPortIterator.hasNext()) {
890                 final MoneyWiseXAnalysisPortfolioBucket myPortfolio = myPortIterator.next();
891 
892                 /* Loop through the buckets */
893                 final Iterator<MoneyWiseXAnalysisSecurityBucket> mySecIterator = myPortfolio.securityIterator();
894                 while (mySecIterator.hasNext()) {
895                     /* Access bucket and category */
896                     final MoneyWiseXAnalysisSecurityBucket myCurr = mySecIterator.next();
897 
898                     /* Remove idle items */
899                     if (myCurr.isIdle() && !myCurr.isActive()) {
900                         mySecIterator.remove();
901                         continue;
902                     }
903 
904                     /* Add to the portfolio bucket and add values */
905                     myPortfolio.addValues(myCurr);
906                     theTotals.addValues(myCurr);
907 
908                     /* Note active security */
909                     haveActiveSecurities = Boolean.TRUE;
910 
911                     /* Handle foreign asset */
912                     if (myCurr.isForeignCurrency()) {
913                         haveForeignCurrency = Boolean.TRUE;
914                     }
915                 }
916 
917                 /* Access the cash bucket */
918                 final MoneyWiseXAnalysisPortfolioCashBucket myCash = myPortfolio.getPortfolioCash();
919 
920                 /* Remove idle items */
921                 if (myPortfolio.getSecurities().isEmpty()
922                         && myCash.isIdle() && !myCash.isActive()) {
923                     myPortIterator.remove();
924                     continue;
925                 }
926 
927                 /* Handle foreign asset */
928                 if (myCash.isForeignCurrency()) {
929                     haveForeignCurrency = Boolean.TRUE;
930                 }
931 
932                 /* Calculate the delta */
933                 myCash.calculateDelta();
934                 myPortfolio.addValues(myCash);
935                 theTotals.addValues(myCash);
936                 myCashTotals.addValues(myCash);
937 
938                 /* Sort the list */
939                 myPortfolio.getSecurities().sortBuckets();
940 
941                 /* Calculate delta for the portfolio */
942                 myPortfolio.calculateDelta();
943             }
944 
945             /* Sort the list */
946             theList.sortList();
947 
948             /* Calculate delta for the totals */
949             theTotals.calculateDelta();
950         }
951 
952         /**
953          * Mark active securities.
954          *
955          * @throws OceanusException on error
956          */
957         public void markActiveSecurities() throws OceanusException {
958             /* Loop through the portfolio buckets */
959             final Iterator<MoneyWiseXAnalysisPortfolioBucket> myIterator = iterator();
960             while (myIterator.hasNext()) {
961                 final MoneyWiseXAnalysisPortfolioBucket myCurr = myIterator.next();
962 
963                 /* Mark active securities */
964                 final MoneyWiseXAnalysisSecurityBucketList mySecurities = myCurr.getSecurities();
965                 if (mySecurities.markActiveSecurities()) {
966                     /* Check closed state */
967                     final MoneyWisePortfolio myPortfolio = myCurr.getPortfolio();
968                     if (Boolean.TRUE.equals(myPortfolio.isClosed())
969                             && theAnalysis.getData().checkClosedAccounts()) {
970                         /* throw exception */
971                         throw new MoneyWiseDataException(myCurr, "Illegally closed portfolio");
972                     }
973 
974                     /* Note that the portfolio is relevant as it has relevant securities */
975                     myPortfolio.setRelevant();
976                 }
977             }
978         }
979     }
980 }