Last active
January 1, 2016 11:29
-
-
Save ZJONSSON/8138007 to your computer and use it in GitHub Desktop.
Bitcoin Arbitrage - Priceonomics
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 +' $$' | |
); | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style>.MathJax_Display {text-align: left !important;}</style> | |
<body> | |
<h1>Bitcoin Arbitrage Exercise</h1> | |
This is my solution to the <a href="http://priceonomics.com/jobs/puzzle/">Bitcoin Arbitrage puzzle</a> posted earlier this year by <a href="http://priceonomics.com">Priceonomics</a>. A matrix of continuously-published currency rates is algorithmically skewed and the challenge is to uncover any arbitrage opportunities. Calculations are re-run every time this page is loaded/refreshed, using <a href="http://developer.yahoo.com/yql/">YQL</a> to fetch the latest rates and <a href="http://www.mathjax.org/">MathJax</a> to render the results. <br><br> | |
All spot market arbitrage can be decomposed into duality-arbitrage (different prices for the same thing) and triangular-arbitrage (relative prices between three assets not unity). Any higher order arbitrage relationships (i.e. involving more than three assets) are a combination of duality- and triangular-arbs. | |
<h2>Duality Arbitrage</h2> | |
Duality arbitrage occurs when the different prices are attached to the same currency pair. | |
<div id="dual"></div> | |
<h2>Triangular Arbitrage</h2> | |
A currency has no absolute value, only relative value against other assets/currencies. Triangular arbitrage occurs when the relative value of a currency towards two different assets does not match the relative value between those assets. For any triangular relationship in this exercise there are 8 combinations of currency pairs to choose from (i.e. the price of EURUSD differs from USDEUR). To avoid double-counting the duality issues identified above, we pick the triangular combination of rates that results in the minimum arbitrage possible (the rest is contaminated by duality-arb) | |
<div id="triangular"></div> | |
<hr> | |
(c) 2013 <a href="https://github.com/zjonsson">Ziggy Jonsson</a> - Licence <a href="http://opensource.org/licenses/MIT">MIT</a> - <a href="https://gist.github.com/ZJONSSON/8138007">(source)</a> | |
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> | |
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script> | |
<script src="arb.js"></script> | |
<script src="http://query.yahooapis.com/v1/public/yql?callback=main&format=json&q=select%20*%20from%20json%20where%20url=%22http://fx.priceonomics.com/v1/rates/%22"></script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment