From: jochen@busybit.mrz.lm.sub.org (Jochen Fahrner)
Newsgroups: de.etc.finanz
Subject: Programm zur Berechnung von Rendite/Effektivzins
Date: 25 Jul 92 10:42:00 GMT
Organization: Busybit's Software Labor, D-8068 Pfaffenhofen
X-Md4-Signature: 7e1894966f322059f12c362c1f7f6733

Das folgende Programm hat mir schon viele wertvolle Dienste geleistet.

cash ist ein kleines aber aeusserst nuetzliches Programm zur
Berechnung von Rendite bzw. Effektivzins bei Geldanlagen oder
Darlehen.

Das Programm ist fuer alle Faelle geeignet, in denen alle Zahlungen in
einer bestimmten Anlage- oder Darlehensform bekannt sind, nicht jedoch
der Nominal- oder der Effektivzins.

-------- hier abreissen -------------
#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 07/25/1992 09:34 UTC by jochen@busybit
# Source directory /u/jochen/kredit
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   4670 -rw-r----- cash.doc
#    446 -rw-r----- screen.doc
#   3613 -rw-r--r-- cash.c
#   3436 -rw-r----- screen.c
#    397 -rw-r--r-- screen.h
#     83 -rw-r----- bau
#
# ============= cash.doc ==============
if test -f 'cash.doc' -a X"$1" != X"-c"; then
	echo 'x - skipping cash.doc (File already exists)'
