#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "cognition_typedefs.h"
#include "cognition_year.h"
#include "cognition_candle.h"
#include "cognition_time.h"
#include "cognition.h"
#include "cognition_db_candle_loader.h"
//#define printf(...) (void)0
typedef struct {
union
{
i32 rule_buffer[5];
struct
{
i32 candle_attrib_1;
i32 candle_lookback_1;
b32 less_than_bigger_than;
i32 candle_attrib_2;
i32 candle_lookback_2;
};
};
} rule;
rule generate_rule(size_t max_lookback)
{
rule Result;
i32 candle_attrib_1 = (u8)rand_f(4);
i32 candle_lookback_1 = (i32)rand_f(max_lookback);
b32 less_than_bigger_than = (b32)rand_f(2);
i32 candle_attrib_2 = (u8)rand_f(4);
i32 candle_lookback_2 = (i32)rand_f(max_lookback);
//if we're on the same candle
//we shouldnt compare if the low is bigger than the high
//or conversely we shouldnt compare if the low is smaller
i32 lookback_delta = (candle_lookback_2 - candle_lookback_1);
if(candle_lookback_1 == candle_lookback_2)
{
if(candle_attrib_1==0)
{
candle_attrib_2 = 3;
}
else if(candle_attrib_1 == 3)
{
candle_attrib_2 = 0;
}
else
{
candle_attrib_1 = (u8)rand_f(2) ? 0 : 3;
if(candle_attrib_1==0)
{
candle_attrib_2 = 3;
}
else if(candle_attrib_1 == 3)
{
candle_attrib_2 = 0;
}
}
} // if lookback is the same
else if( lookback_delta == 1 )
{
//if the candle_attrib is the close
if( candle_attrib_1 == 3 )
{
//pick everything but the open
candle_attrib_2 = (i8)rand_f_lower_bound(1,4);
}
}
else if(lookback_delta == -1)
{
//if the candle_attrib is the open
if( candle_attrib_1 == 0 )
{
//pick everything but the close
candle_attrib_2 = rand_f(3);
}
}
Result.candle_attrib_1 = candle_attrib_1;
Result.candle_attrib_2 = candle_attrib_2;
Result.candle_lookback_1 = candle_lookback_1;
Result.candle_lookback_2 = candle_lookback_2;
Result.less_than_bigger_than = less_than_bigger_than;
return(Result);
}
static char attrib_lut[4] = {'O','H','L','C'};
static char less_than_bigger_than_lut[4] = {'<','>'};
void print_rule(rule rule_to_print)
{
printf(" %c[%d] %c %c[%d] ",
attrib_lut[rule_to_print.candle_attrib_1],
rule_to_print.candle_lookback_1,
less_than_bigger_than_lut[rule_to_print.less_than_bigger_than],
attrib_lut[rule_to_print.candle_attrib_2],
rule_to_print.candle_lookback_2
);
}
b32 rules_are_equal(rule new_rule, rule old_rule)
{
b32 Result_normal = 1;
b32 Result_inverted = 1;
b32 Result_total = 0;
i32 inverted_buffer[5] = {0};
inverted_buffer[0] = old_rule.candle_attrib_2;
inverted_buffer[1] = old_rule.candle_lookback_2;
inverted_buffer[2] = !old_rule.less_than_bigger_than;
inverted_buffer[3] = old_rule.candle_attrib_1;
inverted_buffer[4] = old_rule.candle_lookback_1;
for(size_t i = 0; i < 5; ++i)
{
b32 check = !(new_rule.rule_buffer[i] == old_rule.rule_buffer[i]);
if(check)
{
Result_normal = 0;
break;
}
}
for(size_t i = 0; i < 5; ++i)
{
b32 check = !(new_rule.rule_buffer[i] == inverted_buffer[i]);
if(check)
{
Result_inverted = 0;
break;
}
}
if( (Result_inverted == 1) || (Result_normal == 1) )
{
Result_total = 1;
}
return(Result_total);
}
static inline
void generate_rule_row( rule *work_row, size_t number_of_rules_in_row, size_t pattern_lookback)
{
size_t rules_pushed_in_row = 0;
while(rules_pushed_in_row < number_of_rules_in_row)
{
//generate rule
rule temp_rule = generate_rule(pattern_lookback);
//initialize rules are equal check
b32 rules_are_equal_check = 0;
//for every other rule in the row check if this temp rule is equal to the anyone of the rules
//if it is set the bool to one
for(size_t check_i = 0; check_i < rules_pushed_in_row; check_i+=5)
{
rule check_rule = work_row[ check_i ];
if( rules_are_equal(temp_rule, check_rule) )
{
rules_are_equal_check = 1;
}
}
//if rules raent equal push rule to rule row
//else repeat until we have a row of unique rules
if(!rules_are_equal_check)
{
work_row[rules_pushed_in_row] = temp_rule;
rules_pushed_in_row++;
}
else
{
printf("found equal rule\n");
print_rule(temp_rule);
}
}
}
static inline
b32 replace_work_row_to_table_if_unique(rule *work_row, rule *rule_table, size_t rows_pushed, const size_t number_of_rules_in_row,
size_t replace_this_row)
{
b32 Result = 0;
b32 found_duplicate_row = 0;
for(size_t row_check_i = 0; row_check_i < rows_pushed; ++row_check_i)
{
rule *check_row = &rule_table[ (row_check_i)*number_of_rules_in_row ];
size_t common_rules_found = 0;
for(size_t rule_comp_i = 0; rule_comp_i < number_of_rules_in_row; ++rule_comp_i)
{
rule new_row_rule = work_row[rule_comp_i];
for(size_t rule_comp_j = 0; rule_comp_j < number_of_rules_in_row; ++rule_comp_j)
{
rule check_rule = check_row[rule_comp_j];
if( rules_are_equal(new_row_rule, check_rule) )
{
common_rules_found += 1;
}
}
}
if(common_rules_found == number_of_rules_in_row)
{
found_duplicate_row = 1;
break;
}
}
if(!found_duplicate_row)
{
rule *dest_row = (&rule_table[(replace_this_row) * number_of_rules_in_row]);
for(size_t rule_copy_i = 0; rule_copy_i < number_of_rules_in_row; ++rule_copy_i)
{
dest_row[rule_copy_i] = work_row[rule_copy_i];
}
Result = 1;
}
return(Result);
}
static inline
void push_work_row_to_table_if_unique(rule *work_row, rule *rule_table, size_t *rows_pushed, const size_t number_of_rules_in_row)
{
b32 found_duplicate_row = 0;
//printf("entering function\n");
for(size_t row_check_i = 0; row_check_i < *rows_pushed; ++row_check_i)
{
//printf(" row check i %zu rows pushed %zu\n", row_check_i , *rows_pushed );
rule *check_row = &rule_table[ (row_check_i)*number_of_rules_in_row ];
size_t common_rules_found = 0;
for(size_t rule_comp_i = 0; rule_comp_i < number_of_rules_in_row; ++rule_comp_i)
{
rule new_row_rule = work_row[rule_comp_i];
for(size_t rule_comp_j = 0; rule_comp_j < number_of_rules_in_row; ++rule_comp_j)
{
rule check_rule = check_row[rule_comp_j];
if( rules_are_equal(new_row_rule, check_rule) )
{
common_rules_found += 1;
}
}
}
if(common_rules_found == number_of_rules_in_row)
{
found_duplicate_row = 1;
printf("found duplicate rule row\n");
break;
}
}
if(found_duplicate_row == 0)
{
rule *dest_row = (&rule_table[ (*rows_pushed) * number_of_rules_in_row]);
for(size_t rule_copy_i = 0; rule_copy_i < number_of_rules_in_row; ++rule_copy_i)
{
dest_row[rule_copy_i] = work_row[rule_copy_i];
}
*rows_pushed += 1;
}
else
{
printf("FOUND DUPLICATE skipping\n");
}
}
static inline void copy_rule_row_from_to(rule *src_table, size_t source_row_idx, rule *dest_table, size_t dest_row_idx,size_t row_size)
{
for(size_t copy_rule_i = 0; copy_rule_i < row_size; ++copy_rule_i)
{
rule *src = &src_table[(row_size*source_row_idx)+copy_rule_i];
rule *dest = &dest_table[(row_size*dest_row_idx)+copy_rule_i];
*dest = *src;
}
}
void print_rule_table_with_objective( rule *table, size_t row_size, size_t rows_to_print, f64 *obj)
{
size_t total_rules_to_print = row_size*rows_to_print;
for(size_t print_rule_i = 0; print_rule_i < total_rules_to_print; ++print_rule_i)
{
print_rule(table[print_rule_i]);
if(!(print_rule_i%(row_size-1)) && (print_rule_i != 0))
{
printf(" <----> [%f]\n", obj[print_rule_i/row_size]);
}
}
}
static inline void print_rule_row(rule *rule_row, size_t row_size)
{
for(size_t print_rule_i = 0; print_rule_i < row_size; ++print_rule_i)
{
print_rule( rule_row[print_rule_i]);
}
printf("\n");
}
static inline void print_rule_row_with_objective_function_from_table
(rule *rule_table, size_t row_idx, size_t row_size, f64 objective_function_value)
{
printf("trying to print_parent with row_idx %zu\n",row_idx);
for(size_t print_rule_i = 0; print_rule_i < row_size; ++print_rule_i)
{
print_rule(rule_table[(row_size*row_idx) + print_rule_i]);
}
printf(" <----> [%f]\n", objective_function_value);
}
void print_rule_table( rule *table, size_t row_size, size_t rows_to_print)
{
size_t total_rules_to_print = row_size*rows_to_print;
for(size_t print_rule_i = 0; print_rule_i < total_rules_to_print; ++print_rule_i)
{
if(!(print_rule_i%row_size) && (print_rule_i != 0))
{
printf("\n");
}
print_rule(table[print_rule_i]);
}
printf("\n");
}
typedef struct {
f64 max;
b32 initialized;
}Max;
/*
static inline void copy_from_table_into_work_row(rule *work_row, rule *src_table, size_t source_row_idx)
{
for(size_t copy_rule_i = 0; copy_rule_i < row_size; ++copy_rule_i)
{
rule *src = &src_table[(row_size*source_row_idx)+copy_rule_i];
rule *dest = &work_row[copy_rule_i];
*dest = *src;
}
}
*/
void for_every_candle_check_if_pattern_in_rule_table_is_present_accum_sums_for_matrin_ratio_calc( candle64_vec *candles,
rule *selection_table,
f64 *total_return,
Max *total_return_max,
f64 *ulcer_sq_sum,
u64 *pattern_freq,
size_t population_size,
size_t pattern_lookback,
size_t number_of_rules_in_row
)
{
//for every candle
for(size_t candle_i = pattern_lookback-1; candle_i < candles->held-1; ++candle_i)
{
//for every generated rule
for( size_t rule_row_i = 0; rule_row_i < population_size; ++rule_row_i)
{
rule *current_rule_row = &selection_table[rule_row_i*number_of_rules_in_row];
size_t rules_met = 0;
for(size_t rule_i = 0; rule_i < number_of_rules_in_row; ++rule_i)
{
rule current_rule = current_rule_row[rule_i];
b32 condition = 0;
if( current_rule.less_than_bigger_than == 1 )
{
condition =
(candles->elements[candle_i-current_rule.candle_lookback_1].candle_attribs[current_rule.candle_attrib_1]
>
candles->elements[candle_i-current_rule.candle_lookback_2].candle_attribs[current_rule.candle_attrib_2]);
}
else if(current_rule.less_than_bigger_than == 0)
{
condition =
(candles->elements[candle_i-current_rule.candle_lookback_1].candle_attribs[current_rule.candle_attrib_1]
<
candles->elements[candle_i-current_rule.candle_lookback_2].candle_attribs[current_rule.candle_attrib_2]);
}
else
{
printf("[GENETIC_ALGORITHM_LAYER_ERROR]: found pattern with unrecognizable comparison value \n");
exit(21);
}
if(condition == 1)
{
rules_met+=1;
}
}
//if all rules in the rule row are found accumulate
if(rules_met == number_of_rules_in_row)
{
f64 current_close = candles->elements[candle_i].close;
f64 next_close = candles->elements[candle_i+1].close;
total_return[rule_row_i] += log(next_close/current_close);
pattern_freq[rule_row_i] += 1;
if( !total_return_max[rule_row_i].initialized )
{
total_return_max[rule_row_i].max = total_return[rule_row_i];
total_return_max[rule_row_i].initialized = 1;
}
else
{
if(total_return[rule_row_i] > total_return_max[rule_row_i].max)
{
total_return_max[rule_row_i].max = total_return[rule_row_i];
}
else
{
f64 sqr_comp = ( (total_return[rule_row_i]/total_return_max[rule_row_i].max)-1.0f);
ulcer_sq_sum[rule_row_i] += (sqr_comp*sqr_comp);
}
}
}
}
}
}
typedef struct {
f64 *value;
u64 *key;
size_t held;
size_t capacity;
} u64f64_map;
u64f64_map give_u64_f64_map(size_t size)
{
u64f64_map Result;
Result.key = (u64*)malloc(size*sizeof(u64));
Result.value = (f64*)malloc(size*sizeof(f64));
if(Result.key == NULL)
{
printf("[TYPE_LAYER_ERROR]: failed to allocate memory for key buffer in map buy more ram\n");
exit(21);
}
if(Result.value == NULL)
{
printf("[TYPE_LAYER_ERROR]: failed to allocate memory for value buffer in map buy more ram\n");
exit(21);
}
Result.held = 0;
Result.capacity = size;
return(Result);
}
void zero_u64f64_map( u64f64_map m )
{
for(size_t kv_i = 0; kv_i < m.capacity; ++kv_i)
{
m.key[kv_i] = 0;
m.value[kv_i] = 0;
}
}
void print_u64f64_map( u64f64_map m)
{
for(size_t kv_i = 0; kv_i < m.held; ++kv_i)
{
printf("Key %ld Value %f\n", m.key[kv_i], m.value[kv_i]);
}
}
void free_u64f64_map( u64f64_map m )
{
free(m.key);
free(m.value);
}
void
calculate_martin_ratio_for_every_non_zero_frequency_non_negative_return_pattern_and_push_to_map_in_decending_order_accum_fitness_sum
( size_t population_size,
f64 *total_return,
u64 *pattern_freq,
f64 frequency_threshold,
f64 *ulcer_sq_sum,
f64 *fitness_sum,
u64f64_map *map )
{
for(size_t pattern_i = 0; pattern_i < population_size; ++pattern_i)
{
if( (total_return[pattern_i] > 0.0f) && (pattern_freq[pattern_i] >= frequency_threshold))
{
//calculate ulcer index
f64 UI = sqrt( (ulcer_sq_sum[pattern_i]) / ((f64)pattern_freq[pattern_i]));
//devide tutal return by ulcer index to get a patterns martin raio
f64 martin_ratio_value = total_return[pattern_i]/UI;
//append to fitness sum
*fitness_sum += martin_ratio_value;
//push to map sorting in descending order
if(map->held == 0)
{
map->key[0] = pattern_i;
map->value[0] = martin_ratio_value;
}
else
{
assert(map->held < map->capacity && "capcity error\n");
i64 instert_here = map->held;
map->value[instert_here] = martin_ratio_value;
map->key[instert_here] = pattern_i;
while( (map->value[instert_here-1] < map->value[instert_here]) && (instert_here-1 >= 0) )
{
//swap martin ratio
f64 temp_value = map->value[instert_here-1];
map->value[instert_here-1] = map->value[instert_here];
map->value[instert_here] = temp_value;
//swap idx in selection table ratio
u64 temp_key = map->key[instert_here-1];
map->key[instert_here-1] = map->key[instert_here];
map->key[instert_here] = temp_key;
instert_here--;
}
}
map->held = map->held+1;
}
}
}
void find_two_non_equal_parent_rule_rows( u64 *parent_idx_in_selection_table, u64 *parent_idx_in_map,
f64 fitness_sum, u64f64_map map, size_t population_size )
{
size_t parents_created = 0;
size_t prev_sorted_map_idx = population_size+1; //initializing at invalid value instead of 0 to allow for the best pair to be chose as a parent
while(parents_created < 2)
{
//pick random number from 0 fitness_sum we call this the threshold
f64 fitness_selection_threshold = rand_f(fitness_sum);
f64 fitness_selection_ticker = 0.0f;
size_t sorted_map_idx = 0;
//decend the map until threshold is met once it is that parent is picked given they're not the first parent
while(1) {
fitness_selection_ticker += (map.value[sorted_map_idx]);
assert( (map.key[sorted_map_idx] < (population_size))&& "false value");
if(fitness_selection_ticker >= fitness_selection_threshold)
{
if(sorted_map_idx != prev_sorted_map_idx) {
parent_idx_in_selection_table[parents_created] = map.key[sorted_map_idx];
parent_idx_in_map[parents_created]= sorted_map_idx;
printf("[GENETIC LAYER LOG]: found valid parent %zu/%zu selection_idx %zu sorted_map_idx %zu \n", parents_created, (size_t)2,
parent_idx_in_selection_table[parents_created], sorted_map_idx);
++parents_created;
prev_sorted_map_idx = sorted_map_idx;
}
break;
}
sorted_map_idx+=1;
}
}
}
int main(void)
{
srand(time(0));
u64 start = nanos();
//LOAD IN ONE HOUR CANDLES
candle64_vec eth_hourly_candles = load_candles_from_db("coinbase","ETH-USD",3600,2018,2023);
// initialization parameters
size_t number_of_rules_in_row = 3;
size_t pattern_lookback = 5;
size_t population_size = 200;
f64 mutation_attrib_change_cance = 0.05f;
//size_t rule_row_i = 0;
// ALLOCATION
size_t rules_to_alloc = number_of_rules_in_row*population_size;
rule *rule_rows = (rule *)malloc(sizeof(rule)*rules_to_alloc*2);
rule *selection_table = &rule_rows[0];
rule *breading_table = &rule_rows[population_size*number_of_rules_in_row];
rule *work_row = (rule *)malloc(sizeof(rule)*number_of_rules_in_row);
rule *work_row_two = (rule *)malloc(sizeof(rule)*number_of_rules_in_row);
u64f64_map map = give_u64_f64_map(population_size);
f64 *total_return = malloc( sizeof(f64)* population_size);
f64 *ulcer_sq_sum = malloc( sizeof(f64) * population_size);
Max *total_return_max = malloc( sizeof(Max) * population_size);
u64 *pattern_freq = malloc( sizeof(u64) * population_size);
// ALOCATION
zero_u64f64_map( map );
//INITIAL_GENERATION
size_t rows_pushed = 0;
while(rows_pushed < population_size) {
generate_rule_row( work_row, number_of_rules_in_row, pattern_lookback );
push_work_row_to_table_if_unique( work_row, selection_table, &rows_pushed, number_of_rules_in_row);
}
//INITIAL_GENERATION
size_t generation_count = 0;
size_t total_generations = 3000;
while(1)
{
//print_rule_table(selection_table, number_of_rules_in_row, population_size);
// buffers for ulcer index calculation for each patter in the population
// NOTE(m2_sprite): we could also just add these attributes to each row instead of having this buffers
// but that would prevent their reusablitiy
for(size_t comp_i = 0; comp_i < population_size; ++comp_i)
{
ulcer_sq_sum[comp_i] = 0.0f;
total_return[comp_i] = 0.0f;
pattern_freq[comp_i] = 0;
total_return_max[comp_i].max= 0;
total_return_max[comp_i].initialized= 0;
}
for_every_candle_check_if_pattern_in_rule_table_is_present_accum_sums_for_matrin_ratio_calc( ð_hourly_candles,
selection_table,
total_return,
total_return_max,
ulcer_sq_sum,
pattern_freq,
population_size,
pattern_lookback,
number_of_rules_in_row);
zero_u64f64_map( map );
map.held = 0;
f64 fitness_sum = 0.0f;
size_t frequency_threshold = eth_hourly_candles.held*0.025;
calculate_martin_ratio_for_every_non_zero_frequency_non_negative_return_pattern_and_push_to_map_in_decending_order_accum_fitness_sum
(
population_size,
total_return,
pattern_freq,
frequency_threshold,
ulcer_sq_sum,
&fitness_sum,
&map
);
printf("population fitness sum %f\n", fitness_sum);
//copy rule_row with best martin ratio to the top of the breading table
//ELITISM
copy_rule_row_from_to( selection_table, map.key[0], breading_table, 0, number_of_rules_in_row);
printf("OPTIMAL PATTERN FOUND\n");
print_rule_row_with_objective_function_from_table( breading_table, 0, number_of_rules_in_row,
map.value[0]);
if(generation_count == total_generations)
{
break;
}
size_t new_population_created_count = 1;
//ELITISM
//PARENT SELECTION SPLICING AND BREADING
while( new_population_created_count < population_size )
{
printf("%zu/%zu\n",new_population_created_count, population_size);
u64 parent_idx_in_selection_table[2] = {0};
u64 parent_idx_in_map[2] = {0};
printf("pre function\n");
//print_u64f64_map(map);
find_two_non_equal_parent_rule_rows( parent_idx_in_selection_table, parent_idx_in_map,
fitness_sum,
map,
population_size);
printf("post function\n");
//print_u64f64_map(map);
printf("generation------------------------- %zu\n", generation_count);
rule *parent_one = &selection_table[parent_idx_in_selection_table[0]*number_of_rules_in_row];
rule *parent_two = &selection_table[parent_idx_in_selection_table[1]*number_of_rules_in_row];
printf("post paretns asignment\n");
//print_u64f64_map(map);
printf("NEW PARETNS: \n");
print_rule_row_with_objective_function_from_table( selection_table, parent_idx_in_selection_table[0],
number_of_rules_in_row, map.value[parent_idx_in_map[0]]);
print_rule_row_with_objective_function_from_table( selection_table, parent_idx_in_selection_table[1],
number_of_rules_in_row, map.value[parent_idx_in_map[1]]);
printf("pre split point\n");
//print_u64f64_map(map);
size_t split_point = (size_t)rand_f(number_of_rules_in_row-1);
for(size_t rule_i = 0; rule_i < number_of_rules_in_row; ++rule_i)
{
if(rule_i <= split_point)
{
work_row[rule_i] = parent_one[rule_i];
work_row_two[rule_i] = parent_two[rule_i];
}
else
{
work_row[rule_i] = parent_two[rule_i];
work_row_two[rule_i] = parent_one[rule_i];
}
}
printf("new_population_count %zu\n",new_population_created_count);
//print_rule_table(breading_table, number_of_rules_in_row, new_population_created_count);
printf("CHILDREN: \n");
printf("Child one\n");
push_work_row_to_table_if_unique( work_row, breading_table, &new_population_created_count, number_of_rules_in_row);
print_rule_row(work_row, number_of_rules_in_row);
if(new_population_created_count != population_size)
{
printf("Child two\n");
push_work_row_to_table_if_unique( work_row_two, breading_table, &new_population_created_count, number_of_rules_in_row);
print_rule_row(work_row_two, number_of_rules_in_row);
}
else
{
printf("PUSHING LAST CHILD\n");
break;
}
}
printf("fintness sum %f\n", fitness_sum);
//PARENT SELECTION SPLICING AND BREADING
//MUTATION
b32 do_we_apply_fresh_pattern_change = rand_f(1.0f) <= mutation_attrib_change_cance ? 1 : 0;
printf("before mutation\n");
if(do_we_apply_fresh_pattern_change)
{
while(1)
{
size_t random_row_idx_that_isnt_the_optimal = rand_f_lower_bound(1.0f, (f64)population_size);
assert(random_row_idx_that_isnt_the_optimal < population_size && "[GENETIC_LAYER_ERROR]: TRYING TO INDEX FALSE ROW FOR MUTATION\n");
assert(random_row_idx_that_isnt_the_optimal != 0 && "[GENETIC_LAYER_ERROR]: TRYING TO INDEX FALSE ROW FOR MUTATION\n");
printf("[GENETIC_LAYER_LOG]: Trying to apply fresh pattern muation\n");
generate_rule_row( work_row, number_of_rules_in_row, pattern_lookback );
if(replace_work_row_to_table_if_unique(work_row, breading_table, population_size, number_of_rules_in_row, random_row_idx_that_isnt_the_optimal))
{
printf("[GENETIC_LAYER_LOG]: Applied mutation successfully\n");
break;
}
}
}
//MUTATION
//flip selection table and breading table pointers
rule *temp_selection_table = selection_table;
selection_table = breading_table;
breading_table = temp_selection_table;
generation_count++;
printf("generation_count %zu\n",generation_count);
}
free(rule_rows);
free_u64f64_map(map);
free(work_row);
free(work_row_two);
free(total_return);
free(ulcer_sq_sum);
free(total_return_max);
free(pattern_freq);
u64 end = nanos();
perf_log_runtime_seconds(start, end);
return(0);
}