Created
December 6, 2013 18:41
-
-
Save msnorth/040e9a69ed1e22aad1ac to your computer and use it in GitHub Desktop.
A BitCoin arbitrage bot using a parallelized Bellman-Ford graph algorithm
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
#include<iostream> | |
#include <fstream> | |
#include <stdlib.h> /* atol */ | |
#include <list> | |
#include <stack> | |
#include <limits.h> | |
#include <omp.h> | |
#include <curl/curl.h> | |
#include <sstream> | |
#include <math.h> | |
#include <jsoncpp/json.h> | |
using namespace std; | |
class list_node { | |
int v; | |
double weight; | |
public: | |
list_node(int _v, double _w) { | |
v = _v; | |
weight = _w; | |
} | |
int get_vertex() { | |
return v; | |
} | |
double get_weight() { | |
return weight; | |
} | |
}; | |
class graph { | |
int V; // No. of vertices' | |
// Pointer to an array containing adjacency lists | |
list<list_node> *adj; | |
public: | |
graph(int V); // Constructor | |
// function to add an edge to graph | |
void add_edge(int u, int v, double weight); | |
int bellman_ford(int src, double* dist, int* pre); | |
}; | |
graph::graph(int V) { | |
this->V = V; | |
adj = new list<list_node>[V]; | |
} | |
void graph::add_edge(int u, int v, double weight) { | |
list_node node(v, weight); | |
adj[u].push_back(node); | |
} | |
int graph::bellman_ford(int src, double* dist, int* pre) { | |
for (int i = 0; i < V; i++) { | |
dist[i] = INT_MAX; | |
pre[i] = -1; | |
} | |
dist[src] = 0; | |
int relaxed = 0; | |
clock_t startTime1 = clock(); | |
#define CHUNKSIZE 1 /*defines the chunk size as 1 contiguous iteration*/ | |
/*forks off the threads*/ | |
#pragma omp parallel private(i) | |
{ | |
/*Starts the work sharing construct*/ | |
#pragma omp for schedule(dynamic, CHUNKSIZE) | |
clock_t startTime2 = clock(); | |
list<list_node>::iterator i; | |
for (int u = 0; u < V - 1; u++) { | |
relaxed = 0; | |
if (dist[u] != INT_MAX) { | |
for (i = adj[u].begin(); i != adj[u].end(); ++i) { | |
if (dist[i->get_vertex()] > dist[u] + i->get_weight()) { | |
dist[i->get_vertex()] = dist[u] + i->get_weight(); | |
pre[i->get_vertex()] = u; | |
relaxed = 1; | |
} | |
} | |
} | |
} | |
cout << float( clock() - startTime2) / | |
(float) CLOCKS_PER_SEC << " iteraton seconds." << endl; | |
} | |
cout << float( clock() - startTime1) / | |
(float) CLOCKS_PER_SEC << " seconds." << endl; | |
cout << "Vertex\t\tDistance\t\tPred" << endl; | |
for (int i = 0; i < V; i++) { | |
cout << i << "\t\t" << dist[i]; | |
cout << "\t\t" << pre[i] << "\t\t"; | |
cout << endl; | |
} | |
return relaxed; | |
} | |
//function prototypes | |
//for libcurl | |
size_t write_data(char *ptr, size_t size, size_t nmemb, void *userdata); | |
int main() { | |
//set up currencies | |
string coin[16]; | |
coin[0] = "BTC"; | |
coin[1] = "ANC"; | |
coin[2] = "DGC"; | |
coin[3] = "DVC"; | |
coin[4] = "FRC"; | |
coin[5] = "FTC"; | |
coin[6] = "I0C"; | |
coin[7] = "IXC"; | |
coin[8] = "LTC"; | |
coin[9] = "NMC"; | |
coin[10] = "NVC"; | |
coin[11] = "PPC"; | |
coin[12] = "TRC"; | |
coin[13] = "WDC"; | |
coin[14] = "XPM"; | |
coin[15] = "USD"; | |
int V = 16; | |
//1-(the fees incurred by a trade) | |
double discount = 0.995; | |
std::ostringstream stream; | |
//set up curl | |
CURL *curl; | |
CURLcode res; | |
curl = curl_easy_init(); | |
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream); | |
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); | |
curl_easy_setopt(curl, CURLOPT_URL, "https://vircurex.com/api/get_info_for_currency.json"); | |
/* Perform the request, res will get the return code */ | |
res = curl_easy_perform(curl); | |
/* Check for errors */ | |
if (res != CURLE_OK) { | |
fprintf(stderr, "curl_easy_perform() failed: %s\n", | |
curl_easy_strerror(res)); | |
cout << "error" << endl; | |
return 1; | |
} | |
/* always cleanup */ | |
curl_easy_cleanup(curl); | |
std::string output = stream.str(); | |
Json::Value root; | |
Json::Reader reader; | |
bool parsedSuccess = reader.parse(output, root, false); | |
if (not parsedSuccess) { | |
// Report failures and their locations | |
// in the document. | |
cout << output << endl; | |
cout << "Failed to parse JSON" << endl << reader.getFormatedErrorMessages() << endl; | |
return 1; | |
} | |
//# of currencies | |
graph g(V); | |
for (int i = 0; i < V; i++) { | |
const Json::Value currency1 = root[coin[i]]; | |
for (int j = 0; j < V; j++) { | |
if (j != i) { | |
const Json::Value currency2 = currency1[coin[j]]; | |
double exchange = atof(currency2["highest_bid"].asCString()); | |
double volume = atof(currency2["volume"].asCString()); | |
/*only trade coins that are currently being traded and not worthless*/ | |
if (exchange * discount * volume > 0) { | |
// cout << "exchange " << i << " to " << j << " is: " << exchange * discount << endl; | |
g.add_edge(i, j, -log(exchange * discount)); | |
} | |
} | |
} | |
} | |
//clear stream | |
stream.str(std::string()); | |
int s = 0; | |
double dist[V]; | |
int pre[V]; | |
cout << "Following are shortest distances from source " << coin[s] << " \n"; | |
int relaxed = g.bellman_ford(s, dist, pre); | |
if (relaxed) { | |
//get negative cycle from src | |
for (int u = 0; u < V; u++) { | |
if (dist[u] < 0) { | |
cout << "profit: " << exp(dist[u]) << " path: "; | |
int k = u; | |
cout << coin[s] << "->"; | |
do { | |
cout << coin[k] << "->"; | |
k = pre[k]; | |
} while (k != s); | |
cout << coin[k] << endl; | |
} | |
} | |
} | |
return 0; | |
} | |
size_t write_data(char *ptr, size_t size, size_t nmemb, void *userdata) { | |
std::ostringstream *stream = (std::ostringstream*)userdata; | |
size_t count = size * nmemb; | |
stream->write(ptr, count); | |
return count; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment