爬呀爬-分級基金小助手

  上回作了個簡單的博客園精華客戶端,還挺實用的,如今打算作一個複雜點的應用。額,你們都有學習理財知識嗎,我是有玩股票分級的,個人終極目標是實現機器自動買賣股票,奈何這個目標暫時仍是挺難實現的,那先簡單點吧。其實股票小助手和分級基金小助手我都有作,項目地址是https://github.com/ihambert/test,因爲股票逆大盤的太多,因此我以爲分級基金小助手更有參考價值,本文就簡紹個人分級基金小助手吧。先來個效果圖吧html

 

  那需求有哪些呢,首先,我須要把想要監控的分級基金都監控起來,監控哪些信息呢,好比當前價,最高價,最低價,漲幅等。其次,我須要把想要監控的行業和概念主力資金動向,爲毛要監控這個呢,由於你們都知道,咱散戶的力量是分散無序的,只有主力纔能有效的拉動股價,因此主力資金的動向可做爲咱們的買賣依據,好比整體來看主力都在拼命跑路了,你還在抄底,,那哭可沒人可憐,主力都在拼命買入銀行了,你還說你從不買銀行,那不是你傻嗎。最好能夠生成日誌,以下圖git

  這裏暫時有3個關鍵字,拋吸和抄,拋的意思是建議高拋一部分,吸的意思是建議低吸一部分,抄的意思是在大盤主力資金不明顯流動的狀況下,個別股票異動所帶來的抄底機會,通常狀況下大盤主力資金流出-40億的狀況下建議穩健者空倉,此時出現的買入機會只適合激進者玩以此類推,每隔10億能夠自定義一個倉位,好比流入30億我就半倉或全倉。github

  先無論怎麼耍這玩意吧,研究一下怎麼實現比較務實。老套路,首先準備基礎類:web

  日誌類多線程

public static class Log
    {
        /// <summary>
        ///     記錄異常信息
        /// </summary>
        /// <param name="msg">異常附加信息</param>
        /// <param name="e">異常</param>
        public static void Error(string msg, Exception e)
        {
            if (e == null)
            {
                Warn(msg);
            }
            else
            {
                var innerEx = e.InnerException == null
                ? string.Empty
                : $",InMessage:{e.InnerException.Message},InSource:{e.InnerException.Source},InStackTrace:{e.InnerException.StackTrace}";
                Logger(FileError, $"{msg},Message:{e.Message},Source:{e.Source},StackTrace:{e.StackTrace}{innerEx}");
            }
        }

        /// <summary>
        ///     記錄警告信息
        /// </summary>
        /// <param name="msg">警告內容</param>
        public static void Warn(string msg)
        {
            Logger(FileWarn, msg);
        }

        /// <summary>
        ///     記錄普通訊息
        /// </summary>
        /// <param name="msg">通常信息</param>
        public static void Info(string msg)
        {
            Logger(FileInfo, msg);
        }

        /// <summary>
        ///     記錄調試信息
        /// </summary>
        /// <param name="msg">調試信息</param>
        public static void Debug(string msg)
        {
            Logger(FileDebug, msg);
        }

        private static void Logger(string fileName, string msg)
        {
            //開新線程寫日誌不阻塞原線程(雖然也無需多長時間)
            Task.Factory.StartNew(() =>
            {
                msg = $"{DateTime.Now:yy-M-d H:m:s}:{msg}{Environment.NewLine}";

                if (!Directory.Exists(FileBase))
                    Directory.CreateDirectory(FileBase);

                try
                {
                    //加鎖排隊是必須的,不然快速插入日誌的狀況下會出現異常
                    lock (fileName)
                    {
                        File.AppendAllText(fileName, msg);
                    }
                }
                catch
                {
                    //發生異常通常是文件被佔用,先寫到另外一個文件吧
                    File.AppendAllText(fileName.Replace(".txt", "2.txt"), msg);
                }
            });
        }

        #region 常量

        private const string FileBase = "File/Log";
        //以一個月對一個單位,每月生成一個文件
        private static readonly string FileError = "File/Log/Error" + DateTime.Now.ToString("yyMM") + ".txt";
        private static readonly string FileWarn = "File/Log/Warn" + DateTime.Now.ToString("yyMM") + ".txt";
        private static readonly string FileInfo = "File/Log/Info" + DateTime.Now.ToString("yyMM") + ".txt";
        private static readonly string FileDebug = "File/Log/Debug" + DateTime.Now.ToString("yyMM") + ".txt";

        #endregion
    }

  這個實際上是簡易日誌類,這種小項目還不須要log4net,nlog那些,本身寫一個豈不美哉,我這個日誌類是靜態類來的,用起來很方便,但爲毛log4net,nlog那些大佬都不用靜態類呢,估計是由於他們須要多種日誌類,好比網站的日誌類,控制檯的日誌類等,須要不一樣的配置文件,讀寫路徑也是可配置的,我這裏路徑就寫死算了,反正本身用。。。用法的話很是easy。異步

Log.Debug($"主線程ID:{Thread.CurrentThread.ManagedThreadId}");學習

Log.Info(tip);優化

Log.Error("異常啦", _tasks[i].Exception);網站

Log.Warn("空數據");url

