optional<cache_iterator_type> advance(const tangent_vector& tangent, const double& from_t, const double& to_t, size_t recursion = 1) const { if (recursion > max_recursion_) return optional<cache_iterator_type>(); //log_cout << " Parallel transport: " << from_t << " -> " << to_t << endl; for (set<std::shared_ptr<chart> >::const_iterator i = charts().begin(); i != charts().end(); ++i) if (tangent[*i]) { optional<cache_iterator_type> result(advance_on_chart(*i, *tangent[*i], from_t, to_t)); if (result) return result; } optional<cache_iterator_type> halfway(advance(tangent, from_t, (from_t + to_t) / 2, recursion + 1)); return halfway ? advance(*(*halfway)->second, (from_t + to_t) / 2, to_t, recursion + 1) : optional<cache_iterator_type>(); }
int main(int argc, char* argv[]) { if(argc != 2) { std::cout << "Usage: " << argv[0] << " /path/to/lua/script.lua" << std::endl; exit(1); } std::cout.unsetf(std::ios::floatfield); std::cout.precision(5); std::cout << std::fixed; std::cout << "==================== SETUP ====================" << std::endl; // TODO: detect unusual currency bases for accurate profit calculation auto config = std::make_shared<LuaScript>(argv[1]); std::cout << "Simulating a maximum of " << config->steps() << " steps" << std::endl; std::cout << config->optimizations() << " optimizations per in sample" << std::endl; auto charts = Chart::load_from_string(config->charts()); auto ts = std::make_unique<TickSource>(config->csv_path()); auto vars = Variable::load_from_string(config->variables()); auto optimizer = std::make_unique<Optimizer>(vars); ts->fill_charts(charts); ts->advance_charts_to_next_sunday(charts); int step_number = 1; int weeks_in_sample = std::stoi(config->in_sample_time()); int weeks_out_of_sample = std::stoi(config->out_of_sample_time()); std::vector<float> optimization_scores; std::vector<float> execution_scores; // ----- MAIN LOOP ------------------------------------------------------------------------- for(;;) { // ----- FIND IN AND OUT OF SAMPLE START AND END POINTS ---------------------------- // in_sample_record_end is just out_of_sample_record_start - 1 unsigned long in_sample_record_start = ts->next_record_id(); unsigned long out_of_sample_record_start = 0; unsigned long out_of_sample_record_end = 0; bool tick_is_sunday = true; bool on_sunday = true; int sundays_seen = 0; for(auto tick = ts->next(); tick; tick = ts->next()) { tick_is_sunday = tick->is_sunday(); if(!on_sunday && tick_is_sunday) { on_sunday = true; sundays_seen++; } else if(on_sunday && !tick_is_sunday) { on_sunday = false; } if(sundays_seen == weeks_in_sample) { out_of_sample_record_start = ts->next_record_id() - 1; break; } } if(!out_of_sample_record_start) { bail("ran out of tick source when searching for out of sample start"); } sundays_seen = 0; on_sunday = true; for(auto tick = ts->next(); tick; tick = ts->next()) { tick_is_sunday = tick->is_sunday(); if(!on_sunday && tick_is_sunday) { on_sunday = true; sundays_seen++; } else if(on_sunday && !tick_is_sunday) { on_sunday = false; } if(sundays_seen == weeks_out_of_sample) { // stop on the tick before the current tick out_of_sample_record_end = ts->next_record_id() - 2; break; } } if(!out_of_sample_record_end) { bail("ran out of tick source when searching for out of sample end"); } std::cout << "In sample: " << ts->peek_at(in_sample_record_start)->show() << " through " << ts->peek_at(out_of_sample_record_start - 1)->show() << std::endl; std::cout << "Out of sample: " << ts->peek_at(out_of_sample_record_start)->show() << " through " << ts->peek_at(out_of_sample_record_end)->show() << std::endl; // ----- OPTIMIZE ON IN SAMPLE PERIOD ---------------------------------------------- auto subset = ts->subset(in_sample_record_start, out_of_sample_record_start - 1); auto winner = optimizer->optimize_variables_on(config, charts, subset); // optimizer->print_scores(); // ----- IF OPTIMIZATION SUCCEEDED, EXECUTE ON OUT OF SAMPLE PERIOD ---------------- if(winner->get_score() < config->minimum_optimization_score()) { bail("Failed to find a variables above the cutoff"); } optimization_scores.push_back(winner->get_score()); std::cout << "==================== EXECUTING ON ====================" << std::endl; std::cout << "Winning variables!" << std::endl; std::cout << "Score: " << winner->get_score() << std::endl; for(auto var: winner->get_variables()) { std::cout << "\t" << var->get_name() << " -> " << var->show() << std::endl; } auto new_vars = winner->get_variables(); auto new_config = config; auto new_charts = charts; auto oos = ts->subset(out_of_sample_record_start, out_of_sample_record_end); Simulation sim(new_config, new_charts, new_vars); sim.run(oos); std::cout << "Execution score: " << sim.get_score() << std::endl; std::cout << "% profitable: " << sim.percentage_of_profitable_trades() << "%" << std::endl; std::cout << "Average trade duration: " << sim.average_trade_duration() << std::endl; std::cout << "Worst DD: " << sim.get_worst_drawdown() << "%" << std::endl; std::cout << "Equity high: " << sim.get_equity_high() << std::endl; std::cout << "Equity low: " << sim.get_equity_low() << std::endl; std::cout << "Trades: " << sim.get_trade_count() << std::endl; std::cout << "Winners: " << sim.get_winning_trade_count() << std::endl; std::cout << "Losers: " << sim.get_losing_trade_count() << std::endl; std::cout << "Best: " << sim.best_winning_trade()->profit() << std::endl; std::cout << "Worst: " << sim.worst_losing_trade()->profit() << std::endl; // ----- IF EXECUTION SUCCEEDED, RECORD RESULTS ------------------------------------ if(sim.get_score() < config->minimum_execution_score()) { bail("Failed at optimization step"); } execution_scores.push_back(sim.get_score()); // ----- EXIT IF WE'RE AT MAX STEPS ------------------------------------------------ step_number++; if(step_number > config->steps()) { std::cout << "Reached max steps, breaking" << std::endl; break; } // ----- ADVANCE CHARTS TO NEXT IN SAMPLE START ------------------------------------ ts->seek_to_record(in_sample_record_start); ts->advance_charts_to_next_sunday(charts); } std::cout << "==================== RESULTS ====================" << std::endl; // ----- SHOW PROFIT FOR EACH PERIOD ------------------------------------------------------- std::cout << "Scores:" << std::endl; for(unsigned long i = 0; i < optimization_scores.size(); i++) { std::cout << i + 1 << " - "; std::cout << "Opti: " << optimization_scores[i]; std::cout << " - "; std::cout << "Exec: " << execution_scores[i]; std::cout << std::endl; } // ----- SHOW WALK FORWARD EFFICIENCY ------------------------------------------------------ float optimization_total = sum_vector(optimization_scores); float execution_total = sum_vector(execution_scores); float efficiency = execution_total / optimization_total; std::cout << "Optimization total: " << optimization_total << " - "; std::cout << "Execution total: " << execution_total << " - "; std::cout << "WFA efficiency: " << efficiency << "% - Verdict: "; if(efficiency < 0.5) { std::cout << "FAIL"; } else if(efficiency >= 0.5 && efficiency < 1.0) { std::cout << "ACCEPTABLE"; } else if(efficiency >= 1.0 && efficiency <= 1.5) { std::cout << "GREAT"; } else { std::cout << "TOO GOOD - SUSPICIOUS"; } std::cout << std::endl; // ----- SHOW PROFIT FACTOR ---------------------------------------------------------------- float gross_profit = 0.0; float gross_loss = 0.0; for(auto score: execution_scores) { if(score >= 0.0) gross_profit += score; else gross_loss += score; } gross_loss = std::abs(gross_loss); float profit_factor = gross_profit / gross_loss; std::cout << "Gross profit: " << gross_profit << " - "; std::cout << "Gross loss: " << gross_loss << " - "; std::cout << "Profit factor: " << profit_factor << " - Verdict: "; if(profit_factor < 1.5) { std::cout << "FAIL"; } else if(profit_factor >= 1.5 && profit_factor < 2.0) { std::cout << "ACCEPTABLE"; } else if(profit_factor >= 2.0 && profit_factor <= 3.0) { std::cout << "GREAT"; } else { std::cout << "TOO GOOD - SUSPICIOUS"; } std::cout << std::endl; // ----- SHOW FINAL SCORE ------------------------------------------------------------------ // TODO: final score should be some function of two or more of the following: // execution_total, WFA efficiency, max drawdown, profit factor, % profitable std::cout << "Final score: " << execution_total << " - Minimum score: " << config->minimum_overall_score() << " - Verdict: "; std::cout << (execution_total >= config->minimum_overall_score() ? "PASS" : "FAIL") << std::endl; std::cout << "==================== SHUTDOWN ====================" << std::endl; return 0; }