else
echo 'x - extracting cash.doc (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cash.doc' &&
cash ist ein kleines aber aeusserst nuetzliches Programm zur
Berechnung von Rendite bzw. Effektivzins bei Geldanlagen oder
Darlehen.
X
Das Programm ist fuer alle Faelle geeignet, in denen alle Zahlungen in
einer bestimmten Anlage- oder Darlehensform bekannt sind, nicht jedoch
der Nominal- oder der Effektivzins.
X
Es werden einfach alle Zahlungen innerhalb eines Zeitraumes eingegeben
und das Programm errechnet daraus den Effektivzins. Wichtig ist dabei,
dass ALLE Zahlungen eingegeben werden. Das ganze muss ein
'geschlossenes' System ergeben, d.h. es beginnt mit Kontostand Null
und es endet mit Kontostand Null. Alle Zahlungen in der einen Richtung
muessen ein entgegengesetztes Vorzeichen haben als alle Zahlungen in
der anderen Richtung. Waehlt man z.B. ein positives Vorzeichen fuer
alle Einzahlungen, so muessen Auszahlungen ein negatives Vorzeichen
haben.
X
Ein Beispiel verdeutlicht das am besten. Zu berechnen ist z.B. der
effektive Darlehenszins bei einem Bausparvertrag. Die Darlehenssumme
betraegt 5.000 DM. Es wird eine Bearbeitungsgebuehr von 2 % erhoben,
also 100 DM. Ausserdem wird jaehrlich eine Kontofuehrungsgebuehr von
12 DM berechnet. Die Rueckzahlungen sind wie folgt: In den ersten 4
Jahren werden monatlich 35 DM zurueckbezahlt. In den folgenden 4
Jahren sind es monatlich 40 DM und fuer den Rest der Laufzeit werden
45 DM monatlich bezahlt. Die Laufzeit betraegt 15 Jahre und 11
Monate. In dieser Zeit werden also 192 Monatsraten bezahlt.
X
Wichtig fuer das Programm ist ausserdem das Endedatum der Laufzeit.
Bis zu diesem Termin werden naemlich alle Zahlungen verzinst. Das Ende
darf ruhig groesser sein als das Datum der letzten Zahlung, aber
niemals kleiner (nach der letzten Zahlung ist der Kontostand Null,
dann fallen auch keine Zinsen mehr an. Deshalb ist es vollkommen egal,
ob 0 DM noch einen Tag oder ein Jahr auf dem Konto liegen.)
X
Fuer unser Beispiel soll die Darlehensauszahlung am 1.1.80 erfolgen,
das Ende soll der 1.1.96 sein, das liegt zwar einen Monat nach der
letzten Zahlung, aber was soll's; damit liegen wir auf jeden Fall auf
der sicheren Seite.
X
Die Zahlungen sehen also folgendermassen aus:
X
Datum  Betrag	Anzahl	Monate Abstand    Bemerkung
========================================================================
010180 -4900.00	1	1	Darlehen abzgl. 2 % Bearbeitungsgebuehr
010180 12.00	16	12	16 mal jaehrliche Kontofuehrungsgebuehr
010180 35.00	48	1	4 Jahre lang 35 Mark monatlich
010184 40.00	48	1	4 Jahre lang 40 Mark monatlich
010188 45.00	96	1	8 Jahre lang 45 Mark monatlich
X
Damit errechnet sich ein Effektivzins von 6.78 %
X
Die Bausparkasse hatte bei diesem Vertrag einen Nominalzins von 6.0 %
angegeben. Man sieht also, wie stark sich die horrenden Gebuehren
auswirken.
X
Man koennte es wohl auch Wucher nennen, was die Bausparkassen da
betreiben. Die "ueblichen" Gebuehren liegen bei den Bausparkassen bei
1% der Bausparsumme als Abschlussgebuehr und 2% der Darlehenssumme als
Auszahlungsgebuehr. Das sind bei einer Bausparsumme von 100.000 DM mit
einem Darlehensanteil von 50.000 DM satte 1000 Mark plus 1000 Mark =
2000 Mark Gebuehren, die Kontofuehrungsgebuehr noch gar nicht
mitgerechnet. 2000 Mark dafuer, dass die meine Daten in ihren Computer
tippen ! Man stelle sich mal vor, ein Autohaendler wuerde 1% vom
Kaufpreis verlangen, als Gebuehr fuer die Ausstellung des
Kaufvertrages !
X
Bei einem Bausparvertrag sollte man auch noch an andere Kosten denken:
Die Bausparkasse verlangt einen Grundbucheintrag. Die Kosten fuer
Eintragung und Loeschung sind auch nicht gerade gering. Ausserdem
verlangt die Bausparkasse, dass man eine Risikolebensversicherung
abschliesst. Damit sie auch wirklich ganz ganz sicher ihr Geld
zurueckbekommen.
X
Soviel zum Thema "Bausparen". Nun zur Installation.
X
Die Programme cash.c und screen.c werden uebersetzt und mit -lcurses
und -lm gebunden. Wer keine curses-Bibliothek hat, kann die
Dateneingabe auch ganz einfach auf normales "scanf" umstellen.
screen.c ist dann nicht erforderlich.
X
screen.c enthaelt ein paar nuetzliche Routinen zur Anzeige und Eingabe
von Bildschirmmasken. Die Bedienung ist in screen.doc beschrieben.
X
In cash wird eine Eingabemaske mit Return beendet. Mit Escape wird die
gesamte Dateneingabe beendet und mit der Berechnung begonnen (die mit
Escape beendete Eingabe ist *nicht* mehr Bestandteil der
Berechnungsdaten).
X
Fuer eine wiederholte Berechnung mit leicht geaenderten Daten ist es
nuetzlich, wenn man die Daten in eine Datei schreiben kann. Ein
Beispiel fuer solch eine Eingabedatei findet sich in "bau".
Man ruft das Programm dann auf mit "cash -f bau".
X
Lasst Euch von den Banken nicht uebers Ohr hauen.
X
Jochen
(jochen@busybit.mrz.sub.org)
SHAR_EOF
chmod 0640 cash.doc ||
echo 'restore of cash.doc failed'
Wc_c="`wc -c < 'cash.doc'`"
test 4670 -eq "$Wc_c" ||
	echo 'cash.doc: original size 4670, current size' "$Wc_c"
fi
# ============= screen.doc ==============
if test -f 'screen.doc' -a X"$1" != X"-c"; then
	echo 'x - skipping screen.doc (File already exists)'
else
echo 'x - extracting screen.doc (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'screen.doc' &&
screen.c enthaelt ein nuetzliches Interface zur curses-Bibliothek.
Es vereinfacht die Anzeige und Eingabe von ganzen Bildschirmmasken.
X
Mit TAB gelangt man bei der Eingabe von Feld zu Feld. Mit Return oder
Escape wird eine Eingabe beendet.
X
Innerhalb eines Feldes kann man mit Emacs-kompatiblen Control-Codes
editieren. Welche Control-Codes implementiert sind, ist leicht aus der
kleinen Source zu entnehmen.
X
Jochen
(jochen@busybit.mrz.sub.org)
SHAR_EOF
chmod 0640 screen.doc ||
echo 'restore of screen.doc failed'
Wc_c="`wc -c < 'screen.doc'`"
test 446 -eq "$Wc_c" ||
	echo 'screen.doc: original size 446, current size' "$Wc_c"