介紹完畢。

  接下來進入實盤演練。首先須要準備Stock股票類,GradingFund基金類,Industry行業概念類,GradingFundData數據類,數據類存放前面幾個實體類,用於數據服務類和窗體之間的通訊,固然,這些能夠抽離出來,也能夠用於網站的。

  那麼窗體和數據服務類之間是如何交互的呢,我是用委託事件來實現的,如上圖,主窗體new一個數據服務類,而後用一個新委託來處理更新事件取得的數據,但因爲窗體的主線程才能操做控件,因此須要用BeginInvoke來返回主線程,看Debug日誌

看線程ID,GradingFundUtil主線程ID竟然和窗體主線程是同一條線程,,難怪數據服務類不用新線程的話窗體會卡死啦,因此我在數據服務類裏面new了一個新線程,每分鐘循環獲取數據一次

固然,每分鐘獲取數據最好都是同一個線程啦,而後把那條晾起來一分鐘,看看大盤如今在交易時間不,是開盤時間則更新數據,不然繼續晾起來一分鐘。

  固然,如何快速獲取數據是一個問題,我採用HttpClient,

            //概念數據耗時大概188ms-290ms
            var taskConcept = _web.GetStringAsync(UrlConcept);
            //行業數據耗時大概60ms-188ms
            var taskIndustry = _web.GetStringAsync(UrlIndustry);
            for (var i = 0; i < _data.Stocks.Count; i += RequsetStockCount)
            {
                var url = UrlStock + string.Join(",", _data.Stocks.Skip(i).Take(RequsetStockCount).Select(o => o.Code));
                //股票數據耗時大概86ms
                _tasks.Add(_web.GetStringAsync(url));
            }
            //分級數據耗時大概82ms
            var taskGradings = _web.GetStringAsync(UrlGradingFunds);        

先一次性把URL都發出異步請求,而後先處理耗時低的請求,舉個例子,關於如何處理股票數據

            #region 處理股票實時數據

            for (var i = 0; i < _tasks.Count; i++)
            {
                if (_tasks[i].IsFaulted)
                {
                    Log.Error("金融界股票接口", _tasks[i].Exception);
                    ContinueUpdate();
                    return;
                }

                try
                {
                    html = _tasks[i].Result;
                }
                catch (Exception e)
                {
                    Log.Error("金融界股票接口異常", e);
                    ContinueUpdate();
                    return;
                }

                var hq = StringUtil.GetVal(html, "HqData:[", "]}");
                var fs = StringUtil.GetList(hq, "[", "]");

                if (fs.Count == 0)
                {
                    Log.Warn("金融界股票接口獲取空數據");
                    ContinueUpdate();
                    return;
                }

                var ii = i*RequsetStockCount;

                foreach (var f in fs)
                {
                    var a = f.Split(',');
                    var stock = _data.Stocks[ii++];
                    stock.Name = a[0].Substring(1, a[0].Length - 2);
                    stock.YesterdayPrice = Convert.ToSingle(a[1]);
                    stock.StartPrice = Convert.ToSingle(a[2]);
                    stock.MaxPrice = Convert.ToSingle(a[3]);
                    stock.MinPrice = Convert.ToSingle(a[4]);
                    stock.Price = Convert.ToSingle(a[5]);
                    stock.Tm = float.Parse((Convert.ToSingle(a[6])/10000).ToString("F1"));
                    stock.Cat = Convert.ToSingle(a[7]);
                    stock.Tr = Convert.ToSingle(a[8]);
                    stock.Ape = Convert.ToSingle(a[9]);
                    var rate = (stock.Price - stock.YesterdayPrice)*100/stock.YesterdayPrice;
                    stock.Speed = Math.Round(rate - stock.Rate, 2);
                    stock.Rate = Math.Round(rate, 2);
                    stock.Swing = Math.Round((stock.MaxPrice - stock.MinPrice)*100/stock.YesterdayPrice, 2);
                }
            }
            _tasks.Clear();
            if (_data.IsOpen)
            {
                //刪除停牌個股
                var rc = _data.Stocks.RemoveAll(o => o.MaxPrice == 0);

                if (rc > 0)
                {
                    foreach (var fund in _data.GradingFunds)
                    {
                        fund.Stocks.RemoveAll(o => o.MaxPrice == 0);
                    }
                }
            }

            #endregion        

用異步多線程獲取數據這樣的話獲取數據就快啦。

  關於本程序須要的文件

其中GradingFunds.txt中存放須要檢測的分級基金,一行一個

程序運行後會自動填充右邊的一大把信息,這些信息實際上是該分級的10大重倉股和對應的倉位。這些數據有助於做爲該分級的買賣依據。

還有個Industry.txt存放須要監測的行業概念數據,第一行存放行業數據,第二行存放概念數據,這個已經有默認數據了,通常只存放和分級相關的行業概念,本行業概念數據來源於東方財富行業資金流向圖還有東方財富概念資金流向圖,之後也可能使用同花順的數據。

日誌是自動生成的

其中Info用於記錄大盤資金流向和買賣推薦,之後能夠對着這個來看數據是否有用,做爲優化依據。Error記錄異常信息。1701表明2017年一月份。

  額,說的有點亂,你們感興趣的話仍是去個人github看吧

相關文章
相關標籤/搜索