Skip to content

Instantly share code, notes, and snippets.

@treycordova
Created April 2, 2013 19:40
Show Gist options
  • Save treycordova/5295509 to your computer and use it in GitHub Desktop.
Save treycordova/5295509 to your computer and use it in GitHub Desktop.
ArbitrageFinder produces potential arbitrage information from a 2-dimensional hash table. The algorithm employed is Bellman-Ford's Algorithm, which has an overall runtime of O(n^3).
(function() {
var currencies = {
// US Dollar
"USD": {"USD": 0, "GBP": 0.616056, "EUR": 0.733837, "CAD": 0.983195, "JPY": 83.3786, "CNY": 6.56997, "INR": 45.2, "BRL": 1.66691, "RUB": 29.1986},
// British Pound
"GBP": {"USD": 1.62323, "GBP": 0, "EUR": 1.19119, "CAD": 1.59595, "JPY": 135.343, "CNY": 10.6646, "INR": 73.73, "BRL": 2.70578, "RUB": 47.396},
// Euro
"EUR": {"USD": 1.3627, "GBP": 0.8395, "EUR": 0, "CAD": 1.3398, "JPY": 113.62, "CNY": 8.9529, "INR": 61.5941, "BRL": 2.2715, "RUB": 39.7889},
// Canadian Dollar
"CAD": {"USD": 1.01709, "GBP": 0.626586, "EUR": 0.74638, "CAD": 0, "JPY": 84.8037, "CNY": 6.68227, "INR": 45.9726, "BRL": 1.6954, "RUB": 29.6977},
// Japanese Yen
"JPY": {"USD": 0.0119935, "GBP": 0.00738866, "EUR": 0.00880126, "CAD": 0.0117919, "JPY": 0, "CNY": 0.0787968, "INR": 0.542106, "BRL": 0.0199921, "RUB": 0.350193},
// Chinese Yuan
"CNY": {"USD": 0.152208, "GBP": 0.0937685, "EUR": 0.11696, "CAD": 0.14965, "JPY": 12.6909, "CNY": 0, "INR": 6.87979, "BRL": 0.253717, "RUB": 4.44425},
// Indian Rupee
"INR": {"USD": 0.0221239, "GBP": 0.0136296, "EUR": 0.0162353, "CAD": 0.0217521, "JPY": 1.84466, "CNY": 0.145353, "INR": 0, "BRL": 0.0368785, "RUB": 0.645987},
// Brazilian Real
"BRL": {"USD": 0.599912, "GBP": 0.36958, "EUR": 0.440238, "CAD": 0.589831, "JPY": 50.0199, "CNY": 3.94141, "INR": 27.116, "BRL": 0, "RUB": 17.5166},
// Russian Ruble
"RUB": {"USD": 0.0342482, "GBP": 0.0210988, "EUR": 0.0251326, "CAD": 0.0336727, "JPY": 2.85557, "CNY": 0.22501, "INR": 1.54802, "BRL": 0.0570887, "RUB": 0}
};
/**
* ArbitrageFinder produces potential arbitrage information from a 2-dimensional hash table.
* The algorithm employed is Bellman-Ford's Algorithm, which has an overall runtime of O(n^3).
*
**/
var ArbitrageFinder = function() {
/** Private Variables **/
var original_currencies = {};
var distance = {};
var predecessor = {};
var cycles = {};
/** private:findArbitrage employs the Bellman-Ford Algorithm to produce cyclic information **/
var findArbitrage = function(currencies, justOne) {
// Boolean used as the determining return value
var arbitrage = false;
// The number of currencies
var size = 0;
// A helper variable to keep track of the main for-loop of this algorithm
var val = 0;
// Conversion to -log values for negative-cycle detection
for(currency in currencies) {
original_currencies[currency] = {};
for(exchange in currencies[currency]) {
original_currencies[currency][exchange] = currencies[currency][exchange];
currencies[currency][exchange] = - Math.log(currencies[currency][exchange]);
}
}
// Place a fake source called "ROOT" where every exchange is exactly 0
currencies["ROOT"] = {};
for(exchange in currencies.USD) {
currencies["ROOT"][exchange] = 0;
// Initialize all distances
distance[exchange] = Infinity;
// Initialize all predecessors
predecessor[exchange] = null;
// Keep track of the number of currencies
size++;
}
// Distance for root initialized to 0
distance["ROOT"] = 0;
// The meat of our algorithm, taken directly from Bellman-Ford
for(var i = 0; i < size; ++i) {
for(currency in currencies) {
for(exchange in currencies[currency]) {
if( ( val = ( distance[currency] + currencies[currency][exchange] ) ) < distance[exchange]) {
distance[exchange] = val;
predecessor[exchange] = currency;
}
}
}
}
// Negative cycle detection and retention
for(currency in currencies) {
for(exchange in currencies[currency]) {
if(distance[exchange] > distance[currency] + currencies[currency][exchange]) {
// If we are just using hasArbitrage, we can return instantly
if(justOne) return true;
// Else, we save the fact and grab up the cycle
arbitrage = true;
cycles[exchange] = true;
}
}
}
return arbitrage;
};
/** private:build_sequences takes cyclic information and outputs the path used to create that cycle **/
var build_sequences = function(currencies, cycles) {
var p, visited, sequence, max;
for(cycle in cycles) {
p = cycle;
sequence = [];
visited = [];
while(p != undefined && !visited[p]) {
visited[p] = true;
sequence.unshift(p);
p = predecessor[p];
}
sequence.push(sequence[0]);
val = 1;
for(var i = 0; i < sequence.length - 1; ++i) {
val = val * original_currencies[sequence[i]][sequence[i+1]];
}
if(max == undefined || max[1] < val) max = [sequence, val];
}
return max;
}
/**
* hasArbitrage takes a 2-dimensional (n x n) array of currencies and
* deduces whether of not an arbitrage exists
*
* @currencies: a 2-dimensional (n x n) array of currencies
* @return: A boolean value of true or false
**/
this.hasArbitrage = function(currencies) {
return findArbitrage(currencies, true);
}
/**
* max returns the optimal arbitrage in a a 2-dimensional (n x n) array of currencies.
* The output is an array with the path and value.
*
* @currencies: a 2-dimensional (n x n) array of currencies
* @return: Array, [[path], val]
**/
this.max = function(currencies) {
findArbitrage(currencies, false);
var max = build_sequences(currencies, cycles);
return max;
};
return this;
};
var abfinder = new ArbitrageFinder();
console.log(abfinder.hasArbitrage(currencies));
console.log(abfinder.max(currencies));
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment