MoneyWiseXAnalysisPortfolioBucket.java

/*
 * MoneyWise: Finance Application
 * Copyright 2012-2026. Tony Washer
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets;

import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataList;
import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldTableItem;
import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
import io.github.tonywasher.joceanus.metis.list.MetisListIndexed;
import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisSecurityBucket.MoneyWiseXAnalysisSecurityBucketList;
import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.values.MoneyWiseXAnalysisAccountAttr;
import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.values.MoneyWiseXAnalysisAccountValues;
import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.values.MoneyWiseXAnalysisSecurityAttr;
import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.values.MoneyWiseXAnalysisSecurityValues;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency;
import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseDataException;

import java.util.Currency;
import java.util.Iterator;
import java.util.List;

/**
 * Portfolio Bucket.
 */
public final class MoneyWiseXAnalysisPortfolioBucket
        implements MetisFieldTableItem {
    /**
     * Local Report fields.
     */
    private static final MetisFieldSet<MoneyWiseXAnalysisPortfolioBucket> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseXAnalysisPortfolioBucket.class);

    /*
     * Declare Fields.
     */
    static {
        FIELD_DEFS.declareLocalField(MoneyWiseBasicDataType.PORTFOLIO, MoneyWiseXAnalysisPortfolioBucket::getPortfolio);
        FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.BUCKET_BASEVALUES, MoneyWiseXAnalysisPortfolioBucket::getPortfolioCash);
        FIELD_DEFS.declareLocalField(MoneyWiseBasicDataType.CASH, MoneyWiseXAnalysisPortfolioBucket::getBaseValues);
        FIELD_DEFS.declareLocalField(MoneyWiseBasicDataType.SECURITY.getListId(), MoneyWiseXAnalysisPortfolioBucket::getSecurities);
        FIELD_DEFS.declareLocalFieldsForEnum(MoneyWiseXAnalysisSecurityAttr.class, MoneyWiseXAnalysisPortfolioBucket::getValue);
    }

    /**
     * Totals bucket name.
     */
    private static final MetisDataFieldId NAME_TOTALS = MoneyWiseXAnalysisBucketResource.ANALYSIS_TOTALS;

    /**
     * The portfolio.
     */
    private final MoneyWisePortfolio thePortfolio;

    /**
     * The reporting currency.
     */
    private final MoneyWiseCurrency theCurrency;

    /**
     * The cash bucket.
     */
    private final MoneyWiseXAnalysisPortfolioCashBucket theCash;

    /**
     * The security bucket list.
     */
    private final MoneyWiseXAnalysisSecurityBucketList theSecurities;

    /**
     * Values.
     */
    private final MoneyWiseXAnalysisSecurityValues theValues;

    /**
     * The base values.
     */
    private final MoneyWiseXAnalysisSecurityValues theBaseValues;

    /**
     * Is this a foreign currency?
     */
    private final Boolean isForeignCurrency;

    /**
     * Does this portfolio have foreign currency values?
     */
    private boolean hasForeignCurrency;

    /**
     * Constructor.
     *
     * @param pAnalysis  the analysis
     * @param pPortfolio the portfolio account
     */
    private MoneyWiseXAnalysisPortfolioBucket(final MoneyWiseXAnalysis pAnalysis,
                                              final MoneyWisePortfolio pPortfolio) {
        /* Store the portfolio */
        thePortfolio = pPortfolio;
        theCurrency = pAnalysis.getCurrency();

        /* Create the cash bucket */
        theCash = new MoneyWiseXAnalysisPortfolioCashBucket(pAnalysis, pPortfolio);

        /* Create the securities list */
        theSecurities = pPortfolio != null
                ? new MoneyWiseXAnalysisSecurityBucketList(pAnalysis)
                : null;

        /* Create the value maps and initialise them */
        final Currency myCurrency = theCurrency == null
                ? MoneyWiseXAnalysisAccountBucket.DEFAULT_CURRENCY
                : theCurrency.getCurrency();
        theValues = new MoneyWiseXAnalysisSecurityValues(myCurrency);
        theBaseValues = new MoneyWiseXAnalysisSecurityValues(myCurrency);

        /* Create the valuation value */
        theValues.setValue(MoneyWiseXAnalysisSecurityAttr.VALUATION, new OceanusMoney(myCurrency));
        theBaseValues.setValue(MoneyWiseXAnalysisSecurityAttr.VALUATION, new OceanusMoney(myCurrency));

        /* Determine whether the portfolio is a foreign currency */
        isForeignCurrency = !MetisDataDifference.isEqual(pAnalysis.getCurrency(), theCurrency);
        hasForeignCurrency = isForeignCurrency;
    }

    /**
     * Constructor.
     *
     * @param pAnalysis the analysis
     * @param pBase     the underlying bucket
     * @param pDate     the date for the bucket
     */
    private MoneyWiseXAnalysisPortfolioBucket(final MoneyWiseXAnalysis pAnalysis,
                                              final MoneyWiseXAnalysisPortfolioBucket pBase,
                                              final OceanusDate pDate) {
        /* Copy details from base */
        thePortfolio = pBase.getPortfolio();
        theCurrency = pBase.getCurrency();
        isForeignCurrency = pBase.isForeignCurrency();

        /* Create the cash bucket */
        theCash = new MoneyWiseXAnalysisPortfolioCashBucket(pAnalysis, pBase.getPortfolioCash(), pDate);

        /* Create the securities list */
        theSecurities = thePortfolio != null
                ? new MoneyWiseXAnalysisSecurityBucketList(pAnalysis, pBase.getSecurities(), pDate)
                : null;

        /* Create the value maps and initialise them */
        final Currency myCurrency = theCurrency.getCurrency();
        theValues = new MoneyWiseXAnalysisSecurityValues(myCurrency);
        theBaseValues = new MoneyWiseXAnalysisSecurityValues(myCurrency);
    }

    /**
     * Constructor.
     *
     * @param pAnalysis the analysis
     * @param pBase     the underlying bucket
     * @param pRange    the date range for the bucket
     */
    private MoneyWiseXAnalysisPortfolioBucket(final MoneyWiseXAnalysis pAnalysis,
                                              final MoneyWiseXAnalysisPortfolioBucket pBase,
                                              final OceanusDateRange pRange) {
        /* Copy details from base */
        thePortfolio = pBase.getPortfolio();
        theCurrency = pBase.getCurrency();
        isForeignCurrency = pBase.isForeignCurrency();

        /* Create the cash bucket */
        theCash = new MoneyWiseXAnalysisPortfolioCashBucket(pAnalysis, pBase.getPortfolioCash(), pRange);

        /* Create the securities list */
        theSecurities = thePortfolio != null
                ? new MoneyWiseXAnalysisSecurityBucketList(pAnalysis, pBase.getSecurities(), pRange)
                : null;

        /* Create the value maps and initialise them */
        final Currency myCurrency = theCurrency.getCurrency();
        theValues = new MoneyWiseXAnalysisSecurityValues(myCurrency);
        theBaseValues = new MoneyWiseXAnalysisSecurityValues(myCurrency);
    }

    @Override
    public MetisFieldSet<MoneyWiseXAnalysisPortfolioBucket> getDataFieldSet() {
        return FIELD_DEFS;
    }

    @Override
    public String formatObject(final OceanusDataFormatter pFormatter) {
        return toString();
    }

    @Override
    public String toString() {
        return getName() + theValues;
    }

    /**
     * Is this a foreign currency?
     *
     * @return true/false
     */
    public Boolean isForeignCurrency() {
        return isForeignCurrency;
    }

    /**
     * Does this portfolio hold foreign currency accounts/securities?
     *
     * @return true/false
     */
    public Boolean hasForeignCurrency() {
        return hasForeignCurrency;
    }

    /**
     * Obtain the name.
     *
     * @return the name
     */
    public String getName() {
        return thePortfolio == null
                ? NAME_TOTALS.getId()
                : thePortfolio.getName();
    }

    @Override
    public Integer getIndexedId() {
        return thePortfolio.getIndexedId();
    }

    /**
     * Obtain the portfolio.
     *
     * @return the portfolio
     */
    public MoneyWisePortfolio getPortfolio() {
        return thePortfolio;
    }

    /**
     * Obtain the currency.
     *
     * @return the currency
     */
    public MoneyWiseCurrency getCurrency() {
        return theCurrency;
    }

    /**
     * Obtain the portfolio cash bucket.
     *
     * @return the bucket
     */
    public MoneyWiseXAnalysisPortfolioCashBucket getPortfolioCash() {
        return theCash;
    }

    /**
     * Obtain the security buckets.
     *
     * @return the buckets
     */
    public MoneyWiseXAnalysisSecurityBucketList getSecurities() {
        return theSecurities;
    }

    /**
     * Obtain the security bucket iterator.
     *
     * @return the iterator
     */
    public Iterator<MoneyWiseXAnalysisSecurityBucket> securityIterator() {
        return theSecurities.iterator();
    }

    /**
     * Obtain the values.
     *
     * @return the values
     */
    public MoneyWiseXAnalysisSecurityValues getValues() {
        return theValues;
    }

    /**
     * Obtain the base values.
     *
     * @return the base values
     */
    public MoneyWiseXAnalysisSecurityValues getBaseValues() {
        return theBaseValues;
    }

    /**
     * Set Value.
     *
     * @param pAttr  the attribute
     * @param pValue the value of the attribute
     */
    void setValue(final MoneyWiseXAnalysisSecurityAttr pAttr,
                  final OceanusMoney pValue) {
        /* Set the value into the list */
        theValues.setValue(pAttr, pValue);
    }

    /**
     * Obtain an attribute value.
     *
     * @param pAttr the attribute
     * @return the value of the attribute or null
     */
    private Object getValue(final MoneyWiseXAnalysisSecurityAttr pAttr) {
        /* Obtain the attribute */
        return theValues.getValue(pAttr);
    }

    /**
     * Obtain the SecurityBucket from this portfolio for a security holding.
     *
     * @param pHolding the security holding
     * @return the bucket
     */
    public MoneyWiseXAnalysisSecurityBucket getSecurityBucket(final MoneyWiseSecurityHolding pHolding) {
        /* Return the security bucket for the portfolio's list */
        return theSecurities.getBucket(pHolding);
    }

    /**
     * Obtain the SecurityBucket from this portfolio for a security.
     *
     * @param pSecurity the security
     * @return the bucket
     */
    public MoneyWiseXAnalysisSecurityBucket findSecurityBucket(final MoneyWiseSecurity pSecurity) {
        /* Return the security bucket for the portfolio's list */
        return theSecurities.findItemById(pSecurity.getIndexedId());
    }

    @Override
    public boolean equals(final Object pThat) {
        /* Handle the trivial cases */
        if (this == pThat) {
            return true;
        }
        if (pThat == null) {
            return false;
        }
        if (!(pThat instanceof MoneyWiseXAnalysisPortfolioBucket)) {
            return false;
        }

        /* Compare the Portfolios */
        final MoneyWiseXAnalysisPortfolioBucket myThat = (MoneyWiseXAnalysisPortfolioBucket) pThat;
        return getPortfolio().equals(myThat.getPortfolio());
    }

    @Override
    public int hashCode() {
        return getPortfolio().hashCode();
    }

    /**
     * Calculate delta.
     */
    void calculateDelta() {
        /* Obtain a copy of the value */
        OceanusMoney myValue = theValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
        myValue = new OceanusMoney(myValue);

        /* Subtract any base value */
        final OceanusMoney myBase = theBaseValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
        myValue.subtractAmount(myBase);

        /* Set the delta */
        setValue(MoneyWiseXAnalysisSecurityAttr.VALUEDELTA, myValue);
    }

    /**
     * Add bucket to totals.
     *
     * @param pBucket the underlying bucket
     */
    void addValues(final MoneyWiseXAnalysisSecurityBucket pBucket) {
        /* Add values */
        addValues(theValues, pBucket.getValues());

        /* Add base values */
        addValues(theBaseValues, pBucket.getBaseValues());

        /* Adjust foreign currency indication */
        hasForeignCurrency |= pBucket.isForeignCurrency();
    }

    /**
     * Add bucket to totals.
     *
     * @param pBucket the underlying bucket
     */
    void addValues(final MoneyWiseXAnalysisPortfolioCashBucket pBucket) {
        /* Add values */
        addValues(theValues, pBucket.getValues());

        /* Add base values */
        addValues(theBaseValues, pBucket.getBaseValues());
    }

    /**
     * Add bucket to totals.
     *
     * @param pTotals the totals
     * @param pSource the values to add
     */
    private static void addValues(final MoneyWiseXAnalysisSecurityValues pTotals,
                                  final MoneyWiseXAnalysisAccountValues pSource) {
        /* Add valuation values */
        final OceanusMoney myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
        final OceanusMoney mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisAccountAttr.VALUATION);
        myValue.addAmount(mySrcValue);
    }

    /**
     * Add bucket to totals.
     *
     * @param pTotals the totals
     * @param pSource the values to add
     */
    private static void addValues(final MoneyWiseXAnalysisSecurityValues pTotals,
                                  final MoneyWiseXAnalysisSecurityValues pSource) {
        /* Add valuation values */
        OceanusMoney myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
        OceanusMoney mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
        myValue.addAmount(mySrcValue);

        /* Add cost values */
        myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.RESIDUALCOST);
        mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.RESIDUALCOST);
        myValue.addAmount(mySrcValue);

        /* Add gains values */
        myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.REALISEDGAINS);
        mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.REALISEDGAINS);
        myValue.addAmount(mySrcValue);

        /* Add profit adjustment values */
        myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.GAINSADJUST);
        mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.GAINSADJUST);
        myValue.addAmount(mySrcValue);

        /* Add dividends values */
        myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.DIVIDEND);
        mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.DIVIDEND);
        myValue.addAmount(mySrcValue);

        /* Add market profit values */
        myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.MARKETPROFIT);
        mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.MARKETPROFIT);
        if (mySrcValue != null) {
            myValue.addAmount(mySrcValue);
        }

        /* Add profit values */
        myValue = pTotals.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.PROFIT);
        mySrcValue = pSource.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.PROFIT);
        if (mySrcValue != null) {
            myValue.addAmount(mySrcValue);
        }
    }

    /**
     * Is the portfolio bucket active?
     *
     * @return true/false
     */
    public boolean isActive() {
        /* Look for active cash */
        if (theCash.isActive()) {
            return true;
        }

        /* Loop through securities */
        final Iterator<MoneyWiseXAnalysisSecurityBucket> myIterator = securityIterator();
        while (myIterator.hasNext()) {
            final MoneyWiseXAnalysisSecurityBucket mySecurity = myIterator.next();

            /* Look for active security */
            if (mySecurity.isActive()) {
                return true;
            }
        }

        /* Inactive */
        return false;
    }

    /**
     * Is the portfolio bucket idle?
     *
     * @return true/false
     */
    public boolean isIdle() {
        /* Look for non-idle cash */
        if (!theCash.isIdle()) {
            return false;
        }

        /* Loop through securities */
        final Iterator<MoneyWiseXAnalysisSecurityBucket> myIterator = securityIterator();
        while (myIterator.hasNext()) {
            final MoneyWiseXAnalysisSecurityBucket mySecurity = myIterator.next();

            /* Look for active security */
            if (!mySecurity.isIdle()) {
                return false;
            }
        }

        /* Idle */
        return true;
    }

    /**
     * Obtain cash valuation.
     *
     * @param pBase get base valuation - true/false
     * @return the valuation minus the cash value
     */
    public OceanusMoney getCashValue(final boolean pBase) {
        /* Obtain the cash valuation */
        final MoneyWiseXAnalysisAccountValues myCashValues = pBase
                ? theCash.getBaseValues()
                : theCash.getValues();
        return new OceanusMoney(myCashValues.getMoneyValue(MoneyWiseXAnalysisAccountAttr.VALUATION));
    }

    /**
     * Obtain non-cash valuation.
     *
     * @param pBase get base valuation - true/false
     * @return the valuation minus the cash value
     */
    public OceanusMoney getNonCashValue(final boolean pBase) {
        /* Handle valuation by subtracting the cash valuation */
        final MoneyWiseXAnalysisSecurityValues myValues = pBase
                ? theBaseValues
                : theValues;
        final OceanusMoney myValue = new OceanusMoney(myValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION));
        myValue.subtractAmount(getCashValue(pBase));
        return myValue;
    }

    /**
     * PortfolioBucket list class.
     */
    public static final class MoneyWiseXAnalysisPortfolioBucketList
            implements MetisFieldItem, MetisDataList<MoneyWiseXAnalysisPortfolioBucket> {
        /**
         * Local Report fields.
         */
        private static final MetisFieldSet<MoneyWiseXAnalysisPortfolioBucketList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseXAnalysisPortfolioBucketList.class);

        /*
         * Declare Fields.
         */
        static {
            FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.ANALYSIS_NAME, MoneyWiseXAnalysisPortfolioBucketList::getAnalysis);
            FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.ANALYSIS_TOTALS, MoneyWiseXAnalysisPortfolioBucketList::getTotals);
        }

        /**
         * The analysis.
         */
        private final MoneyWiseXAnalysis theAnalysis;

        /**
         * The list.
         */
        private final MetisListIndexed<MoneyWiseXAnalysisPortfolioBucket> theList;

        /**
         * The totals.
         */
        private final MoneyWiseXAnalysisPortfolioBucket theTotals;

        /**
         * Do we have a foreign portfolio account?
         */
        private Boolean haveForeignCurrency = Boolean.FALSE;

        /**
         * Do we have active securities?
         */
        private Boolean haveActiveSecurities = Boolean.FALSE;

        /**
         * Construct a top-level List.
         *
         * @param pAnalysis the analysis
         */
        MoneyWiseXAnalysisPortfolioBucketList(final MoneyWiseXAnalysis pAnalysis) {
            /* Initialise class */
            theAnalysis = pAnalysis;
            theTotals = allocateTotalsBucket();
            theList = new MetisListIndexed<>();
            theList.setComparator((l, r) -> l.getPortfolio().compareTo(r.getPortfolio()));
        }

        /**
         * Construct a dated List.
         *
         * @param pAnalysis the analysis
         * @param pBase     the base list
         * @param pDate     the Date
         */
        MoneyWiseXAnalysisPortfolioBucketList(final MoneyWiseXAnalysis pAnalysis,
                                              final MoneyWiseXAnalysisPortfolioBucketList pBase,
                                              final OceanusDate pDate) {
            /* Initialise class */
            this(pAnalysis);

            /* Loop through the buckets */
            final Iterator<MoneyWiseXAnalysisPortfolioBucket> myIterator = pBase.iterator();
            while (myIterator.hasNext()) {
                final MoneyWiseXAnalysisPortfolioBucket myCurr = myIterator.next();

                /* Access the bucket for this date */
                final MoneyWiseXAnalysisPortfolioBucket myBucket = new MoneyWiseXAnalysisPortfolioBucket(pAnalysis, myCurr, pDate);

                /* Ignore if portfolio is idle */
                if (!myBucket.isIdle()) {
                    /* Add to the list */
                    theList.add(myBucket);
                }
            }
        }

        /**
         * Construct a ranged List.
         *
         * @param pAnalysis the analysis
         * @param pBase     the base list
         * @param pRange    the Date Range
         */
        MoneyWiseXAnalysisPortfolioBucketList(final MoneyWiseXAnalysis pAnalysis,
                                              final MoneyWiseXAnalysisPortfolioBucketList pBase,
                                              final OceanusDateRange pRange) {
            /* Initialise class */
            this(pAnalysis);

            /* Loop through the buckets */
            final Iterator<MoneyWiseXAnalysisPortfolioBucket> myIterator = pBase.iterator();
            while (myIterator.hasNext()) {
                final MoneyWiseXAnalysisPortfolioBucket myCurr = myIterator.next();

                /* Access the bucket for this range */
                final MoneyWiseXAnalysisPortfolioBucket myBucket = new MoneyWiseXAnalysisPortfolioBucket(pAnalysis, myCurr, pRange);

                /* If the bucket is non-idle or active */
                if (myBucket.isActive() || !myBucket.isIdle()) {
                    /* Add to the list */
                    theList.add(myBucket);
                }
            }
        }

        @Override
        public MetisFieldSet<MoneyWiseXAnalysisPortfolioBucketList> getDataFieldSet() {
            return FIELD_DEFS;
        }

        @Override
        public List<MoneyWiseXAnalysisPortfolioBucket> getUnderlyingList() {
            return theList.getUnderlyingList();
        }

        @Override
        public String formatObject(final OceanusDataFormatter pFormatter) {
            return getDataFieldSet().getName();
        }

        /**
         * Obtain the analysis.
         *
         * @return the analysis
         */
        MoneyWiseXAnalysis getAnalysis() {
            return theAnalysis;
        }

        /**
         * Obtain the Totals.
         *
         * @return the totals
         */
        public MoneyWiseXAnalysisPortfolioBucket getTotals() {
            return theTotals;
        }

        /**
         * Do we have a foreign currency?
         *
         * @return true/false
         */
        public Boolean haveForeignCurrency() {
            return haveForeignCurrency;
        }

        /**
         * Do we have active securities?
         *
         * @return true/false
         */
        public Boolean haveActiveSecurities() {
            return haveActiveSecurities;
        }

        /**
         * Obtain item by id.
         *
         * @param pId the id to lookup
         * @return the item (or null if not present)
         */
        public MoneyWiseXAnalysisPortfolioBucket findItemById(final Integer pId) {
            /* Return results */
            return theList.getItemById(pId);
        }

        /**
         * Obtain the PortfolioBucket for a given portfolio.
         *
         * @param pPortfolio the portfolio
         * @return the bucket
         */
        public MoneyWiseXAnalysisPortfolioBucket getBucket(final MoneyWisePortfolio pPortfolio) {
            /* Locate the bucket in the list */
            MoneyWiseXAnalysisPortfolioBucket myItem = findItemById(pPortfolio.getIndexedId());

            /* If the item does not yet exist */
            if (myItem == null) {
                /* Create the new bucket */
                myItem = new MoneyWiseXAnalysisPortfolioBucket(theAnalysis, pPortfolio);

                /* Add to the list */
                theList.add(myItem);
            }

            /* Return the bucket */
            return myItem;
        }

        /**
         * Obtain the PortfolioBucket for a given portfolio.
         *
         * @param pPortfolio the portfolio
         * @return the bucket
         */
        public MoneyWiseXAnalysisPortfolioCashBucket getCashBucket(final MoneyWisePortfolio pPortfolio) {
            /* Locate the bucket in the list */
            final MoneyWiseXAnalysisPortfolioBucket myItem = getBucket(pPortfolio);

            /* Return the bucket */
            return myItem.getPortfolioCash();
        }

        /**
         * Obtain the SecurityBucket for a given security holding.
         *
         * @param pHolding the holding
         * @return the bucket
         */
        public MoneyWiseXAnalysisSecurityBucket getBucket(final MoneyWiseSecurityHolding pHolding) {
            /* Locate the portfolio bucket in the list */
            final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
            final MoneyWiseXAnalysisPortfolioBucket myBucket = getBucket(myPortfolio);

            /* Return the security bucket for the portfolio's list */
            return myBucket.getSecurityBucket(pHolding);
        }

        /**
         * Obtain the matching PortfolioBucket.
         *
         * @param pPortfolio the portfolio
         * @return the matching bucket
         */
        public MoneyWiseXAnalysisPortfolioBucket getMatchingPortfolio(final MoneyWisePortfolio pPortfolio) {
            /* Return the matching portfolio if it exists else an orphan bucket */
            final MoneyWiseXAnalysisPortfolioBucket myPortfolio = findItemById(pPortfolio.getIndexedId());
            return myPortfolio != null
                    ? myPortfolio
                    : new MoneyWiseXAnalysisPortfolioBucket(theAnalysis, pPortfolio);
        }

        /**
         * Obtain the matching SecurityBucket.
         *
         * @param pSecurity the security
         * @return the matching bucket
         */
        public MoneyWiseXAnalysisSecurityBucket getMatchingSecurityHolding(final MoneyWiseSecurityHolding pSecurity) {
            /* Find the portfolio and holding */
            final MoneyWiseXAnalysisPortfolioBucket myPortfolio = findItemById(pSecurity.getPortfolio().getIndexedId());
            final MoneyWiseXAnalysisSecurityBucket mySecurity = myPortfolio == null
                    ? null
                    : myPortfolio.findSecurityBucket(pSecurity.getSecurity());
            return mySecurity != null
                    ? mySecurity
                    : new MoneyWiseXAnalysisSecurityBucket(theAnalysis, pSecurity);
        }

        /**
         * Obtain the default PortfolioBucket.
         *
         * @return the default bucket
         */
        public MoneyWiseXAnalysisPortfolioBucket getDefaultPortfolio() {
            /* Return the first portfolio in the list if it exists */
            return isEmpty()
                    ? null
                    : theList.getUnderlyingList().get(0);
        }

        /**
         * Obtain the default SecurityBucket.
         *
         * @return the default bucket
         */
        public MoneyWiseXAnalysisSecurityBucket getDefaultSecurityHolding() {
            /* Loop through the portfolio buckets */
            final Iterator<MoneyWiseXAnalysisPortfolioBucket> myIterator = iterator();
            while (myIterator.hasNext()) {
                final MoneyWiseXAnalysisPortfolioBucket myPortfolio = myIterator.next();

                /* Loop through the security buckets */
                final Iterator<MoneyWiseXAnalysisSecurityBucket> mySecIterator = myPortfolio.securityIterator();
                if (mySecIterator.hasNext()) {
                    /* Access bucket and category */
                    return mySecIterator.next();
                }
            }

            /* No security bucket found */
            return null;
        }

        /**
         * Allocate the Totals PortfolioBucket.
         *
         * @return the bucket
         */
        private MoneyWiseXAnalysisPortfolioBucket allocateTotalsBucket() {
            /* Create the totals portfolio */
            return new MoneyWiseXAnalysisPortfolioBucket(theAnalysis, null);
        }

        /**
         * Analyse securities.
         */
        public void analyseSecurities() {
            /* Access details */
            final MoneyWiseXAnalysisPortfolioCashBucket myCashTotals = theTotals.getPortfolioCash();

            /* Loop through the portfolio buckets */
            final Iterator<MoneyWiseXAnalysisPortfolioBucket> myPortIterator = iterator();
            while (myPortIterator.hasNext()) {
                final MoneyWiseXAnalysisPortfolioBucket myPortfolio = myPortIterator.next();

                /* Loop through the buckets */
                final Iterator<MoneyWiseXAnalysisSecurityBucket> mySecIterator = myPortfolio.securityIterator();
                while (mySecIterator.hasNext()) {
                    /* Access bucket and category */
                    final MoneyWiseXAnalysisSecurityBucket myCurr = mySecIterator.next();

                    /* Remove idle items */
                    if (myCurr.isIdle() && !myCurr.isActive()) {
                        mySecIterator.remove();
                        continue;
                    }

                    /* Add to the portfolio bucket and add values */
                    myPortfolio.addValues(myCurr);
                    theTotals.addValues(myCurr);

                    /* Note active security */
                    haveActiveSecurities = Boolean.TRUE;

                    /* Handle foreign asset */
                    if (myCurr.isForeignCurrency()) {
                        haveForeignCurrency = Boolean.TRUE;
                    }
                }

                /* Access the cash bucket */
                final MoneyWiseXAnalysisPortfolioCashBucket myCash = myPortfolio.getPortfolioCash();

                /* Remove idle items */
                if (myPortfolio.getSecurities().isEmpty()
                        && myCash.isIdle() && !myCash.isActive()) {
                    myPortIterator.remove();
                    continue;
                }

                /* Handle foreign asset */
                if (myCash.isForeignCurrency()) {
                    haveForeignCurrency = Boolean.TRUE;
                }

                /* Calculate the delta */
                myCash.calculateDelta();
                myPortfolio.addValues(myCash);
                theTotals.addValues(myCash);
                myCashTotals.addValues(myCash);

                /* Sort the list */
                myPortfolio.getSecurities().sortBuckets();

                /* Calculate delta for the portfolio */
                myPortfolio.calculateDelta();
            }

            /* Sort the list */
            theList.sortList();

            /* Calculate delta for the totals */
            theTotals.calculateDelta();
        }

        /**
         * Mark active securities.
         *
         * @throws OceanusException on error
         */
        public void markActiveSecurities() throws OceanusException {
            /* Loop through the portfolio buckets */
            final Iterator<MoneyWiseXAnalysisPortfolioBucket> myIterator = iterator();
            while (myIterator.hasNext()) {
                final MoneyWiseXAnalysisPortfolioBucket myCurr = myIterator.next();

                /* Mark active securities */
                final MoneyWiseXAnalysisSecurityBucketList mySecurities = myCurr.getSecurities();
                if (mySecurities.markActiveSecurities()) {
                    /* Check closed state */
                    final MoneyWisePortfolio myPortfolio = myCurr.getPortfolio();
                    if (Boolean.TRUE.equals(myPortfolio.isClosed())
                            && theAnalysis.getData().checkClosedAccounts()) {
                        /* throw exception */
                        throw new MoneyWiseDataException(myCurr, "Illegally closed portfolio");
                    }

                    /* Note that the portfolio is relevant as it has relevant securities */
                    myPortfolio.setRelevant();
                }
            }
        }
    }
}