fi
# ============= cash.c ==============
if test -f 'cash.c' -a X"$1" != X"-c"; then
	echo 'x - skipping cash.c (File already exists)'
else
echo 'x - extracting cash.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cash.c' &&
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "screen.h"
X
#define abs(a)	(a < 0 ? (-a) : (a))
#define sgn(a)	(a < 0 ? (-1) : (1))
X
static FILE *infile;
X
double datdiff(a,b)
long a,b;
{
X	int tta, ttb, mma, mmb, jja, jjb;
X
X	jja = a % 100;
X	jjb = b % 100;
X	if (jja < 70) jja += 100;
X	if (jjb < 70) jjb += 100;
X	mma = a / 100 % 100;
X	mmb = b / 100 % 100;
X	tta = a / 10000 % 100;
X	ttb = b / 10000 % 100;
X	if (tta > 30) tta = 30;
X	if (ttb > 30) ttb = 30;
X
X	return ((jja -jjb) * 360 + (mma - mmb) * 30 + tta - ttb) / 360.00;
}
X
long datadd(a,mon)
long a;
int mon;
{
X	int tt,mm,jj;
X
X	jj = a % 100;
X	mm = a / 100 % 100 + mon;
X	tt = a / 10000 % 100;
X
X	while (mm > 12) {
X		jj++;
X		mm -= 12;
X	}
X	return tt * 10000L + mm * 100L + jj;
}
X
struct RATE {
X	long zahldat;
X	double betrag;
X	double dauer;
};
X
struct RATE rate[1000];
int anzrat;
X
long ende;
X
char str_datum[7];
char str_betrag[11];
char str_anzahl[5];
char str_abstand[5] = {"1"};
char old_datum[7];
char old_betrag[11];
char old_anzahl[5];
char old_abstand[5];
X
Screen maske[] = {
X  {1,10,0,"Cash: Eingabe der Zahlungen"},
X  {2,10,0,"==========================="},
X  {4,10,0,"Letzte Eingabe:"},
X  {4,26,0,old_datum},
X  {4,33,0,old_betrag},
X  {4,44,0,old_anzahl},
X  {4,49,0,old_abstand},
X  {6,10,0,"Datum erste Rate: TTMMJJ:"},
X  {6,36,6,str_datum},
X  {7,10,0,"Betrag .................:"},
X  {7,36,10,str_betrag},
X  {8,10,0,"Anzahl .................:"},
X  {8,36,4,str_anzahl},
X  {8,41,0,"Abstand (Monate) :"},
X  {8,60,4,str_abstand}
};
X
int ratein()
{
X  long datum;
X  double betrag;
X  int i, anzahl;
X  int abstand = 0;
X  char zeile[80];
X  extern long atol();
X  extern double atof();
X
X  if (infile) {			/* Eingabe von Datei */
X    if (fgets(zeile,sizeof(zeile),infile) == NULL)
X      return 0;
X    sscanf(zeile,"%ld %lf %d %d",&datum,&betrag,&anzahl,&abstand);
X  }
X  else {			/* Dialogteil */
X    strcpy(old_datum,str_datum);
X    strcpy(old_betrag,str_betrag);
X    strcpy(old_anzahl,str_anzahl);
X    strcpy(old_abstand,str_abstand);
X
X    *str_datum = *str_betrag = *str_anzahl = 0;
X    strcpy(str_abstand,"1");
X
X    clear();
X    display(maske);
X    if (accept(maske) == 27) return 0;
X
X    datum = atol(str_datum);
X    betrag = atof(str_betrag);
X    anzahl = atoi(str_anzahl);
X    abstand = atoi(str_abstand);
X  }
X
X  /* Aufbau der Zahlungstabelle */
X
X  for (i=0; i<anzahl; i++) {
X    rate[anzrat].zahldat = datum;
X    rate[anzrat].betrag = betrag;
X    rate[anzrat].dauer = datdiff(ende,datum);
X    datum = datadd(datum,abstand);
X    anzrat++;
X  }
X  return 1;
}
X		
main(int argc, char *argv[])
{
X  double x, dx, delta, lastd;
X  int i,c;
X  char zeile[80];
X  extern char *optarg;
X
X  while ((c = getopt(argc, argv, "f:")) != -1)
X    switch (c) {
X    case 'f':
X      if ((infile=fopen(optarg,"r")) == NULL) {
X	fprintf(stderr,"Kann %s nicht oeffnen.\n",optarg);
X	exit(1);
X      }
X      break;
X    }
X
X
X  if (infile)
X    fgets(zeile,sizeof(zeile),infile);
X  else {
X    printf("Ende TTMMJJ: ");
X    fgets(zeile,sizeof(zeile),stdin);
X  }
X  sscanf(zeile,"%ld",&ende);
X	
X  anzrat = 0;
X  if (!infile)
X    screenmode(1);
X  while (ratein()) ;
X  if (!infile)
X    screenmode(0);
X
X  /* Jetzt geht die Berechnung los */
X
X  x = 1.0;			/* Aufzinsungsfaktor */
X  dx = 0.1;
X  delta = lastd = 10E8;
X  do {
X    if (abs(delta) > abs(lastd)) {
X      dx = - dx / 2.0;
X      x += dx;
X    }
X    x += dx;
X    lastd = delta;
X    delta = 0.0;
X    for (i=0; i<anzrat; i++) {
X      delta += rate[i].betrag * pow(x,rate[i].dauer);
X    }
X    printf("%lf (%lf)  \r",x,delta);
X    fflush(stdout);
X
X  } while (abs(delta) > 0.005);
X  printf("\n\nRendite = %3.2lf %%\n",(x - 1.0) * 100.0);
}
SHAR_EOF
chmod 0644 cash.c ||
echo 'restore of cash.c failed'
Wc_c="`wc -c < 'cash.c'`"
test 3613 -eq "$Wc_c" ||
	echo 'cash.c: original size 3613, current size' "$Wc_c"
