forked from ntsinh90/EA_SMC_Mql5
286 lines
22 KiB
MQL5
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
|
|
//+------------------------------------------------------------------+
|