Arbitrage-Handel (Bellman-Ford-Algorithmus)



Der Handel an der Börse ist normalerweise mit Risiken verbunden. Dies gilt absolut fĂŒr die meisten Handelsstrategien. Der Erfolg des Handels in diesen FĂ€llen hĂ€ngt ausschließlich von der FĂ€higkeit ab, Risiken richtig einzuschĂ€tzen und zu managen. Aber nicht alle Handelsstrategien sind. Es gibt risikofreie Strategien, zu denen insbesondere die Schiedsgerichtsbarkeit gehört. In diesem Artikel wird erlĂ€utert, was Arbitrierung ist und wie sie mithilfe eines klassischen Graph-Algorithmus wie dem Bellman-Ford-Algorithmus implementiert wird.

Was ist ein Schiedsverfahren?


Bei der Schiedsgerichtsbarkeit handelt es sich um einige logisch zusammenhĂ€ngende Transaktionen, die darauf abzielen, von den Preisunterschieden fĂŒr dasselbe oder verwandte Vermögenswerte zur gleichen Zeit auf verschiedenen MĂ€rkten (rĂ€umliche Schiedsgerichtsbarkeit) oder auf demselben Markt zu verschiedenen Zeitpunkten (vorĂŒbergehende Schiedsgerichtsbarkeit) zu profitieren. .

Betrachten Sie als einfaches Beispiel die rĂ€umliche Arbitrage. In New York und London können GeschĂ€fte abgeschlossen werden, um Dollar fĂŒr Euro und Euro fĂŒr Dollar zu kaufen. In New York kann dies zum Preis von 4 Dollar fĂŒr 3 Euro und in London zum Preis von 5 Dollar fĂŒr 3 Euro erfolgen. Dieser Ratenunterschied eröffnet die Möglichkeit einer rĂ€umlichen Arbitrierung.



Mit 4 Dollar kann man in New York 3 Euro kaufen. Danach kaufen Sie in London fĂŒr diese 3 Euro 5 Dollar. Wie Sie sehen können, bringt eine so einfache Abfolge von Transaktionen 1 Dollar Gewinn pro 4 investierten Dollar. Wenn es anfĂ€nglich 4 Millionen US-Dollar gibt, liegt der Gewinn bereits bei einer Million.

Wenn sich die Wechselkurse (wir berĂŒcksichtigen den Spread nicht) fĂŒr dasselbe WĂ€hrungspaar unterscheiden, ist die zur Implementierung der Arbitrage-Strategie erforderliche Abfolge von Transaktionen sehr einfach. Wenn der Wechselkurs fĂŒr ein WĂ€hrungspaar festgelegt ist, jedoch mehrere WĂ€hrungspaare parallel gehandelt werden, ist auch eine Schiedsgerichtsbarkeit möglich, die Reihenfolge der Transaktionen ist jedoch bereits nicht trivial. Zum Beispiel können Sie 4 Euro fĂŒr 5 Dollar, 3 Pfund fĂŒr 4 Euro und dann 6 Dollar fĂŒr 3 Pfund kaufen. Der Gewinn aus einer solchen Abfolge von Transaktionen betrĂ€gt 1 US-Dollar pro 5 investierten US-Dollar.



Hunderte von WÀhrungspaaren können an der Börse gehandelt werden, und die Wechselkurse Àndern sich stÀndig. Es ist bereits unmöglich zu verstehen, welche Abfolge von Transaktionen ohne eine algorithmische Lösung Gewinn bringt.

Übergang zum algorithmischen Problem


Stellen Sie sich mögliche Devisentransaktionen in algorithmischer Form vor, und zwar in Form eines Diagramms. Die Eckpunkte in diesem Diagramm stellen WÀhrungen dar, und die Kanten sind potenzielle Trades. Die LÀnge der Rippe entspricht dem Wechselkurs, zu dem die Transaktion abgeschlossen werden kann.


