Skip to content

Instantly share code, notes, and snippets.

@film42
Last active August 29, 2015 14:04
Show Gist options
  • Save film42/e400aee59ff97a795522 to your computer and use it in GitHub Desktop.
Save film42/e400aee59ff97a795522 to your computer and use it in GitHub Desktop.
(WIP) Pattern Matching Algorithm using Stock Tick Data: Uses FooPlot for Rendering Graphs
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <cmath>
#include "functional.h"
#include "graph.h"
// DATA PARSING
#define PATTERN_DISTANCE 30
typedef std::vector< long long > DateVector;
typedef std::vector< long double > DataVector;
typedef std::vector< DataVector > PatternVector;
struct DataSet {
DataSet( long long approx_size = 0 ) {
date_times.reserve( approx_size );
bid_prices.reserve( approx_size );
asking_prices.reserve( approx_size );
}
DateVector date_times;
DataVector bid_prices;
DataVector asking_prices;
DataVector average_prices() {
// Cached?
if( !m_average_prices.empty() ) return m_average_prices;
// Make
m_average_prices.reserve( bid_prices.size() );
for( int i = 0; i < bid_prices.size(); ++i ) {
auto average = ( bid_prices[i] + asking_prices[i] ) / 2;
m_average_prices.push_back( average );
}
return m_average_prices;
}
private:
DataVector m_average_prices;
};
DataSet parse_file( std::string path, long long approximate_size = 0 ) {
DataSet data_set( approximate_size );
std::ifstream file( path );
std::string buffer;
while( std::getline(file, buffer) ) {
// First 14 is for date
auto date = buffer.substr( 0, 14 );
// Reset the buffer
buffer = buffer.substr( 15 );
size_t index = buffer.find(',');
// Get data points
auto data_point_1 = buffer.substr( 0, index );
auto data_point_2 = buffer.substr( index + 1 );
// Save the data points
data_set.date_times.push_back( std::stoll( date ) );
data_set.bid_prices.push_back( std::stod( data_point_1 ) );
data_set.asking_prices.push_back( std::stod( data_point_2 ) );
}
return data_set;
}
// ALGORITHM
long double percent_change( long double start, long double current ) {
// Refactor me!!
try {
long double result = ( ( current - start ) / ( std::fabs(start) ) ) * 100;
if( result == 0 ) return 0.0000001;
else return result;
} catch(...) {
return 0.0000001;
}
}
PatternVector pattern_storage( DataVector average_line ) {
PatternVector all_patterns;
long long y = PATTERN_DISTANCE;
long long x = average_line.size() - (2 * PATTERN_DISTANCE);
while( y < x ) {
DataVector pattern;
// Save All Patterns
for( int i = ( PATTERN_DISTANCE - 1 ); i >= 0; --i ) {
auto index = y - PATTERN_DISTANCE;
auto point = percent_change( average_line[ index ] , average_line[y - i] );
pattern.push_back( point );
}
all_patterns.push_back( pattern );
// Outcome Range and Current Point
DataVector outcome_range;
long long end_range = y + PATTERN_DISTANCE;
long long i = ( y + 20 );
for( ; i < end_range; ++i) {
outcome_range.push_back( average_line[i] );
}
long double current_point = average_line[y];
// Average Outcome
auto fn_outcome_range = Functional< DataVector >( outcome_range );
auto average_outcome = fn_outcome_range.average();
// Future Outcome
auto future_outcome = percent_change( current_point , average_outcome );
// FINISH ME!
++y; // Inc
}
return all_patterns;
}
DataVector current_pattern( DataVector average_line ) {
DataVector pattern;
pattern.reserve( PATTERN_DISTANCE );
for( int i = (PATTERN_DISTANCE - 1); i >= 0; --i ) {
auto result = percent_change( average_line[ PATTERN_DISTANCE ], average_line[i] );
pattern.push_back( result );
}
return pattern;
}
void pattern_recognition( PatternVector pattern_vec, DataVector current_pattern ) {
for( auto& pattern : pattern_vec ) {
long double similarity_total = 0;
for( int i = 0; i < PATTERN_DISTANCE; ++i ) {
long double _change = percent_change( pattern[i], current_pattern[i] );
similarity_total += ( 100 - std::fabs( _change ) );
}
long double similarity_average = similarity_total / PATTERN_DISTANCE;
if( similarity_average > 60 ) {
std::cout << "Similarity: " << similarity_average << "%" << std::endl;
Graph< DataVector > graph;
graph.add_line( pattern, "FF0000" );
graph.add_line( current_pattern, "001DAD" );
std::cout << graph.to_fooplot() << std::endl;
}
}
}
// TESTER
int main(int argc, const char * argv[]) {
std::cout << "Start" << std::endl;
auto data_set = parse_file( "/tmp/GBPUSD/GBPUSD1d.txt", 1000 );
std::cout << "Done" << std::endl;
// Just do it (TM)
auto average_line = data_set.average_prices();
auto patterns = pattern_storage( average_line );
auto current = current_pattern( average_line );
pattern_recognition( patterns, current );
// insert code here...
std::cout << "Hello, World!\n";
return 0;
}
#include <functional>
#include <vector>
template< typename T >
class Functional {
public:
typedef typename T::value_type V;
Functional( T collection ): m_collection(collection) {}
V sum() {
V total = 0;
for( auto val : m_collection ) {
total += val;
}
return total;
}
V average() {
return sum() / m_collection.size();
}
private:
T m_collection;
};
#include <vector>
#include <sstream>
#include "base64.h"
template< class T >
class Graph {
public:
Graph() {}
void add_line( T data_points, std::string color = "000000" ) {
std::ostringstream outstream;
outstream << "{\"type\":3,\"eq\":[";
for(int i = 0; i < data_points.size(); ++i) {
// Json comma
if( i > 0 ) {
outstream << ",";
}
// Point
outstream << "[\"" << (i+1) << "\",\"" << data_points[i] << "\"]";
}
outstream << "]";
outstream << ",\"color\":\"#" << color << "\"}";
m_chart_lines.push_back( outstream.str() );
}
std::string to_json() {
std::ostringstream outstream;
outstream << "[";
for( auto line : m_chart_lines ) {
outstream << line << ",";
}
outstream << "{\"type\":1000,\"window\":[\"2.427692307692307\",\"31.427692307692304\",\"-0.01092341430854167\",\"0.008419398805292345\"]}]";
return outstream.str();
}
std::string to_fooplot() {
std::string json = to_json();
auto b64_input_json = reinterpret_cast<const unsigned char*>( json.c_str() );
auto b64_encoded_json = base64_encode( b64_input_json , (unsigned int) json.length() );
std::ostringstream outstream;
outstream << "http://fooplot.com/#";
outstream << b64_encoded_json;
return outstream.str();
}
private:
std::vector< std::string > m_chart_lines;
};
// Source: https://github.com/ReneNyffenegger/development_misc/tree/master/base64
// Only change:
while((i++ < 3))
ret += '=';
// Becomes
while((i++ < 3))
ret += '-';
// To match the fooplot b64 encoder

