Pimp up my programme



  • hallo,
    ich bin absoluter laie und habe mit meinen beschränkten C++ kenntnisse folgende Monte Carlo Simulation erstellt.
    (fyi: Es handelt sich um das Heston Modell unter Anwendung von Zhus TV)
    die programmierung stimmt soweit (soll heissen, die ergebnisse sind bei ausreichend vielen simulationen nah genug am analytischen ergebnis.)

    jetzt nur meine frage:
    kriegt man das ganze (als monte carlo simulation) noch irgendwie schneller?
    als anfänger nehme ich an, dass ich schlampig programmiert habe und es sicher irgendwie methoden gibt deutlich zeit einzusparen bei der berechnung mit einer besseren programmierung.

    hier mein code:

    // HESTON MODELL - Transformed Volatility Scheme - Moment Matching Method
    
    #include <cmath>
    #include <cstdlib>
    #include <iostream>
    #include <stdio.h>
    #include <math.h>
    #include <ctime>
    
    using namespace std;
    
    // Eingabe der Parameter für die Optionsberechnung:
    double T=6;                       // Laufzeit der Option
    double K=100;                     // Strike
    double S0=100;                    // Startkurs Basiswert
    double Vola=0.15;                 // Start-Vola (sigma) [Umrechnung in Varianz erfolgt an späterer Stelle]
    double r=0.04;                    // risikoloser Zins (c.c.)
    double Speed=2;                   // Stärke des Mean Reverse (kappa)
    double meanVar=0.04;              // durchscnittliche langfristige Varianz (theta)
    double VolVol=0.3;                // Die Volatilität der stochastischen Volatilität des Underlying (xi)
    unsigned long Pfade=10000;         // Anzahl Akitenpfad-Simulationen
    double Steps=32;                  // Anzahl der unterjährigen Kursbeobachtungen (time steps)
    double p=(-0.5);                  // Korrelation der ZVen zwischen Basiswert und Volatilität (rho)
    // Ende Parametereingabe
    
    double V;                         // aktuelle Varianz 
    double v;                         // aktuelle Volatilität
    double StepsTotal=Steps*T;        // Anzahl der gesamten Kursbeobachtungen über gesamte Laufzeit
    double t=(1/Steps);               // Dauer zwischen den Knoten (in Jahren)
    double X;                         // ln(S)
    double QA=0;                      // Quadrierte Auszahlungen
    double Standardfehler;
    
    // 1. Generierung normalverteilter Zufallszahlen
    double NVZV()
    {
           double result;
           double x;
           double y;
           double sizeSquared;
           do
           {                 
           x = 2.0000*rand()/static_cast<double>(RAND_MAX)-1; // erstellt GVZV von -1 bis +1 
           y = 2.0000*rand()/static_cast<double>(RAND_MAX)-1;      
           sizeSquared = x*x + y*y;
           }
           while
           (( sizeSquared >= 1.000000)||( sizeSquared <= 0.000000));
                  result = x*sqrt(-2*log(sizeSquared)/sizeSquared); 
           return result;
    }
    // Ende 1.
    
    // 2. Monte-Carlo-Simulation
    double MonteCarlo 
    (double T,
    double K,
    double S0, 
    double Vola,
    double r,
    double KnotenGesamt,
    unsigned long Pfade)
    {
    
             double Summe=0;
    
             double ST; // Aktienendkurs           
             for(unsigned long i=0; i <Pfade; i++) // Anzahl der Simulationen
             {
             v=Vola; 
             X=log(S0);                                  
                     for(unsigned long j=0; j <KnotenGesamt; j++) // Anzahl der Knoten
                     {                          
                      V=v*v;
                      double Z=NVZV();
                      double q=1-(p*p);
                      double W= sqrt(q)*NVZV() + p*Z;                                           
    
                      X += ((r- 0.5*V)*t)+(sqrt(t)*v*Z);      
    
                      double alpha = meanVar+(V-meanVar)*exp(-Speed*t)-(VolVol*VolVol/(4*Speed)*(1-exp(-Speed*t)));
                      alpha = alpha >0 ? alpha : 0;                
                      double beta = sqrt(alpha);
                      double mmm= (beta-v*exp(-0.5*Speed*t))/(1-exp(-0.5*Speed*t));     // mmm = moment matched mean level
    
                      v += (0.5*Speed*(mmm - v)*t) + (0.5*VolVol*sqrt(t)*W);       
                      }  
    
             ST=exp(X);    
             double Auszahlung = ST - K;
             Auszahlung = Auszahlung >0 ? Auszahlung : 0;
             Auszahlung *=exp(-r*T); 
             Summe += Auszahlung;                        
             QA +=Auszahlung*Auszahlung; 
             }     
             double average = Summe / Pfade;
    
             double Varianz=((QA/Pfade))-(average*average); 
             Standardfehler=sqrt(Varianz)/sqrt(Pfade-1);                
             return average;                                                             
    
    }
    // Ende 2.
    
    // Hauptprogramm:
    int main()
    {
    // START UHR
    clock_t start, end;
    double dif;
    start = clock();  
    
    // Start: für zeitabhängig generierte Zufallsvariablen
    long n = time(0);
    srand(n);
    // Ende:  für zeitabhängig generierte Zufallsvariablen
    
    double Ergebnis = MonteCarlo(T, K, S0,  Vola, r, StepsTotal, Pfade);
    cout << "Der Preis der Call-Option betraegt: " <<Ergebnis << " EUR"  << endl << endl;     
    cout << "Standardfehler: ";
    cout <<Standardfehler;      
    cout <<endl<<endl<<endl;                   
    
    // Ende Uhr
    end = clock();
    dif = double (end - start) / CLOCKS_PER_SEC;
    cout << "Benoetigte Zeit: " << dif << "s" << endl;
    cout <<endl<<endl<<endl;
    
    system("PAUSE");                   
    }
    // Programmende
    

    danke schonmal im voraus!

    lg tobi



  • Kannst Du deinen Beitrag bitte editieren und C++ Tags verwenden? Es ist hammer schwer Code ohne Syntax highlighting zu lesen.

    Wie schnell terminiert die Schleife in der NVZV Funktion?
    Du kannst standardnormalverteilte Zuvallszahlen mit der Polarmethode ohne Iteration erzeugen.
    Siehe hier:
    http://www.c-plusplus.net/forum/104493


  • Mod

    Ein paar Sachen die mir auffallen:
    1. Der Standardzufallsgenerator ist nicht geeignet für MC. Man weiß nie was für einen Generator man bekommt und im Zweifelsfall ist es ein ziemlich schlechter. Nimm einen guten PRNG aus einer Numerikbibliothek. Diese liefern dir dann auch gute Algorithmen für normalverteilte Zufallszahlen.

    2. Apropos normalverteilt: Ist das hier wirklich nötig? Das ist eine ziemlich teure Operation. Ich mag jetzt nicht im Detail nachvollziehen, was du genau machst, aber oftmals reicht es (wegen dem zentralen Grenzwertsatz), wenn das nullte und erste Moment stimmen, was man bei richtiger Skalierung auch mit einer Gleichverteilung geht.

    3. In deinem inneren Loop machst du viele teure Sachen: Zig Wurzeln werden berechnet und Beträge werden gezogen. Ist dies alles nötig? So etwas kann man oft vermeiden, indem man z.B. die ganze Zeit mit den Quadraten rechnet und erst im letzten Schritt die Wurzel zieht.

    4. Dein Code sieht leider so aus, wie bei den meisten Wissenschaftlern, die man zum Programmieren gezwungen hat 😞 . Das heißt, absolut unlesbar. Daher kann ich nur so Allgemeinfloskeln wie meine Punkte 1-3 bieten, anstatt konkret zu werden. Ehrlich gesagt, habe ich nicht einmal eine Ahnung, was dein Programm überhaupt macht. Dein Problem wird sein, dass es dir genauso ergehen wird, wenn du mal ein paar Tage nicht mehr mit deinem Programm gearbeitet hast (z.B. nach einem langen Wochenende wie Ostern 😃 ) und du dann wieder mehrere Tage brauchst, um dich einzufinden. Und wwehe, du musst jemals einen Fehler suchen! Dagegen hilft aber eigentlich nur Programmieren üben und sich mal über guten Stil kundig zu machen. Weiß jetzt nicht, ob du während deiner normalen Tätigkeit die Zeit dafür hast, weil das ein paar Wochen bis Monate verschlingen wird. Aber hinterher wirst du dankbar sein.



  • Dein Code ist wirklich ziemlich unaufgeräumt und schwer zu lesen. Für konkrete Tipps musst du deinen Code wirklich zuerst aufräumen (alte c-Header, globale Variablen, Variablennamen, Einrückung, etc.).

    Ein allgemeiner Tipp: Zieh alle Berechungen, die nicht in jeder Iteration gemacht werden müssen aus den Schleifen raus.

    Bsp:

    double alpha = meanVar+(V-meanVar)*exp(-Speed*t)-(VolVol*VolVol/(4*Speed)*(1-exp(-Speed*t)));
    

    Was ändert sich da von Durchgang zu Durchgang, ausser V? Ist das ganze evt. nur eine Multiplikation der Form

    double alpha=a+b*V
    

    mit a,b konstant?



  • Hallo,
    also Danke erstmals, bevor ich auf die einzelnen kommentar eingehe.

    @whosucks:

    Kannst Du deinen Beitrag bitte editieren und C++ Tags verwenden? Es ist hammer schwer Code ohne Syntax highlighting zu lesen.

    o.k., hier muss ich mich leider als totaler informatik-dummie outen:
    was sind C++ Tags?^^
    zu dem Link: Dort wird die Box-Müller-Methode (anstatt der Polar bzw. Marsaglia Methode) vorgestellt. Die Polar-Methode ist aber schneller in C++ obwohl ca. 23% aller ZV rausgekickt werden. Hat iwie mit den Zugriffen auf die Bib und trig. Fkt zu tun. (Hab die Box Müller schon selber ausprobiert, welche dort gezeigt wird.)
    Aber für eine effizientere Programmierung /Mehtode wäre ich natürlich dankbar...

    @SeppJ:

    1. Der Standardzufallsgenerator ist nicht geeignet für MC. (...) Nimm einen guten PRNG aus einer Numerikbibliothek. Diese liefern dir dann auch gute Algorithmen für normalverteilte Zufallszahlen.

    Ja, das habe ich auch schon öfters gehört. aber
    a) bin ich als laie zu dumm, es gebacken zu kriegen mir ne andere bib zu implementieren (auch meine freunde konnten mir da nicht helfen) und
    b) komme ich mit meinen simulationsergebnissen sehr nah an den analytischen Preis heran. (das ganze ist ja eine optionspreisberechnung) für 1Mrd. Aktienkurssimulationen hatte ich nur eine Preisabweichung zum analytischen Preis von 0,0048%. somit ist dessen unzulänglichkeit für mich noch hinnehmbar.^^
    Nichts desto trotz werde ich noch versuchen, dass hin zu kriegen 😉
    damit auch zu Punkt 2: also ich denke schon, dass ich normalverteilte ZV brauche, eben um den Aktienkurs und die Volatilität einem stochastischen Prozess unterlaufen zu lassen. Und dafür muss ich mir halt schon ein paar hundert Millionen normalverteilte ZV ziehen.
    zu Punkt 3: mal schaun was sich da machen lässt. aber ich glaube nicht viel.^^
    zu Punkt 4: also für mich ist es ohne probleme lesbar.^^ aber da bin ich halt wohl der einzige.^^ das problem ist halt meine fehlende programmier-erfahrung und mein somit schlechter stil, in kombination mit (meiner meinung nach) einem komplexeren stochastischen prozess und diskretisierungsschemata. (siehe Zhus Transformed Volatility Scheme für das Heston Modell)

    @lustig:

    Für konkrete Tipps musst du deinen Code wirklich zuerst aufräumen (alte c-Header, globale Variablen, Variablennamen, Einrückung, etc.).

    ähm...^^, ich wüsste leider nicht, welche Header ich wie ersetzen wüsste.
    meinst du, ich sollte die variablen erklären? (das wäre halt n bisl komplizierter.) oder das die alle an den anfang müssten?
    einrückungen könnte ich natürlich wegnehmen, aber ich dachte die könnten der inhaltlichen trennung behilflich sein.^^ bei mir tuts dies jedenfalls.^^
    (einrückungen sollen daher eine unterprogrammierung darstellen).

    zum gesamtkontext:
    das hier ist eine monte carlo simulation. es geht um die bewertung von optionen auf Aktienen mit dem stochastischen Volatilitätsmodell von Heston. Sowohl der Aktienkurs als auch dessen Volatilität unterliegt einem stochastischen Prozess.
    Aus bestimmten Gründen wird der numerische Ansatz anstatt des analytischen gewählt. Damit hat man das Diskretisierungsproblem für die Simulation, dass der Diffusionterm des Heston-Modelles (für die Vola) eine Wurzel hat. (square root diffusion)
    da die varianz daher negativ werden kann würde die simulation abgebrochen. ein diskretisierungsschema ist das von Zhu. und das wird hier angewendet. dieser teil mit "alpha" "beta" und "mmm" ist um das long run mean level der vola zu matchen. sry, dass ich das nicht eher geschrieben hatte.

    zur programmierung: mal schaun, was ich von den tipps schaffe umzusetzen. hinsichtlich der programmierung sehe ich mich aufgrund meines begrenzten verständisses aber wohl nicht in der lage eine bessere programmierung zu bieten.^^ (siehe meine jeweilgen kommentare^^)
    ich hatte nur gesehen, dass mit einer anderen mathematica programmierung dessen performance (zeit) um ein vielfaches verbessert wurde. daher hab ich mich gefragt, was man bei mir wohl noch so rausholen könnte.^^

    lg
    tobi



  • @Lustig:
    hast recht, einen teil könnte ich aus alpha am anfang berechnen lassen, da nur V stochastisch ist.
    statt:

    double alpha = meanVar+(V-meanVar)*exp(-Speed*t)-(VolVol*VolVol/(4*Speed)*(1-exp(-Speed*t)));
    

    also statt: alpha= a + bV - c
    könnte ich dann d= a -c
    und alpha dann in der schleife als: alpha = d + bV.
    das könnte etwas zeit sparen! 🙂



  • Also code Tags: nicht mit [ c o d e ] sondern mit [ c p p ] für c++, da gibts sogar extra einen Button dafür unter dem Eingabefeld.

    Genauere Infos zum schlecht lesbaren Code:

    #include <cmath>
    #include <cstdlib>
    #include <iostream>
    #include <math.h> // Für was? Du hast ja das c++ äquivalent cmath bereits drin
    #include <stdio.h> // dito
    // ...
    
    // Das ist KEINE Parametereingabe.
    // Wenn das alles konstant ist, dann mach Konstanten daraus
    // Wieso muss das alles global sein
    double T=6;                       // Laufzeit der Option
    double K=100;                     // Strike
    
    // Wieso ist das alles global?
    double V;                         // aktuelle Varianz 
    double v;                         // aktuelle Volatilität
    double StepsTotal=Steps*T;        // Anzahl der gesamten Kursbeobachtungen über gesamte Laufzeit
    double t=(1/Steps);               // Dauer zwischen den Knoten (in Jahren)
    double X;                         // ln(S)
    double QA=0;                      // Quadrierte Auszahlungen
    double Standardfehler;
    
    // Zu den Variablennamen: Wieso mal deutsch und mal englisch? 
    // Wieso kryptische Abkürzungen wie QA, X, V, v statt varianz, volatilitaet, ...
    
    // ...
    
    // Unübersichtliche & Inkonsequente Einrückung hier:        
             for(unsigned long i=0; i <Pfade; i++) // Anzahl der Simulationen
             {
             v=Vola; 
             X=log(S0);                                  
                     for(unsigned long j=0; j <KnotenGesamt; j++) // Anzahl der Knoten
                     {     
                      // Hier auch unschöne Einrückung                    
                      V=v*v;
    // ...                  
    
    // Gar keine Einrückung
    int main()
    {
    // START UHR
    clock_t start, end;
    double dif;
    //...
    


  • ah ok. die header können dann anscheinend raus.^^
    ich hab ja keine genaue ahnung welche bib was macht und hab mir dann halt das rausgesucht, was ich in verlgeichbaren programmierungen oder büchern gefunden habe.^^ aber danke schonmal für den hinweis

    Das ist KEINE Parametereingabe.

    => naja, das ist dort wo die konstanten parameter bzw. start-parameter der stochastischen prozesse der option eingegeben werden. (basierend auf marktdaten.)
    ich wüsste nicht wie ich sonst die eingabe der options-parameter darstellen sollte. in der maske wollte ich es nicht haben, weil ich so bei jeder simulation alle werte neu eingeben müsste und das wäre echt nervig...
    oder was wäre die alternative?

    Wenn das alles konstant ist, dann mach Konstanten daraus

    => wie? bzw. wo ist der unterschied? das sind ja die paramter aus marktdaten die man immer nur leicht variieren will.

    Wieso muss das alles global sein

    => meinst du damit direkt am anfang? weil ich die nicht in der schleife jedes mal neu erstellen will. und wenn ich die erst in der schleife erwähne, dann macht der manchmal fehlermeldungen. war also für mich so einfacher.^^

    Wieso kryptische Abkürzungen wie QA, X, V, v statt varianz, volatilitaet

    => die Variablen, die sich verändern, sind als einzelne Buchstaben. Da deren erwähnung häufiger vorkommt (und zur abgerenzung mit den ausgangswerten) hab ich einfach nur buchstaben genommen. Es wäre finde ich ein wenig verwirrender, wenn ich "Vola" und "Volatilität" hätte. Daher lieber "v" und "V". (Abgrenzung Ausgangswerte vs. sich ändernde bzw. stochastische Werte).



  • Ein absolut nicht bös gemeinter Rat:
    Lies ein gutes Buch über C++. Oder vielleicht C, das könnte dir genauso dienen. So wie ich das rauslese hast du zwar mal ein bisschen C++-gelernt, aber hauptsächlich aus Querlesen aus einem bunten Mischmasch aus C und C++-Büchern, da ist es recht schwer gezielte Erklärungen zu liefern, weil man deinen Wissensstand schlecht abschätzen kann.

    MfG



  • Keine Sorge, ich nehme keine Antwort / Anregung / Kritik böse. 🙂
    Nur fehlt mir natürlich zum intensiven Studium von C++ die Zeit.
    Zumal davon 99% wahrscheinlich eh "verschwendetes" Wissen wäre, welches ich niemals bräuchte, da diese Programmierung ja eine relative schmale Anforderung hat. Zudem bin ich auch eigentlich mit meiner Programmierung recht zufrieden, weil sie gut läuft. Mich hätte halt nur interessiert ob man irgendwie die Schleifen durch eine ganzen anderen Methode ersetzen hätte können und somit alles irgendwie effizienter wird. (Bei Mathematica war es z.B. so)
    Oder irgend etwas anderes. Wie gesagt, ich dachte mir ein paar Fachleute zu fragen könnte nicht schaden.^^

    Trotzdem, danke an alle.


Anmelden zum Antworten