//+------------------------------------------------------------------+
//|                                           02_SwingPointView.mq5 |
//|                          Copyright 2026, FXおもしろラボ          |
//|                          https://fx-omoshiro-lab.com/            |
//+------------------------------------------------------------------+
#property copyright   "Copyright 2026, FXおもしろラボ"
#property link        "https://fx-omoshiro-lab.com/"
#property version     "1.00"
#property description "直近の高値/安値に価格と時刻を表示するSwingPoint View インジケーター"
#property description "元ネタ: とあるMetaTraderの備忘秘録 (2009-09-09)"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

//--- 入力パラメータ
input int    InpSwingBars     = 10;      // スイング判定期間（前後N本）
input int    InpTextSize      = 9;       // テキストサイズ
input color  InpHighColor     = clrRed;  // 高値テキストの色
input color  InpLowColor      = clrBlue; // 安値テキストの色
input int    InpMaxLabels     = 50;      // 最大表示ラベル数
input double InpTextGapPips   = 10.0;    // 価格と時刻の間隔（pips）

//--- グローバル変数
string g_prefix = "SwingPV_";           // オブジェクト名のプリフィックス
double g_point;                          // ポイント値

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- ポイント値を設定
   g_point = _Point;
   
   //--- 5桁/3桁ブローカー対応
   if(_Digits == 3 || _Digits == 5)
      g_point = _Point * 10;
   
   Print("SwingPoint View initialized. SwingBars=", InpSwingBars);
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   //--- 作成したオブジェクトをすべて削除
   ObjectsDeleteAll(0, g_prefix);
   Print("SwingPoint View removed. Objects deleted.");
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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 < InpSwingBars * 2 + 1)
      return(0);
   
   //--- 配列を時系列として設定
   ArraySetAsSeries(time, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   
   //--- 既存のオブジェクトを削除
   ObjectsDeleteAll(0, g_prefix);
   
   //--- スイングポイントを検出してラベルを作成
   int highCount = 0;
   int lowCount = 0;
   int maxCheck = MathMin(rates_total - InpSwingBars - 1, 500); // 最大500本をチェック
   
   for(int i = InpSwingBars; i < maxCheck && (highCount < InpMaxLabels || lowCount < InpMaxLabels); i++)
   {
      //--- スイングハイの検出
      if(highCount < InpMaxLabels && IsSwingHigh(high, i, InpSwingBars))
      {
         CreateSwingLabel(time[i], high[i], true, highCount);
         highCount++;
      }
      
      //--- スイングローの検出
      if(lowCount < InpMaxLabels && IsSwingLow(low, i, InpSwingBars))
      {
         CreateSwingLabel(time[i], low[i], false, lowCount);
         lowCount++;
      }
   }
   
   return(rates_total);
}

//+------------------------------------------------------------------+
//| スイングハイかどうかを判定                                        |
//+------------------------------------------------------------------+
bool IsSwingHigh(const double &high[], int index, int period)
{
   double centerHigh = high[index];
   
   //--- 前後N本より高いかチェック
   for(int i = 1; i <= period; i++)
   {
      if(high[index - i] >= centerHigh)
         return false;
      if(high[index + i] >= centerHigh)
         return false;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| スイングローかどうかを判定                                        |
//+------------------------------------------------------------------+
bool IsSwingLow(const double &low[], int index, int period)
{
   double centerLow = low[index];
   
   //--- 前後N本より低いかチェック
   for(int i = 1; i <= period; i++)
   {
      if(low[index - i] <= centerLow)
         return false;
      if(low[index + i] <= centerLow)
         return false;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| スイングポイントにラベルを作成                                    |
//+------------------------------------------------------------------+
void CreateSwingLabel(datetime barTime, double price, bool isHigh, int count)
{
   //--- オブジェクト名を生成
   string priceName = g_prefix + (isHigh ? "H_P_" : "L_P_") + IntegerToString(count);
   string timeName  = g_prefix + (isHigh ? "H_T_" : "L_T_") + IntegerToString(count);
   
   //--- 色を設定
   color textColor = isHigh ? InpHighColor : InpLowColor;
   
   //--- 価格テキストを作成
   string priceText = DoubleToString(price, _Digits);
   CreateTextObject(priceName, barTime, price, priceText, textColor, isHigh ? ANCHOR_LOWER : ANCHOR_UPPER);
   
   //--- 時刻テキストを作成（価格の上下に配置）
   double gap = InpTextGapPips * g_point;
   double timePrice = isHigh ? price + gap : price - gap;
   string timeText = TimeToString(barTime, TIME_DATE | TIME_MINUTES);
   CreateTextObject(timeName, barTime, timePrice, timeText, textColor, isHigh ? ANCHOR_LOWER : ANCHOR_UPPER);
}

//+------------------------------------------------------------------+
//| テキストオブジェクトを作成                                        |
//+------------------------------------------------------------------+
void CreateTextObject(string name, datetime time, double price, string text, color clr, ENUM_ANCHOR_POINT anchor)
{
   if(ObjectCreate(0, name, OBJ_TEXT, 0, time, price))
   {
      ObjectSetString(0, name, OBJPROP_TEXT, text);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, InpTextSize);
      ObjectSetString(0, name, OBJPROP_FONT, "Arial");
      ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   }
}
//+------------------------------------------------------------------+
