Virtrend: virtual yield


Name origin

Virtrend is not a trend, so it’s not to be read as vir-trend (from viro? A word for ‘man’ in Interlingua and Esperanto), but instead as virt-rend. The name of this program virtrend.c, a first version of which I wrote in 2002, derives from the Dutch ‘virtueel rendement’. Rendement means yield, return, returns, profit.

The ‘virtueel’ or ‘virtual’ part means the program calculates the effective fixed yearly interest percentage of a virtual, non-existent savings account, the return of which is the same as that of a given investment, in shares, bonds, an investment fund, precious metals like gold, etc. It might even be bitcoin, although personally I prefer to have nothing to do with that.

User interface

There is no web interface, and no graphical program interface for Mac, Windows, Linux, or anything. All there is, is a single source file in programming language C. You compile it and run it in the command line. A typical compilation command might be:
cc virtrend.c -o virtrend
If there’s no C compiler on your system, bad luck. Ask someone who understands about these things.

Virtrend takes its input from one or more text files, the names of which (or in fact: their paths) should be specified as arguments in the command line. Then virtrend’s output goes to standard out, stdout, that’s usually your screen, or a file or pipe if you redirect it.

Examples of input and output

Suppose you bought 1000 dollars (or euros, pounds, Swiss francs or whatever other currency) worth of shares, bonds, participations in an investment fund, ounces of gold, you name it. Now a year later, its net value is 1050, that is, you could sell them to receive that amount of money. But maybe you won’t sell, that doesn’t matter.

Then the input file might look like this:

# A simple input example for virtrend.

20211207   1000.00	# Invested for this amount.
20221207   1050		# Snapshot of current value.

As you can see, blank lines are ignored, and so is the hash sign (#) and anything that follows it in the same line of text. A typical line contains two items that are not ignored: a date in the format CCYYMMDD (century, year, month, day), any number of spaces or tabs, then an amount.

The amount may have a decimal point (not comma, locale is ignored) and optional pennies or cents. Don’t use a comma (nor a point or dot) to divide an amount in groups of digits: all digits must be adjacent.

The output in this example would be:

Reading file example
After 20 attempts:  4.988%
Result compared with -4%:     89.87
Result compared with -3%:     79.92
Result compared with -2%:     69.96
Result compared with -1%:     59.99
Result compared with  0%:     50.00
Result compared with  1%:     40.00
Result compared with  2%:     29.99
Result compared with  3%:     19.96
Result compared with  4%:      9.93
Result compared with  5%:     -0.13
Result compared with  6%:    -10.19
Result compared with  7%:    -20.26
Result compared with  8%:    -30.35

As was to be expected, the equivalent savings account interest percentage is about 5, because 5 percent of one thousand is fifty, and 1000 plus 50 equals 1050.

Compared with a zero percent savings account, this investment is 50 dollars more profitable. Against one percent, the difference is 40, etc.


Why is the calculated result 4.988% and not exactly five percent? And why is the difference with the 5% percent savings account not exactly 0.00, but -0.13?

Two reasons:

  1. Virtrend accurately calculates interest periods in days, using the calendar. But it also assumes any year has the average length of 365.25 days, although in reality some have 365 and some have 366. So if in the example, you change the years 2021 and 2022 to 2019 and 2020, which does include a leap year and February 29th, result will be slightly different. But really only slightly.

  2. Virtrend assumes the interest on the virtual savings account is paid out every first of January. So in the example, interest earned between December 7th and January 1st, from then on also earns interest. This compound interest or composite interest slightly changes results, depending on the dates.

Currency rate

In addition to the date and amount, there can be a third parameter: the currency rate. The default is 1.0. This used to be useful around the time of the introduction of the euro. An investment requiring 100 guilders a month, then continuing in euros, could be coded for results in euros as follows:

# [...]
20011130 100	2.20371
20011231 100	2.20371
20020131  45.38
20020228  45.38
# [...]

Plus or minus

An investment (purchase of stock) should a positive amount, preceded with an optional ASCII plus sign (+), or no sign at all. The same is true of later extra purchases.

Selling part of it should be negative amount, preceded by a simple ASCII dash serving as a minus sign (-).

A remaining end value at the date under consideration, in the last non-blank, non-comment line of the file, should be positive.

A dividend that is immediately reinvested as extra stock need not booked here. But a dividend paid out to a money account should. Likewise, costs or provisions charged against the existing stock (so partial shares are automatically sold to cover the costs) should not be booked, but charges against a money account should.

It is possible to make all amounts the reverse, to simulate a loan with periodic payments. The first amount, for taking out the loan, will then be negative. All payment terms are positive. Payment terms may include interest, or that may be paid and booked separately, also specified as a positive amount.

For example, a 12,000 dollar car loan, paying 26 monthly terms of 500 dollars, results in a interest rate of 7.529%, as calculated by virtrend.

One file or several

The pathnames of the files containing the input data are specified as command line arguments. That means there can be any number of files. Shell wildcards can be used. All files are read in succession, and only then the calculation starts, using the merged and sorted data from all files together.

This way you can calculate the average, effective yield on all investments combined.

In each file, the date in the last non-comment, non-empty line specifies a snapshot of the actual value (which can be zero if the investment has ended, i.e. everything was sold). In case there are several files, the latest date thus found is decisive. Any earlier end dates in other files are ignored, i.e., assumed to also be that latest date.

Normally, it only makes sense if all end dates were already the same, as specified, unless some files refer to past investments that have already ended.

Date sorting

Dates are internally sorted before the calculations start. That may lead to unexpected results in case of typos. So do carefully check your input data.

Why the attempts?

Why does it say “After 20 attempts: 4.988%”? Why isn’t the percentage calculated in one go?

Perhaps that is possible mathematically, using logarithms and exponential functions. That certainly works for one amount, one period and one percentage. But here, where changing amounts and several different durations can be involved, that is hard. Perhaps impossible.

That is why I chose a simple, naive and inefficient approach: start with some guessed percentage. Calculate the end value for that, and compare it with the actual, specified value. If too high, try a lower percentage. Once that is too low, go up a little again. And so on, until the algorithm converges to a near-perfect percentage, or the loop stops because of an excess number of tries.

This doesn’t require any mathematical functions, just multi­plications and additions. Quite a lot of them, actually. But with today’s hardware, that is not an issue. Even with quite a lot of data, large amounts which must match to the penny, and therefore a lot of tries, the whole approximation still only takes a few milli­seconds at most.

The approximation iterations can be observed by compiling the source file with the symbol DEBUG2 defined, like so:
cc virtrend.c -DDEBUG2 -o virtrend