如何查找数组中数值最接近的K线高点并绘制HLine?
实现数组中接近数值识别的方案
前置修正
你现有代码存在类型错误:totalHigh、FinishingTotalHigh等变量用int类型存储价格会丢失全部小数精度,需要改为double类型。
核心实现逻辑
你需要先明确两个可自定义的规则:
- 接近阈值:两个高点差值小于该值时判定为数值接近,建议设置为交易品种最小变动价位的3-10倍,比如直盘货币对可设为0.0005
- 有效聚类最小数量:至少有N个高点互相接近时才判定为有效水平位,建议设为2-3,避免偶然的接近被误判
识别逻辑如下:
- 将扫描到的所有高点存入临时数组
- 对数组进行升序排序,数值接近的元素会变为相邻状态
- 遍历排序后的数组,将差值小于接近阈值的相邻元素归为同一个聚类
- 过滤掉元素数量小于有效聚类最小数量的无效聚类
- 每个有效聚类的平均值就是你需要绘制水平线的位置
完整修改后代码
extern int candleCount = 100; // 新增自定义参数 extern double nearThreshold = 0.0005; // 接近阈值,可根据品种调整 extern int minClusterSize = 3; // 有效聚类最少高点数量 extern string linePrefix = "HighClusterLine_"; // 水平线命名前缀 double totalHigh; double FinishingTotalHigh; double totalLow; double FinishingTotalLow; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // 退出时删除所有创建的水平线 for(int i=ObjectsTotal(0)-1; i>=0; i--) { string objName = ObjectName(0, i); if(StringFind(objName, linePrefix) == 0) { ObjectDelete(0, objName); } } } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { totalHigh = 0; totalLow = 0; double Highest = High[0]; double Lowest = Low[0]; // 存储所有高点的临时数组 double highArr[]; ArrayResize(highArr, candleCount+1); // 扫描100根K线 for (int i = 0; i <= candleCount; i++) { highArr[i] = High[i]; totalHigh = High[i] + totalHigh; totalLow = Low[i] + totalLow; if (High[i] > Highest) Highest = High[i]; if (Low[i] < Lowest) Lowest = Low[i]; } FinishingTotalHigh = totalHigh/candleCount; FinishingTotalLow = totalLow/candleCount; // 打印基础结果 Alert("Highest price found is "+Highest); Alert("Lowest price found is "+Lowest); Alert("Avg High: "+FinishingTotalHigh); Alert("Avg Low: "+FinishingTotalLow); // --- 新增:识别接近的高点聚类 --- // 1. 高点数组排序 ArraySort(highArr); // 2. 划分聚类 double currentCluster[]; ArrayResize(currentCluster, 1); currentCluster[0] = highArr[0]; // 先删除之前画的线避免重复 for(int i=ObjectsTotal(0)-1; i>=0; i--) { string objName = ObjectName(0, i); if(StringFind(objName, linePrefix) == 0) { ObjectDelete(0, objName); } } int lineIndex = 0; for(int i=1; i<=candleCount; i++) { if(highArr[i] - currentCluster[ArraySize(currentCluster)-1] < nearThreshold) { // 差值小于阈值,加入当前聚类 ArrayResize(currentCluster, ArraySize(currentCluster)+1); currentCluster[ArraySize(currentCluster)-1] = highArr[i]; } else { // 聚类结束,判断是否有效 if(ArraySize(currentCluster) >= minClusterSize) { // 计算聚类均值 double clusterAvg = 0; for(int j=0; j<ArraySize(currentCluster); j++) { clusterAvg += currentCluster[j]; } clusterAvg /= ArraySize(currentCluster); // 画水平线 string lineName = linePrefix + IntegerToString(lineIndex); ObjectCreate(0, lineName, OBJ_HLINE, 0, 0, clusterAvg); ObjectSetInteger(0, lineName, OBJ_COLOR, clrRed); ObjectSetInteger(0, lineName, OBJ_STYLE, STYLE_SOLID); ObjectSetInteger(0, lineName, OBJ_WIDTH, 2); lineIndex++; } // 重置聚类 ArrayResize(currentCluster, 1); currentCluster[0] = highArr[i]; } } // 处理最后一个聚类 if(ArraySize(currentCluster) >= minClusterSize) { double clusterAvg = 0; for(int j=0; j<ArraySize(currentCluster); j++) { clusterAvg += currentCluster[j]; } clusterAvg /= ArraySize(currentCluster); string lineName = linePrefix + IntegerToString(lineIndex); ObjectCreate(0, lineName, OBJ_HLINE, 0, 0, clusterAvg); ObjectSetInteger(0, lineName, OBJ_COLOR, clrRed); ObjectSetInteger(0, lineName, OBJ_STYLE, STYLE_SOLID); ObjectSetInteger(0, lineName, OBJ_WIDTH, 2); } } //+------------------------------------------------------------------+
调整说明
你可以根据交易品种的波动幅度调整nearThreshold和minClusterSize参数,波动大的品种阈值可以适当调大,对水平位验证要求高的可以把最小聚类数量调大。
内容的提问来源于stack exchange,提问作者Javier Macias
相关产品推荐
相关产品推荐

