|
// Get all possible states for a k length array |
|
function states(k) { |
|
if (k==1) return [0,1]; |
|
var ret = []; |
|
states(k-1).forEach(function(d) { |
|
ret.push([0].concat(d)); |
|
ret.push([1].concat(d)); |
|
}); |
|
return ret; |
|
} |
|
|
|
// Get all k-combinations from array |
|
function combinations(arr, k){ |
|
if (k==1) return arr; |
|
var ret = []; |
|
arr.forEach(function(d,i) { |
|
combinations(arr.slice(i+1, arr.length), k-1) |
|
.forEach(function(sub) { |
|
var next = [].concat(sub); |
|
next.unshift(d); |
|
ret.push( next ); |
|
}); |
|
}); |
|
return ret; |
|
} |
|
|
|
// Generate a LaTex fraction |
|
function frac(a,b) { |
|
if (!b || b == 1) return a; |
|
return '\\frac{'+(a || 1)+'}{'+b+'}'; |
|
} |
|
|
|
// Main calculation - callback to the JSONP request |
|
function main(input) { |
|
input = input.query.results.json; |
|
|
|
// Build rates matrix |
|
var rates = {}; |
|
Object.keys(input).forEach(function(key) { |
|
var numerator = key.slice(0,3), |
|
denominator = key.slice(4); |
|
if (!rates[numerator]) rates[numerator] = {}; |
|
rates[numerator][denominator] = input[key]; |
|
}); |
|
|
|
// List FX rates |
|
fx = Object.keys(rates); |
|
|
|
// Go through all single-pair arbs |
|
combinations(fx,2).forEach(function(d) { |
|
var ret = rates[d[0]][d[1]]*rates[d[1]][d[0]], |
|
first = frac(d[0],d[1])+' \\cdot '+frac(d[1],d[0]), |
|
second = rates[d[0]][d[1]]+'\\cdot '+rates[d[1]][d[0]]; |
|
|
|
// If return is below 1 the arb is in the inverse |
|
if (ret < 1) { |
|
first = frac(1,first); |
|
second = frac(1,second); |
|
ret = 1 / ret; |
|
} |
|
|
|
$("#dual").append('$$'+first+' = '+second+' = '+ret+ '$$'); |
|
}); |
|
|
|
// Go through all triangular combinations |
|
combinations(fx,3).forEach(function(c) { |
|
var best = {}; |
|
|
|
// Go through all possible selections of currency pairs for those three currencies |
|
states(3).forEach(function(states) { |
|
var d = {result: 1, above: [], below: []}; |
|
|
|
states.forEach(function(state,i) { |
|
var a = (state) ? c[i] : c[i+1] || c[0], |
|
b = (state) ? c[i+1] || c[0] : c[i], |
|
rate = rates[a][b]; |
|
|
|
if (state) d.above.push([a,b,rate]); |
|
else d.below.push([a,b,rate]); |
|
|
|
d.result *= state ? rate : 1/rate; |
|
}); |
|
|
|
// If the result is below 1 the arb is in the inverse |
|
if (d.result < 1) { |
|
var tmp = d.below; |
|
d.below = d.above; |
|
d.above = tmp; |
|
d.result = 1 / d.result; |
|
} |
|
|
|
// To eliminate duality-arb effect, we take the lowest triangular arb |
|
if (!best.result || d.result < best.result) best = d; |
|
|
|
}); |
|
|
|
$("#triangular").append('$$ '+frac( |
|
best.above.map(function(d) { return frac(d[0],d[1]);}).join(' \\cdot '), |
|
best.below.map(function(d) { return frac(d[0],d[1]);}).join(' \\cdot ') |
|
)+' = '+ frac( |
|
best.above.map(function(d) { return d[2];}).join(' \\cdot '), |
|
best.below.map(function(d) { return d[2];}).join(' \\cdot ') |
|
)+ ' = '+best.result +' $$' |
|
); |
|
}); |
|
} |