#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <tuple>
using namespace std;
using Portfolio = vector<int>; // weights in percent, size = 5
double percentile15(vector<double>& data) {
sort(data.begin(), data.end());
size_t index = static_cast<size_t>(floor(0.15 * data.size()));
if (index >= data.size()) index = data.size() - 1;
return data[index];
}
double lumpSumCAGR(const vector<double>& returns) {
double compound = 1.0;
for (double r : returns) {
compound *= (1.0 + r);
}
return pow(compound, 1.0 / returns.size()) - 1.0;
}
double portfolioReturn(const vector<vector<double>>& assets, const Portfolio& weights, int year) {
double result = 0.0;
for (int i = 0; i < 5; ++i) {
result += (weights[i] / 100.0) * assets[i][year];
}
return result;
}
void generatePortfolios(vector<Portfolio>& portfolios, Portfolio current, int pos, int totalWeight, int maxAssets) {
if (pos == 5) {
if (totalWeight == 100) {
int nonZero = count_if(current.begin(), current.end(), [](int w) { return w > 0; });
if (nonZero <= maxAssets) {
portfolios.push_back(current);
}
}
return;
}
for (int w = 0; w <= 100 - totalWeight; w += 5) {
current[pos] = w;
generatePortfolios(portfolios, current, pos + 1, totalWeight + w, maxAssets);
}
}
int main() {
const int numAssets = 5;
const int maxAssets = 4;
const int step = 5;
int duration = 15;
// Asset names
vector<string> assetNames = {"scv60Lcg15", "scv95", "vti", "11all", "33gld"};
// Replace these with actual return data for each asset
vector<vector<double>> assets = {
// Asset 1
{-0.04,0.14,0.15,-0.14,-0.19,0.26,0.28,0.04,0.08,0.28,0.08,-0.06,0.24,0.22,-0.01,0.26,0.14,-0.02,0.14,0.16,
-0.16,0.29,0.15,0.18,-0.03,0.20,0.13,0.20,0.02,0.08,-0.04,0.04,-0.07,0.33,0.12,0.06,0.15,0.01,-0.26,0.31,
0.22,-0.03,0.15,0.19,0.08,-0.06,0.16,0.13,-0.11,0.21,0.13,0.12,-0.18,0.15,0.12},
// Asset 2
{-0.03,0.13,0.06,-0.29,-0.31,0.43,0.46,0.08,0.07,0.21,0.12,0.06,0.31,0.38,0.02,0.31,0.12,-0.08,0.23,0.14,
-0.22,0.37,0.24,0.19,-0.02,0.24,0.17,0.31,-0.06,0.02,0.04,0.11,-0.13,0.41,0.17,0.04,0.15,-0.09,-0.32,0.33,
0.23,-0.06,0.18,0.32,0.09,-0.06,0.23,0.09,-0.15,0.20,0.03,0.19,-0.16,0.11,0.08},
// Asset 3
{-0.02,0.11,0.12,-0.25,-0.36,0.30,0.21,-0.11,-0.01,0.05,0.16,-0.11,0.19,0.19,0.01,0.28,0.15,-0.02,0.12,0.24,
-0.10,0.29,0.06,0.08,-0.01,0.34,0.19,0.30,0.24,0.19,-0.13,-0.09,-0.25,0.30,0.08,0.02,0.12,0.01,-0.37,0.26,
0.16,-0.02,0.14,0.31,0.12,0.00,0.11,0.19,-0.07,0.28,0.19,0.18,-0.24,0.22,0.20},
// Asset 4
{-0.02,0.12,0.14,-0.13,-0.19,0.22,0.26,0.04,0.06,0.19,0.07,-0.07,0.20,0.20,0.03,0.26,0.19,0.00,0.14,0.15,
-0.16,0.24,0.10,0.20,-0.02,0.19,0.12,0.17,0.01,0.05,0.01,0.02,-0.06,0.32,0.14,0.07,0.17,-0.01,-0.25,0.25,
0.20,0.00,0.14,0.15,0.11,-0.04,0.13,0.12,-0.10,0.20,0.07,0.12,-0.19,0.10,0.08},
// Asset 5 (new)
{-0.01,0.11,0.19,0.03,-0.05,0.13,0.23,0.07,0.10,0.44,0.08,-0.13,0.20,0.14,-0.04,0.19,0.16,0.02,0.06,0.08,
-0.15,0.17,0.08,0.16,-0.03,0.17,0.11,0.12,0.00,0.02,-0.01,0.02,-0.02,0.30,0.11,0.07,0.19,0.03,-0.21,0.25,
0.23,0.00,0.13,0.08,0.09,-0.06,0.13,0.10,-0.08,0.20,0.10,0.11,-0.14,0.11,0.14}
};
for(duration = 1; duration <=31; duration +=2)
{ cout<<"----------\n----------\nDuration: \t "<<duration<<"\n--------------------\n";
int numYears = assets[0].size();
vector<Portfolio> portfolios;
generatePortfolios(portfolios, Portfolio(5, 0), 0, 0, maxAssets);
vector<tuple<Portfolio, double>> portfolioResults;
for (const Portfolio& p : portfolios) {
vector<double> cagrList;
for (int start = 0; start <= numYears - duration; ++start) {
vector<double> subReturns;
for (int i = 0; i < duration; ++i) {
double portRet = portfolioReturn(assets, p, start + i);
subReturns.push_back(portRet);
}
double cagr = lumpSumCAGR(subReturns);
cagrList.push_back(cagr);
}
double bottom15 = percentile15(cagrList);
portfolioResults.emplace_back(p, bottom15);
}
sort(portfolioResults.begin(), portfolioResults.end(),
[](const auto& a, const auto& b) {
return get<1>(a) > get<1>(b);
});
cout << fixed << setprecision(2);
for (int i = 0; i < 2 && i < portfolioResults.size(); ++i) {
auto [weights, cagr] = portfolioResults[i];
cout << "Portfolio " << i+1 << ":\n";
for (int j = 0; j < numAssets; ++j) {
if (weights[j] > 0)
cout << " " << assetNames[j] << ": " << weights[j] << "%\n";
}
cout << " Bottom 15th Percentile CAGR: " << cagr * 100 << "%\n\n";
}
}
return 0;
}
I2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8dmVjdG9yPgojaW5jbHVkZSA8Y21hdGg+CiNpbmNsdWRlIDxhbGdvcml0aG0+CiNpbmNsdWRlIDxpb21hbmlwPgojaW5jbHVkZSA8dHVwbGU+Cgp1c2luZyBuYW1lc3BhY2Ugc3RkOwoKdXNpbmcgUG9ydGZvbGlvID0gdmVjdG9yPGludD47IC8vIHdlaWdodHMgaW4gcGVyY2VudCwgc2l6ZSA9IDUKCmRvdWJsZSBwZXJjZW50aWxlMTUodmVjdG9yPGRvdWJsZT4mIGRhdGEpIHsKICAgIHNvcnQoZGF0YS5iZWdpbigpLCBkYXRhLmVuZCgpKTsKICAgIHNpemVfdCBpbmRleCA9IHN0YXRpY19jYXN0PHNpemVfdD4oZmxvb3IoMC4xNSAqIGRhdGEuc2l6ZSgpKSk7CiAgICBpZiAoaW5kZXggPj0gZGF0YS5zaXplKCkpIGluZGV4ID0gZGF0YS5zaXplKCkgLSAxOwogICAgcmV0dXJuIGRhdGFbaW5kZXhdOwp9Cgpkb3VibGUgbHVtcFN1bUNBR1IoY29uc3QgdmVjdG9yPGRvdWJsZT4mIHJldHVybnMpIHsKICAgIGRvdWJsZSBjb21wb3VuZCA9IDEuMDsKICAgIGZvciAoZG91YmxlIHIgOiByZXR1cm5zKSB7CiAgICAgICAgY29tcG91bmQgKj0gKDEuMCArIHIpOwogICAgfQogICAgcmV0dXJuIHBvdyhjb21wb3VuZCwgMS4wIC8gcmV0dXJucy5zaXplKCkpIC0gMS4wOwp9Cgpkb3VibGUgcG9ydGZvbGlvUmV0dXJuKGNvbnN0IHZlY3Rvcjx2ZWN0b3I8ZG91YmxlPj4mIGFzc2V0cywgY29uc3QgUG9ydGZvbGlvJiB3ZWlnaHRzLCBpbnQgeWVhcikgewogICAgZG91YmxlIHJlc3VsdCA9IDAuMDsKICAgIGZvciAoaW50IGkgPSAwOyBpIDwgNTsgKytpKSB7CiAgICAgICAgcmVzdWx0ICs9ICh3ZWlnaHRzW2ldIC8gMTAwLjApICogYXNzZXRzW2ldW3llYXJdOwogICAgfQogICAgcmV0dXJuIHJlc3VsdDsKfQoKdm9pZCBnZW5lcmF0ZVBvcnRmb2xpb3ModmVjdG9yPFBvcnRmb2xpbz4mIHBvcnRmb2xpb3MsIFBvcnRmb2xpbyBjdXJyZW50LCBpbnQgcG9zLCBpbnQgdG90YWxXZWlnaHQsIGludCBtYXhBc3NldHMpIHsKICAgIGlmIChwb3MgPT0gNSkgewogICAgICAgIGlmICh0b3RhbFdlaWdodCA9PSAxMDApIHsKICAgICAgICAgICAgaW50IG5vblplcm8gPSBjb3VudF9pZihjdXJyZW50LmJlZ2luKCksIGN1cnJlbnQuZW5kKCksIFtdKGludCB3KSB7IHJldHVybiB3ID4gMDsgfSk7CiAgICAgICAgICAgIGlmIChub25aZXJvIDw9IG1heEFzc2V0cykgewogICAgICAgICAgICAgICAgcG9ydGZvbGlvcy5wdXNoX2JhY2soY3VycmVudCk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgcmV0dXJuOwogICAgfQogICAgZm9yIChpbnQgdyA9IDA7IHcgPD0gMTAwIC0gdG90YWxXZWlnaHQ7IHcgKz0gNSkgewogICAgICAgIGN1cnJlbnRbcG9zXSA9IHc7CiAgICAgICAgZ2VuZXJhdGVQb3J0Zm9saW9zKHBvcnRmb2xpb3MsIGN1cnJlbnQsIHBvcyArIDEsIHRvdGFsV2VpZ2h0ICsgdywgbWF4QXNzZXRzKTsKICAgIH0KfQoKaW50IG1haW4oKSB7CiAgICBjb25zdCBpbnQgbnVtQXNzZXRzID0gNTsKICAgIGNvbnN0IGludCBtYXhBc3NldHMgPSA0OwogICAgY29uc3QgaW50IHN0ZXAgPSA1OwogICAgIGludCBkdXJhdGlvbiA9IDE1OwoKICAgIC8vIEFzc2V0IG5hbWVzCiAgICB2ZWN0b3I8c3RyaW5nPiBhc3NldE5hbWVzID0geyJzY3Y2MExjZzE1IiwgInNjdjk1IiwgInZ0aSIsICIxMWFsbCIsICIzM2dsZCJ9OwoKICAgIC8vIFJlcGxhY2UgdGhlc2Ugd2l0aCBhY3R1YWwgcmV0dXJuIGRhdGEgZm9yIGVhY2ggYXNzZXQKICAgIHZlY3Rvcjx2ZWN0b3I8ZG91YmxlPj4gYXNzZXRzID0gewogICAgLy8gQXNzZXQgMQogICAgey0wLjA0LDAuMTQsMC4xNSwtMC4xNCwtMC4xOSwwLjI2LDAuMjgsMC4wNCwwLjA4LDAuMjgsMC4wOCwtMC4wNiwwLjI0LDAuMjIsLTAuMDEsMC4yNiwwLjE0LC0wLjAyLDAuMTQsMC4xNiwKICAgICAtMC4xNiwwLjI5LDAuMTUsMC4xOCwtMC4wMywwLjIwLDAuMTMsMC4yMCwwLjAyLDAuMDgsLTAuMDQsMC4wNCwtMC4wNywwLjMzLDAuMTIsMC4wNiwwLjE1LDAuMDEsLTAuMjYsMC4zMSwKICAgICAwLjIyLC0wLjAzLDAuMTUsMC4xOSwwLjA4LC0wLjA2LDAuMTYsMC4xMywtMC4xMSwwLjIxLDAuMTMsMC4xMiwtMC4xOCwwLjE1LDAuMTJ9LAoKICAgIC8vIEFzc2V0IDIKICAgIHstMC4wMywwLjEzLDAuMDYsLTAuMjksLTAuMzEsMC40MywwLjQ2LDAuMDgsMC4wNywwLjIxLDAuMTIsMC4wNiwwLjMxLDAuMzgsMC4wMiwwLjMxLDAuMTIsLTAuMDgsMC4yMywwLjE0LAogICAgIC0wLjIyLDAuMzcsMC4yNCwwLjE5LC0wLjAyLDAuMjQsMC4xNywwLjMxLC0wLjA2LDAuMDIsMC4wNCwwLjExLC0wLjEzLDAuNDEsMC4xNywwLjA0LDAuMTUsLTAuMDksLTAuMzIsMC4zMywKICAgICAwLjIzLC0wLjA2LDAuMTgsMC4zMiwwLjA5LC0wLjA2LDAuMjMsMC4wOSwtMC4xNSwwLjIwLDAuMDMsMC4xOSwtMC4xNiwwLjExLDAuMDh9LAoKICAgIC8vIEFzc2V0IDMKICAgIHstMC4wMiwwLjExLDAuMTIsLTAuMjUsLTAuMzYsMC4zMCwwLjIxLC0wLjExLC0wLjAxLDAuMDUsMC4xNiwtMC4xMSwwLjE5LDAuMTksMC4wMSwwLjI4LDAuMTUsLTAuMDIsMC4xMiwwLjI0LAogICAgIC0wLjEwLDAuMjksMC4wNiwwLjA4LC0wLjAxLDAuMzQsMC4xOSwwLjMwLDAuMjQsMC4xOSwtMC4xMywtMC4wOSwtMC4yNSwwLjMwLDAuMDgsMC4wMiwwLjEyLDAuMDEsLTAuMzcsMC4yNiwKICAgICAwLjE2LC0wLjAyLDAuMTQsMC4zMSwwLjEyLDAuMDAsMC4xMSwwLjE5LC0wLjA3LDAuMjgsMC4xOSwwLjE4LC0wLjI0LDAuMjIsMC4yMH0sCgogICAgLy8gQXNzZXQgNAogICAgey0wLjAyLDAuMTIsMC4xNCwtMC4xMywtMC4xOSwwLjIyLDAuMjYsMC4wNCwwLjA2LDAuMTksMC4wNywtMC4wNywwLjIwLDAuMjAsMC4wMywwLjI2LDAuMTksMC4wMCwwLjE0LDAuMTUsCiAgICAgLTAuMTYsMC4yNCwwLjEwLDAuMjAsLTAuMDIsMC4xOSwwLjEyLDAuMTcsMC4wMSwwLjA1LDAuMDEsMC4wMiwtMC4wNiwwLjMyLDAuMTQsMC4wNywwLjE3LC0wLjAxLC0wLjI1LDAuMjUsCiAgICAgMC4yMCwwLjAwLDAuMTQsMC4xNSwwLjExLC0wLjA0LDAuMTMsMC4xMiwtMC4xMCwwLjIwLDAuMDcsMC4xMiwtMC4xOSwwLjEwLDAuMDh9LAoKICAgIC8vIEFzc2V0IDUgKG5ldykKICAgIHstMC4wMSwwLjExLDAuMTksMC4wMywtMC4wNSwwLjEzLDAuMjMsMC4wNywwLjEwLDAuNDQsMC4wOCwtMC4xMywwLjIwLDAuMTQsLTAuMDQsMC4xOSwwLjE2LDAuMDIsMC4wNiwwLjA4LAogICAgIC0wLjE1LDAuMTcsMC4wOCwwLjE2LC0wLjAzLDAuMTcsMC4xMSwwLjEyLDAuMDAsMC4wMiwtMC4wMSwwLjAyLC0wLjAyLDAuMzAsMC4xMSwwLjA3LDAuMTksMC4wMywtMC4yMSwwLjI1LAogICAgIDAuMjMsMC4wMCwwLjEzLDAuMDgsMC4wOSwtMC4wNiwwLjEzLDAuMTAsLTAuMDgsMC4yMCwwLjEwLDAuMTEsLTAuMTQsMC4xMSwwLjE0fQp9OwoKZm9yKGR1cmF0aW9uID0gMTsgZHVyYXRpb24gPD0zMTsgZHVyYXRpb24gKz0yKQogICAgeyBjb3V0PDwiLS0tLS0tLS0tLVxuLS0tLS0tLS0tLVxuRHVyYXRpb246IFx0ICI8PGR1cmF0aW9uPDwiXG4tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIjsKICAgIAlpbnQgbnVtWWVhcnMgPSBhc3NldHNbMF0uc2l6ZSgpOwogICAgdmVjdG9yPFBvcnRmb2xpbz4gcG9ydGZvbGlvczsKICAgIGdlbmVyYXRlUG9ydGZvbGlvcyhwb3J0Zm9saW9zLCBQb3J0Zm9saW8oNSwgMCksIDAsIDAsIG1heEFzc2V0cyk7CgogICAgdmVjdG9yPHR1cGxlPFBvcnRmb2xpbywgZG91YmxlPj4gcG9ydGZvbGlvUmVzdWx0czsKCiAgICBmb3IgKGNvbnN0IFBvcnRmb2xpbyYgcCA6IHBvcnRmb2xpb3MpIHsKICAgICAgICB2ZWN0b3I8ZG91YmxlPiBjYWdyTGlzdDsKCiAgICAgICAgZm9yIChpbnQgc3RhcnQgPSAwOyBzdGFydCA8PSBudW1ZZWFycyAtIGR1cmF0aW9uOyArK3N0YXJ0KSB7CiAgICAgICAgICAgIHZlY3Rvcjxkb3VibGU+IHN1YlJldHVybnM7CiAgICAgICAgICAgIGZvciAoaW50IGkgPSAwOyBpIDwgZHVyYXRpb247ICsraSkgewogICAgICAgICAgICAgICAgZG91YmxlIHBvcnRSZXQgPSBwb3J0Zm9saW9SZXR1cm4oYXNzZXRzLCBwLCBzdGFydCArIGkpOwogICAgICAgICAgICAgICAgc3ViUmV0dXJucy5wdXNoX2JhY2socG9ydFJldCk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZG91YmxlIGNhZ3IgPSBsdW1wU3VtQ0FHUihzdWJSZXR1cm5zKTsKICAgICAgICAgICAgY2Fnckxpc3QucHVzaF9iYWNrKGNhZ3IpOwogICAgICAgIH0KCiAgICAgICAgZG91YmxlIGJvdHRvbTE1ID0gcGVyY2VudGlsZTE1KGNhZ3JMaXN0KTsKICAgICAgICBwb3J0Zm9saW9SZXN1bHRzLmVtcGxhY2VfYmFjayhwLCBib3R0b20xNSk7CiAgICB9CgogICAgc29ydChwb3J0Zm9saW9SZXN1bHRzLmJlZ2luKCksIHBvcnRmb2xpb1Jlc3VsdHMuZW5kKCksCiAgICAgICAgIFtdKGNvbnN0IGF1dG8mIGEsIGNvbnN0IGF1dG8mIGIpIHsKICAgICAgICAgICAgIHJldHVybiBnZXQ8MT4oYSkgPiBnZXQ8MT4oYik7CiAgICAgICAgIH0pOwoKICAgIGNvdXQgPDwgZml4ZWQgPDwgc2V0cHJlY2lzaW9uKDIpOwogICAgZm9yIChpbnQgaSA9IDA7IGkgPCAyICYmIGkgPCBwb3J0Zm9saW9SZXN1bHRzLnNpemUoKTsgKytpKSB7CiAgICAgICAgYXV0byBbd2VpZ2h0cywgY2Fncl0gPSBwb3J0Zm9saW9SZXN1bHRzW2ldOwogICAgICAgIGNvdXQgPDwgIlBvcnRmb2xpbyAiIDw8IGkrMSA8PCAiOlxuIjsKICAgICAgICBmb3IgKGludCBqID0gMDsgaiA8IG51bUFzc2V0czsgKytqKSB7CiAgICAgICAgICAgIGlmICh3ZWlnaHRzW2pdID4gMCkKICAgICAgICAgICAgICAgIGNvdXQgPDwgIiAgIiA8PCBhc3NldE5hbWVzW2pdIDw8ICI6ICIgPDwgd2VpZ2h0c1tqXSA8PCAiJVxuIjsKICAgICAgICB9CiAgICAgICAgY291dCA8PCAiICBCb3R0b20gMTV0aCBQZXJjZW50aWxlIENBR1I6ICIgPDwgY2FnciAqIDEwMCA8PCAiJVxuXG4iOwogICAgfQogICAgfQogICAgcmV0dXJuIDA7Cn0K