fi
# ============= screen.c ==============
if test -f 'screen.c' -a X"$1" != X"-c"; then
	echo 'x - skipping screen.c (File already exists)'
else
echo 'x - extracting screen.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'screen.c' &&
#include <stdlib.h>
#include <string.h>
#include "screen.h"
X
void screenmode(int mode)
{
X  if (mode) {
X    initscr();
X    cbreak();
X    noecho();
X    nonl();
X    intrflush(stdscr,FALSE);
X    keypad(stdscr,TRUE);
X  }
X  else {
X    endwin();
X  }
}
X
void Display(Screen maske[], int n)
{
X  register int i, j;
X
X  for (i=0; i<n; i++) {
X    mvaddstr(maske[i].zl-1,maske[i].sp-1,maske[i].var);
X    if (maske[i].len > 0) {
X      for (j=strlen(maske[i].var); j < maske[i].len; j++)
X	addch(' ');
X    }
X  }
X  refresh();
}
X
int Accept_Field(int zl, int sp, int len, char *var)
{
X  register int cpos, c, i;
X  int savec;
X
X  zl--;
X  sp--;				/* curses beginnt bei (0,0) */
X
X  cpos = 0;
X  move(zl,sp+len);
X  savec = inch();
X  addch('<');			/* Feldbegrenzung */
X
X  while (1) {
X    move(zl, sp+cpos);
X    refresh();
X    c = getch();
X    if (isprint(c)) {
X      var[cpos] = c;
X      addch(c);
X      cpos++;
X    }
X    else {
X      switch(c) {
X      case '\r':		/* CR, quit screen */
X      case '\t':		/* TAB, next field */
X      case 27:			/* ESC, abort screen */
X      case 'N' & 0x1F:		/* ^N, next field */
X      case 'P' & 0x1F:		/* ^P, previous field */
X      case 'L' & 0x1F:		/* ^L, redisplay screen */
X	mvaddch(zl,sp+len,savec);
X	refresh();
X	return c;
X	break;
X      case 'A' & 0x1F:		/* ^A, Begin of Field */
X	cpos = 0;
X	break;
X      case 'B' & 0x1F:		/* ^B, Backward */
X	cpos--;
X	break;
X      case 'D' & 0x1F:		/* ^D, delete char */
X	memmove(&var[cpos],&var[cpos+1],len-cpos-1);
X	addstr(&var[cpos]);
X	addch(' ');
X	break;
X      case 'E' & 0x1F:		/* ^E, End of Field */
X	cpos = len-1;
X	while (cpos && var[cpos] == 0)
X	  cpos--;
X	if (cpos)
X	  cpos++;
X	break;
X      case 'F' & 0x1F:		/* ^F, Forward */
X	if (var[cpos] != 0)
X	  cpos++;
X	break;
X      case 'K' & 0x1F:		/* ^K, Kill Rest of line */
X	for (i=cpos; i<len; i++) {
X	  var[i] = 0;
X	  addch(' ');
X	}
X	break;
X      case 'V' & 0x1F:		/* ^V, Insert char */
X	if (cpos < len-1) {
X	  memmove(&var[cpos+1],&var[cpos],len-cpos-1);
X	  var[cpos] = ' ';
X	  addstr(&var[cpos]);
X	}
X	break;
X      case 0x7F:		/* DEL, delete char backward */
X	if (cpos > 0) {
X	  cpos--;
X	  memmove(&var[cpos],&var[cpos+1],len-cpos-1);
X	  mvaddstr(zl,sp+cpos,&var[cpos]);
X	  addch(' ');
X	}
X	break;
X      }
X    }
X    if (cpos >= len)
X      cpos = len-1;
X    if (cpos < 0)
X      cpos = 0;
X  }
X  return -1;
}
X    
X
int Accept(Screen maske[], register int n)
{
X  register int fpos, i;
X  int retkey;
X  int dir;
X  char *p;
X  int len;
X  int nvar;
X
X  nvar = 0;
X  for (fpos=0; fpos < n; fpos++) { /* alle Variablen mit 0 auffuellen */
X    if ((len=maske[fpos].len) > 0) {
X      nvar++;
X      p = maske[fpos].var;
X      for (i=strlen(p); i <= len; i++)
X	p[i] = 0;
X    }
X  }
X  if (nvar == 0) {
X    screenmode(0);
X    fprintf(stderr,"Error in Accept, no input fields\n");
X    exit(1);
X  }
X
X  fpos = -1;
X  dir = 1;
X  while (1) {			/* Schleife ueber alle Felder */
X    fpos = (fpos+dir) % n;			/* naechstes Feld */
X    if (fpos < 0)
X      fpos = n-1;
X    while (maske[fpos].len == 0) {		/* skip output Felder */
X      fpos = (fpos+dir) % n;
X      if (fpos < 0)
X	fpos = n-1;
X    }
X
X    if ((retkey = Accept_Field(maske[fpos].zl,maske[fpos].sp,maske[fpos].len,
X			       maske[fpos].var)) == 27 || retkey == '\r')
X      return retkey;
X    
X    if (retkey == ('P' & 0x1F))
X      dir = -1;
X    else if (retkey == ('L' & 0x1F)) {
X      erase();
X      refresh();
X      Display(maske,n);
X      dir = 0;
X    }
X    else
X      dir = 1;
X  }
X  return -1;
}
SHAR_EOF
chmod 0640 screen.c ||
echo 'restore of screen.c failed'
Wc_c="`wc -c < 'screen.c'`"
test 3436 -eq "$Wc_c" ||
	echo 'screen.c: original size 3436, current size' "$Wc_c"