Similarity: 60.2744%

http://fooplot.com/#W3sidHlwZSI6MywiZXEiOltbIjEiLCItMC4wMDU0NzEwMSJdLFsiMiIsIi0wLjAwNzQwMTk1Il0sWyIzIiwiLTAuMDA3NzIzNzciXSxbIjQiLCItMC4wMDU3OTI4MyJdLFsiNSIsIi0wLjAwNjExNDY1Il0sWyI2IiwiLTAuMDA2MTE0NjUiXSxbIjciLCItMC4wMDgzNjc0MiJdLFsiOCIsIi0wLjAwNTc5MjgzIl0sWyI5IiwiLTAuMDA3NDAxOTUiXSxbIjEwIiwiLTAuMDA3NzIzNzciXSxbIjExIiwiLTAuMDA3NDAxOTUiXSxbIjEyIiwiLTAuMDA4MDQ1NiJdLFsiMTMiLCItMC4wMDc3MjM3NyJdLFsiMTQiLCItMC4wMDgwNDU2Il0sWyIxNSIsIi0wLjAwNTQ3MTAxIl0sWyIxNiIsIi0wLjAwNDgyNzM2Il0sWyIxNyIsIi0wLjAwNzA4MDEyIl0sWyIxOCIsIi0wLjAwODA0NTYiXSxbIjE5IiwiLTAuMDA4MzY3NDIiXSxbIjIwIiwiLTAuMDA4MzY3NDIiXSxbIjIxIiwiLTAuMDA4MzY3NDIiXSxbIjIyIiwiLTAuMDA4Njg5MjQiXSxbIjIzIiwiLTAuMDA4MzY3NDIiXSxbIjI0IiwiLTAuMDA4MzY3NDIiXSxbIjI1IiwiLTAuMDA4Njg5MjQiXSxbIjI2IiwiLTAuMDA4MzY3NDIiXSxbIjI3IiwiLTAuMDA4MzY3NDIiXSxbIjI4IiwiLTAuMDA4Njg5MjQiXSxbIjI5IiwiLTAuMDA4MDQ1NiJdLFsiMzAiLCItMC4wMDc3MjM3NyJdXSwiY29sb3IiOiIjRkYwMDAwIn0seyJ0eXBlIjozLCJlcSI6W1siMSIsIi0wLjAwMjg5NjQ1Il0sWyIyIiwiLTAuMDA2NzU4MzkiXSxbIjMiLCItMC4wMDY3NTgzOSJdLFsiNCIsIi0wLjAwNDUwNTU5Il0sWyI1IiwiLTAuMDA0NTA1NTkiXSxbIjYiLCItMC4wMDQxODM3NiJdLFsiNyIsIi0wLjAwNDgyNzQyIl0sWyI4IiwiLTAuMDA0ODI3NDIiXSxbIjkiLCItMC4wMDU3OTI5Il0sWyIxMCIsIi0wLjAwNjExNDczIl0sWyIxMSIsIi0wLjAwNjQzNjU2Il0sWyIxMiIsIi0wLjAwNjc1ODM5Il0sWyIxMyIsIi0wLjAwNTc5MjkiXSxbIjE0IiwiLTAuMDA1NDcxMDgiXSxbIjE1IiwiLTAuMDA1NDcxMDgiXSxbIjE2IiwiLTAuMDA2MTE0NzMiXSxbIjE3IiwiLTAuMDA2MTE0NzMiXSxbIjE4IiwiLTAuMDA0NTA1NTkiXSxbIjE5IiwiLTAuMDA0NTA1NTkiXSxbIjIwIiwiLTAuMDA0MTgzNzYiXSxbIjIxIiwiLTAuMDA0MTgzNzYiXSxbIjIyIiwiLTAuMDA1NzkyOSJdLFsiMjMiLCItMC4wMDUxNDkyNSJdLFsiMjQiLCItMC4wMDM1NDAxMSJdLFsiMjUiLCItMC4wMDMyMTgyOCJdLFsiMjYiLCItMC4wMDI4OTY0NSJdLFsiMjciLCItMC4wMDI4OTY0NSJdLFsiMjgiLCItMC4wMDE2MDkxNCJdLFsiMjkiLCIwLjAwMDMyMTgyOCJdLFsiMzAiLCIwLjAwMTI4NzMxIl1dLCJjb2xvciI6IiMwMDFEQUQifSx7InR5cGUiOjEwMDAsIndpbmRvdyI6WyIyLjQyNzY5MjMwNzY5MjMwNyIsIjMxLjQyNzY5MjMwNzY5MjMwNCIsIi0wLjAxMDkyMzQxNDMwODU0MTY3IiwiMC4wMDg0MTkzOTg4MDUyOTIzNDUiXX1d

@film42
Copy link
Author

film42 commented Aug 6, 2014

Pattern Matching Series: http://youtu.be/LOyWJnFJEDY?t=3m28s

@film42
Copy link
Author

film42 commented Aug 10, 2014

Data available here: http://sentdex.com/GBPUSD.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment