«

»

Jan 02

Erzeugung von smarten, reproduzierbaren Zufallszahlen mit festen Abständen und in fest definierten Bereichen

smartRandomNumberCentralDie Erzeugung von Zufallszahlen ist an sich recht einfach. Man muss den Zufallszahlengenerator initialisieren (in der Regel über MathSrand(irgendEinStartpunkt)) und direkt danach, kann man Zufallszahlen erzeugen.
Früher oder später kommt man bei der Entwicklung aber an zwei typische Probleme

1. Wie gelingt es Zufallszahlen (trotz allem Zufall) reproduzierbar, also in gewisser Weise vorhersagbar zu erzeugen?

2. Wie kann man “smarte” Zufallszahlen erzeugen, also nicht irgendwie irgendeine Zufallszahl, sondern Zufallszahlen die in gewissen Bereichen und mit gewissen Abständen liegen.

Beide Themen werden in diesem Blog-Eintrag behandelt und wie immer anhand eines nachvollziehbaren MetaTrader Quellcodes zum Download und zur Verwendung zur Verfügung gestellt.

Die Erzeugung von Zufallszahlen ist dabei zwischen MQL, C# oder C++ sehr ähnlich. Wer hingegen mit etwas ausgefeilteren mathematischen Programmen, wie beispielsweise Python arbeitet, der kann sich bei der Erzeugung der Zufallszahlen sogar noch die zu Grunde liegende mathematische Verteilung auswählen …

Wie gelingt es einen reproduzierbaren Zufallszahlenstream zu erzeugen

Bevor ich erläutere wie man sich einen reproduzierbaren Zufallszahlenstream erzeugt, vorweg noch ein paar Anmerkungen wozu man das eigentlich gebrauchen könnte.
Jeder Trader / Systementwickler hat sicherlich schon mal etwas von der Random-Walk-Theorie gehört. Sie basiert auf der Markteffizienz-Theorie und besagt ganz grob, dass es vollkommen unmöglich ist zukünftige Marktentwicklungen vorherzusagen. Wenn wir diese Theorie alle akzeptieren würden, könnten wir an dieser Stelle aufhören zu lesen und jegliches Trading einstellen, denn es gibt gemäß Theorie eh keine Chance die Märkte zu schlagen, die Märkte sind effizient, alle Informationen sind jederzeit im Preis inbegriffen und zukünftige Preisentwicklungen alleine aufgrund neuer Informationen zum Zeitpunkt des Eintreffens ableitbar, aber niemals im Voraus …
Vom Prinzip her sind alle Trader und Systementwickler jedoch auf der ständigen Suche nach dem “Gegenbeweis” dieser Theorie. Jeder Fund Manager, jeder professionelle Händler, jeder Handelssystem-Entwickler versucht ein sogenanntes Alpha zu finden, also eine Möglichkeit eine Outperformance gegenüber der Masse / dem allgemeinen Markt.
Auf der Suche nach einem erfolgsversprechenden Handelssystem mit einer guten Outperformance gegenüber der Masse (MSCI World o.ä.) könnte man also wie folgt vorgehen:

– Man schreibt einen Expert-Advisor, der zufällige Trades generiert
– Diesen Expert-Advisor lässt man auf einem beliebigen Währungspaar im Optimierungsmodus laufen, man erzeugt sich also beliebig viele Backtests
– Bei jedem einzelnen Backtest wird der Seed, also die Basis für die dann erzeugten Zufallszahlen auf eine vorher definierte Zahl gelegt (zum Beispiel den Backtest-Counter 1 2 3 …)
– Man wertet alle diese Backtests aus und wird (schon alleine weil es der Zufall so will) definitiv früher oder später ein sehr gutes Ergebnis vorfinden
– Nachdem man diverse statistische Tests gemacht hat und beispielsweise die Nullhypothese widerlegen konnte, das “System” bestehend aus random Trades also scheinbar statistische Relevanz hat, kommt der nächste Schritt, die Analyse der Trades
– Eine typische Herangehensweise zur Auswertung der Trades, wäre eine einfache Mustererkennung, wie sie auch bei vielen Machine Learning Verfahren Anwendung findet. Nur wie soll man Muster erkennen, wenn man die Trades nicht mehr reproduzieren kann, weil sie zufällig entstanden sind?!
– Wenn man nun also genau diese Trades dieses hervorragenden Backtests in MetaTrader mit einem einzelnen Backtest reproduzieren will, dann hat man ein Problem, das man nur Lösen kann, wenn man die Zufallszahlenreihe zu jedem beliebigen späteren Zeitpunkt genau reproduzieren kann >> und genau dafür braucht man beispielsweise eine Zufallszahlenreihe, die man zu 100% reproduzieren kann.

Auf Wikipedia wird der Nutzen der Reproduzierbarkeit wie folgt beschrieben

Bei jedem Start der Berechnung mit gleichem Startwert (engl. seed, Saatkorn) wird die gleiche Folge erzeugt, weshalb diese deterministisch erzeugten Pseudozufallszahlen bei hinreichend genauer Dokumentation später reproduziert werden können. Diese Eigenschaft der Reproduzierbarkeit ist bedeutsam für die Anerkennung wissenschaftlicher Experimente.

 

Ich habe also ein MT Tool programmiert, bei dem ich mir Zufallszahlen erzeugen lassen kann, die von der Abfolge her immer identisch sind.

enum EUUM_SEED_METHOD {useUniqueSeed, useFixedSeedToReproduceRandomProcess};
extern EUUM_SEED_METHOD randomNumberSeedMethod     = useUniqueSeed;
extern   int      fixedSeed                        = 1;

Stellt man die externe Variable randomNumberSeedMethod auf useFixedSeedToReproduceRandomProcess, so wird der Zufallszahlen-Generator immer mit der selben Zahl (fixedSeed) initiiert.

Der Aufruf der Initiierung erfolgt in einer gesonderten Funktion und sieht wie folgt aus

void f_createRandomNumberSeed()
{
   if (randomNumberSeedMethod == useUniqueSeed)
      MathSrand((int)TimeCurrent());
   else
      MathSrand(fixedSeed);
}

Nachfolgend eine Screenshot bei dem man sieht, dass der selbe Seed (Startpunkt) der Zufallszahlenreihe zum absolut gleichen Ergebnis führt.
smartRandomNumberSameSeed

Als Alternative gibt es die Möglichkeit die aktuelle UTC Zeit des MT4-Broker-Servers zu verwenden.
Die Initialisierung über …

MathSrand((int)TimeCurrent());

… führt also jede Sekunde zu einem anderen Zufallszahlenstream.

Wie gelingt es smarte Zufallszahlen zu erzeugen?

Was ist für mich eine “smarte” Zufallszahl:
Eine smarte Zufallszahl ist eine Zufallszahl, die
a) die sich innerhalb einer definierten Range befindet, also einen Mindestwert nicht unterschreitet und einen Maximalwert nicht überschreitet
b) die einen definierten Abstand zu jeder nächst höheren / tieferen Zufallszahl hat

Im MQL Quellcode gibt es dafür folgende einstellbare Variablen…

extern   double   randomNumber_Base                = 2.5;
extern   double   randomNumber_Stepping            = 0.5;
extern   double   randomNumber_Steps               = 3;
extern   int      amoutOfDigitsForRounding         = 1;


randomNumber_Base = jede erzeugte Zufallszahl wird auf diese Zahl aufaddiert. Sie stellt also den Mindestwert dar.
randomNumber_Stepping = jede Zufallszahl hat diesen Mindestabstand zur ihrer “benachbarten” Zufallszahl.
randomNumber_Steps = die Maximalzahl an Zufallszahlen mit vorgenanntem Stepping (3 * 0.5 = 1.5)
amoutOfDigitsForRounding = die Anzahl der Kommastellen jeder Zufallszahl