fi
# ============= screen.h ==============
if test -f 'screen.h' -a X"$1" != X"-c"; then
	echo 'x - skipping screen.h (File already exists)'
else
echo 'x - extracting screen.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'screen.h' &&
#include <ctype.h>
#include <curses.h>
X
typedef struct {
X  unsigned char zl, sp;
X  unsigned char len;
X  char *var;
} Screen;
X
#define display(m)	Display(m,sizeof(m)/sizeof(Screen))
#define accept(m)	Accept(m,sizeof(m)/sizeof(Screen))
X
void screenmode(int mode);
void Display(Screen maske[], int n);
int Accept_Field(int zl, int sp, int len, char *var);
int Accept(Screen maske[], register int n);
SHAR_EOF
chmod 0644 screen.h ||
echo 'restore of screen.h failed'
Wc_c="`wc -c < 'screen.h'`"
test 397 -eq "$Wc_c" ||
	echo 'screen.h: original size 397, current size' "$Wc_c"
fi
# ============= bau ==============
if test -f 'bau' -a X"$1" != X"-c"; then
	echo 'x - skipping bau (File already exists)'
else
echo 'x - extracting bau (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'bau' &&
010198
010180	-4900	1
010180	12	16	12
010180	35	48	1
010184	40	48	1
010188	45	96	1
SHAR_EOF
chmod 0640 bau ||
echo 'restore of bau failed'
Wc_c="`wc -c < 'bau'`"
test 83 -eq "$Wc_c" ||
	echo 'bau: original size 83, current size' "$Wc_c"
fi
exit 0

Jochen
-- 
Jochen Fahrner, D-8068 Pfaffenhofen, jochen@busybit.mrz.lm.sub.org