//############ OmegaChart Display Realtime Chart ##########
/*
ツールバーのトグルボタン「Real」をOnにすると、表示銘柄のみ4本値(+出来高)をヤフー詳細ページから取得してローソク足に追加します。
当日の最新データのみで過去日に欠落があったとしても、時系列までは取得しません。
取得したデータはあくまでメモリ上に保持するのみでデータファイルに追加保存はしません。
データ更新タイミングは
①ボタンをオンにした時
②表示銘柄(指標は101,102,106のみ)を変更した場合
③チャートのダブルクリックまたは「R」キーを押した場合です。
タイマーによる自動更新は実装しませんでした。
取得時刻はステータスバーで確認できます。
あくまで現在値のチャート表示のみで、スクリーニング等には対応していません。

充分に動作検証していませんので、不具合があるかもしれませんがクレームは受つけませんので了解願います。
著作権を放棄しますので、利用価値があれば、自由に改変・公開してもらって結構です。
*/
// 以下に変更点のみ記録しますので、各自コードを追加して試してください。
//行番号はpanacoranさんのfe69a3fd345d14eda275825f5484f3159e99f157に対応しました
//追加したコードにより、行番号が変わるので注意してください。

// **************Yahoo.cs  305行(新規行)
//----------------------------------------- for realtime
namespace WebPage
{
    class Yahoo
    {
        public static string[] ReadDetail(string yahooSymbol)
        {
            string url = string.Format("http://stocks.finance.yahoo.co.jp/stocks/detail/?code={0}", yahooSymbol);
            string source = Html.GetSource(url, System.Text.Encoding.UTF8);
            if (source.IndexOf("<html") < 0)
                return new string[] { source };
            string[] prices = new string[6];
            string[] patterns = new string[4];
            patterns[0] = "<td class=\"stoksPrice\">(?<value>.*?)</td>";    //price
            patterns[1] = "<dd class=\"yjSb real\"><span>(?<value>.*?)</span>"; //time
            patterns[2] = "<div class=\"lineFi clearfix\">(?<1>.*?)<stron(?<2>.*?)>(?<value>.*?)</strong>(?<3>.*?)<dt class=\"title\">(?<field>.*?)<";
            patterns[3] = "<div id=\"globalDate\"(?<1>.*?)<p>(?<d1>.*?)<stron(?<2>.*?)>(?<d2>.*?)</strong>(?<d3>.*?)</p>";
            for (int i = 0; i < patterns.Length; i++)
            {
                if (i == 2)
                {
                    MatchCollection mc = Regex.Matches(source, patterns[i], RegexOptions.IgnoreCase | RegexOptions.Singleline);
                    foreach (System.Text.RegularExpressions.Match m in mc)
                    {
                        if (m.Groups["field"].Value == "始値")
                            prices[1] = m.Groups["value"].Value;
                        else if (m.Groups["field"].Value == "高値")
                            prices[2] = m.Groups["value"].Value;
                        else if (m.Groups["field"].Value == "安値")
                            prices[3] = m.Groups["value"].Value;
                        else if (m.Groups["field"].Value == "出来高")
                            prices[5] = m.Groups["value"].Value;
                    }
                }
                else
                {
                    Regex reg = new Regex(patterns[i], RegexOptions.IgnoreCase | RegexOptions.Singleline);
                    Match m1 = reg.Match(source);
                    if (m1.Success)
                    {
                        switch (i)
                        {
                            case 0:
                                prices[4] = m1.Groups["value"].Value; ;
                                break;
                            case 1:
                                prices[0] = m1.Groups["value"].Value;
                                break;
                            case 3:
                                prices[0] = m1.Groups["d1"].Value + m1.Groups["d2"].Value + m1.Groups["d3"].Value;
                                break;
                        }
                    }
                }
            }
            foreach (string s in prices)
                System.Diagnostics.Debug.Print(s);
            return prices;
        }
    }
    class Html
    {
        public static string GetSource(string url, System.Text.Encoding enc)
        {
            using (WebClient wc = new WebClient())
            {
                string source = "";
                try
                {
                    using (Stream sr = wc.OpenRead(url))
                    {
                        using (StreamReader reader = new StreamReader(sr, enc))
                        {
                            source = reader.ReadToEnd();
                            reader.Close();
                            sr.Close();
                        }
                    }
                }
                catch (Exception ex)
                {
                    source = ex.ToString();
                }
                return source;
            }
        }
    }
}// add by n

