MoneyWiseXAnalysisTakeover.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.analyse;

import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusUnits;
import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisAccountBucket;
import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisPortfolioBucket.MoneyWiseXAnalysisPortfolioBucketList;
import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisSecurityBucket;
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.MoneyWiseAssetBase;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction;
import io.github.tonywasher.joceanus.moneywise.tax.MoneyWiseCashType;

import java.util.Currency;
import java.util.Objects;

/**
 * Stock Takeover analysis.
 */
public class MoneyWiseXAnalysisTakeover {
    /**
     * The portfolioBuckets.
     */
    private final MoneyWiseXAnalysisPortfolioBucketList thePortfolios;

    /**
     * The analysis state.
     */
    private final MoneyWiseXAnalysisState theState;

    /**
     * The transAnalyser.
     */
    private final MoneyWiseXAnalysisTransAnalyser theTransAnalyser;

    /**
     * The securityAnalyser.
     */
    private final MoneyWiseXAnalysisSecurity theSecurity;

    /**
     * The transaction.
     */
    private MoneyWiseXAnalysisTransaction theTransaction;

    /**
     * Constructor.
     *
     * @param pAnalyser the event analyser
     * @param pSecurity the securityAnalyser
     */
    MoneyWiseXAnalysisTakeover(final MoneyWiseXAnalysisEventAnalyser pAnalyser,
                               final MoneyWiseXAnalysisSecurity pSecurity) {
        thePortfolios = pAnalyser.getAnalysis().getPortfolios();
        theState = pAnalyser.getState();
        theSecurity = pSecurity;
        theTransAnalyser = theSecurity.getTransAnalyser();
    }

    /**
     * Process a transaction that is a stockTakeover.
     *
     * @param pTrans the transaction
     */
    void processStockTakeover(final MoneyWiseXAnalysisTransaction pTrans) {
        /* Store the transaction */
        theTransaction = pTrans;

        /* Access returned cash */
        final MoneyWiseTransaction myTrans = pTrans.getTransaction();
        final OceanusMoney myAmount = myTrans.getReturnedCash();

        /* If we have a returned cash part of the transaction */
        if (myAmount != null
                && myAmount.isNonZero()) {
            /* Process a Stock And Cash TakeOver */
            processStockAndCashTakeOver(myAmount);
        } else {
            /* Process a StockOnly TakeOver */
            processStockOnlyTakeOver();
        }
    }

    /**
     * Process a transaction that is a StockOnlyTakeover.
     */
    void processStockOnlyTakeOver() {
        /* Access details */
        final MoneyWiseSecurityHolding myCreditHolding = (MoneyWiseSecurityHolding) theTransaction.getCreditAccount();
        final MoneyWiseSecurityHolding myDebitHolding = (MoneyWiseSecurityHolding) theTransaction.getDebitAccount();

        /* Access the Asset Security Buckets */
        final MoneyWiseXAnalysisSecurityBucket myDebitAsset = thePortfolios.getBucket(myDebitHolding);
        final MoneyWiseXAnalysisSecurityValues myDebitValues = myDebitAsset.getValues();
        final MoneyWiseXAnalysisSecurityBucket myCreditAsset = thePortfolios.getBucket(myCreditHolding);
        final MoneyWiseXAnalysisSecurityValues myCreditValues = myCreditAsset.getValues();

        /* Determine value of the stock in both parts of the takeOver */
        final OceanusMoney myDebitXferValue = myDebitValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
        final OceanusPrice myCreditPrice = myDebitValues.getPriceValue(MoneyWiseXAnalysisSecurityAttr.PRICE);
        final OceanusUnits myCreditUnits = theTransaction.getCreditUnitsDelta();
        OceanusMoney myCreditXferValue = myCreditUnits.valueAtPrice(myCreditPrice);
        if (myCreditAsset.isForeignCurrency()) {
            final Currency myCurrency = theTransAnalyser.getCurrency().getCurrency();
            final OceanusRatio myRate = myCreditValues.getRatioValue(MoneyWiseXAnalysisSecurityAttr.EXCHANGERATE);
            myCreditXferValue = myCreditXferValue.convertCurrency(myCurrency, myRate);
        }

        /* Record the transferValues */
        myDebitValues.setValue(MoneyWiseXAnalysisSecurityAttr.XFERREDVALUE, myDebitXferValue);
        myCreditValues.setValue(MoneyWiseXAnalysisSecurityAttr.XFERREDVALUE, myCreditXferValue);

        /* Adjust units of debit and credit */
        myCreditAsset.adjustUnits(myCreditUnits);
        myDebitValues.setZeroUnits(MoneyWiseXAnalysisSecurityAttr.UNITS);

        /* Adjust the residual cost of debit and credit */
        final OceanusMoney myXferCost = myDebitValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.RESIDUALCOST);
        myDebitValues.setZeroMoney(MoneyWiseXAnalysisSecurityAttr.RESIDUALCOST);
        myCreditAsset.adjustResidualCost(myXferCost);

