量化魔方 发表于 5 天前

【箭头指标】有源码,本人原创

https://www.eahub.cn/data/attachment/forum/202605/27/165147quh0yg7hvw3ygfhn.png

https://www.eahub.cn/data/attachment/forum/202605/27/165202zrfffrf35acafzrn.png
https://www.eahub.cn/data/attachment/forum/202605/27/165212l5m5m0055d7mr0kq.png

#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 = EMPTY_VALUE;
      卖出箭头Buffer = EMPTY_VALUE;
      波段连线Buffer = 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;

   for(int j = 1; j <= 左右确认K线数; j++)
   {
      if(h <= high)
         return(false);

      if(h < high)
         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;

   for(int j = 1; j <= 左右确认K线数; j++)
   {
      if(l >= low)
         return(false);

      if(l > low)
         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 <= open)
         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 >= open)
         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 = low - 箭头距离点数 * 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 = high + 箭头距离点数 * Point;

   return(drawShift);
}


//+------------------------------------------------------------------+
//| 清除旧信号                                                       |
//+------------------------------------------------------------------+
void ClearSignal(int bufferIndex)
{
   if(bufferIndex < 0)
      return;

   买入箭头Buffer = EMPTY_VALUE;
   卖出箭头Buffer = EMPTY_VALUE;
}


//+------------------------------------------------------------------+
//| 画波段连线                                                       |
//+------------------------------------------------------------------+
void DrawSwingLine(int shift, double price)
{
   if(!显示波段连线)
      return;

   if(shift < 0)
      return;

   波段连线Buffer = price;
}


//+------------------------------------------------------------------+
//| 信号提醒                                                         |
//+------------------------------------------------------------------+
void CheckAlert(const datetime &time[])
{
   int maxCheck = 左右确认K线数 + 5;

   for(int shift = 1; shift <= maxCheck; shift++)
   {
      if(买入箭头Buffer != EMPTY_VALUE)
      {
         if(time == 上次提醒时间)
            return;

         string buyMsg = Symbol() + " 出现结构波段买入信号";

         if(开启弹窗提醒)
            Alert(buyMsg);

         if(开启声音提醒)
            PlaySound(声音文件);

         上次提醒时间 = time;
         return;
      }

      if(卖出箭头Buffer != EMPTY_VALUE)
      {
         if(time == 上次提醒时间)
            return;

         string sellMsg = Symbol() + " 出现结构波段卖出信号";

         if(开启弹窗提醒)
            Alert(sellMsg);

         if(开启声音提醒)
            PlaySound(声音文件);

         上次提醒时间 = time;
         return;
      }
   }
}


页: [1]
查看完整版本: 【箭头指标】有源码,本人原创