EA_SMC_Mql5/Include/CChartDrawer.mqh
syncoolz 34715b0f92
2025-07-03 23:38:38 +07:00

286 lines
22 KiB
MQL5

//+------------------------------------------------------------------+
//| CChartDrawer.mqh |
//| v5.0 - Dynamic Zone Drawing Logic |
//+------------------------------------------------------------------+
#ifndef CCHARTDRAWER_MQH
#define CCHARTDRAWER_MQH
#include <Arrays/ArrayString.mqh>
#include "CEnumSharing.mqh"
#include "SMC_DataStructures.mqh"
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CChartDrawer
{
private:
long m_chart_id;
string m_prefix;
public:
CChartDrawer(long chart_id, long magic_number)
{
m_chart_id = chart_id;
m_prefix = StringFormat("EASMC_%d_", magic_number);
}
~CChartDrawer() {}
// --- ฟังก์ชันนี้ไม่ได้ใช้แล้ว แต่เก็บไว้เผื่อกรณีฉุกเฉิน ---
void ClearAllDrawings()
{
ObjectsDeleteAll(m_chart_id, 0, -1, m_prefix);
}
void SynchronizeDrawings(CArrayObj* all_zones, CArrayObj* all_swings)
{
CArrayString* valid_object_names = new CArrayString();
if(all_zones != NULL)
{
for(int i=0; i<all_zones.Total(); i++)
{
CSignalZone* zone = all_zones.At(i);
if(zone != NULL)
{
valid_object_names.Add(zone.object_name);
valid_object_names.Add(zone.object_name + "_Text");
}
}
}
if(all_swings != NULL)
{
for(int i=0; i<all_swings.Total(); i++)
{
SwingPoint* swing = all_swings.At(i);
if(swing != NULL && swing.status != STATUS_NOISE && swing.status != STATUS_UNCONFIRMED)
{
valid_object_names.Add(m_prefix + "Swing_" + (string)swing.time);
if(swing.status == STATUS_INDUCEMENT && swing.linked_major_index >=0)
{
valid_object_names.Add(m_prefix + "Link_" + (string)swing.time);
}
if(swing.status == STATUS_MAJOR && swing.bos_confirmation_time > 0)
{
valid_object_names.Add(m_prefix + "BOS_" + (string)swing.time);
}
}
}
}
for(int i = ObjectsTotal(m_chart_id) - 1; i >= 0; i--)
{
string name = ObjectName(m_chart_id, i);
if(StringFind(name, m_prefix) == 0)
{
if(valid_object_names.Search(name) < 0)
{
ObjectDelete(m_chart_id, name);
}
}
}
delete valid_object_names;
}
void DrawZone(CSignalZone* zone, CDataContext &context)
{
if(zone == NULL)
return;
string obj_name = zone.object_name;
// 1. กำหนดสีและสไตล์ตามสถานะของโซน
color box_color = clrWhite;
ENUM_LINE_STYLE box_style = (zone.zone_type == ZONE_FVG) ? STYLE_DOT : STYLE_SOLID;
bool fill_box = (zone.zone_type != ZONE_FVG);
switch(zone.state)
{
case STATE_FRESH:
box_color = (zone.direction == DIRECTION_BULLISH ? clrDarkGreen : clrMaroon);
if(zone.zone_type==ZONE_FVG)
box_color=clrGold;
break;
if(zone.zone_type == ZONE_FLIP)
box_color = clrAqua;
else
if(zone.zone_type == ZONE_BREAKOUT_RETEST)
box_color = clrDodgerBlue;
case STATE_ACTIVE:
box_color = (zone.direction == DIRECTION_BULLISH ? clrGreen : clrTomato);
break;
case STATE_MITIGATED:
box_color = (zone.direction == DIRECTION_BULLISH ? clrForestGreen : clrIndianRed);
break;
//case STATE_CONFIRMED: box_color = (zone.direction == DIRECTION_BULLISH ? clrLime : clrRed); break;
case STATE_INVALIDATED:
box_color = clrDimGray;
fill_box = false;
break;
}
// 2. หาจุดสิ้นสุดของการวาดโซน
datetime end_time;
int start_idx = iBarShift(_Symbol, PERIOD_CURRENT, zone.time);
if(start_idx < 0 || start_idx >= context.bars_to_load)
{
// ObjectDelete(m_chart_id, zone.object_name); // อาจจะลบ object เก่าทิ้งถ้ามี
return;
}
// หาแท่งเทียนที่ "ทำลาย" หรือ "บัง" โซน
int end_idx = -1;
for(int i = start_idx - 1; i >= 0; i--)
{
// สำหรับโซน Demand, หาแท่งที่ Low ต่ำกว่าขอบล่างของโซน
if(zone.direction == DIRECTION_BULLISH && context.rates[i].low < zone.bottom)
{
end_idx = i;
break;
}
// สำหรับโซน Supply, หาแท่งที่ High สูงกว่าขอบบนของโซน
if(zone.direction == DIRECTION_BEARISH && context.rates[i].high > zone.top)
{
end_idx = i;
break;
}
}
// 3. กำหนดเวลาสิ้นสุด
if(end_idx != -1)
{
// ถ้าเจอแท่งที่ทำลายโซน ให้จบการวาดที่แท่งนั้น
end_time = context.rates[end_idx].time;
}
else
{
// ถ้าไม่เจอ ให้ลากยาวไปจนถึงอนาคตเล็กน้อย (ขอบขวาสุดของจอ)
end_time = context.rates[0].time + PeriodSeconds() * 10;
}
// ถ้ายังไม่มี Object นี้ ให้สร้างใหม่
if(ObjectFind(m_chart_id, obj_name) < 0)
{
ObjectCreate(m_chart_id, obj_name, OBJ_RECTANGLE, 0, zone.time, zone.top, end_time, zone.bottom);
}
// ถ้ามีอยู่แล้ว ให้อัปเดตเฉพาะสิ่งที่อาจเปลี่ยนแปลง
else
{
ObjectSetInteger(m_chart_id, obj_name, OBJPROP_TIME, 1, end_time);
}
// อัปเดตคุณสมบัติอื่นๆ ทุกครั้ง เผื่อมีการเปลี่ยนแปลง
ObjectSetInteger(m_chart_id, obj_name, OBJPROP_COLOR, box_color);
ObjectSetInteger(m_chart_id, obj_name, OBJPROP_STYLE, box_style);
ObjectSetInteger(m_chart_id, obj_name, OBJPROP_FILL, fill_box);
ObjectSetInteger(m_chart_id, obj_name, OBJPROP_BACK, true);
// วาด/อัปเดต Text Label
string text_name = obj_name + "_Text";
string text = EnumToString(zone.zone_type) + " " + EnumToString(zone.state);
if(zone.mitigation_count > 0)
text += " T:" + (string)zone.mitigation_count;
if(ObjectFind(m_chart_id, text_name) < 0)
{
ObjectCreate(m_chart_id, text_name, OBJ_TEXT, 0, zone.time, zone.top);
}
ObjectSetString(m_chart_id, text_name, OBJPROP_TEXT, text);
ObjectSetInteger(m_chart_id, text_name, OBJPROP_COLOR, box_color);
ObjectSetInteger(m_chart_id, text_name, OBJPROP_FONTSIZE, 8);
ObjectSetInteger(m_chart_id, text_name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
}
void DrawSwing(SwingPoint* swing, CArrayObj* all_swings, CDataContext &context)
{
if(swing == NULL || swing.status == STATUS_NOISE || swing.status == STATUS_UNCONFIRMED)
return;
string obj_name = m_prefix + "Swing_" + (string)swing.time;
color symbol_color = clrWhite;
int symbol_code = 0;
int symbol_size = 1;
switch(swing.status)
{
case STATUS_MAJOR:
symbol_color = (swing.type == SWING_HIGH) ? clrRed : clrLimeGreen;
symbol_size = 3;
// FIXED: Use character codes for Wingdings font
symbol_code = (swing.type == SWING_HIGH) ? 159 : 159;
break;
case STATUS_INDUCEMENT:
symbol_color = clrGold;
symbol_size = 2;
symbol_code = 159; // '$' sign
break;
case STATUS_MINOR:
symbol_color = clrDimGray;
symbol_size = 1;
symbol_code = 159; // Solid Circle
break;
default:
return;
}
/*double atr_val = (swing.bar_index < context.bars_to_load) ? context.atr_buffer[swing.bar_index] : 0;
double price_offset = atr_val * 0.2;*/
double draw_price = (swing.type == SWING_HIGH) ? swing.price + 12 : swing.price ;
// วาด/อัปเดต สัญลักษณ์หลัก
if(ObjectFind(m_chart_id, obj_name) < 0)
{
ObjectCreate(m_chart_id, obj_name, OBJ_ARROW, 0, swing.time, draw_price);
}
else
{
ObjectSetDouble(m_chart_id, obj_name, OBJPROP_PRICE, 0, draw_price);
}
ObjectSetInteger(m_chart_id, obj_name, OBJPROP_ARROWCODE, symbol_code);
ObjectSetInteger(m_chart_id, obj_name, OBJPROP_COLOR, symbol_color);
ObjectSetInteger(m_chart_id, obj_name, OBJPROP_WIDTH, symbol_size);
ObjectSetInteger(m_chart_id, obj_name, OBJPROP_BACK, true);
// วาด/อัปเดต เส้นเชื่อม Inducement . Major
/*string link_name = m_prefix + "Link_" + (string)swing.time;
if(swing.status == STATUS_INDUCEMENT && swing.linked_major_index >= 0 && swing.linked_major_index < all_swings.Total()) {
SwingPoint *major_swing = all_swings.At(swing.linked_major_index);
if(major_swing != NULL) {
if(ObjectFind(m_chart_id, link_name) < 0) {
ObjectCreate(m_chart_id, link_name, OBJ_TREND, 0, swing.time, swing.price, major_swing.time, major_swing.price);
}
ObjectSetInteger(m_chart_id, link_name, OBJPROP_COLOR, clrGoldenrod);
ObjectSetInteger(m_chart_id, link_name, OBJPROP_STYLE, STYLE_DOT);
}
}*/
// วาด/อัปเดต ป้าย "BOS"
if(swing.status == STATUS_MAJOR && swing.bos_confirmation_time > 0)
{
string bos_name = m_prefix + "BOS_" + (string)swing.time;
datetime bos_time = swing.bos_confirmation_time;
int bos_idx = iBarShift(_Symbol, PERIOD_CURRENT, bos_time);
if(bos_idx >= 0 && bos_idx < context.bars_to_load)
{
double bos_atr_val = context.atr_buffer[bos_idx];
double bos_price_offset = bos_atr_val * 0.5;
double bos_price = (swing.type == SWING_LOW) ? context.rates[bos_idx].high + bos_price_offset : context.rates[bos_idx].low - bos_price_offset;
ObjectCreate(m_chart_id, bos_name, OBJ_TEXT, 0, bos_time, bos_price);
ObjectSetString(m_chart_id, bos_name, OBJPROP_TEXT, "BOS");
ObjectSetInteger(m_chart_id, bos_name, OBJPROP_COLOR, clrAqua);
ObjectSetInteger(m_chart_id, bos_name, OBJPROP_FONTSIZE, 8);
ObjectSetInteger(m_chart_id, bos_name, OBJPROP_BACK, true);
if(swing.type == SWING_LOW)
ObjectSetInteger(m_chart_id, bos_name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
else
ObjectSetInteger(m_chart_id, bos_name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
}
}
}
};
#endif
//+------------------------------------------------------------------+