// ================================================== // MT4 EQUITY CSV LOGGER - STANDALONE CODE COPY // ================================================== // Source note: logger code in Setka_1.54.22.mq4 and Setka_1.54.22_RSI_CCI.mq4 is identical. // Source files were not modified. // 1. GLOBAL VARIABLES #define EQUITY_LOGGER_VERSION "Setka_v1.54.22" #define EQUITY_LOG_LOAD_STEP 0.005 #define EQUITY_LOG_WORST_EQUITY_STEP_MONEY 1.0 #define EQUITY_LOG_USE_PENDING_WORST_EQUITY true #define EQUITY_LOG_SUPPRESS_LOAD_ONLY_SAME_MINUTE true int equity_log_file_handle = INVALID_HANDLE; bool equity_log_has_last_values = false; double equity_log_last_balance = 0.0; double equity_log_last_equity = 0.0; double equity_log_last_deposit_load = 0.0; double equity_log_last_recorded_max_floating_dd = 0.0; bool equity_log_has_pending_worst = false; datetime equity_log_pending_worst_minute = 0; double equity_log_pending_worst_balance = 0.0; double equity_log_pending_worst_equity = 0.0; double equity_log_pending_worst_dd = 0.0; double equity_log_pending_worst_deposit_load = 0.0; datetime equity_log_last_written_minute = 0; datetime g_equityLogFirstTestTime = 0; datetime g_equityLogLastTestTime = 0; string g_equityLogFileName = ""; // 2. LOGGER FUNCTIONS string equity_log_timeframe_to_string() { switch ( Period() ) { case PERIOD_M1: return "M1"; case PERIOD_M5: return "M5"; case PERIOD_M15: return "M15"; case PERIOD_M30: return "M30"; case PERIOD_H1: return "H1"; case PERIOD_H4: return "H4"; case PERIOD_D1: return "D1"; case PERIOD_W1: return "W1"; case PERIOD_MN1: return "MN1"; } return IntegerToString ( Period() ); } string equity_log_sanitize_file_part ( string value ) { StringReplace ( value, "\\", "_" ); StringReplace ( value, "/", "_" ); StringReplace ( value, ":", "-" ); StringReplace ( value, "*", "_" ); StringReplace ( value, "?", "_" ); StringReplace ( value, "\"", "_" ); StringReplace ( value, "<", "_" ); StringReplace ( value, ">", "_" ); StringReplace ( value, "|", "_" ); StringReplace ( value, " ", "_" ); return value; } string equity_log_numbered_file_name ( string base_name ) { for ( int i = 1; i < 10000; i++ ) { string candidate = base_name + " (" + IntegerToString ( i ) + ").csv"; if ( !FileIsExist ( candidate, 0 ) ) { return candidate; } } return base_name + " (9999).csv"; } string equity_log_build_file_name() { string symbol = equity_log_sanitize_file_part ( Symbol() ); string base_name = EQUITY_LOGGER_VERSION + "_" + symbol + "_" + equity_log_timeframe_to_string(); return equity_log_numbered_file_name ( base_name ); } string equity_log_build_final_file_name() { if ( g_equityLogFirstTestTime == 0 || g_equityLogLastTestTime == 0 ) { return g_equityLogFileName; } string symbol = equity_log_sanitize_file_part ( Symbol() ); string test_period = TimeToString ( g_equityLogFirstTestTime, TIME_DATE ) + "-" + TimeToString ( g_equityLogLastTestTime, TIME_DATE ); test_period = equity_log_sanitize_file_part ( test_period ); string base_name = EQUITY_LOGGER_VERSION + "_" + symbol + "_" + equity_log_timeframe_to_string() + "_" + test_period; return equity_log_numbered_file_name ( base_name ); } datetime equity_log_current_minute() { return ( datetime ) ( ( int ) TimeCurrent() - ( ( int ) TimeCurrent() % 60 ) ); } bool equity_log_values_changed ( double balance, double deposit_load ) { if ( !equity_log_has_last_values ) { return true; } double load_rounded = NormalizeDouble ( deposit_load, 4 ); double last_load_rounded = NormalizeDouble ( equity_log_last_deposit_load, 4 ); bool balance_changed = MathAbs ( balance - equity_log_last_balance ) >= 0.005; bool deposit_load_opened = ( last_load_rounded == 0.0 && load_rounded > 0.0 ); bool deposit_load_closed = ( last_load_rounded > 0.0 && load_rounded == 0.0 ); bool deposit_load_changed = MathAbs ( load_rounded - last_load_rounded ) >= EQUITY_LOG_LOAD_STEP; return balance_changed || deposit_load_opened || deposit_load_closed || deposit_load_changed; } bool equity_log_important_same_minute_event ( double balance, double deposit_load ) { if ( !equity_log_has_last_values ) { return true; } double load_rounded = NormalizeDouble ( deposit_load, 4 ); double last_load_rounded = NormalizeDouble ( equity_log_last_deposit_load, 4 ); bool balance_changed = MathAbs ( balance - equity_log_last_balance ) >= 0.005; bool deposit_load_opened = ( last_load_rounded == 0.0 && load_rounded > 0.0 ); bool deposit_load_closed = ( last_load_rounded > 0.0 && load_rounded == 0.0 ); return balance_changed || deposit_load_opened || deposit_load_closed; } void equity_log_write_snapshot ( datetime snapshot_minute, double balance, double equity, double deposit_load ) { string row = TimeToString ( snapshot_minute, TIME_DATE | TIME_MINUTES ) + "\t" + DoubleToString ( balance, 2 ) + "\t" + DoubleToString ( equity, 2 ) + "\t" + DoubleToString ( deposit_load, 4 ) + "\r\n"; FileWriteString ( equity_log_file_handle, row ); if ( g_equityLogFirstTestTime == 0 ) { g_equityLogFirstTestTime = snapshot_minute; } g_equityLogLastTestTime = snapshot_minute; equity_log_last_balance = balance; equity_log_last_equity = equity; equity_log_last_deposit_load = deposit_load; equity_log_last_written_minute = snapshot_minute; equity_log_has_last_values = true; } void equity_log_capture_current_state() { if ( equity_log_file_handle == INVALID_HANDLE || !EQUITY_LOG_USE_PENDING_WORST_EQUITY ) { return; } double balance = AccountBalance(); double equity = AccountEquity(); double deposit_load = 0.0; if ( equity != 0.0 ) { deposit_load = AccountMargin() / equity * 100.0; } double floating_dd = NormalizeDouble ( balance - equity, 2 ); if ( deposit_load <= 0.0 ) { return; } if ( floating_dd <= equity_log_last_recorded_max_floating_dd + EQUITY_LOG_WORST_EQUITY_STEP_MONEY ) { return; } if ( !equity_log_has_pending_worst || floating_dd > equity_log_pending_worst_dd ) { equity_log_has_pending_worst = true; equity_log_pending_worst_minute = equity_log_current_minute(); equity_log_pending_worst_balance = balance; equity_log_pending_worst_equity = equity; equity_log_pending_worst_deposit_load = deposit_load; equity_log_pending_worst_dd = floating_dd; } } void equity_log_write_current_state ( bool force_write ) { if ( force_write ) { Print ( "EQUITY_LOG_FORCE_WRITE called" ); } if ( equity_log_file_handle == INVALID_HANDLE ) { return; } double balance = AccountBalance(); double equity = AccountEquity(); double deposit_load = 0.0; datetime current_minute = equity_log_current_minute(); if ( equity != 0.0 ) { deposit_load = AccountMargin() / equity * 100.0; } double floating_dd = NormalizeDouble ( balance - equity, 2 ); if ( EQUITY_LOG_USE_PENDING_WORST_EQUITY && deposit_load > 0.0 ) { if ( !equity_log_has_pending_worst || floating_dd > equity_log_pending_worst_dd ) { equity_log_has_pending_worst = true; equity_log_pending_worst_minute = current_minute; equity_log_pending_worst_balance = balance; equity_log_pending_worst_equity = equity; equity_log_pending_worst_deposit_load = deposit_load; equity_log_pending_worst_dd = floating_dd; } } bool values_changed = equity_log_values_changed ( balance, deposit_load ); bool important_same_minute_event = equity_log_important_same_minute_event ( balance, deposit_load ); bool pending_worst_changed = EQUITY_LOG_USE_PENDING_WORST_EQUITY && equity_log_has_pending_worst && ( equity_log_pending_worst_dd > equity_log_last_recorded_max_floating_dd + EQUITY_LOG_WORST_EQUITY_STEP_MONEY ); if ( !force_write && !values_changed && !pending_worst_changed ) { return; } if ( pending_worst_changed ) { equity_log_write_snapshot ( equity_log_pending_worst_minute, equity_log_pending_worst_balance, equity_log_pending_worst_equity, equity_log_pending_worst_deposit_load ); equity_log_last_recorded_max_floating_dd = equity_log_pending_worst_dd; equity_log_has_pending_worst = false; } if ( force_write || values_changed ) { if ( force_write || !EQUITY_LOG_SUPPRESS_LOAD_ONLY_SAME_MINUTE || current_minute != equity_log_last_written_minute || important_same_minute_event ) { equity_log_write_snapshot ( current_minute, balance, equity, deposit_load ); if ( floating_dd > equity_log_last_recorded_max_floating_dd ) { equity_log_last_recorded_max_floating_dd = floating_dd; } } } } void equity_log_init() { Print ( "EQUITY_LOG_INIT called" ); if ( !IsTesting() || IsOptimization() ) { return; } g_equityLogFileName = equity_log_build_file_name(); Print ( "EQUITY_LOG_FILE_NAME=", g_equityLogFileName ); equity_log_file_handle = FileOpen ( g_equityLogFileName, FILE_WRITE | FILE_TXT | FILE_UNICODE ); if ( equity_log_file_handle == INVALID_HANDLE ) { Print ( "EQUITY_LOG_FILE_OPEN_FAILED error=", GetLastError() ); return; } Print ( "EQUITY_LOG_FILE_OPEN_OK handle=", equity_log_file_handle ); FileWriteString ( equity_log_file_handle, "\t\t\t\r\n" ); Print ( "EQUITY_LOG_HEADER_WRITTEN" ); equity_log_write_current_state ( true ); } void equity_log_deinit() { Print ( "EQUITY_LOG_DEINIT called" ); if ( equity_log_file_handle == INVALID_HANDLE ) { return; } equity_log_write_current_state ( true ); FileClose ( equity_log_file_handle ); equity_log_file_handle = INVALID_HANDLE; string final_file_name = equity_log_build_final_file_name(); if ( final_file_name != g_equityLogFileName ) { if ( !FileMove ( g_equityLogFileName, 0, final_file_name, 0 ) ) { Print ( "EquityLog: cannot rename file, error=", GetLastError(), ", file=", g_equityLogFileName ); } } } // ===== END Setka 1.54.11 MT4 tester equity curve logger ===== // 3. INSERT INTO OnInit() // Add near the beginning of every OnInit() variant, after _init_failed reset: equity_log_init(); // 4. INSERT INTO OnTick() // Add at the beginning of OnTick(), before trading handlers: equity_log_capture_current_state(); // Add at the end of OnTick(), after trading handlers: equity_log_write_current_state ( false ); // 5. INSERT INTO OnDeinit() // Add near the end of OnDeinit(), after EA cleanup and before exit: equity_log_write_current_state ( true ); equity_log_deinit();