Die nĂ€chste Frage ist, wie man eine Abfolge von Transaktionen in einer solchen Spalte findet, die Gewinn bringt. Da es sich am Anfang der Sequenz und am Ende um dieselbe WĂ€hrung handeln muss, muss die Sequenz natĂŒrlich dem Zyklus in der angegebenen Grafik entsprechen. Als nĂ€chstes mĂŒssen Sie bestimmen, wie der Wechselkurs zwischen den beiden WĂ€hrungen berechnet wird, wenn sie nicht direkt, sondern ĂŒber eine bestimmte dritte WĂ€hrung (oder eine beliebige Anzahl von Zwischenoperationen) ausgetauscht werden. Alles hier ist auch ganz einfach. Ein solcher Wechselkurs wird als Produkt der Wechselkurse von ZwischengeschĂ€ften berechnet. Eine profitable Abfolge von Transaktionen wird, wenn dieses Produkt einen Wert von weniger als eins annimmt. Mit anderen Worten, wenn eine WĂ€hrungseinheit weniger als eine Einheit derselben WĂ€hrung gekauft werden kann.


Klassische Graph-Algorithmen eignen sich schlecht fĂŒr die Arbeit mit Produkten mit KantenlĂ€ngen. Solche Algorithmen zielen hauptsĂ€chlich darauf ab, einen Pfad zu finden, der als Summe dieser LĂ€ngen definiert ist. Um diese EinschrĂ€nkung zu umgehen, gibt es jedoch einen mathematischen Weg, um von einem Produkt zu einer Summe zu gelangen. Dieser Weg ist Logarithmus. Wenn das Produkt unter dem Logarithmus erscheint, kann ein solcher Logarithmus in die Summe der Logarithmen umgewandelt werden. Auf der rechten Seite dieser Gleichung ist die gewĂŒnschte Zahl kleiner als eins, was bedeutet, dass der Logarithmus dieser Zahl kleiner als Null sein sollte.




Mit einem solchen einfachen mathematischen Trick können Sie von der Suche nach einem Zyklus, dessen Produkt aus KantenlĂ€ngen kleiner als eins ist, zur Suche nach einem Zyklus ĂŒbergehen, dessen Summe der KantenlĂ€ngen kleiner als Null ist. Eine solche Aufgabe scheint bereits mit klassischen Graph-Algorithmen und genauer mit dem Bellman-Ford-Algorithmus lösbarer zu sein.

Bellman - Ford Algorithmus


Der Bellman-Ford- Algorithmus wird normalerweise verwendet, um den Abstand von einem bestimmten Scheitelpunkt zu allen anderen Scheitelpunkten eines Graphen zu ermitteln. Seine Modifikation ermöglicht jedoch das Auffinden von Zyklen negativer LÀnge.

Die Grundoperation dieses Algorithmus ist die Kantenrelaxation . Das Wesentliche dieser Operation ist wie folgt. Angenommen, es gibt eine Kanteein→bund auch die zuvor berechneten vorlĂ€ufigen Werte der AbstĂ€nde zu den Peaks sind bekannt einund b. Um eine Kantenrelaxation durchzufĂŒhren, muss der Abstand zur Spitze berechnet werdenbwenn der Weg durch die Spitze ging einund Rippe ein→b. Diese Entfernung wird als Summe der Entfernung nach oben berechnet.ein und RippenlĂ€ngen ein→b. Wenn dieser Abstand geringer ist als der aktuelle vorlĂ€ufige Abstand zueindann ist dies genau die Entfernung zu einEntspricht und nimmt einen neuen, gerade berechneten Wert an.

Der Rest des Algorithmus ist ebenfalls unkompliziert. NotwendigN. Zeiten (N.Ist die Anzahl der Eckpunkte des Diagramms), umgehen Sie die Liste der Kanten und wenden Sie fĂŒr jede Runde eine Relaxationsoperation an. Die KomplexitĂ€t des Algorithmus wird erhaltenN.∗M. (Wo N.- die Anzahl der Eckpunkte und M.- Anzahl der Rippen). Bei einem Diagramm ohne negative Zyklen fĂŒhrt eine weitere Kantenrelaxation nicht zu einer Änderung des Abstands zu den Scheitelpunkten. Gleichzeitig verringert die Relaxation bei einem Diagramm mit einem negativen Zyklus den Abstand zu den Eckpunkten und danachN.Umleitungen. Diese Eigenschaft kann verwendet werden, um den gewĂŒnschten Zyklus zu finden.