        /* Adjust the debit Funded to zero */
        myDebitValues.setZeroMoney(MoneyWiseXAnalysisSecurityAttr.FUNDED);

        /* Value the assets */
        theSecurity.adjustAssetValuation(myDebitAsset);
        theSecurity.adjustAssetValuation(myCreditAsset);

        /* Register the transaction */
        theState.registerBucketInterest(myDebitAsset);
        theState.registerBucketInterest(myCreditAsset);
    }

    /**
     * Process a transaction that is StockAndCashTakeover.
     *
     * @param pCashValue the cash part of the takeOver
     */
    private void processStockAndCashTakeOver(final OceanusMoney pCashValue) {
        /* Access details */
        final MoneyWiseSecurityHolding myDebit = (MoneyWiseSecurityHolding) theTransaction.getDebitAccount();
        final MoneyWiseSecurityHolding myCredit = (MoneyWiseSecurityHolding) theTransaction.getCreditAccount();
        final MoneyWiseAssetBase myReturnedCashAccount = (MoneyWiseAssetBase) Objects.requireNonNull(theTransaction.getTransaction().getReturnedCashAccount());

        /* Access the Asset Security Buckets */
        final MoneyWiseXAnalysisSecurityBucket myDebitAsset = thePortfolios.getBucket(myDebit);
        final MoneyWiseXAnalysisSecurityValues myDebitValues = myDebitAsset.getValues();
        final MoneyWiseXAnalysisSecurityBucket myCreditAsset = thePortfolios.getBucket(myCredit);
        final MoneyWiseXAnalysisSecurityValues myCreditValues = myDebitAsset.getValues();

        /* Get the appropriate prices for the assets */
        final OceanusMoney myStartingDebitValue = myDebitValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
        final OceanusMoney myStartingCreditValue = myCreditValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
        myDebitValues.setValue(MoneyWiseXAnalysisSecurityAttr.XFERREDVALUE, myStartingDebitValue);

        /* Adjust units of the stocks */
        myDebitAsset.adjustUnits(theTransaction.getDebitUnitsDelta());
        myCreditAsset.adjustUnits(theTransaction.getCreditUnitsDelta());

        /* Adjust the debit Funded to zero */
        myDebitValues.setZeroMoney(MoneyWiseXAnalysisSecurityAttr.FUNDED);

        /* Determine value of the stock part of the takeOver */
        myCreditAsset.valueAsset();
        myCreditAsset.adjustValuation();
        OceanusMoney myStockValue = myCreditValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.VALUATION);
        myStockValue = new OceanusMoney(myStockValue);
        myStockValue.addAmount(myStartingCreditValue);
        myCreditValues.setValue(MoneyWiseXAnalysisSecurityAttr.XFERREDVALUE, myStockValue);

        /* Calculate the total consideration */
        final OceanusMoney myConsideration = new OceanusMoney(pCashValue);
        myConsideration.addAmount(myStockValue);
        myDebitValues.setValue(MoneyWiseXAnalysisSecurityAttr.CONSIDERATION, myConsideration);

        /* Determine whether this is a large cash transaction */
        final OceanusMoney myPortion = myConsideration.valueAtRate(MoneyWiseXAnalysisXferOut.LIMIT_RATE);
        final boolean isLargeCash = pCashValue.compareTo(MoneyWiseXAnalysisXferOut.LIMIT_VALUE) > 0
                && pCashValue.compareTo(myPortion) > 0;
        myDebitValues.setValue(MoneyWiseXAnalysisSecurityAttr.CASHTYPE, isLargeCash
                ? MoneyWiseCashType.LARGECASH
                : MoneyWiseCashType.SMALLCASH);

        /* Access the current debit cost */
        final OceanusMoney myCost = myDebitValues.getMoneyValue(MoneyWiseXAnalysisSecurityAttr.RESIDUALCOST);
        final OceanusMoney myAllowedCost;
        final OceanusMoney myCostXfer;

        /* If this is a large cash takeOver */
        if (isLargeCash) {
            /* Determine the transferable cost */
            myCostXfer = myCost.valueAtWeight(myStockValue, myConsideration);

            /* Determine the cost dilution */
            final OceanusRatio myCostDilution = new OceanusRatio(pCashValue, myConsideration);
            myDebitValues.setValue(MoneyWiseXAnalysisSecurityAttr.COSTDILUTION, myCostDilution);

            /* Determine the allowed cost */
            myAllowedCost = new OceanusMoney(myCost);
            myAllowedCost.subtractAmount(myCostXfer);

            /* else this is viewed as small and is taken out of the cost */
        } else {
            /* Set the allowed cost to be the least of the cost or the returned cash */
            myAllowedCost = pCashValue.compareTo(myCost) > 0
                    ? new OceanusMoney(myCost)
                    : new OceanusMoney(pCashValue);

            /* Transferred cost is cost minus the allowed cost */
            myCostXfer = new OceanusMoney(myCost);
            myCostXfer.subtractAmount(myAllowedCost);
        }
        myDebitValues.setValue(MoneyWiseXAnalysisSecurityAttr.XFERREDVALUE, myStockValue);
        myCreditValues.setValue(MoneyWiseXAnalysisSecurityAttr.XFERREDVALUE, myStockValue);

        /* Value the assets */
        theSecurity.adjustAssetValuation(myDebitAsset);
        myCreditAsset.calculateUnrealisedGains();

        /* Register the transaction */
        theState.registerBucketInterest(myDebitAsset);
        theState.registerBucketInterest(myCreditAsset);

        /* Determine the capital gain */
        final OceanusMoney myCapitalGain = new OceanusMoney(pCashValue);
        myCapitalGain.subtractAmount(myAllowedCost);
        if (myCapitalGain.isNonZero()) {
            /* Record the delta gains */
            myDebitAsset.adjustRealisedGains(myCapitalGain);
            myDebitValues.setValue(MoneyWiseXAnalysisSecurityAttr.CAPITALGAIN, myCapitalGain);
            theSecurity.adjustStandardGain(myDebit, myCapitalGain);
        }

        /* Adjust residualCost of debit/credit */
        myCreditAsset.adjustResidualCost(myCostXfer);
        myDebitValues.setZeroMoney(MoneyWiseXAnalysisSecurityAttr.RESIDUALCOST);

        /* Adjust the ThirdParty account bucket */
        final MoneyWiseXAnalysisAccountBucket<?> myBucket = theTransAnalyser.getAccountBucket(myReturnedCashAccount);
        myBucket.addToBalance(pCashValue);
        myBucket.adjustValuation();
        theState.registerBucketInterest(myBucket);
    }
}