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
4.9 KiB
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 จะ:
- เรียก
OnDeinit(reason) - เรียก
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_COMMONflag เพื่อเขียนได้ทุก profile