Die folgende kleine Implementierung des oben auf Kotlin beschriebenen Algorithmus soll denjenigen helfen, die mit dem Aussortieren von Code besser vertraut sind.

fun findNegativeCycle(nodes: Int, edges: Array<Edge>, source: Int): List<Int>? {
    // Initialize distances and prev arrays. All distances but the distance to
    // the source node are infinite, distance to the source node is zero.
    val distances = DoubleArray(nodes) { if (it == source) 0.0 else INFINITY }
    val prev = IntArray(nodes) { -1 }

    // Relax all edges N times where N is the number of nodes.
    repeat(nodes) {
        edges.forEach { it.relax(distances, prev) }
    }

    // Try to relax at least one more edge. If it's possible memorize the node,
    // otherwise return from the method.
    val firstRelaxedEdge = edges.firstOrNull { it.relax(distances, prev) }
    var node = firstRelaxedEdge?.to ?: return null

    // Step back N times where N is the number of nodes. As a result, the node will
    // be in the loop for sure.
    repeat(nodes) {
        node = prev[node]
    }

    // Recover the loop by the node that is inside it and prev links.
    val lastNode = node
    return buildList {
        do {
            add(node)
            node = prev[node]
        } while (node != lastNode)

        reverse()
    }
}

// Edge DTO with implemented relaxation operation.
data class Edge(val from: Int, val to: Int, val length: Double) {

    fun relax(distances: DoubleArray, prev: IntArray): Boolean {
        if (distances[from] + length >= distances[to]) {
            return false
        }

        distances[to] = distances[from] + length
        prev[to] = from
        return true
    }
}

Betrachten wir ein Beispiel mit einem kleinen Graphen, der einen Zyklus negativer LĂ€nge enthĂ€lt. Damit der Algorithmus funktioniert, muss jeder Scheitelpunkt den aktuell bekannten Abstand zu ihm sowie eine VerknĂŒpfung zu seinem vorherigen Scheitelpunkt beibehalten. Die Bezugnahme auf den vorherigen Scheitelpunkt wird in diesem Fall durch die erfolgreiche Relaxation der Rippe bestimmt. Wenn die Relaxationsoperation erfolgreich war und der Abstand zum Scheitelpunkt aktualisiert wurde, wird auch die VerknĂŒpfung zum vorherigen Scheitelpunkt dieses Scheitelpunkts aktualisiert und nimmt den Wert des Quellscheitelpunkts der angegebenen Kante an.

Zuerst mĂŒssen Sie die Scheitelpunkte initialisieren, indem Sie den Abstand zu allen Scheitelpunkten mit Ausnahme der anfĂ€nglichen gleichen Unendlichkeit festlegen. FĂŒr den anfĂ€nglichen Scheitelpunkt wird ein Abstand von Null eingestellt.


Die erste Runde aller Rippen folgt und ihre Entspannung wird durchgefĂŒhrt. Fast alle Entspannungen fĂŒhren zu keinem Ergebnis, außer Rippenentspannungein→b. Durch die Entspannung dieser Rippe können Sie den Abstand zu aktualisierenb.


Darauf folgt die zweite Runde aller Kanten des Graphen und die entsprechende Relaxation. Diesmal ist das Ergebnis eine Entspannung der Rippen.b→c, und auch b→d. Die AbstĂ€nde zu den Gipfeln werden aktualisiert.c und d. Hierbei ist zu beachten, dass das Ergebnis von der Reihenfolge abhĂ€ngt, in der die Kanten durchlaufen werden.


In der dritten Rippenrunde ist es möglich, bereits drei Rippen, nĂ€mlich die Rippen, erfolgreich zu entspannen mit→d, d→e, d→b. In diesem Fall, wenn die Rippen entspannt sindmit→d und d→bbereits aufgezeichnete Entfernungen zu dund bsowie entsprechende Links zu vorherigen Eckpunkten.


In der vierten Runde werden die Rippenrelaxationsoperationen erfolgreich abgeschlossen. b→cund b→ein. In diesem Fall werden bereits aufgezeichnete Werte der AbstĂ€nde zu den Peaks erneut aktualisiert.ein und csowie die entsprechenden Links zu vorherigen Eckpunkten.


