MQL5交易策略自动化(第十六部分):基于结构突破(BoS)价格行为的午夜区间突破策略·进阶篇
(2/3)· 从谐波形态转战午夜低波动区,用结构突破过滤假突破的完整代码路径
零点到六点的区间扫描怎么写
做日内突破策略,第一步往往是把凌晨 0 点到早 6 点这段的波动框出来。下面这段逻辑在每根新日线出现时重置状态:sixAM 按 midnight 加 6*3600 秒算,scanBarTime 取 6 点后第一根当前周期 bar,midnight_price 抓 D1 第 0 根收盘价,validBreakTime_end 固定到 11 点(midnight + 11*3600),最大最小值分别重置成 -DBL_MAX / DBL_MAX,几个交易标志位全清 false。 新 bar 触发后,若当前 bar 时间正好等于 scanBarTime 且当日区间还没算过,就进扫描:total_bars 用 (sixAM - midnight)/PeriodSeconds(_Period)+1 算出 0 点到 6 点共多少根。实测在 M5 周期下,这个数值稳定是 72 根(360 分钟 ÷ 5)。 随后从 i=1 循环到 total_bars,对每根只取 open 与 close 的较大者当 highest_price_i、较小者当 lowest_price_i,再和全局 maximum_price 比,刷新最高价及其 bar 索引。注意它没用高低点而是用开收价,所以算出来的是「实体区间」,比含影线区间更紧,假突破概率可能更低。 开 MT5 把这段塞进 EA 的 OnTick 前面做状态机,先把 Print 日志打开,跑一周 EURUSD 看 total_bars 和你 broker 的 session 偏移是否对得上,再决定要不要接后续突破判定。外汇和贵金属杠杆高,实体区间只是过滤手段,不代表信号胜率。
sixAM = midnight + class="num">6 * class="num">3600; class=class="str">"cmt">//--- Recalculate class="num">6 AM time for the new day scanBarTime = sixAM + class="num">1 * PeriodSeconds(_Period); class=class="str">"cmt">//--- Update the scan bar time to the next bar after class="num">6 AM midnight_price = iClose(_Symbol,PERIOD_D1,class="num">0); class=class="str">"cmt">//--- Update the midnight closing price Print("Midnight price = ",midnight_price,", Time = ",midnight); class=class="str">"cmt">//--- Log the midnight price and time validBreakTime_start = scanBarTime; class=class="str">"cmt">//--- Reset the start time for valid breakouts validBreakTime_end = midnight + (class="num">6+class="num">5) * class="num">3600; class=class="str">"cmt">//--- Reset the end time for valid breakouts to class="num">11 AM maximum_price = -DBL_MAX; class=class="str">"cmt">//--- Reset the maximum price to negative infinity minimum_price = DBL_MAX; class=class="str">"cmt">//--- Reset the minimum price to positive infinity isHaveDailyRange_Prices = class="kw">false; class=class="str">"cmt">//--- Reset the flag indicating daily range calculation isHaveRangeBreak = class="kw">false; class=class="str">"cmt">//--- Reset the flag indicating a range breakout isTakenTrade = class="kw">false; class=class="str">"cmt">//--- Reset the flag indicating a trade is taken } } if (isNewBar()){ class=class="str">"cmt">//--- Check if a new bar has formed on the current timeframe class="type">class="kw">datetime currentBarTime = iTime(_Symbol,_Period,class="num">0); class=class="str">"cmt">//--- Get the time of the current bar if (currentBarTime == scanBarTime && !isHaveDailyRange_Prices){ class=class="str">"cmt">//--- Check if it&class="macro">#x27;s time to scan for daily range and range is not yet calculated Print("WE HAVE ENOUGH BARS DATA FOR DOCUMENTATION. MAKE THE EXTRACTION"); class=class="str">"cmt">//--- Log that the scan for daily range is starting class="type">int total_bars = class="type">int((sixAM - midnight)/PeriodSeconds(_Period))+class="num">1; class=class="str">"cmt">//--- Calculate the number of bars from midnight to class="num">6 AM Print("Total Bars for scan = ",total_bars); class=class="str">"cmt">//--- Log the total number of bars to scan class="type">int highest_price_bar_index = -class="num">1; class=class="str">"cmt">//--- Initialize the index of the bar with the highest price class="type">int lowest_price_bar_index = -class="num">1; class=class="str">"cmt">//--- Initialize the index of the bar with the lowest price for (class="type">int i=class="num">1; i<=total_bars ; i++){ class=class="str">"cmt">//--- Loop through each bar from midnight to class="num">6 AM class="type">class="kw">double open_i = open(i); class=class="str">"cmt">//--- Get the open price of the i-th bar class="type">class="kw">double close_i = close(i); class=class="str">"cmt">//--- Get the close price of the i-th bar class="type">class="kw">double highest_price_i = (open_i > close_i) ? open_i : close_i; class=class="str">"cmt">//--- Determine the highest price(open or close) of the bar class="type">class="kw">double lowest_price_i = (open_i < close_i) ? open_i : close_i; class=class="str">"cmt">//--- Determine the lowest price(open or close) of the bar if (highest_price_i > maximum_price){ class=class="str">"cmt">//--- Check if the bar&class="macro">#x27;s highest price exceeds the current maximum maximum_price = highest_price_i; class=class="str">"cmt">//--- Update the maximum price highest_price_bar_index = i; class=class="str">"cmt">//--- Store the bar index of the maximum price
◍ 把当日极值锚到具体K线与时间
扫描完一段 K 线后,真正有用的不是「最高价多少」这一个数字,而是它落在第几根、什么时间。下面这段逻辑在循环里持续比对:每当某根最高价刷新记录就存下价格、bar 索引和时间;最低价同理。
循环结束用 Print 把四个字段直接打到日志:最高价配最高价所在 bar 索引与时间,最低价配最低价所在 bar 索引与时间。实盘里你开 MT5 按 F8 打开「专家」标签页,就能看到类似 Maximum Price = 1.09234, Bar index = 142, Time = 2024.05.21 14:00 的输出,据此可反推波动发起的时点。
随后把 isHaveDailyRange_Prices 置 true,等于告诉后续模块「当日区间已算完,可以画矩形或判结构了」。
helper 函数把 iOpen/iHigh/iLow/iClose/iTime 包了一层,当前图表直接用 open(i) 这种短写法;重载版 high(i, tf_bos) 则允许跨周期取 BoS 框架的高低点。少写重复参数,改周期只动一处。
maximum_time = time(i); class=class="str">"cmt">//--- Store the time of the maximum price } if (lowest_price_i < minimum_price){ class=class="str">"cmt">//--- Check if the bar&class="macro">#x27;s lowest price is below the current minimum minimum_price = lowest_price_i; class=class="str">"cmt">//--- Update the minimum price lowest_price_bar_index = i; class=class="str">"cmt">//--- Store the bar index of the minimum price minimum_time = time(i); class=class="str">"cmt">//--- Store the time of the minimum price } } Print("Maximum Price = ",maximum_price,", Bar index = ",highest_price_bar_index,", Time = ",maximum_time); class=class="str">"cmt">//--- Log the maximum price, its bar index, and time Print("Minimum Price = ",minimum_price,", Bar index = ",lowest_price_bar_index,", Time = ",minimum_time); class=class="str">"cmt">//--- Log the minimum price, its bar index, and time isHaveDailyRange_Prices = true; class=class="str">"cmt">//--- Set the flag to indicate that the daily range is calculated } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Helper functions for price and time data | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double open(class="type">int index){class="kw">return (iOpen(_Symbol,_Period,index));} class=class="str">"cmt">//--- Return the open price of the specified bar index on the current timeframe class="type">class="kw">double high(class="type">int index){class="kw">return (iHigh(_Symbol,_Period,index));} class=class="str">"cmt">//--- Return the high price of the specified bar index on the current timeframe class="type">class="kw">double low(class="type">int index){class="kw">return (iLow(_Symbol,_Period,index));} class=class="str">"cmt">//--- Return the low price of the specified bar index on the current timeframe class="type">class="kw">double close(class="type">int index){class="kw">return (iClose(_Symbol,_Period,index));} class=class="str">"cmt">//--- Return the close price of the specified bar index on the current timeframe class="type">class="kw">datetime time(class="type">int index){class="kw">return (iTime(_Symbol,_Period,index));} class=class="str">"cmt">//--- Return the time of the specified bar index on the current timeframe class="type">class="kw">double high(class="type">int index,ENUM_TIMEFRAMES tf_bos){class="kw">return (iHigh(_Symbol,tf_bos,index));} class=class="str">"cmt">//--- Return the high price of the specified bar index on the BoS timeframe class="type">class="kw">double low(class="type">int index,ENUM_TIMEFRAMES tf_bos){class="kw">return (iLow(_Symbol,tf_bos,index));} class=class="str">"cmt">//--- Return the low price of the specified bar index on the BoS timeframe class="type">class="kw">datetime time(class="type">int index,ENUM_TIMEFRAMES tf_bos){class="kw">return (iTime(_Symbol,tf_bos,index));} class=class="str">"cmt">//--- Return the time of the specified bar index on the BoS timeframe class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Function to create a rectangle object | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void create_Rectangle(class="type">class="kw">string objName,class="type">class="kw">datetime time1,class="type">class="kw">double price1,
「用函数封装矩形与趋势线的绘制」
在 MT5 脚本里反复手写 ObjectCreate 很容易出错,把矩形和带文字的趋势线各自封成一个函数,调用时只传坐标和颜色就行。下面两段代码都先判了 ObjectFind(0,objName)<0,意味着同名对象不存在才新建,避免每次刷新图表叠出一堆重复图形。 矩形函数把 OBJPROP_BACK 设成 false,图形会压在 K 线 foreground,适合标突破区;趋势线函数额外收了 width 和 text 参数,能直接挂说明文字。两者末尾都调 ChartRedraw(0) 强制重绘,不然高频 OnTick 里可能看不到即时更新。 实盘加载后,若发现矩形不填充,优先查 OBJPROP_FILL 是否 true;线宽传 0 在部分版本会隐身,建议默认给 1。外汇与贵金属波动剧烈,这类标记仅作视觉辅助,信号失效概率不低,需结合其他确认。
class="type">void create_Rectangle(class="type">class="kw">string objName,class="type">class="kw">datetime time1,class="type">class="kw">double price1,class="type">class="kw">datetime time2,class="type">class="kw">double price2,class="type">color clr){ if(ObjectFind(class="num">0,objName)<class="num">0){ ObjectCreate(class="num">0,objName,OBJ_RECTANGLE,class="num">0,time1,price1,time2,price2); ObjectSetInteger(class="num">0,objName,OBJPROP_TIME,class="num">0,time1); ObjectSetDouble(class="num">0,objName,OBJPROP_PRICE,class="num">0,price1); ObjectSetInteger(class="num">0,objName,OBJPROP_TIME,class="num">1,time2); ObjectSetDouble(class="num">0,objName,OBJPROP_PRICE,class="num">1,price2); ObjectSetInteger(class="num">0,objName,OBJPROP_FILL,true); ObjectSetInteger(class="num">0,objName,OBJPROP_COLOR,clr); ObjectSetInteger(class="num">0,objName,OBJPROP_BACK,class="kw">false); ChartRedraw(class="num">0); } } class="type">void create_Line(class="type">class="kw">string objName,class="type">class="kw">datetime time1,class="type">class="kw">double price1,class="type">class="kw">datetime time2,class="type">class="kw">double price2,class="type">int width,class="type">color clr,class="type">class="kw">string text){ if(ObjectFind(class="num">0,objName)<class="num">0){ ObjectCreate(class="num">0,objName,OBJ_TREND,class="num">0,time1,price1,time2,price2); ObjectSetInteger(class="num">0,objName,OBJPROP_TIME,class="num">0,time1); ObjectSetDouble(class="num">0,objName,OBJPROP_PRICE,class="num">0,price1); ObjectSetInteger(class="num">0,objName,OBJPROP_TIME,class="num">1,time2); ObjectSetDouble(class="num">0,objName,OBJPROP_PRICE,class="num">1,price2); ObjectSetInteger(class="num">0,objName,OBJPROP_WIDTH,width); } }
随图表缩放自适应价格的文字标注
在 MT5 里手动画好区间线之后,如果不给末端加价格文字,盯盘时很容易忘了这条线是哪根高低点。上面这段逻辑就是在横线终点贴一个 OBJ_TEXT,把具体价格值直接写在图右侧。 关键在字体大小不是写死的。代码先用 ChartGetInteger 取 CHART_SCALE,拿到 0~5 的缩放档位;若取不到就 Print 报警并用默认 scale=0。随后按档位映射 fontsize:scale 0 对应 5 号字,1 对应 6,2 对应 7,3 对应 9,4 对应 11,5 对应 13。也就是说图表放到最大时文字自动加到 13,缩到最小时降到 5,避免小图把字挤成一团。 文字对象用 objName+" Right Price" 做唯一名,锚点设 ANCHOR_LEFT、字体 Calibri,内容由外部传进来的 text(一般是 DoubleToString(price,_Digits))决定。最后 ChartRedraw(0) 强制重绘,线和标注才立刻可见。 实际验证时,你可以把 create_Line 里最后那个 text 参数改成任意字符串,比如 "H4 高点",贴上去的就是自定义标签而不是裸价格。外汇和贵金属波动快、杠杆高,这类视觉辅助只帮你看清结构,不构成方向判断。
ObjectSetInteger(class="num">0,objName,OBJPROP_COLOR,clr); class=class="str">"cmt">//--- 设置线条颜色 ObjectSetInteger(class="num">0,objName,OBJPROP_BACK,class="kw">false); class=class="str">"cmt">//--- 确保线条绘制在前台 class="type">long scale = class="num">0; class=class="str">"cmt">//--- 初始化变量存储图表缩放 if(!ChartGetInteger(class="num">0,CHART_SCALE,class="num">0,scale)){ class=class="str">"cmt">//--- 尝试获取图表缩放 Print("UNABLE TO GET THE CHART SCALE. DEFAULT OF ",scale," IS CONSIDERED"); class=class="str">"cmt">//--- 获取失败则打印日志 } class="type">int fontsize = class="num">11; class=class="str">"cmt">//--- 设置文字默认字号 if (scale==class="num">0){fontsize=class="num">5;} class=class="str">"cmt">//--- 最小缩放对应5号字 else if (scale==class="num">1){fontsize=class="num">6;} class=class="str">"cmt">//--- 缩放1对应6号字 else if (scale==class="num">2){fontsize=class="num">7;} class=class="str">"cmt">//--- 缩放2对应7号字 else if (scale==class="num">3){fontsize=class="num">9;} class=class="str">"cmt">//--- 缩放3对应9号字 else if (scale==class="num">4){fontsize=class="num">11;} class=class="str">"cmt">//--- 缩放4对应11号字 else if (scale==class="num">5){fontsize=class="num">13;} class=class="str">"cmt">//--- 最大缩放对应13号字 class="type">class="kw">string txt = " Right Price"; class=class="str">"cmt">//--- 定义价格标签的文字后缀 class="type">class="kw">string objNameDescr = objName + txt; class=class="str">"cmt">//--- 为文字对象生成唯一名 ObjectCreate(class="num">0,objNameDescr,OBJ_TEXT,class="num">0,time2,price2); class=class="str">"cmt">//--- 在线末端创建文字对象 ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_COLOR,clr); class=class="str">"cmt">//--- 设置文字颜色 ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_FONTSIZE,fontsize); class=class="str">"cmt">//--- 设置文字字号 ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT); class=class="str">"cmt">//--- 文字锚点设为左对齐 ObjectSetString(class="num">0,objNameDescr,OBJPROP_TEXT," "+text); class=class="str">"cmt">//--- 写入文字内容(价格值) ObjectSetString(class="num">0,objNameDescr,OBJPROP_FONT,"Calibri"); class=class="str">"cmt">//--- 字体设为Calibri ChartRedraw(class="num">0); class=class="str">"cmt">//--- 重绘图表以显示线条和文字
◍ 在图上钉死突破点
画完当日高低区间线之后,真正有价值的是把价格第一次捅穿边界的位置标记出来。下面这个函数专门干这件事:在突破发生的 K 线时间和价位上挂一个箭头,再补一行「Break」文字,方向不同锚点也不同。 向上破位时箭头锚点设为 ANCHOR_TOP,文字走 ANCHOR_LEFT_UPPER;向下破位则分别落到 ANCHOR_BOTTOM 与 ANCHOR_LEFT_LOWER。字体统一 12 号,颜色由调用方传入,方便你用红绿区分上下破。 函数开头先用 ObjectFind 判断同名对象是否存在,避免重复创建把图表弄脏。外汇与贵金属波动快,这种标记能帮你回看时一眼定位亚盘区间被破的时点,但突破后延续概率仍受时段流动性影响,属高风险品种,别单凭箭头就追。 把这段代码直接塞进你的 EA 或脚本,配合前一篇的日内区间计算,MT5 图上就能自动留痕,省掉手动划线。
class="type">void drawBreakPoint(class="type">class="kw">string objName,class="type">class="kw">datetime time,class="type">class="kw">double price,class="type">int arrCode, class="type">color clr,class="type">int direction){ class=class="str">"cmt">//--- Define a function to draw a breakpoint marker if (ObjectFind(class="num">0,objName) < class="num">0){ class=class="str">"cmt">//--- Check if the breakpoint object does not already exist ObjectCreate(class="num">0,objName,OBJ_ARROW,class="num">0,time,price); class=class="str">"cmt">//--- Create an arrow object at the specified time and price ObjectSetInteger(class="num">0,objName,OBJPROP_ARROWCODE,arrCode); class=class="str">"cmt">//--- Set the arrow code for the marker ObjectSetInteger(class="num">0,objName,OBJPROP_COLOR,clr); class=class="str">"cmt">//--- Set the class="type">color of the arrow ObjectSetInteger(class="num">0,objName,OBJPROP_FONTSIZE,class="num">12); class=class="str">"cmt">//--- Set the font size for the arrow if (direction > class="num">0) ObjectSetInteger(class="num">0,objName,OBJPROP_ANCHOR,ANCHOR_TOP); class=class="str">"cmt">//--- Set the anchor to top for upward breaks if (direction < class="num">0) ObjectSetInteger(class="num">0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM); class=class="str">"cmt">//--- Set the anchor to bottom for downward breaks class="type">class="kw">string txt = " Break"; class=class="str">"cmt">//--- Define the text suffix for the breakpoint label class="type">class="kw">string objNameDescr = objName + txt; class=class="str">"cmt">//--- Create a unique name for the text object ObjectCreate(class="num">0,objNameDescr,OBJ_TEXT,class="num">0,time,price); class=class="str">"cmt">//--- Create a text object at the breakpoint ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_COLOR,clr); class=class="str">"cmt">//--- Set the class="type">color of the text ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_FONTSIZE,class="num">12); class=class="str">"cmt">//--- Set the font size of the text if (direction > class="num">0) { class=class="str">"cmt">//--- Check if the breakout is upward ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_UPPER); class=class="str">"cmt">//--- Set the text anchor to left upper ObjectSetString(class="num">0,objNameDescr,OBJPROP_TEXT, " " + txt); class=class="str">"cmt">//--- Set the text content } if (direction < class="num">0) { class=class="str">"cmt">//--- Check if the breakout is downward ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER); class=class="str">"cmt">//--- Set the text anchor to left lower ObjectSetString(class="num">0,objNameDescr,OBJPROP_TEXT, " " + txt); class=class="str">"cmt">//--- Set the text content }
「突破判定与拐点箭头绘制」
前一根 K 线收盘价是判断区间突破的基准:取 close(1) 与 time(1),只在 validBreakTime_start 到 validBreakTime_end 的时间窗内比对。若 barClose 大于 maximum_price 且尚未标记突破,倾向视为上破;小于 minimum_price 则倾向下破,两者都会把 isHaveRangeBreak 置真并打印日志。 上破调用 drawBreakPoint 时箭头代码传 234、颜色 clrBlack、偏移 -1;下破传 233、clrBlue、偏移 1。这两个 ArrowCode 对应 MT5 Wingdings 字体里的具体符号,在图表上能直接区分高低破位。 drawSwingPoint 负责画 BoS 拐点:ObjectFind 查重,不存在才 ObjectCreate 一个 OBJ_ARROW。锚点按方向分——direction>0 设 ANCHOR_TOP(摆荡低点),direction<0 设 ANCHOR_BOTTOM(摆荡高点),字体固定 10 号。外汇与贵金属波动剧烈,这类标记只作结构参考,实盘仍需结合止损。
} ChartRedraw(class="num">0); class=class="str">"cmt">//--- Redraw the chart to display the breakpoint } class="type">class="kw">double barClose = close(class="num">1); class=class="str">"cmt">//--- Get the closing price of the previous bar class="type">class="kw">datetime barTime = time(class="num">1); class=class="str">"cmt">//--- Get the time of the previous bar if (barClose > maximum_price && isHaveDailyRange_Prices && !isHaveRangeBreak && barTime >= validBreakTime_start && barTime <= validBreakTime_end ){ class=class="str">"cmt">//--- Check for a breakout above the maximum price within the valid time window Print("CLOSE Price broke the HIGH range. ",barClose," > ",maximum_price); class=class="str">"cmt">//--- Log the breakout above the high range isHaveRangeBreak = true; class=class="str">"cmt">//--- Set the flag to indicate a range breakout has occurred drawBreakPoint(TimeToString(barTime),barTime,barClose,class="num">234,clrBlack,-class="num">1); class=class="str">"cmt">//--- Draw a breakpoint marker for the high breakout } else if (barClose < minimum_price && isHaveDailyRange_Prices && !isHaveRangeBreak && barTime >= validBreakTime_start && barTime <= validBreakTime_end ){ class=class="str">"cmt">//--- Check for a breakout below the minimum price within the valid time window Print("CLOSE Price broke the LOW range. ",barClose," < ",minimum_price); class=class="str">"cmt">//--- Log the breakout below the low range isHaveRangeBreak = true; class=class="str">"cmt">//--- Set the flag to indicate a range breakout has occurred drawBreakPoint(TimeToString(barTime),barTime,barClose,class="num">233,clrBlue,class="num">1); class=class="str">"cmt">//--- Draw a breakpoint marker for the low breakout } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Function to draw a swing point marker | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void drawSwingPoint(class="type">class="kw">string objName,class="type">class="kw">datetime time,class="type">class="kw">double price,class="type">int arrCode, class="type">color clr,class="type">int direction){ class=class="str">"cmt">//--- Define a function to draw a swing point marker if (ObjectFind(class="num">0,objName) < class="num">0){ class=class="str">"cmt">//--- Check if the swing point object does not already exist ObjectCreate(class="num">0,objName,OBJ_ARROW,class="num">0,time,price); class=class="str">"cmt">//--- Create an arrow object at the specified time and price ObjectSetInteger(class="num">0,objName,OBJPROP_ARROWCODE,arrCode); class=class="str">"cmt">//--- Set the arrow code for the marker ObjectSetInteger(class="num">0,objName,OBJPROP_COLOR,clr); class=class="str">"cmt">//--- Set the class="type">color of the arrow ObjectSetInteger(class="num">0,objName,OBJPROP_FONTSIZE,class="num">10); class=class="str">"cmt">//--- Set the font size for the arrow if (direction > class="num">0) {ObjectSetInteger(class="num">0,objName,OBJPROP_ANCHOR,ANCHOR_TOP);} class=class="str">"cmt">//--- Set the anchor to top for swing lows if (direction < class="num">0) {ObjectSetInteger(class="num">0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM);} class=class="str">"cmt">//--- Set the anchor to bottom for swing highs class="type">class="kw">string text = "BoS"; class=class="str">"cmt">//--- Define the text label for Break of Structure class="type">class="kw">string objName_Descr = objName + text; class=class="str">"cmt">//--- Create a unique name for the text object