最后由 ea38849 于 2026-5-27 16:54 编辑
#property strict
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_color1 Lime
#property indicator_color2 Red
#property indicator_color3 DeepSkyBlue
#property indicator_width1 2
#property indicator_width2 2
#property indicator_width3 2
//+------------------------------------------------------------------+
//| 结构波段拐点指标 |
//| 局部高低点 + ATR过滤 + 最小波段幅度确认 |
//+------------------------------------------------------------------+
//======================== 核心参数 ========================//
extern int 左右确认K线数 = 3; // 越小越灵敏,越大越稳
extern int 最小波段点数 = 300; // 小于该幅度的波动不提示
extern bool 使用ATR动态过滤 = true; // 是否使用ATR动态过滤
extern int ATR周期 = 14; // ATR周期
extern double ATR倍数 = 0.80; // 最小波段 = max(固定点数, ATR*倍数)
extern int 箭头显示位置 = 1; // 0=确认K线位置,1=拐点K线位置
extern int 箭头距离点数 = 120; // 箭头距离K线高低点距离
//======================== 过滤参数 ========================//
extern bool 使用RSI过滤 = false; // 是否启用RSI过滤
extern int RSI周期 = 6; // RSI周期
extern double RSI买入上限 = 45.0; // 买入信号要求RSI低于该值
extern double RSI卖出下限 = 55.0; // 卖出信号要求RSI高于该值
extern bool 使用确认K线方向 = false; // 买入要求确认K线为阳线,卖出要求阴线
//======================== 显示与提醒 ========================//
extern bool 显示波段连线 = true; // 是否显示波段连接线
extern bool 开启弹窗提醒 = true; // 是否弹窗提醒
extern bool 开启声音提醒 = true; // 是否声音提醒
extern string 声音文件 = "alert.wav";
//======================== 指标缓存 ========================//
double 买入箭头Buffer[];
double 卖出箭头Buffer[];
double 波段连线Buffer[];
//======================== 防重复提醒 ========================//
datetime 上次提醒时间 = 0;
//+------------------------------------------------------------------+
//| 初始化 |
//+------------------------------------------------------------------+
int OnInit()
{
IndicatorShortName("结构波段拐点指标");
SetIndexBuffer(0, 买入箭头Buffer);
SetIndexStyle(0, DRAW_ARROW);
SetIndexArrow(0, 233);
SetIndexLabel(0, "波段买入");
SetIndexBuffer(1, 卖出箭头Buffer);
SetIndexStyle(1, DRAW_ARROW);
SetIndexArrow(1, 234);
SetIndexLabel(1, "波段卖出");
SetIndexBuffer(2, 波段连线Buffer);
if(显示波段连线)
SetIndexStyle(2, DRAW_SECTION);
else
SetIndexStyle(2, DRAW_NONE);
SetIndexLabel(2, "波段连线");
ArraySetAsSeries(买入箭头Buffer, true);
ArraySetAsSeries(卖出箭头Buffer, true);
ArraySetAsSeries(波段连线Buffer, true);
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 <= 左右确认K线数 * 2 + ATR周期 + 10)
return(0);
for(int k = 0; k < rates_total; k++)
{
买入箭头Buffer[k] = EMPTY_VALUE;
卖出箭头Buffer[k] = EMPTY_VALUE;
波段连线Buffer[k] = EMPTY_VALUE;
}
int oldest = rates_total - 左右确认K线数 - 2;
int lastDirection = 0; // 1=上一个是低点买入,-1=上一个是高点卖出
int lastPivotIndex = -1;
int lastBufferIndex = -1;
double lastPivotPrice = 0.0;
for(int i = oldest; i >= 左右确认K线数; i--)
{
bool swingHigh = IsSwingHigh(i, high, rates_total);
bool swingLow = IsSwingLow(i, low, rates_total);
if(!swingHigh && !swingLow)
continue;
//======================== 初始信号 ========================//
if(lastDirection == 0)
{
if(swingLow && BuyFilterOK(i, open, close))
{
lastDirection = 1;
lastPivotIndex = i;
lastPivotPrice = low;
lastBufferIndex = DrawBuySignal(i, low, rates_total);
continue;
}
if(swingHigh && SellFilterOK(i, open, close))
{
lastDirection = -1;
lastPivotIndex = i;
lastPivotPrice = high;
lastBufferIndex = DrawSellSignal(i, high, rates_total);
continue;
}
}
//======================== 上一个是买点,等待高点卖出 ========================//
if(lastDirection == 1)
{
// 如果还没出现卖点,又出现更低的低点,则替换买点
if(swingLow && low < lastPivotPrice && BuyFilterOK(i, open, close))
{
ClearSignal(lastBufferIndex);
lastPivotIndex = i;
lastPivotPrice = low;
lastBufferIndex = DrawBuySignal(i, low, rates_total);
continue;
}
// 出现有效高点,且涨幅达到最小波段要求,给出卖点
if(swingHigh && SellFilterOK(i, open, close))
{
double needMove = GetMinWaveDistance(i);
if(high - lastPivotPrice >= needMove)
{
DrawSwingLine(lastPivotIndex, lastPivotPrice);
DrawSwingLine(i, high);
lastDirection = -1;
lastPivotIndex = i;
lastPivotPrice = high;
lastBufferIndex = DrawSellSignal(i, high, rates_total);
}
}
}
//======================== 上一个是卖点,等待低点买入 ========================//
else if(lastDirection == -1)
{
// 如果还没出现买点,又出现更高的高点,则替换卖点
if(swingHigh && high > lastPivotPrice && SellFilterOK(i, open, close))
{
ClearSignal(lastBufferIndex);
lastPivotIndex = i;
lastPivotPrice = high;
lastBufferIndex = DrawSellSignal(i, high, rates_total);
continue;
}
// 出现有效低点,且跌幅达到最小波段要求,给出买点
if(swingLow && BuyFilterOK(i, open, close))
{
double needMove2 = GetMinWaveDistance(i);
if(lastPivotPrice - low >= needMove2)
{
DrawSwingLine(lastPivotIndex, lastPivotPrice);
DrawSwingLine(i, low);
lastDirection = 1;
lastPivotIndex = i;
lastPivotPrice = low;
lastBufferIndex = DrawBuySignal(i, low, rates_total);
}
}
}
}
CheckAlert(time);
return(rates_total);
}
//+------------------------------------------------------------------+
//| 判断摆动高点 |
//+------------------------------------------------------------------+
bool IsSwingHigh(int shift, const double &high[], int rates_total)
{
if(shift - 左右确认K线数 < 0)
return(false);
if(shift + 左右确认K线数 >= rates_total)
return(false);
double h = high[shift];
for(int j = 1; j <= 左右确认K线数; j++)
{
if(h <= high[shift - j])
return(false);
if(h < high[shift + j])
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
//| 判断摆动低点 |
//+------------------------------------------------------------------+
bool IsSwingLow(int shift, const double &low[], int rates_total)
{
if(shift - 左右确认K线数 < 0)
return(false);
if(shift + 左右确认K线数 >= rates_total)
return(false);
double l = low[shift];
for(int j = 1; j <= 左右确认K线数; j++)
{
if(l >= low[shift - j])
return(false);
if(l > low[shift + j])
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
//| 获取最小波段距离 |
//+------------------------------------------------------------------+
double GetMinWaveDistance(int shift)
{
double fixedDistance = 最小波段点数 * Point;
double result = fixedDistance;
if(使用ATR动态过滤)
{
double atr = iATR(NULL, 0, ATR周期, shift);
double atrDistance = atr * ATR倍数;
if(atrDistance > result)
result = atrDistance;
}
return(result);
}
//+------------------------------------------------------------------+
//| 买入过滤 |
//+------------------------------------------------------------------+
bool BuyFilterOK(int shift, const double &open[], const double &close[])
{
if(使用RSI过滤)
{
double rsi = iRSI(NULL, 0, RSI周期, PRICE_CLOSE, shift);
if(rsi > RSI买入上限)
return(false);
}
if(使用确认K线方向)
{
int confirmShift = shift - 左右确认K线数;
if(confirmShift < 0)
return(false);
if(close[confirmShift] <= open[confirmShift])
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
//| 卖出过滤 |
//+------------------------------------------------------------------+
bool SellFilterOK(int shift, const double &open[], const double &close[])
{
if(使用RSI过滤)
{
double rsi = iRSI(NULL, 0, RSI周期, PRICE_CLOSE, shift);
if(rsi < RSI卖出下限)
return(false);
}
if(使用确认K线方向)
{
int confirmShift = shift - 左右确认K线数;
if(confirmShift < 0)
return(false);
if(close[confirmShift] >= open[confirmShift])
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
//| 画买入信号 |
//+------------------------------------------------------------------+
int DrawBuySignal(int pivotShift, const double &low[], int rates_total)
{
int drawShift = pivotShift;
if(箭头显示位置 == 0)
drawShift = pivotShift - 左右确认K线数;
if(drawShift < 0 || drawShift >= rates_total)
return(-1);
买入箭头Buffer[drawShift] = low[drawShift] - 箭头距离点数 * Point;
return(drawShift);
}
//+------------------------------------------------------------------+
//| 画卖出信号 |
//+------------------------------------------------------------------+
int DrawSellSignal(int pivotShift, const double &high[], int rates_total)
{
int drawShift = pivotShift;
if(箭头显示位置 == 0)
drawShift = pivotShift - 左右确认K线数;
if(drawShift < 0 || drawShift >= rates_total)
return(-1);
卖出箭头Buffer[drawShift] = high[drawShift] + 箭头距离点数 * Point;
return(drawShift);
}
//+------------------------------------------------------------------+
//| 清除旧信号 |
//+------------------------------------------------------------------+
void ClearSignal(int bufferIndex)
{
if(bufferIndex < 0)
return;
买入箭头Buffer[bufferIndex] = EMPTY_VALUE;
卖出箭头Buffer[bufferIndex] = EMPTY_VALUE;
}
//+------------------------------------------------------------------+
//| 画波段连线 |
//+------------------------------------------------------------------+
void DrawSwingLine(int shift, double price)
{
if(!显示波段连线)
return;
if(shift < 0)
return;
波段连线Buffer[shift] = price;
}
//+------------------------------------------------------------------+
//| 信号提醒 |
//+------------------------------------------------------------------+
void CheckAlert(const datetime &time[])
{
int maxCheck = 左右确认K线数 + 5;
for(int shift = 1; shift <= maxCheck; shift++)
{
if(买入箭头Buffer[shift] != EMPTY_VALUE)
{
if(time[shift] == 上次提醒时间)
return;
string buyMsg = Symbol() + " 出现结构波段买入信号";
if(开启弹窗提醒)
Alert(buyMsg);
if(开启声音提醒)
PlaySound(声音文件);
上次提醒时间 = time[shift];
return;
}
if(卖出箭头Buffer[shift] != EMPTY_VALUE)
{
if(time[shift] == 上次提醒时间)
return;
string sellMsg = Symbol() + " 出现结构波段卖出信号";
if(开启弹窗提醒)
Alert(sellMsg);
if(开启声音提醒)
PlaySound(声音文件);
上次提醒时间 = time[shift];
return;
}
}
}
|