// **************Env.cs     リアルタイムフラグ変数追加(class Env) 48行
        //----------------------------------------- for realtime
        private static bool _isRealtime = false;
        public static bool IsRealtime
        {
            get { return _isRealtime; }
            set { _isRealtime = value; }
        }// add by n

//*************** Command.cs の enum CIDに追加 120行
        // -------------------- for realtime
        ToggleRealtime,
        UpdateRealtime,
        // add by n
//*************** Command.cs の class CommandExec 内にメソッド追加 493行から(+前項目行数)
	internal class CommandExec {      // <-- オリジナルコード493行

        // for realtime
        public static CommandResult UpdateRealtime()
        {

            if (Env.IsRealtime)
            {
                Env.Frame.Cursor = Cursors.WaitCursor;
                Env.Frame.SetStatusBarText("", "");
                AbstractBrand br = Env.Frame.ChartCanvas.GetBrand();
                int code = br.Code;
                string symbol = "";
                if (code > 1300 & code < 10000)
                    symbol = code.ToString();
                else if (code == 101)
                    symbol = "998407.O";
                else if (code == 102)
                    symbol = "998405.T";
                else if (code == 106)
                    symbol = "23337.T";
                if (symbol != "")
                {
                    string[] prices = WebPage.Yahoo.ReadDetail(symbol);
                    if (prices.Length < 6)
                    {
                        if (prices[0].IndexOf("") > 0)
                            Env.Frame.SetStatusBarText("インターネット接続を確認してください", "");
                        else
                            Env.Frame.SetStatusBarText(prices[0], "");
                    }
                    else
                    {
                        double[] val = new double[] { double.NaN, double.NaN, double.NaN, double.NaN, double.NaN };
                        if (double.TryParse(prices[1], out val[0]) && double.TryParse(prices[2], out val[1])
                            && double.TryParse(prices[3], out val[2]) && double.TryParse(prices[4], out val[3])
                            && (code > 1300 & double.TryParse(prices[5], out val[4]) | code < 1300))
                        {
                            DateTime d = DateTime.Now.Date;
                            TimeSpan ts = new TimeSpan();
                            if (prices[0].IndexOf(":") > 0 && TimeSpan.TryParse(prices[0], out ts))
                                d += ts;
                            else if (prices[0].IndexOf("/") > 0 && DateTime.TryParse(prices[0], out d))
                                d += new TimeSpan(15, 0, 0);
                            else
                                d = DateTime.Now;
                            prices[0] = d.ToString("yyyy/MM/dd ddd HH:mm");
                            string[] pre = new string[] { "O:", "H:", "L:", "C:", code > 1300 ? "V:" : "" };
                            for (int i = 0; i < pre.Length; i++)
                                prices[i + 1] = pre[i] + prices[i + 1];
                            int date = int.Parse(d.ToString("yyyyMMdd"));
                            int ratio = 10;
                            if (code < 1300)
                                ratio = 100;
                            for (int i = 0; i < val.Length; i++)
                                val[i] *= ratio;
                            int[] data = new int[] { date, (int)val[0], (int)val[1], (int)val[2], (int)val[3], (int)val[4], 0, 0 };

                            br.ReserveFarm().AddData(data);
                            Env.Frame.ChartCanvas.Refresh();
                            Env.Frame.SetStatusBarText(string.Join(" ", prices), "");
                            JumpDate(Keys.End);
                        }
                        else
                            Env.Frame.SetStatusBarText("リアルタイム株価取得できません " + prices[0], "");
                    }
                }
                else
                    Env.Frame.SetStatusBarText("この銘柄のリアルタイム株価は表示できません", "");
                Env.Frame.Cursor = Cursors.Default;
            }
            return CommandResult.Succeeded;
        }   // add by n
//*************** Command.cs の class Command : ICloneable  の CommandResult Exec() 内に追加 292行付近
                //---------------------------- for realtime
                case CID.UpdateRealtime:
                    return CommandExec.UpdateRealtime();
                // add by n
//*************** Command.cs の class CommandCollection : ICloneable の Init(StorageNode keys)内に追加 398行付近
            //----------------------- for realtime
            AddCommand(CID.UpdateRealtime, "表示銘柄のヤフーの株価を取得します", keys, Keys.R); //Keys.Rを好みで変更
            // add by n

//*************** ChartTitle.cs の class ChartTitle に チェックボックスを追加 63行
        // ----------------------- for realtime
        private CheckBox _checkRealtime;
        // add by n
//*************** ChartTitle.cs の class ChartTitle のpublic ChartTitle()に 190行に以下を追加
            // ----------------------- for realtime
            _checkRealtime = new CheckBox();
            _checkRealtime.Text = "Real";
            _checkRealtime.Checked = Env.IsRealtime;
            _checkRealtime.ForeColor = Color.OrangeRed;
            _checkRealtime.FlatStyle = FlatStyle.Popup;
            _checkRealtime.Appearance = Appearance.Button;//ボタン表示でなくチェックボックス表示の場合はコメントアウトしてください
            _checkRealtime.Width = _checkRealtime.Appearance == Appearance.Button ? 35 : 45;
            _checkRealtime.CheckedChanged += _checkRealtime_CheckedChanged;
            _toolTip.SetToolTip(_checkRealtime, "YahooFinanceからのリアルタイムデータを取得します");
            // add by n
            // ----------------------- for realtime
            this.Controls.Add(_checkRealtime);
            // add by n
//*************** ChartTitle.cs の class ChartTitleのOnResize(EventArgs e)イベントにコントロール位置情報を追加 235行
            // ----------------------- for realtime
            _checkRealtime.Location = new Point(x, TOP);
            x += _checkRealtime.Width;
            x += 8;
            // add by n
        }  //  <-- original code
