return.C -- performance tracking tool (fwd)
I have been using this program for a while and decided to share it with the other readers. It is written in C++. /*********************************************************************** This program calculates the total portfolio return. It may be used to find out an individual's stock picking performance and compare it to some benchmark. Right now it compiles under Unix, but I would expect it to work under DOS and Win32 console interface. To compile it, type gcc -lm -lg++ -o return return.C This program was written because I was not satisfied with the existing portfolio tracking websites due to their extremely limited and incorrect functionality. It works by treating your investments as a "mutual fund". It tracks the number of "shares" held. It means that additional cash infusions, withdrawals, stock splits, sales of securities do not affect the per share value. This is done so that the changing size of the portfolio would not skew the estimate of the true performance of the stock picker. However, dividends, fees and recorded capital appreciation do affect the per share value. Short sales are allowed by specifying a negative stock amount. I strongly suggest that investors regularly record stock prices into their transaction files (using the VAL operator) to monitor how they are doing. USAGE: portfolio-return transaction-file-name TICKER=price TICKER=price ... EXAMPLE portfolio-return MYSTOCKS.TXT MSFT=130 T=65.56 It reads a text file that contains the record of transactions, and prices, and requests to print out the portfolio value. # This is a typical file used to calculate total return. # This file contains comments, marked by "#" characters, # and transaction info. # # Transaction info consists of records of the following form: # # INV Amount -- tells how much was invested in Category # PUR Ticker numshares share_price -- stock purchase info # SAL Ticker numshares share_price -- stock sale # DIV Ticker Amount -- how much dividend was received # WDR Amount -- cash withdrawal # VAL Ticker Amount -- Value per share # SPL Ticker new_to_old -- stock split # FEE amount -- various fees (like margin loan # interest, commission etc) # (negative FEE means incoming cash, from e.g interest on the cash held # by the broker) # PRN Comment -- Print portfolio with Comment # Happy investing. Copyright (c) Igor Chudov 1997. ichudov@algebra.com http://www.algebra.com/~ichudov GNU Copyright applies. There is NO WARRANTY WHATSOEVER. By using this program you agree to indemnify and hold it author harmless from any liabilities resulting in connection with your use of the program. You are allowed to copy this program freely provided that the copyright notice remains intact. */ #include <stdio.h> #include <iostream.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #define Money double #define StockTicker char * #define Date int class Stock { public: Stock( const StockTicker theTicker, int theQuantity = 0, Money theValue = 0.0 ) : Ticker( strdup( theTicker ) ), Quantity( theQuantity ), Value( theValue ) { // nothing } ~Stock( void ) { delete[] Ticker; } void Purchase( int theQuantity ) { Quantity += theQuantity; } int GetQuantity( void ) const { return Quantity; }; void SetValue( Money theValue ) { Value = theValue; } Money GetValue( void ) const { return Value; } const StockTicker GetTicker( void ) const { return Ticker; } void Split( Money factor ) { Quantity *= factor, Value /= factor; } private: Money Value; StockTicker Ticker; int Quantity; }; typedef Stock * PStock; class Portfolio { public: Portfolio( void ); ~Portfolio( void ); // What can happen to the portfolios: // Add cash to the portfolio void Invest( Money Cash ); // Purchase (or sell (even short) if Quantity is negative) void Purchase( const StockTicker Ticker, int Quantity, Money Value ); // Dividend received, 1 success, 0 failure int Dividend( const StockTicker Ticker, Money DividendPerShare ); // Split, factor is new # of shares to old # of shares, 1 success, 0 failure int Split( const StockTicker Ticker, Money factor ); // Withdrawal void Withdraw( Money Amount ); // Fee -- margin interest, commission etc void Fee( Money amount ); // Change in Stock valuation int Valuation( StockTicker Ticker, Money theValue ); ///////////////////////////// // Equity = Cash + StockValue Money Equity( void ) const; Money EquityPerShare( void ) const { return Equity()/NumShares; } int GetNumShares( void ) const { return NumShares; } void Print( ostream & os, const char * comment = 0 ); protected: void Revaluate( void ); int FindStock( const StockTicker Ticker ); private: Stock ** Stocks; int NumberStocks; int MaxNumberStocks; Money Return; Money LastDate; Money Cash; Money NumShares; }; Portfolio::Portfolio( void ) : NumberStocks( 0 ), MaxNumberStocks( 100 ), Cash( 0.0 ), NumShares( 0 ), Return( 0.0 ), LastDate( 0 ) { Stocks = new PStock[ MaxNumberStocks ]; } Portfolio::~Portfolio( void ) { delete[] Stocks; } void Portfolio::Invest( Money theCash ) { //cout << "investing " << theCash << endl; if( NumShares != 0 ) { NumShares += theCash/EquityPerShare(); Cash += theCash; } else { Cash = theCash; NumShares = Cash; } } void Portfolio::Withdraw( Money Amount ) { Invest( -Amount ); } void Portfolio::Fee( Money Amount ) { Cash -= Amount; } int Portfolio::FindStock( const StockTicker Ticker ) { for( int i = 0; i < NumberStocks; i++ ) if( !strcmp( Ticker, Stocks[i]->GetTicker() ) ) { return i; break; } return -1; } void Portfolio::Purchase( const StockTicker Ticker, int Quantity, Money Value ) { int found = FindStock( Ticker ); if( found == -1 ) // need to add a stock { if( NumberStocks == MaxNumberStocks ) // need to resize { Stock ** NewStocks = new PStock[ MaxNumberStocks * 2 ]; memcpy( NewStocks, Stocks, sizeof( Stock ) * MaxNumberStocks ); delete[] Stocks; Stocks = NewStocks; MaxNumberStocks *= 2; } found = NumberStocks++; Stocks[found] = new Stock( Ticker, Quantity, Value ); } else { Stocks[found]->Purchase( Quantity ); Stocks[found]->SetValue( Value ); } Cash -= Quantity * Value; } int Portfolio::Dividend( const StockTicker Ticker, Money DividendPerShare ) { int found = FindStock( Ticker ); if( found == -1 ) return 0; Cash += DividendPerShare * Stocks[found]->GetQuantity(); } int Portfolio::Split( const StockTicker Ticker, Money Factor ) { int found = FindStock( Ticker ); if( found == -1 ) return 0; Stocks[found]->Split( Factor ); } int Portfolio::Valuation( StockTicker Ticker, Money theValue ) { int found = FindStock( Ticker ); if( found == -1 ) return 0; // failure Stocks[found]->SetValue( theValue ); return 1; // success } Money Portfolio::Equity( void ) const { Money e = Cash; for( int i =0; i < NumberStocks; i++ ) e += Stocks[i]->GetQuantity() * Stocks[i]->GetValue(); return e ; } void Portfolio::Print( ostream & os, const char * comment ) { os << endl << "Portfolio: " << (comment ? comment : "" ) << endl; for( int i = 0; i < NumberStocks; i++ ) os << Stocks[i]->GetTicker() << " " << Stocks[i]->GetQuantity() << " " << Stocks[i]->GetValue() << ", Total = " << Stocks[i]->GetValue() * Stocks[i]->GetQuantity() << endl; os << "Cash: " << Cash << endl; cout << "Equity = " << Equity() << endl; cout << "Number of Shares = " << GetNumShares() << endl; cout << "Share Value = " << EquityPerShare() << endl; } void ReadHistory( Portfolio & portfolio, FILE * f ) { while( !feof( f ) ) { char buf[2048]; buf[0] = 0; fgets( buf, sizeof( buf ), f ); //cout << buf; char * p = strchr( buf, '#' ); if( p != 0 ) *p = 0; // Comment # for( p = buf; *p; p++ ) *p = toupper( *p ); if( !strncmp( buf, "INV", 3 ) ) { //cout << "investing" << endl; float amount; if( sscanf( buf+4, "%f", &amount ) == 1 ) portfolio.Invest( amount ); else fprintf( stderr, "Invalid transaction: %s\n", buf ); } else if( !strncmp( buf, "WDR", 3 ) ) { float amount; if( sscanf( buf+4, "%f", &amount ) == 1 ) portfolio.Invest( -amount ); else fprintf( stderr, "Invalid transaction: %s\n", buf ); } else if( !strncmp( buf, "PUR", 3 ) ) { float price; int numshares; char ticker[2048]; if( sscanf( buf+4, "%s %d %f", &ticker, &numshares, &price ) == 3 ) portfolio.Purchase( ticker, numshares, price ); else fprintf( stderr, "Invalid transaction: %s\n", buf ); } else if( !strncmp( buf, "FEE", 3 ) ) { float amount; if( sscanf( buf+4, "%f", &amount ) == 1 ) portfolio.Fee( amount ); else fprintf( stderr, "Invalid transaction: %s\n", buf ); } else if( !strncmp( buf, "SAL", 3 ) ) { float price; int numshares; char ticker[2048]; if( sscanf( buf+4, "%s %d %f", &ticker, &numshares, &price ) == 3 ) portfolio.Purchase( ticker, -numshares, price ); else fprintf( stderr, "Invalid transaction: %s\n", buf ); } else if( !strncmp( buf, "DIV", 3 ) ) { float div; char ticker[2048]; if( sscanf( buf+4, "%s %f", &ticker, &div ) == 2 ) portfolio.Dividend( ticker, div ); else fprintf( stderr, "Invalid transaction: %s\n", buf ); } else if( !strncmp( buf, "VAL", 3 ) ) { float val; char ticker[2048]; if( sscanf( buf+4, "%s %f", &ticker, &val ) == 2 ) portfolio.Valuation( ticker, val ); else fprintf( stderr, "Invalid transaction: %s\n", buf ); } else if( !strncmp( buf, "SPL", 3 ) ) { float factor; char ticker[2048]; if( sscanf( buf+4, "%s %f", &ticker, &factor ) == 2 ) portfolio.Split( ticker, factor ); else fprintf( stderr, "Invalid transaction: %s\n", buf ); } else if( !strncmp( buf, "PRN", 3 ) ) { portfolio.Print( cout, buf + 4 ); } } } int main( int argc, char *argv[] ) { Portfolio portfolio; FILE * input; if( argc >= 2 ) { if( (input = fopen( argv[1], "r" )) == 0 ) { fprintf( stderr, "Can't open %s.\n" "Usage: %s activity-file TICK=val TICK1=val ...\n", argv[1], argv[0] ); exit( 1 ); } } else { input = stdin; } ReadHistory( portfolio, input ); for( argc--; argc > 1; argc-- ) // process arguments of form "TICKER=value" { char buf[ 2048 ]; strcpy( buf, argv[argc] ); for( char * p = buf; *p; p++ ) *p = toupper( *p ); char * pvalue = strchr( buf, '=' ); if( pvalue == 0 ) { cerr << "Wrong argument: " << argv[argc] << ". Must be of form TICKER=value" << endl; continue; } *pvalue++=0; float value; if( sscanf( pvalue, "%f", &value ) != 1 ) { cerr << "Wrong argument: " << argv[argc] << ". Must be of form TICKER=value" << endl; continue; } if( !portfolio.Valuation( buf, value ) ) { cerr << "Ticker " << buf << " not found in the portfolio!" << endl; } } portfolio.Print( cout, "Final Result" ); printf( "\n\n" "Copyright (C) Igor Chudov, ichudov@algebra.com,\n" " http://www.algebra.com/~ichudov\n\n" "No warranty is provided with this free program. GNU Copyright applies.\n" ); }
Igor Chudov @ home wrote:
I have been using this program for a while and decided to share it with the other readers. It is written in C++.
this is not a financial mailing list. and this code is not C++... it just needs a C++ compiler to compile. real C++ code doesn't use fprintf, scanf, nor strcmp... $0.02 alx
In <34F2CF5D.8276D7FB@infomaniak.ch>, on 02/24/98 at 02:47 PM, Alexandre Maret <amaret@infomaniak.ch> said:
Igor Chudov @ home wrote:
I have been using this program for a while and decided to share it with the other readers. It is written in C++.
this is not a financial mailing list. and this code is not C++... it just needs a C++ compiler to compile. real C++ code doesn't use fprintf, scanf, nor strcmp...
Let's not forget that *real* C++ apps are bloated several time that of comparable C apps (god forbid anyone write some tight ASM code). $1.00 <== Inflation -- --------------------------------------------------------------- William H. Geiger III http://users.invweb.net/~whgiii Geiger Consulting Cooking With Warp 4.0 Author of E-Secure - PGP Front End for MR/2 Ice PGP & MR/2 the only way for secure e-mail. OS/2 PGP 2.6.3a at: http://users.invweb.net/~whgiii/pgpmr2.html --------------------------------------------------------------- Tag-O-Matic: Get OS/2 - the best Windows tip around!
William H. Geiger III wrote:
at 02:47 PM, Alexandre Maret <amaret@infomaniak.ch> said:
Igor Chudov @ home wrote:
I have been using this program for a while and decided to share it with the other readers. It is written in C++.
this is not a financial mailing list. and this code is not C++... it just needs a C++ compiler to compile. real C++ code doesn't use fprintf, scanf, nor strcmp...
Excuse me, mah friend, it is C++ in its pure form, with classes and all.
Let's not forget that *real* C++ apps are bloated several time that of comparable C apps (god forbid anyone write some tight ASM code).
Not this one. But you have a good point. - Igor.
Alexandre Maret wrote:
Igor Chudov @ home wrote:
I have been using this program for a while and decided to share it with the other readers. It is written in C++.
this is not a financial mailing list.
This is not a gun mailing list, not a chemical mailing list, not a flame mailing list, but all of these things are also discussed.
and this code is not C++... it just needs a C++ compiler to compile. real C++ code doesn't use fprintf, scanf, nor strcmp...
It is the purest form of C++. As my grandma used to say, take your eyes in your hands and take a look. - Igor.
participants (3)
-
Alexandre Maret
-
ichudov@algebra.com
-
William H. Geiger III