// Oldham, Jeffrey D. // 2000 Feb 21 // CS3352 // Simulate Annual Withdrawal and Market Forces // Command-line Arguments: // 1. name of file containing historical data // 2. name of file containing asset allocations // omits year (first) column and inflation (last) column // 3. initial investment value // 4. annual increment amount // 5. number of years #include #include // has EXIT_SUCCESS #include #include // hold current line's data #include #include // has for_each() #include // has istream_iterator #include // has accumulate() #include // has multiplies() typedef double amount; typedef amount rate; typedef unsigned long year; typedef unsigned long stats; class yearData { public: year y; vector rates; yearData(void) {} yearData(const vector::size_type & s) : rates(s) {} friend istream& operator>>(istream&in, yearData & yd); }; // Read in one line of data into a vector. // If istringstream was available, this would be unnecessary. // input <- istream // vector with size equalling the number of columns // output-> istream // vector is field with data if the istream obliged istream & operator>>(istream & in, yearData& yd) { assert(!yd.rates.empty()); assert(in); // cannot figure out how to use an istream_iterator<>() :( if (in >> yd.y) for (vector::iterator pos = yd.rates.begin(); pos != yd.rates.end() && in >> *pos; ++pos) ; return in; } // Read the asset allocation file into a vector. // input <- name of asset allocation file // output-> vector with data vector obtainAssetAllocations(const char * filename) { const rate eps = 0.001; ifstream in(filename); if (!in) throw "missing asset allocation file"; vector v; copy(istream_iterator(in), istream_iterator(), back_inserter(v)); in.close(); if (fabs(accumulate(v.begin(), v.end(), 0.0) - 1.0) > eps) throw "asset allocation does not sum to 1.0"; return v; } // Multiply a rate and an amount. inline amount multiplyRateAndAmount(const rate & r, const amount & amt) { return (1.0 + r / 100.0) * amt; } // Determine the data's first and last years. // input <- name of data file // number of assets // references to store first and last year // output-> values are stored // throws an exception if any problem occurs // ASSUME the historical data fileis in numerical order according to year. void dataBeginEnd(const char file[], const unsigned long nuAssets, year & beginYear, year & endYear) { ifstream historical(file); if (!historical) { cerr << file << " not found\n"; throw "missing historical file"; } yearData yd(nuAssets+1); if (historical >> yd) { beginYear = endYear = yd.y; while (historical >> yd) endYear = yd.y; } historical.close(); return; } int main(int argc, char *argv[]) { // Check the command-line arguments. if (argc != 6) { cerr << argv[0] << ": historical-data-file asset-allocation-file initial-value annual-increment-amount number-of-years\n"; throw "incorrect command line"; } // Read the asset allocations. vector assetAllocation; try { assetAllocation = obtainAssetAllocations(argv[2]); } catch (...) { cerr << argv[0] << ": " << argv[2] << " not found\n"; throw "missing, empty, or incorrect asset allocation file"; } assert(assetAllocation.size() > 0); // Convert the command-line arguments. // current amounts for each asset // amounts.back() equals annual increment amount vector amounts(assetAllocation.size()+1); // no error checking :( const amount initial = strtod(argv[3], static_cast(0)); const amount increment = strtod(argv[4], static_cast(0)); const year nuYears = strtoul(argv[5], static_cast(0), 0); // Cycle through each year, collecting a statistic. year begin; // beginning of time frame stats nuTrials; // number of trials stats successes; // number of successful trials year beginYear, endYear; // first and last years in the data dataBeginEnd(argv[1], assetAllocation.size(), beginYear, endYear); for (begin = beginYear, nuTrials = successes = 0; begin <= endYear-nuYears+1; ++begin, ++nuTrials) { // Store values in amounts (void) transform(assetAllocation.begin(), assetAllocation.end(), amounts.begin(), bind2nd(multiplies(), initial)); amounts[assetAllocation.size()] = increment; // Open the input file. ifstream historical(argv[1]); if (!historical) { cerr << argv[0] << ": " << argv[1] << " not found\n"; throw "missing historical file"; } // column 0: year // column 1: large company stocks // column 2: small company stocks // column 3: long-term corporate bonds // column 4: long-term government bonds // column 5: intermediate-term government bonds // column 6: U.S. Treasury bills // column 7: inflation yearData yd(assetAllocation.size()+1); amount total; while (historical >> yd && yd.y < begin + nuYears) { #ifdef DEBUG cout << yd.y << "\t"; copy(yd.rates.begin(), yd.rates.end(), ostream_iterator(cout,"\t")); cout << endl; #endif if (begin <= yd.y) { total = inner_product(yd.rates.begin(), yd.rates.end(), amounts.begin(), 0.0, plus(), multiplyRateAndAmount); #ifdef DEBUG cout << "total: " << total << endl; #endif (void) transform(assetAllocation.begin(), assetAllocation.end(), amounts.begin(), bind2nd(multiplies(), total)); amounts.back() = multiplyRateAndAmount(yd.rates.back(), amounts.back()); // handle inflation #ifdef DEBUG cout << "amounts\t"; copy(amounts.begin(), amounts.end(), ostream_iterator(cout,"\t")); cout << endl; #endif } } #ifdef DEBUG2 // Print the total amount of assets. cout << accumulate(amounts.begin(), amounts.end()-1, 0.0) << endl; // Ignore the last entry, which is for withdrawal amount. #endif // DEBUG2 if (accumulate(amounts.begin(), amounts.end()-1, 0.0) >= 0.0) { ++successes; #ifdef DEBUG2 cout << "success: " << begin << '-' << begin+nuYears-1 << endl; #endif // DEBUG2 } #ifdef DEBUG2 else cout << "failure: " << begin << '-' << begin+nuYears-1 << endl; #endif // DEBUG2 historical.close(); } // Print the probability. cout << static_cast(successes)/nuTrials << endl; return EXIT_SUCCESS; }