Die fĂŒnfte Runde ist die letzte. WĂ€hrend dieses Spaziergangs entspannen sich die Rippenmit→d, d→b, d→e. Hier können Sie sehen, dass das Vorhandensein eines Zyklus negativer LĂ€nge bereits bestimmte Anpassungen an den Werten der AbstĂ€nde zu den Spitzen vornimmt.


Wenn der Graph nach dieser Tour keinen Zyklus negativer LĂ€nge enthalten wĂŒrde, wĂ€re der Algorithmus abgeschlossen, da die Relaxation einer Kante keine Änderungen vorgenommen hĂ€tte. FĂŒr diesen Graphen kann man jedoch aufgrund des Vorhandenseins eines Zyklus negativer LĂ€nge immer noch eine Kante finden, deren Relaxation den Abstand zu einem der Eckpunkte aktualisiert.


Es wird eine Kante gefunden, deren Entspannung die Entfernung zum Gipfel aktualisiert. Dies bestĂ€tigt das Vorhandensein eines Zyklus negativer LĂ€nge. Jetzt mĂŒssen Sie diesen Zyklus selbst finden. Es ist wichtig, dass der Scheitelpunkt, dessen Abstand jetzt aktualisiert wird, sowohl innerhalb als auch außerhalb des Zyklus liegen kann. Im Beispiel ist dies die Spitzeeund sie ist aus dem Kreislauf. Als nĂ€chstes mĂŒssen Sie auf die Links zu den vorherigen Scheitelpunkten verweisen, die in allen Schritten des Algorithmus sorgfĂ€ltig aktualisiert wurden. Um garantiert in den Zyklus zu kommen, mĂŒssen Sie zurĂŒcktretenN.Peaks ĂŒber diese Links.

In diesem Beispiel lauten die ÜbergĂ€nge wie folgt:e→d→c→b→d→c. So befindet sich die Spitzec, die garantiert in einem Zyklus negativer LĂ€nge liegt.


Weiter eine Frage der Technologie. Um den gewĂŒnschten Zyklus zurĂŒckzugeben, mĂŒssen Sie die VerknĂŒpfungen zu den vorherigen Scheitelpunkten erneut durchlaufen, bis sich der Scheitelpunkt wieder trifftc. Dies bedeutet, dass der Zyklus geschlossen wurde. Sie mĂŒssen lediglich die Reihenfolge umkehren, da die Iteration wĂ€hrend der Iterationen ĂŒber VerknĂŒpfungen zu vorherigen Scheitelpunkten umgekehrt wurde.

Der obige Algorithmus setzt das Vorhandensein eines anfĂ€nglichen Scheitelpunkts voraus, aus dem die AbstĂ€nde berechnet werden. Das Vorhandensein eines solchen Scheitelpunkts ist nicht erforderlich, damit der Algorithmus funktioniert, wurde jedoch in grĂ¶ĂŸerem Umfang eingefĂŒhrt, um dem ursprĂŒnglichen Bellman-Ford-Algorithmus zu entsprechen. Wenn das interessierende Thema ein Zyklus negativer LĂ€nge ist, können wir annehmen, dass alle Eckpunkte eines gegebenen Graphen initial sind. Mit anderen Worten, dass der Abstand zu allen Eckpunkten anfĂ€nglich Null ist.

Fazit


Die Verwendung des Bellman-Ford-Algorithmus im Arbitrage-Handelsproblem ist ein hervorragendes Beispiel dafĂŒr, wie klassische Algorithmen echte GeschĂ€ftsprobleme insbesondere im Finanzbereich lösen können. Asymptotische KomplexitĂ€t des Algorithmus, gleichN.3FĂŒr ein vollstĂ€ndig verbundenes Diagramm kann es sich als ziemlich groß herausstellen. Daran muss man sich wirklich erinnern. In vielen FĂ€llen, wie zum Beispiel beim Geldwechsel, verursacht diese KomplexitĂ€t jedoch aufgrund der relativ geringen Anzahl von Knoten in der Grafik keine Probleme .

Source: https://habr.com/ru/post/undefined/


All Articles