Files
Kunthawat Greethong b26c8199a5 Update skills: add website-creator, mql-developer, ecommerce-astro
Changes:
- Add FAL_KEY and GEMINI_API_KEY to .env.example
- Update picture-it to use ~/.config/opencode/.env (unified creds)
- Remove shodh-memory skill (no longer used)
- Remove alphaear-* skills (deprecated)
- Remove thai-frontend-dev skill (replaced by website-creator)
- Remove theme-factory skill
- Add mql-developer skill (MQL5 trading)
- Add ecommerce-astro skill (Astro e-commerce)
- Add website-creator skill (Next.js + Payload CMS)
- Update install script for new skills
2026-04-16 17:40:27 +07:00

4.9 KiB

name, description
name description
mql5-dashboard-survive-tf-change ทำให้ Dashboard edit fields และ runtime values อยู่รอดเมื่อ user เปลี่ยน timeframe (OnDeinit + OnInit re-run) โดยใช้ JSON file persistence. รวมถึงวิธีแก้ข้อจำกัดของ MQL5 ที่ห้ามมี input declarations ใน .mqh files.

MQL5 Dashboard: ทำให้ค่าบน Chart Objects อยู่รอดเมื่อเปลี่ยน Timeframe

ปัญหา

เมื่อ user เปลี่ยน timeframe บน MetaTrader, terminal จะ:

  1. เรียก OnDeinit(reason)
  2. เรียก OnInit() ใหม่

ถ้า OnInit() อ่านค่าจาก input parameters หรือ global variables ที่ไม่ได้ persist, ค่าที่ user พิมพ์ผ่าน dashboard edit fields จะ หายหมดเมื่อเปลี่ยน TF

วิธีแก้: Persistent Storage ด้วย JSON File

1. บันทึกก่อน OnDeinit

string MakeRuntimeFilePath() {
    string path = TerminalInfoString(TERMINAL_DATA_PATH);
    string safeSymbol = StringReplace(g_symbol, "/", "_");
    return path + "\\MQL5\\Files\\SmartEntry_" + safeSymbol + "_runtime.json";
}

bool SaveRuntimeValues() {
    string path = MakeRuntimeFilePath();
    int handle = FileOpen(path, FILE_WRITE | FILE_ANSI | FILE_COMMON);
    if(handle == INVALID_HANDLE) return false;

    string json = StringFormat(
        "{\"upperZone\":%.5f,\"lowerZone\":%.5f,\"sl\":%.5f,\"tp\":%.5f,\"beOffset\":%d,\"isBuySetup\":%s}",
        g_upperZone, g_lowerZone, g_SL, g_TP, (int)g_beOffset, g_isBuySetup ? "true" : "false");

    FileWriteString(handle, json + "\n");
    FileClose(handle);
    return true;
}

void OnDeinit(const int reason) {
    SaveRuntimeValues();  // ← บันทึกก่อน cleanup ทุกครั้ง
    // ... release handles, destroy panel
}

2. โหลดกลับตอน OnInit

bool LoadRuntimeValues() {
    string path = MakeRuntimeFilePath();
    int handle = FileOpen(path, FILE_READ | FILE_ANSI | FILE_COMMON);
    if(handle == INVALID_HANDLE) return false;  // ไม่มีไฟล์ = first run

    string json = FileReadString(handle);
    FileClose(handle);

    g_upperZone = StringToDouble(ParseJsonField(json, "upperZone"));
    g_lowerZone = StringToDouble(ParseJsonField(json, "lowerZone"));
    // ...
    return true;
}

int OnInit() {
    // Load ก่อน ถ้าไม่มีไฟล์ค่อย fallback ไป input defaults
    if(!LoadRuntimeValues()) {
        g_upperZone = InputUpperZone;
        // ...
    }
    // ...
}

3. Parse JSON field (MQL5 ไม่มี native JSON)

string ParseJsonField(string json, string fieldName) {
    string search = "\"" + fieldName + "\":";
    int pos = StringFind(json, search);
    if(pos < 0) return "0";

    int start = pos + StringLen(search);
    while(start < StringLen(json) && json[start] == ' ') start++;

    if(json[start] == '"') {
        int end = start + 1;
        while(end < StringLen(json) && json[end] != '"') end++;
        return StringSubstr(json, start + 1, end - start - 1);
    } else {
        int end = start;
        while(end < StringLen(json) && json[end] != ',' && json[end] != '}' && json[end] != '\n') end++;
        return StringSubstr(json, start, end - start);
    }
}

ข้อควรระวัง: input ใน .mqh File

MQL5 ไม่อนุญาต input declarations ใน .mqh include files — compile error

ผิด (compile error):

// Dashboard.mqh
input double InputUpperZone = 0;  // ← ERROR: input not allowed in .mqh

ถูก:

// Dashboard.mqh — ไม่มี input declarations ที่นี่

// สร้าง method ให้ EA เรียกหลัง Init
void CDashboard::SetInitialEditTexts(double upperZone, double lowerZone,
                                     double sl, double tp, int beOffset) {
    if(upperZone > 0)
        ObjectSetString(0, m_prefix + "EREdit", OBJPROP_TEXT, DoubleToString(upperZone, _Digits));
    // ...
}

// SmartEntryEA.mq5
g_dashboard.Init(g_symbol, true);
g_dashboard.SetInitialEditTexts(g_upperZone, g_lowerZone, g_SL, g_TP, (int)g_beOffset);

ข้อควรระวัง: Chart Objects หายเมื่อ RecreatePanel

CreatePanel() สร้าง objects ใหม่ทุกครั้งที่ ObjectFind() < 0 check อยู่แล้ว — safe สำหรับ TF change เพราะ OnDeinit จะ DestroyPanel() ก่อน

Symbols

  • g_symbol — ต้องกำหนดก่อนเรียก MakeRuntimeFilePath()
  • File path ใช้ FILE_COMMON flag เพื่อเขียนได้ทุก profile