Im vorliegenden Beispiel gibt es also folgende Zufallszahlen
2.5 // 3.0 // 3.5 // 4.0

Wie sieht das ganze im Quellcode aus …

void f_createSmartRandomNumber()
{
   uint thisRandomNumber = 0;
   while (thisRandomNumber < amountOfRandomNumbersToShow)
   {
      double nextRandomNumber = randomNumber_Base + randomNumber_Stepping * f_getRandomNumber();

      randomNumberSavingArray[thisRandomNumber] = nextRandomNumber;

      thisRandomNumber++;
   }
}
double f_getRandomNumber()
{
   int newRandomNumber = MathRand();
   double newRandomNumberNormalizedOnZeroToOne = (double)newRandomNumber/(double)32768;

   double randomNumber = NormalizeDouble(randomNumber_Steps * newRandomNumberNormalizedOnZeroToOne, 0);

   return (randomNumber);
}


Mit:

int newRandomNumber = MathRand();

… erzeuge ich eine Zufallszahl zwischen 1 und 32768.

Mit:

double newRandomNumberNormalizedOnZeroToOne = (double)newRandomNumber/(double)32768;

… “normalisiere” ich die integer Zufallszahl auf eine Zahl zwischen 0,01 (Periode 0) und 1.

Die “normalisierte” Zufallszahl multipliziert mit der maximalen Anzahl der randomNumber_Steps und gerundet auf eine ganze Zahl ergibt den zu verwendenden Step.

double randomNumber = NormalizeDouble(randomNumber_Steps * newRandomNumberNormalizedOnZeroToOne, 0);

In der Zeile …

double nextRandomNumber = randomNumber_Base + randomNumber_Stepping * f_getRandomNumber();

… multipliziere ich dann den erzeugten Zufallszahlen Step mit dem Stepping (also zum Beispiel 2 * 0.5) und addiere das ganze auf die Zufallszahlen Basis und voilà fertig ist meine schön gesteppte Zufallszahl, die auch noch auf die Kommastelle genau innerhalb eines gewissen Bereiches liegt.

smartRandomNumber_03
Der letzte Screenshot zeigt eine Zufallszahlenreihe die bei 2 beginnt, mit dem Stepping 0.15 erzeugt wird und maximal 20 Steps ausführt (also von 2 bis 5 (2 + 20*0.15 = 5))

Ein genereller Hinweis für die Zufallszahlen die in MetaTrader erstellt werden: Sie sind alles andere als zufällig, sondern sogenannte Pseudozufallszahlen. Sie folgen einem bestimmten Muster, welches einfach zu entschlüsseln ist. Wenn Sie aus sicherheitstechnischen Gründen tatsächlich “echte” Zufallszahlen verwenden möchten, so gibt es in C# beispielsweise einen spezielle Random-Crypto-Service. Wenn Sie also für etwas speziellere Probleme Zufallszahlen benötigen, sollten Sie nicht einfach MathRand verwenden!


Zum Schluss der gesamte MQL-Quellcode und auch die sofort verwendbare ex4-Datei hier kostenlos zum Download für alle registrierten Nutzer und natürlich wie immer für jegliche private Nutzung frei verfügbar. Wer noch keinen User hat, einfach einen eigenen USER-Registrieren, valide Mail-Adresse eingeben und schon steht sämtlicher Quellcode aller Blog-Einträge zum Download zur Verfügung.
Wir übernehmen wie immer keine Garantie für die korrekte Funktionsweise dieses Tools bei allen MetaTrader Brokern die es auf der Welt gibt. Bitte auch dieses MetaTrader Tool wie immer bei Ihrem Broker testen und bei Fragen gerne melden.

Download Quellcode zum Tool zur Erzeugung smarter und reproduzierbarer Zufallszahlen

Viel Erfolg beim selber ausprobieren
Thomas @ ForexInnovation

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*