//+------------------------------------------------------------------+
//| 24_LWMA_MACD_Cross_Signals_v1_00.mq5                           |
//| Shows confirmed-bar crosses of an LWMA-based MACD.              |
//+------------------------------------------------------------------+
#property copyright "FXおもしろラボ"
#property version   "1.00"
#property strict
#property description "Marks confirmed-bar crosses of an LWMA-based MACD."

#property indicator_chart_window
#property indicator_buffers 4
#property indicator_plots   4

#property indicator_label1 "Bullish Cross"
#property indicator_type1  DRAW_ARROW
#property indicator_color1 clrLimeGreen
#property indicator_width1 2

#property indicator_label2 "Bearish Cross"
#property indicator_type2  DRAW_ARROW
#property indicator_color2 clrTomato
#property indicator_width2 2

#property indicator_label3 "LWMA MACD Main"
#property indicator_type3  DRAW_NONE

#property indicator_label4 "LWMA MACD Signal"
#property indicator_type4  DRAW_NONE

input int    InpFastPeriod       = 12;   // 短期LWMA期間
input int    InpSlowPeriod       = 26;   // 長期LWMA期間
input int    InpSignalPeriod     = 9;    // MACDシグナルのLWMA期間
input int    InpBarsToCalculate  = 3000; // 計算する直近バー数
input double InpArrowOffsetPips  = 4.0;  // 矢印を足から離す距離（pips）

double g_bullish[];
double g_bearish[];
double g_main[];
double g_signal[];
int g_fast_handle = INVALID_HANDLE;
int g_slow_handle = INVALID_HANDLE;

double PipSize();

int OnInit()
{
   if(InpFastPeriod < 1 || InpSlowPeriod <= InpFastPeriod ||
      InpSignalPeriod < 1 || InpBarsToCalculate < 100 ||
      InpArrowOffsetPips <= 0.0)
   {
      Print("LWMA MACD Cross Signals: invalid input parameters.");
      return INIT_PARAMETERS_INCORRECT;
   }

   if(!SetIndexBuffer(0, g_bullish, INDICATOR_DATA) ||
      !SetIndexBuffer(1, g_bearish, INDICATOR_DATA) ||
      !SetIndexBuffer(2, g_main, INDICATOR_DATA) ||
      !SetIndexBuffer(3, g_signal, INDICATOR_DATA))
   {
      PrintFormat("LWMA MACD Cross Signals: SetIndexBuffer failed, error=%d", GetLastError());
      return INIT_FAILED;
   }
   ArraySetAsSeries(g_bullish, false);
   ArraySetAsSeries(g_bearish, false);
   ArraySetAsSeries(g_main, false);
   ArraySetAsSeries(g_signal, false);

   PlotIndexSetInteger(0, PLOT_ARROW, 233);
   PlotIndexSetInteger(1, PLOT_ARROW, 234);
   for(int plot = 0; plot < 4; ++plot)
      PlotIndexSetDouble(plot, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   g_fast_handle = iMA(_Symbol, _Period, InpFastPeriod, 0, MODE_LWMA, PRICE_CLOSE);
   g_slow_handle = iMA(_Symbol, _Period, InpSlowPeriod, 0, MODE_LWMA, PRICE_CLOSE);
   if(g_fast_handle == INVALID_HANDLE || g_slow_handle == INVALID_HANDLE)
   {
      PrintFormat("LWMA MACD Cross Signals: iMA handle creation failed, error=%d", GetLastError());
      return INIT_FAILED;
   }

   IndicatorSetString(
      INDICATOR_SHORTNAME,
      StringFormat("LWMA MACD Cross (%d,%d,%d)", InpFastPeriod, InpSlowPeriod, InpSignalPeriod)
   );
   return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
   if(g_fast_handle != INVALID_HANDLE)
   {
      IndicatorRelease(g_fast_handle);
      g_fast_handle = INVALID_HANDLE;
   }
   if(g_slow_handle != INVALID_HANDLE)
   {
      IndicatorRelease(g_slow_handle);
      g_slow_handle = INVALID_HANDLE;
   }
}

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[]
)
{
   const int minimum_bars = InpSlowPeriod + InpSignalPeriod + 2;
   if(rates_total < minimum_bars ||
      BarsCalculated(g_fast_handle) < rates_total ||
      BarsCalculated(g_slow_handle) < rates_total)
      return 0;

   ArraySetAsSeries(high, false);
   ArraySetAsSeries(low, false);

   const int copy_count = MathMin(
      rates_total,
      InpBarsToCalculate + InpSignalPeriod + 3
   );
   double fast_values[];
   double slow_values[];
   ArrayResize(fast_values, copy_count);
   ArrayResize(slow_values, copy_count);
   ArraySetAsSeries(fast_values, false);
   ArraySetAsSeries(slow_values, false);

   ResetLastError();
   const int fast_copied = CopyBuffer(g_fast_handle, 0, 0, copy_count, fast_values);
   const int slow_copied = CopyBuffer(g_slow_handle, 0, 0, copy_count, slow_values);
   if(fast_copied != copy_count || slow_copied != copy_count)
   {
      PrintFormat(
         "LWMA MACD Cross Signals: CopyBuffer expected=%d fast=%d slow=%d error=%d",
         copy_count, fast_copied, slow_copied, GetLastError()
      );
      return prev_calculated;
   }

   if(prev_calculated == 0)
   {
      ArrayInitialize(g_bullish, EMPTY_VALUE);
      ArrayInitialize(g_bearish, EMPTY_VALUE);
      ArrayInitialize(g_main, EMPTY_VALUE);
      ArrayInitialize(g_signal, EMPTY_VALUE);
   }
   else
   {
      const int clear_first = MathMax(0, rates_total - copy_count - 1);
      for(int i = clear_first; i < rates_total; ++i)
      {
         g_bullish[i] = EMPTY_VALUE;
         g_bearish[i] = EMPTY_VALUE;
         g_main[i] = EMPTY_VALUE;
         g_signal[i] = EMPTY_VALUE;
      }
   }

   const int first = rates_total - copy_count;
   for(int local = 0; local < copy_count; ++local)
      g_main[first + local] = fast_values[local] - slow_values[local];

   const double weight_sum = (double)InpSignalPeriod * (InpSignalPeriod + 1) / 2.0;
   for(int local = InpSignalPeriod - 1; local < copy_count; ++local)
   {
      double weighted_sum = 0.0;
      for(int step = 0; step < InpSignalPeriod; ++step)
      {
         const int weight = step + 1;
         weighted_sum += g_main[first + local - (InpSignalPeriod - 1) + step] * weight;
      }
      g_signal[first + local] = weighted_sum / weight_sum;
   }

   const int signal_first = first + InpSignalPeriod - 1;
   const int marker_first = MathMax(signal_first + 1, rates_total - InpBarsToCalculate);
   const int last_confirmed = rates_total - 2;
   const double offset = InpArrowOffsetPips * PipSize();
   for(int i = marker_first; i <= last_confirmed; ++i)
   {
      if(g_main[i] > g_signal[i] && g_main[i - 1] <= g_signal[i - 1])
         g_bullish[i] = low[i] - offset;
      else if(g_main[i] < g_signal[i] && g_main[i - 1] >= g_signal[i - 1])
         g_bearish[i] = high[i] + offset;
   }

   return rates_total;
}

double PipSize()
{
   return (_Digits == 3 || _Digits == 5) ? _Point * 10.0 : _Point;
}
