如果用知乎,可以關注專欄:.NET開源項目和PowerBI社區
重點重點:我沒有買股票,沒有買股票,股市是個坑,小心割韭菜哦。
本文的初衷是數據分析(分析結果就不說了,就是想看看篩選點數據),只不過搞下來發現比我想象的要簡單多了。本文采集的數據是:2000年到2018年2月份,上證和深證交易所所有的上市股票交易數據,按天采集,不是小時哦,有興趣的朋友,可以稍微改造,做到實時(這和我就無關了)。
.NET開源文章目錄:本博客.NET開源項目文章目錄
本文原文地址:【開源】C#.NET股票歷史數據采集,【附18年歷史數據和源代碼】
1.數據采集需求
原始需求:想分析某些股票的歷史天交易數據里面滿足某些條件的股票。
初步分析:需要股票的基礎數據,如名稱,編碼,交易所等信息,然后就是每天的開票收盤的價格,漲幅等信息。
以為很簡單,起始搞起來越滾越大,剛開始以為2個表就夠了,沒想到搞來搞去,有6個表了。
還好,我們有強大的XCode組件,數據庫設計,開發都極其簡單,總共零零散散也就10個小時不到就完工了。主要的時間不是寫代碼,其實60%的時間都是在找資料,分析接口和想怎么設計上面,以及跑數據,還好源數據都保存下來了,重寫跑起來很快。
2.股市數據接口
很早以前,有朋友也想讓我給給他采集股票實時數據,而且用的是商業接口,由於時間匆忙,而且對股票一無所知,所以就拒絕了。
這次開工之前,心里也很忐忑,會不會很復雜,反正是自己想玩和想看,所以抱着試一試的心態,沒想到比我想的要簡單很多。
首先,我們得找到數據來源,否則一切無從談起,而且我需要的是歷史數據,對數據實時性要求不高:
此處。。。。。。。。。。。。省略1萬字,因為搜索和找了很多資源,最終用的是下面的接口,簡單,實測速度快,18年的數據不到20分鍾刷刷刷搞下來了。
2.1 股票基礎數據
股票基礎數據我用的是這個網址:http://quote.eastmoney.com/stocklist.html
里面包括了上證和深圳交易所的所有股票代碼信息,只需要直接采集即可,速度很快。
如果要做實時,每天更新一次即可,注意:我開始也沒注意,股票代碼有很多含義,除了交易所之外,還有啥創業板,基金之類的,我沒仔細研究,我只把我需要的類型進行了標記。可以在這里看看代碼的一些類型:https://baike.so.com/doc/4974613-5197406.html
股票基礎數據結構比較簡單:
編碼(唯一),名稱,交易所,類型1是要分析 ,0是暫時忽略的,上市日期
2.2 股票歷史天數據
股票歷史天數據剛開始想應該很大,找了一些結構才發現,基本每天的主要指標也就10個字段左右,計算一下,每只股票就算20年,也就6000條而已。
即使10000支股票,最多也就6000萬而已,所以剛開始的時候直接全部擼到一個表里面了,實際上后面在分析的時候,極其不合理。分析的比較很復雜,搜索非常慢,所以后來我把歷史數據進行了拆分,然后分析的時候多線程,速度瞬間提升10倍。由於XCode組件天生對分表分庫和數據庫反向工程的支持,所以開發起來非常快。
股票歷史數據找了很多,最早用的是搜狐的一個接口:http://www.cnblogs.com/ldlchina/p/5392670.html
它的格式很簡單,拼接股票代碼和起始結束日期即可,后來還發現它還能查詢指數信息:
http://q.stock.sohu.com/hisHq?code={code}&start={start}&end={end},{code}替換為股票代碼,大陸股票代碼加前綴cn_。
返回的json格式很標准,使用了Newlife組件的JsonParser類,輕松搞定。根據返回的數據信息,找了幾支股票核對一下,就知道其意義了。在后面的數據庫設計中會詳細描述。說明:采集的時候我是先用臨時表統一把返回的結果保存,防止程序有bug,下次又去請求,浪費人家的流量。也是保存在sqliet數據庫。
2.3 其他附加
在后面分析的時候,我還用到了板塊信息,相當於給每支股票加一個類型,屬於什么板塊,這樣分析的時候有針對性。板塊有類型,然后每個類型下面又有一些股票代碼,有2個表,數據來源也是搜狐的:
http://q.stock.sohu.com/cn/bk.shtml
當然如果還要做復雜和完善一點,還有很多數據要采集,比如公司的一些基本信息,我暫時沒有用到,后面我會把代碼開源,大家隨意折騰。
3.數據庫設計
數據庫設計我們采用XCode開發的設計規范,都用xml文件,可以自動生成實體類,后面有時間我會針對XCode寫一篇開發實踐的文章,再一次帶大家溫習了XCode,在這里感謝@大石頭,10多年碼農,X組件博大精深,極大的提高了開發效率,簡單,簡單,簡單到你有時候懷疑人生。
1.股票基礎信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<
Table
Name="StockBaseInfo" Description="股票基礎信息" ConnName="stock_base">
<
Columns
>
<
Column
Name="Code" DataType="String" PrimaryKey="True" Description="股票編碼" />
<
Column
Name="Name" DataType="String" Master="True" Description="名稱" />
<
Column
Name="Exchange" DataType="String" Description="交易所" />
<
Column
Name="Kind" DataType="Int32" Description="類型。1是要分析股票,0是暫時不分析" />
<
Column
Name="StartDate" DataType="DateTime" Description="上市日期" />
<
Column
Name="CreateDate" DataType="DateTime" Description="創建時間" />
</
Columns
>
<
Indexes
>
<
Index
Columns="Name" />
<
Index
Columns="StartDate" />
</
Indexes
>
</
Table
>
|
2.股票歷史日數據
其實在項目代碼的xml文件表結構中,還有一個歷史信息,就是一次性獲取所有歷史Josn文本存儲,避免重復抓取Josn數據。結構很簡單,就不貼了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<
Table
Name="StockDayData" Description="股票日數據" ConnName="stock_day">
<
Columns
>
<
Column
Name="ID" DataType="String" PrimaryKey="True" Description="編號。code+日期" />
<
Column
Name="Code" DataType="String" Description="股票編碼" />
<
Column
Name="StatDate" DataType="DateTime" Description="數據日期" />
<
Column
Name="StartPrice" DataType="Double" Description="開盤價格" />
<
Column
Name="EndPrice" DataType="Double" Description="收盤價格" />
<
Column
Name="ChangePrice" DataType="Double" Description="漲跌金額" />
<
Column
Name="ChangeRatio" DataType="Double" Description="漲跌幅度" />
<
Column
Name="LowPrice" DataType="Double" Description="最低價格" />
<
Column
Name="HighPrice" DataType="Double" Description="最高價格" />
<
Column
Name="TotalHand" DataType="Int32" Description="總手" />
<
Column
Name="TotalAmount" DataType="Double" Description="總金額(萬)" />
<
Column
Name="HandRate" DataType="Double" Description="換手率" />
<
Column
Name="UpdateDate" DataType="DateTime" Description="更新日期" />
</
Columns
>
</
Table
>
|
3.板塊分類和股票板塊信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<
Table
Name="GroupKind" Description="板塊分類" ConnName="stock_base">
<
Columns
>
<
Column
Name="ID" DataType="String" PrimaryKey="True" Description="編碼。url相關" />
<
Column
Name="Name" DataType="String" Master="True" Description="板塊名稱" />
<
Column
Name="Kind" DataType="String" Description="分類。1.行業,2地域,3.概念" />
<
Column
Name="Total" DataType="Int32" Description="總數" />
<
Column
Name="CreateDate" DataType="DateTime" Description="創建時間" />
</
Columns
>
<
Indexes
>
<
Index
Columns="Kind" />
<
Index
Columns="Name" />
</
Indexes
>
</
Table
>
<
Table
Name="StockGroup" Description="股票板塊信息" ConnName="stock_base">
<
Columns
>
<
Column
Name="ID" DataType="String" PrimaryKey="True" Description="編號。groupid+stockid" />
<
Column
Name="GroupID" DataType="String" Description="板塊ID" />
<
Column
Name="Kind" DataType="String" Description="分類。1.行業,2地域,3.概念" />
<
Column
Name="Code" DataType="String" Description="股票代碼" />
<
Column
Name="StockName" DataType="String" Description="股票名稱" />
<
Column
Name="CreateDate" DataType="DateTime" Description="創建時間" />
</
Columns
>
<
Indexes
>
<
Index
Columns="GroupID" />
<
Index
Columns="Code" />
</
Indexes
>
</
Table
>
|
4.關鍵信息采集
下面我們把數據采集過程簡單分析一下,然后把源代碼和數據庫共享給大家。套路很簡單,熟悉起來很快就可以搞定。
我的博客中,前幾年,寫過好幾篇關於C#數據采集的方法,套路都比較通用,主要是:HtmlAgilityPack + XCode
C#+HtmlAgilityPack+XPath帶你采集數據(以采集天氣數據為例子)
【開源】分享2011-2015年全國城市歷史天氣數據庫【Sqlite+C#訪問程序】
HtmlAgilityPack是.NET 下的一個強大的HTML 解析類庫,支持用 XPath 。配合上自帶的HAPExplorer,很快就可以解決問題。
具體使用可以參考上面2篇文章,下面我們也會上實際代碼。
4.1 基礎數據采集
先打開http://quote.eastmoney.com/stocklist.html右鍵,源代碼,獲取完整的HTML代碼,用HAPExplorer工具找到上海和深圳列表的位置,如下圖:
我們發現上海和深圳交易所的列表分別在下面位置:
1
2
|
/html[1]/body[1]/div[9]/div[2]/div[1]/ul[1]
/html[1]/body[1]/div[9]/div[2]/div[1]/ul[2]
|
還往下一個層級就是li標簽列表,在HtmlAgilityPack中有現成的方法獲取整個列表,並進行解析,如下面代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
/// 獲取所有股票代碼和名稱基礎信息
public
static
void
ReadAllStockBaseInfo()
{
//上海:/html[1]/body[1]/div[9]/div[2]/div[1]/ul[1]
//下級是li列表 ,Text值就是股票名稱和代碼 XXX()
//深圳:上海:/html[1]/body[1]/div[9]/div[2]/div[1]/ul[2]
string
url =
@"http://quote.eastmoney.com/stocklist.html"
;
HtmlWeb htmlweb =
new
HtmlWeb();
htmlweb.OverrideEncoding = Encoding.GetEncoding(936);
HtmlDocument doc = htmlweb.Load(url);
Dictionary<
string
,
string
> dic =
new
Dictionary<
string
,
string
>()
{
{
"上海"
,
@"/html[1]/body[1]/div[9]/div[2]/div[1]/ul[1]"
},
{
"深圳"
,
@"/html[1]/body[1]/div[9]/div[2]/div[1]/ul[2]"
}
};
#region 獲取
Dictionary<String, StockBaseInfo> list =
new
Dictionary<
string
, StockBaseInfo>();
foreach
(
var
item
in
dic)
{
//獲取所有子節點
var
res = doc.DocumentNode.SelectSingleNode(item.Value).SelectNodes(
@"li"
);
if
(res.Count > 0)
{
foreach
(
var
node
in
res)
{
//獲取名稱和代碼
var
name = node.InnerText.Trim();
if
(name.IsNullOrEmpty())
continue
;
var
str = name.Split(
'('
,
')'
);
if
(str.Length < 2)
continue
;
StockBaseInfo et =
new
StockBaseInfo()
{
Code = str[1],
Name = str[0],
Exchange = item.Key,
StartDate =
new
DateTime(2000, 1, 1),
CreateDate = DateTime.Now
};
if
(!list.ContainsKey(et.Code))
{
list.Add(et.Code,et);
}
}
}
}
list.ToValueArray().Insert(
true
);
#endregion
}
|
獲取到子節點后,解析名稱,然后用批量Insert到數據庫。
用XCode默認都是使用Sqlite數據庫,輕量級,非常方便,數據庫表結構都是自動新建。
4.2 股票歷史數據
我們使用2.2節中提到的接口,如下,配合前面采集到的所有股票基礎數據,就可以便利進行歷史數據抓取了,說一下,這個接口很給力,速度相當快。我是從2000年1月1日開始采集的,截止時間是2018年2月10日(放假閑的你懂的)。歷史json數據按股票ID單獨保存,后面寫了一個轉換程序單獨從歷史數據庫轉換即可。
http://q.stock.sohu.com/hisHq?code={code}&start={start}&end={end},{code}替換為股票代碼,大陸股票代碼加前綴cn_。
這里對老司機來說,其實沒多少難度,就是拼接URL,請求獲取json數據,然后解析json格式,我解析用了Newlife的JsonParser,用起來很簡單有空我單獨講一下,就是把Json用字典和List<Object>保存下來,知道結構后,直接強制轉換和取值即可。上代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public
static
void
GetHistoryFromWeb(
string
stockCode, DateTime start, DateTime end,
string
type=
"cn"
)
{
string
url =
@"http://q.stock.sohu.com/hisHq?code={3}_{0}&start={1}&end={2}"
.F(stockCode, start.ToString(
"yyyyMMdd"
),
end.ToString(
"yyyyMMdd"
),type);
WebClientX client =
new
WebClientX();
client.Timeout = 1000 * 120;
var
text = client.GetHtml(url);
var
doc =
new
HtmlDocument();
doc.LoadHtml(text);
var
value = doc.DocumentNode.InnerText;
var
et =
new
StockHisText()
{
Code = stockCode,
Start = start,
End = end,
HisText = value
};
try
{
if
(type ==
"zs"
) et.Code =
"{0}_{1}"
.F(
"Index"
,et.Code);
//加前綴區分
et.Insert();
}
catch
(Exception err)
{
XTrace.WriteException(err);
}
}
|
上面是獲取單個股票其指定日期范圍內的歷史數據,直接到歷史表,下面是解析部分,外面套的循環就不貼代碼了,可以下載源代碼看。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
public
static
void
PraseHistoryData()
{
var
all = FindAll();
int
index = 1;
Parallel.For(0, all.Count,
new
ParallelOptions() { MaxDegreeOfParallelism = 1 }, i =>
{
XTrace.WriteLine(
"進度:{0}/{1}"
,index ++,all.Count);
#region 單個文本解析
JsonParser jp =
new
JsonParser(all[i].HisText);
var
decode = (List<
object
>)jp.Decode();
if
(decode.Count < 1)
return
;
var
main = (Dictionary<
string
,
object
>)decode[0];
//字典
if
(main.ContainsKey(
"hq"
))
{
var
obj = (List<
object
>)main[
"hq"
];
if
(obj.Count > 0)
{
List<StockDayData> res =
new
List<StockDayData>();
foreach
(
var
item
in
obj)
{
#region 單條記錄解析
//item是一個10個元素的數組
//日期,今開價格,今天收盤價格,漲跌金額,漲跌幅度,最低價格,最高價格,總手,總金額(萬),換手率
//"2018-02-09", "31.46", "31.46", "2.86", "10.00%", "31.46", "31.46", "303", "95.32", "0.15%"
var
list = (List<
object
>)item;
StockDayData sd =
new
StockDayData()
{
Code = all[i].Code,
StatDate = list[0].ToDateTime(),
StartPrice = list[1].ToDouble(),
EndPrice = list[2].ToDouble(),
ChangePrice = list[3].ToDouble(),
ChangeRatio = ((
string
)list[4]).Replace(
"%"
,
""
).ToDouble(),
LowPrice = list[5].ToDouble(),
HighPrice = list[6].ToDouble(),
TotalHand = list[7].ToInt(),
TotalAmount = list[8].ToDouble(),
HandRate = ((
string
)list[9]).Replace(
"%"
,
""
).ToDouble(),
UpdateDate = DateTime.Now
};
sd.ID =
"{0}_{1}"
.F(sd.Code, sd.StatDate.ToString(
"yyyyMMdd"
));
#endregion
res.Add(sd);
}
res.Save(
true
);
}
}
#endregion
});
}
|
我的代碼里面有直接把歷史數據解析分庫存儲的,方法在 股票歷史文本數據.Biz.cs 文件的,PraseHistoryDataV2方法中。分庫方法非常簡單,保存之前修改一下鏈接即可。
5.源代碼和數據庫
代碼很簡單丑陋,不要吐槽,代碼如下:https://github.com/asxinyu/Stock (兄台,搞點數據不容易,點個贊或者給個Star一下吧)
基礎數據Sqlite數據庫:https://pan.baidu.com/s/1qZJIy8s,密碼:61e3
2000年到2018年歷史Json源數據:https://pan.baidu.com/s/1jIY70bG,密碼:cmpw
2000年到2018年日歷史數據Sqlite文件:https://pan.baidu.com/s/1eTxcjdC 密碼:ujbn
Sqlite很好玩,強烈推薦工具,navicat,可以去CSDN找一個破解版下載玩玩。
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!歡迎各位轉載,但是未經作者本人同意,轉載文章之后必須在文章頁面明顯位置給出作者和原文連接,否則保留追究法律責任的權利。
出處:http://www.cnblogs.com/asxinyu/p/dotnet_stock_data_design.html