Skip to content

自定义指标-挤压指标

指标原理

挤压指标由布林带(Bollinger Bands)和肯特纳通道(Keltner Channel,又称ATR通道)结合而成,用于寻找价格突破点。

  • 布林带(白色粗线):由中线(移动平均线)、上轨和下轨组成,反映价格波动范围。
  • 肯特纳通道(虚线):基于真实波动幅度(ATR)计算,反映价格波动的通道范围。

挤压状态:当布林带的上轨和下轨完全进入肯特纳通道内时,市场处于水平震荡,无明确方向;当价格突破时,布林带扩张并脱离肯特纳通道,此时是潜在的突破交易时机。

代码实现

cpp
#property indicator_chart_window

#property indicator_buffers 11
#property indicator_plots 6

#property indicator_label1 "挤压指标"
#property indicator_type1 DRAW_FILLING
#property indicator_color1 Gold

#property indicator_label2 "布林上轨"
#property indicator_type2 DRAW_COLOR_LINE
#property indicator_color2 clrGainsboro, clrLightGreen
#property indicator_width2 3

#property indicator_label3 "肯特纳通道上轨"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrSilver
#property indicator_style3 STYLE_DOT

#property indicator_label4 "布林和肯特纳通道中轨"
#property indicator_type4 DRAW_COLOR_LINE
#property indicator_color4 clrGreen, clrRed
#property indicator_width4 2

#property indicator_label5 "肯特纳通道下轨"
#property indicator_type5 DRAW_LINE
#property indicator_color5 clrSilver
#property indicator_style5 STYLE_DOT

#property indicator_label6 "布林下轨"
#property indicator_type6 DRAW_COLOR_LINE
#property indicator_color6 clrGainsboro, clrRed
#property indicator_width6 3

input int inpPeriod = 20;                        // 布林和肯特纳周期
input ENUM_APPLIED_PRICE inpPrice = PRICE_CLOSE; // 应用到
input double inpBbMultiplier = 2.0;              // 布林标准差倍数
input double inpKcMultiplier = 1.5;              // 肯特纳 系数  设置<=0 和布林相同
// 中线,中线颜色,布林上轨,布林上轨颜色,布林下轨, 布林下轨颜色,  肯特纳上轨,肯特纳下轨, 挤压区域上沿,挤压区域下沿, atr
double mid[], midc[], bbup[], bbupc[], bbdn[], bbdnc[], kelu[], keld[], sqzu[], sqzd[], atr[];

double kcMultiplier;
int bbHandle, atrHandle;

int OnInit(void)
{
    SetIndexBuffer(0, sqzu, INDICATOR_DATA);
    SetIndexBuffer(1, sqzd, INDICATOR_DATA);

    SetIndexBuffer(2, bbup, INDICATOR_DATA);
    SetIndexBuffer(3, bbupc, INDICATOR_COLOR_INDEX);
    SetIndexBuffer(4, kelu, INDICATOR_DATA);

    SetIndexBuffer(5, mid, INDICATOR_DATA);
    SetIndexBuffer(6, midc, INDICATOR_COLOR_INDEX);
    SetIndexBuffer(7, keld, INDICATOR_DATA);

    SetIndexBuffer(8, bbdn, INDICATOR_DATA);
    SetIndexBuffer(9, bbdnc, INDICATOR_COLOR_INDEX);
    SetIndexBuffer(10, atr, INDICATOR_CALCULATIONS);

    kcMultiplier = (inpKcMultiplier <= 0) ? inpBbMultiplier : inpKcMultiplier;

    bbHandle = iBands(_Symbol, 0, inpPeriod, 0, inpBbMultiplier, inpPrice);
    atrHandle = iATR(_Symbol, 0, inpPeriod);
    // 主窗口只有鼠标悬停在指标上才能显示
    IndicatorSetString(INDICATOR_SHORTNAME, "布林带挤压(" + (string)inpPeriod + "," + (string)inpBbMultiplier + "," + (string)inpKcMultiplier + ")");

    return (INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    if (rates_total < inpPeriod)
        return 0;

    // 检查需要的相关指标是否已经准备好,没准备好就返回上一次计算的结果
    if(BarsCalculated(bbHandle) < rates_total || BarsCalculated(atrHandle) < rates_total){
        Print("bbHandle 或 atrHandle 数据没有全部计算");
        return (prev_calculated);
    }
    // 计算需要复制的柱数 +1 是确保至少要复制一个数据  MathMin防止数组越界
    int copyCount =MathMin(rates_total- prev_calculated + 1, rates_total);
    // 复制指标数据
    if(CopyBuffer(bbHandle,BASE_LINE,0,copyCount,mid)!=copyCount) return (prev_calculated);
    if(CopyBuffer(bbHandle,LOWER_BAND,0,copyCount,bbdn)!=copyCount) return (prev_calculated);
    if(CopyBuffer(bbHandle,UPPER_BAND,0,copyCount,bbup)!=copyCount) return (prev_calculated);
    if(CopyBuffer(atrHandle,BASE_LINE,0,copyCount,atr)!=copyCount) return (prev_calculated);

    // 初始化循环变量i 确保从上一次计算结束的位置开始计算  i写在外面是因为最终要返回出去
    int i = (int) MathMax(prev_calculated-1,0);
    // 循环计算肯特纳通道的上轨和下轨, 判断是否发生挤压
    for(; i < rates_total&& !_StopFlag; i++){
        keld[i] = mid[i] -atr[i] *  kcMultiplier;
        kelu[i] = mid[i] +atr[i] *  kcMultiplier;
        bool squeeze = (kelu[i] > bbup[i]);
        sqzu[i] = (squeeze)?bbup[i] :mid[i];
        sqzd[i] = (squeeze)?bbdn[i] :mid[i];

        bbupc[i] = (squeeze)?1 :0;
        bbdnc[i] = (squeeze)?1 :0;

        midc[i] = (squeeze)?1 :0;
    }
        
    // 为什么是返回i而不是prev_calculated 或者 rates_total
    return (i);
}
//+------------------------------------------------------------------+

最终效果

image.png