//*************** ChartTitle.cs の class ChartTitleにイベントを追加 237行付近
        // ----------------------- for realtime        
        void _checkRealtime_CheckedChanged(object sender, EventArgs e)
        {
            //Env.Command.Exec(CID.ToggleRealtime);
            Env.IsRealtime = !Env.IsRealtime;

            if (Env.IsRealtime)
                _checkRealtime.BackColor = Color.Yellow;
            else
                _checkRealtime.BackColor = SystemColors.Control;
            Env.Command.Exec(CID.UpdateRealtime);
        }// add by n

//*************** ChartCanvas.cs の class ChartCanvas の LoadBrand(AbstractBrand br, bool preserve_date)にリアルタイム取得コマンド追加 163行
            // ----------------------- for realtime
            Env.Frame.SetStatusBarText("", "");
            if (Env.IsRealtime)
                Env.Command.Exec(Commands.CID.UpdateRealtime);
            // add by n
//*************** ChartCanvas.cs に ダブルクリックイベントのオーバーライド  520行
        //------------------- for realtime
        protected override void OnDoubleClick(EventArgs e)
        {
            base.OnDoubleClick(e);
            if (Env.IsRealtime)
            {
                Commands.CommandExec.UpdateRealtime();
            }
        }// add by n

//*************** Data.cs の class DataFarm : IDisposable に Farmにデータ追加するメソッド 97行
        // ----------------------- for realtime
        public void AddData(int[] data)
        {
            byte[] buf = new byte[RECORD_LENGTH];
            for (int j = 0; j < 8; j++)
            {
                byte[] byteArray = BitConverter.GetBytes(data[j]);
                for (int jj = 0; jj < 4; jj++)
                    buf[jj + 4 * j] = byteArray[jj];
            }

            int length = _farm.Length;
            int recCount = length / RECORD_LENGTH;
            int lastDate = BitConverter.ToInt32(_farm, (recCount - 1) * RECORD_LENGTH);
            if (data[0] > lastDate)
            {
                byte[] farm = new byte[_farm.Length];
                Array.Copy(_farm, farm, length);
                _farm = new byte[length + RECORD_LENGTH];
                Array.Copy(farm, _farm, length);
                length += RECORD_LENGTH;
            }
            for (int i = 0; i < RECORD_LENGTH; i++)
                _farm[length - RECORD_LENGTH + i] = buf[i];
            _filledLength = length / RECORD_LENGTH;
            _byteLength = length;
            int future_length = Env.CurrentIndicators.GetAddedFutureLength(ChartFormat.Daily);
            _data = new TradeData[_filledLength + future_length];
            _isEmpty = false;
        }
        // add by n
// ###### 以上です