TadonkiVialMeanVarianceOptimizer.java

  1. package org.drip.portfolioconstruction.cardinality;

  2. /*
  3.  * -*- mode: java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  4.  */

  5. /*!
  6.  * Copyright (C) 2020 Lakshmi Krishnamurthy
  7.  * Copyright (C) 2019 Lakshmi Krishnamurthy
  8.  *
  9.  *  This file is part of DROP, an open-source library targeting analytics/risk, transaction cost analytics,
  10.  *      asset liability management analytics, capital, exposure, and margin analytics, valuation adjustment
  11.  *      analytics, and portfolio construction analytics within and across fixed income, credit, commodity,
  12.  *      equity, FX, and structured products. It also includes auxiliary libraries for algorithm support,
  13.  *      numerical analysis, numerical optimization, spline builder, model validation, statistical learning,
  14.  *      and computational support.
  15.  *  
  16.  *      https://lakshmidrip.github.io/DROP/
  17.  *  
  18.  *  DROP is composed of three modules:
  19.  *  
  20.  *  - DROP Product Core - https://lakshmidrip.github.io/DROP-Product-Core/
  21.  *  - DROP Portfolio Core - https://lakshmidrip.github.io/DROP-Portfolio-Core/
  22.  *  - DROP Computational Core - https://lakshmidrip.github.io/DROP-Computational-Core/
  23.  *
  24.  *  DROP Product Core implements libraries for the following:
  25.  *  - Fixed Income Analytics
  26.  *  - Loan Analytics
  27.  *  - Transaction Cost Analytics
  28.  *
  29.  *  DROP Portfolio Core implements libraries for the following:
  30.  *  - Asset Allocation Analytics
  31.  *  - Asset Liability Management Analytics
  32.  *  - Capital Estimation Analytics
  33.  *  - Exposure Analytics
  34.  *  - Margin Analytics
  35.  *  - XVA Analytics
  36.  *
  37.  *  DROP Computational Core implements libraries for the following:
  38.  *  - Algorithm Support
  39.  *  - Computation Support
  40.  *  - Function Analysis
  41.  *  - Model Validation
  42.  *  - Numerical Analysis
  43.  *  - Numerical Optimizer
  44.  *  - Spline Builder
  45.  *  - Statistical Learning
  46.  *
  47.  *  Documentation for DROP is Spread Over:
  48.  *
  49.  *  - Main                     => https://lakshmidrip.github.io/DROP/
  50.  *  - Wiki                     => https://github.com/lakshmiDRIP/DROP/wiki
  51.  *  - GitHub                   => https://github.com/lakshmiDRIP/DROP
  52.  *  - Repo Layout Taxonomy     => https://github.com/lakshmiDRIP/DROP/blob/master/Taxonomy.md
  53.  *  - Javadoc                  => https://lakshmidrip.github.io/DROP/Javadoc/index.html
  54.  *  - Technical Specifications => https://github.com/lakshmiDRIP/DROP/tree/master/Docs/Internal
  55.  *  - Release Versions         => https://lakshmidrip.github.io/DROP/version.html
  56.  *  - Community Credits        => https://lakshmidrip.github.io/DROP/credits.html
  57.  *  - Issues Catalog           => https://github.com/lakshmiDRIP/DROP/issues
  58.  *  - JUnit                    => https://lakshmidrip.github.io/DROP/junit/index.html
  59.  *  - Jacoco                   => https://lakshmidrip.github.io/DROP/jacoco/index.html
  60.  *
  61.  *  Licensed under the Apache License, Version 2.0 (the "License");
  62.  *      you may not use this file except in compliance with the License.
  63.  *  
  64.  *  You may obtain a copy of the License at
  65.  *      http://www.apache.org/licenses/LICENSE-2.0
  66.  *  
  67.  *  Unless required by applicable law or agreed to in writing, software
  68.  *      distributed under the License is distributed on an "AS IS" BASIS,
  69.  *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  70.  *  
  71.  *  See the License for the specific language governing permissions and
  72.  *      limitations under the License.
  73.  */

  74. /**
  75.  * <i>TadonkiVialMeanVarianceOptimizer</i> builds an Optimal Portfolio Based on MPT Using the Asset Pool
  76.  *  Statistical Properties with the Specified Lower/Upper Bounds on the Component Assets, along with an Upper
  77.  *  Bound on Portfolio Cardinality, using the Tadonki and Vial (2004) Heuristic Scheme. The References are:
  78.  *
  79.  * <br><br>
  80.  *  <ul>
  81.  *      <li>
  82.  *          Chang, T., J., N. Meade, J. E. Beasley, and Y. M. Sharaiha (2000): Heuristics for Cardinality
  83.  *              Constrained Portfolio Optimization <i>Computers and Operations Research</i> <b>27 (13)</b>
  84.  *              1271-1302
  85.  *      </li>
  86.  *      <li>
  87.  *          Chvatal, V. (1973): Edmonds Polytopes in a Hierarchy of Combinatorial Problems <i>Discrete
  88.  *              Mathematics</i> <b>4 (4)</b> 305-337
  89.  *      </li>
  90.  *      <li>
  91.  *          Jobst, N. J., M. D. Horniman, C. A. Lucas, and G. Mitra (2001): Computational Aspects of
  92.  *              Alternative Portfolio Selection Models in the Presence of Discrete Asset Choice Constraints
  93.  *              <i>Quantitative Finance</i> <b>1 (5)</b> 1-13
  94.  *      </li>
  95.  *      <li>
  96.  *          Letchford, A. N. and A. Lodi (2002): Strengthening Chvatal-Gomory Cuts and Gomory Fractional Cuts
  97.  *              <i>Operations Research Letters</i> <b>30 (2)</b> 74-82
  98.  *      </li>
  99.  *      <li>
  100.  *          Tadonki, C., and J. P. Vial (2004): Portfolio Selection with Cardinality and Bound Constraints
  101.  *              https://www.cri.ensmp.fr/~tadonki/PaperForWeb/Tadonki_PF.pdf
  102.  *      </li>
  103.  *  </ul>
  104.  *
  105.  *  <br><br>
  106.  *  <ul>
  107.  *      <li><b>Module </b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/PortfolioCore.md">Portfolio Core Module</a></li>
  108.  *      <li><b>Library</b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/AssetAllocationAnalyticsLibrary.md">Asset Allocation Analytics</a></li>
  109.  *      <li><b>Project</b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/src/main/java/org/drip/portfolioconstruction/README.md">Portfolio Construction under Allocation Constraints</a></li>
  110.  *      <li><b>Package</b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/src/main/java/org/drip/portfolioconstruction/allocator/README.md">MVO Based Portfolio Allocation Construction</a></li>
  111.  *  </ul>
  112.  *
  113.  * @author Lakshmi Krishnamurthy
  114.  */

  115. public class TadonkiVialMeanVarianceOptimizer
  116.     extends org.drip.portfolioconstruction.allocator.ConstrainedMeanVarianceOptimizer
  117. {

  118.     private org.drip.portfolioconstruction.allocator.BoundedHoldingsAllocationControl
  119.         workingPortfolioAllocationControl (
  120.             final java.lang.String[] assetIDArray,
  121.             final org.drip.portfolioconstruction.allocator.BoundedHoldingsAllocationControl
  122.                 boundedHoldingsAllocationControl,
  123.             final java.util.Set<java.lang.String> pruneAssetIDSet)
  124.     {
  125.         int prunedAssetIDIndex = 0;
  126.         org.drip.portfolioconstruction.allocator.BoundedHoldingsAllocationControl
  127.             workingPortfolioAllocationControl = null;

  128.         java.lang.String[] prunedAssetIDArray =
  129.             new java.lang.String[assetIDArray.length - pruneAssetIDSet.size()];

  130.         for (int assetIndex = 0;
  131.             assetIndex < assetIDArray.length;
  132.             ++assetIndex)
  133.         {
  134.             if (null == pruneAssetIDSet ||
  135.                 !pruneAssetIDSet.contains (
  136.                     assetIDArray[assetIndex]
  137.                 )
  138.             )
  139.             {
  140.                 prunedAssetIDArray[prunedAssetIDIndex++] = assetIDArray[assetIndex];
  141.             }
  142.         }

  143.         try
  144.         {
  145.             workingPortfolioAllocationControl =
  146.                 new org.drip.portfolioconstruction.allocator.BoundedHoldingsAllocationControl (
  147.                     prunedAssetIDArray,
  148.                     boundedHoldingsAllocationControl.customRiskUtilitySettings(),
  149.                     boundedHoldingsAllocationControl.equalityConstraintSettings()
  150.                 );

  151.             for (int prunedAssetIndex = 0;
  152.                 prunedAssetIndex < prunedAssetIDArray.length;
  153.                 ++prunedAssetIndex)
  154.             {
  155.                 workingPortfolioAllocationControl.addBound (
  156.                     prunedAssetIDArray[prunedAssetIndex],
  157.                     0.,
  158.                     boundedHoldingsAllocationControl.upperBound (
  159.                         prunedAssetIDArray[prunedAssetIndex]
  160.                     )
  161.                 );
  162.             }

  163.             return workingPortfolioAllocationControl;
  164.         }
  165.         catch (java.lang.Exception e)
  166.         {
  167.             e.printStackTrace();
  168.         }

  169.         return null;
  170.     }

  171.     private int firstGreedyPruneList (
  172.         final org.drip.portfolioconstruction.asset.Portfolio optimalPortfolio,
  173.         final org.drip.portfolioconstruction.allocator.BoundedHoldingsAllocationControl
  174.             boundedHoldingsAllocationControl,
  175.         final java.util.Set<java.lang.String> pruneAssetIDSet)
  176.     {
  177.         int pruneCount = 0;

  178.         org.drip.portfolioconstruction.asset.AssetComponent[] assetComponentArray =
  179.             optimalPortfolio.assetComponentArray();

  180.         try
  181.         {
  182.             for (int assetIndex = 0;
  183.                 assetIndex < assetComponentArray.length;
  184.                 ++assetIndex)
  185.             {
  186.                 java.lang.String assetID = assetComponentArray[assetIndex].id();

  187.                 if (assetComponentArray[assetIndex].amount() <
  188.                     boundedHoldingsAllocationControl.lowerBound (
  189.                         assetID
  190.                     )
  191.                 )
  192.                 {
  193.                     pruneAssetIDSet.add (
  194.                         assetID
  195.                     );

  196.                     ++pruneCount;
  197.                 }
  198.             }
  199.         }
  200.         catch (java.lang.Exception e)
  201.         {
  202.             e.printStackTrace();
  203.         }


  204.         return pruneCount;
  205.     }

  206.     private boolean secondGreedyPruneList (
  207.         final org.drip.portfolioconstruction.asset.Portfolio optimalPortfolio,
  208.         final org.drip.portfolioconstruction.allocator.BoundedHoldingsAllocationControl
  209.             parentBoundedPortfolioConstructionParameters,
  210.         final java.util.Set<java.lang.String> pruneAssetIDSet,
  211.         int pruneCount)
  212.     {
  213.         java.util.Map<java.lang.Double, java.lang.String> boundsDepartureMap =
  214.             new java.util.TreeMap<java.lang.Double, java.lang.String>();

  215.         org.drip.portfolioconstruction.asset.AssetComponent[] assetComponentArray =
  216.             optimalPortfolio.assetComponentArray();

  217.         for (int assetIndex = 0;
  218.             assetIndex < assetComponentArray.length;
  219.             ++assetIndex)
  220.         {
  221.             java.lang.String assetID = assetComponentArray[assetIndex].id();

  222.             try
  223.             {
  224.                 boundsDepartureMap.put (
  225.                     assetComponentArray[assetIndex].amount() -
  226.                     parentBoundedPortfolioConstructionParameters.lowerBound (
  227.                         assetID
  228.                     ),
  229.                     assetID
  230.                 );
  231.             }
  232.             catch (java.lang.Exception e)
  233.             {
  234.                 e.printStackTrace();

  235.                 return false;
  236.             }
  237.         }

  238.         for (java.util.Map.Entry<java.lang.Double, java.lang.String> boundsDepartureEntry :
  239.             boundsDepartureMap.entrySet())
  240.         {
  241.             if (0 == pruneCount)
  242.             {
  243.                 break;
  244.             }

  245.             pruneAssetIDSet.add (
  246.                 boundsDepartureEntry.getValue()
  247.             );

  248.             --pruneCount;
  249.         }

  250.         return true;
  251.     }

  252.     /**
  253.      * TadonkiVialMeanVarianceOptimizer Constructor
  254.      *
  255.      * @param interiorPointBarrierControl Interior Fixed Point Barrier Control Parameters
  256.      * @param lineStepEvolutionControl Line Step Evolution Control Parameters
  257.      */

  258.     public TadonkiVialMeanVarianceOptimizer (
  259.         final org.drip.function.rdtor1solver.InteriorPointBarrierControl interiorPointBarrierControl,
  260.         final org.drip.function.rdtor1descent.LineStepEvolutionControl lineStepEvolutionControl)
  261.     {
  262.         super (
  263.             interiorPointBarrierControl,
  264.             lineStepEvolutionControl
  265.         );
  266.     }

  267.     @Override public org.drip.portfolioconstruction.cardinality.TadonkiVialHoldingsAllocation allocate (
  268.         final org.drip.portfolioconstruction.allocator.HoldingsAllocationControl
  269.             holdingsAllocationControl,
  270.         final org.drip.portfolioconstruction.params.AssetUniverseStatisticalProperties
  271.             assetUniverseStatisticalProperties)
  272.     {
  273.         if (!(holdingsAllocationControl instanceof
  274.             org.drip.portfolioconstruction.cardinality.UpperBoundHoldingsAllocationControl))
  275.         {
  276.             return null;
  277.         }

  278.         org.drip.portfolioconstruction.cardinality.UpperBoundHoldingsAllocationControl
  279.             upperBoundHoldingsAllocationControl =
  280.                 (org.drip.portfolioconstruction.cardinality.UpperBoundHoldingsAllocationControl)
  281.                     holdingsAllocationControl;

  282.         int cardinalityUpperBound = upperBoundHoldingsAllocationControl.cardinalityUpperBound();

  283.         java.lang.String[] assetIDArray = upperBoundHoldingsAllocationControl.assetIDArray();

  284.         if (cardinalityUpperBound >= assetIDArray.length)
  285.         {
  286.             return org.drip.portfolioconstruction.cardinality.TadonkiVialHoldingsAllocation.Standard (
  287.                 super.allocate (
  288.                     holdingsAllocationControl,
  289.                     assetUniverseStatisticalProperties
  290.                 )
  291.             );
  292.         }

  293.         java.util.Set<java.lang.String> pruneAssetIDSet = new java.util.HashSet<java.lang.String>();

  294.         org.drip.portfolioconstruction.allocator.BoundedHoldingsAllocationControl
  295.             workingPortfolioAllocationControl = workingPortfolioAllocationControl (
  296.                 assetIDArray,
  297.                 upperBoundHoldingsAllocationControl,
  298.                 pruneAssetIDSet
  299.             );

  300.         org.drip.portfolioconstruction.allocator.HoldingsAllocation floorPassHoldingsAllocation =
  301.             super.allocate (
  302.                 workingPortfolioAllocationControl,
  303.                 assetUniverseStatisticalProperties
  304.             );

  305.         if (null == floorPassHoldingsAllocation)
  306.         {
  307.             return null;
  308.         }

  309.         org.drip.portfolioconstruction.allocator.HoldingsAllocation firstPrunePassHoldingsAllocation =
  310.             floorPassHoldingsAllocation;

  311.         org.drip.portfolioconstruction.asset.Portfolio optimalPortfolio =
  312.             firstPrunePassHoldingsAllocation.optimalPortfolio();

  313.         while (0 != firstGreedyPruneList (
  314.             optimalPortfolio,
  315.             upperBoundHoldingsAllocationControl,
  316.             pruneAssetIDSet
  317.         ))
  318.         {
  319.             workingPortfolioAllocationControl = workingPortfolioAllocationControl (
  320.                 assetIDArray,
  321.                 upperBoundHoldingsAllocationControl,
  322.                 pruneAssetIDSet
  323.             );

  324.             firstPrunePassHoldingsAllocation = super.allocate (
  325.                 workingPortfolioAllocationControl,
  326.                 assetUniverseStatisticalProperties
  327.             );

  328.             if (null == firstPrunePassHoldingsAllocation)
  329.             {
  330.                 return null;
  331.             }

  332.             optimalPortfolio = firstPrunePassHoldingsAllocation.optimalPortfolio();
  333.         }

  334.         if (cardinalityUpperBound >= optimalPortfolio.cardinality())
  335.         {
  336.             org.drip.portfolioconstruction.cardinality.TadonkiVialHoldingsAllocation
  337.                 tadonkiVialHoldingsAllocation =
  338.                     org.drip.portfolioconstruction.cardinality.TadonkiVialHoldingsAllocation.Standard (
  339.                         firstPrunePassHoldingsAllocation
  340.                     );

  341.             return null == tadonkiVialHoldingsAllocation ||
  342.                 !tadonkiVialHoldingsAllocation.setFloorPassHoldingsAllocation (
  343.                     floorPassHoldingsAllocation
  344.                 ) || !tadonkiVialHoldingsAllocation.setFirstPrunePassHoldingsAllocation (
  345.                     firstPrunePassHoldingsAllocation
  346.                 ) ? null : tadonkiVialHoldingsAllocation;
  347.         }

  348.         secondGreedyPruneList (
  349.             optimalPortfolio,
  350.             upperBoundHoldingsAllocationControl,
  351.             pruneAssetIDSet,
  352.             optimalPortfolio.cardinality() - cardinalityUpperBound
  353.         );

  354.         workingPortfolioAllocationControl = workingPortfolioAllocationControl (
  355.             assetIDArray,
  356.             upperBoundHoldingsAllocationControl,
  357.             pruneAssetIDSet
  358.         );

  359.         org.drip.portfolioconstruction.allocator.HoldingsAllocation secondPrunePassHoldingsAllocation =
  360.             super.allocate (
  361.                 workingPortfolioAllocationControl,
  362.                 assetUniverseStatisticalProperties
  363.             );

  364.         if (null == secondPrunePassHoldingsAllocation)
  365.         {
  366.             return null;
  367.         }

  368.         org.drip.portfolioconstruction.cardinality.TadonkiVialHoldingsAllocation
  369.             tadonkiVialHoldingsAllocation =
  370.                 org.drip.portfolioconstruction.cardinality.TadonkiVialHoldingsAllocation.Standard (
  371.                     secondPrunePassHoldingsAllocation
  372.                 );

  373.         return null == tadonkiVialHoldingsAllocation ||
  374.             !tadonkiVialHoldingsAllocation.setFloorPassHoldingsAllocation (
  375.                 floorPassHoldingsAllocation
  376.             ) || !tadonkiVialHoldingsAllocation.setFirstPrunePassHoldingsAllocation (
  377.                 firstPrunePassHoldingsAllocation
  378.             ) || !tadonkiVialHoldingsAllocation.setSecondPrunePassHoldingsAllocation (
  379.                 secondPrunePassHoldingsAllocation
  380.             ) ? null : tadonkiVialHoldingsAllocation;
  381.     }
  382. }