如果用知乎,可以关注专栏:.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,下次又去请求,浪费人家的流量。也是保存在sqlite数据库。

2.3 其他附加

  在后面分析的时候,我还用到了板块信息,相当于给每支股票加一个类型,属于什么板块,这样分析的时候有针对性。板块有类型,然后每个类型下面又有一些股票代码,有2个表,数据来源也是搜狐的:

  http://q.stock.sohu.com/cn/bk.shtml

  当然如果还要做复杂和完善一点,还有很多数据要采集,比如公司的一些基本信息,我暂时没有用到,后面我会把代码开源,大家随意折腾。

3.数据库设计

数据库设计我们采用XCode开发的设计规范,都用xml文件,可以自动生成实体类,后面有时间我会针对XCode写一篇开发实践的文章,再一次带大家温习了XCode,在这里感谢@大石头,10多年码农,X组件博大精深,极大的提高了开发效率,简单,简单,简单到你有时候怀疑人生。

1.股票基础信息

<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文件表结构中,还有一个历史信息,就是一次性获取所有历史Json文本存储,避免重复抓取Json数据。结构很简单,就不贴了。

<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.板块分类和股票板块信息

<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工具找到上海和深圳列表的位置,如下图:

我们发现上海和深圳交易所的列表分别在下面位置:

/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中有现成的方法获取整个列表,并进行解析,如下面代码:

/// 获取所有股票代码和名称基础信息
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>保存下来,知道结构后,直接强制转换和取值即可。上代码:

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);
}
}

上面是获取单个股票其指定日期范围内的历史数据,直接到历史表,下面是解析部分,外面套的循环就不贴代码了,可以下载源代码看。

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找一个破解版下载玩玩。

【开源】C#.NET股票历史数据采集,【附18年历史数据和源代码】的更多相关文章

  1. C#.NET股票历史数据采集,【附18年历史数据和源代码】

    阅读目录 1.数据采集需求 2.股市数据接口 3.数据库设计 4.关键信息采集 5.源代码和数据库 如果用知乎,可以关注专栏:.NET开源项目和PowerBI社区 重点重点:我没有买股票,没有买股票, ...

  2. Python获取股票历史、实时数据与更新到数据库

    要做量化投资,数据是基础,正所谓"巧妇难为无米之炊" 在免费数据方面,各大网站的财经板块其实已提供相应的api,如新浪.雅虎.搜狐...可以通过urlopen相应格式的网址获取数据 ...

  3. SSL握手通信详解及linux下c/c++ SSL Socket代码举例(另附SSL双向认证客户端代码)

    SSL握手通信详解及linux下c/c++ SSL Socket代码举例(另附SSL双向认证客户端代码) 摘自: https://blog.csdn.net/sjin_1314/article/det ...

  4. Ajax (Asynchronous javascript xml) 搜索框核心代码(JQuery) Ajax判断用户名存在核心代码 附:原生js的Ajax代码 其中有json的一句话解释

    前端 <script type="text/javascript"> $(function(){ $("#tid").keyup(function( ...

  5. 快Key:按一下鼠标【滚轮】,帮你自动填写用户名密码,快速登录,可制作U盘随身(开源免费-附安装文件和源代码)

    * 代码以本文所附下载文件包为准,安装文件和源文件包均在本文尾部可下载. * 快Key及本文所有内容仅供交流使用,使用者责任自负,由快Key对使用者及其相关人员或组织造成的任何损失均由使用者自负,与本 ...

  6. 利用Python+pyecharts+tushare图形化展示股票历史财务信息

    在微信或其他平台上,经常能看到别人推荐股票,分析的头头是道,让自己懊恼于没有早点关注到这只股票,好像错失了几个亿.但是投资股票又忌讳听消息跟风,总不能看到别人推荐自己就无脑买入. 看到了一只股票,自己 ...

  7. Linux 下编译Android-VLC开源播放器详解(附源码下载)

    这两天需要做音视频播放相关的东西,所以重新找了目前android下的解码库.Android自带的解码库支持不全,因此很多第三方播放器都是自带解码器,绝大部分都是使用FFMpeg作为解码库.我11年的时 ...

  8. 使用python和tushare查询股票历史名称变更记录

    接口:namechange 描述:历史名称变更记录 注:tushare库下载和初始化教程,请查阅我之前的文章 输入参数 名称       |       类型       |       必选    ...

  9. 盘它!!一步到位,Tensorflow 2的实战 !!LSTM下的股票预测(附详尽代码及数据集)

    关键词:tensorflow2.LSTM.时间序列.股票预测 Tensorflow 2.0发布已经有一段时间了,各种新API的确简单易用,除了官方文档以外能够找到的学习资料也很多,但是大都没有给出实战 ...

随机推荐

  1. Android Studio 下获取debug sha1和md5

    Open Android Studio Open Your Project Click on Gradle (From Right Side Panel, you will see Gradle Ba ...

  2. java to kotlin (2) - lambda编程

    前言 Kotlin Note 是我学习kotlin整理笔记向博客,文章中的例子参考了kotlin in action这本书籍,同时我也极力推荐这本书,拥有Java基础的人可以很快的就使用kotlin来 ...

  3. 【转】Robust regression(稳健回归)

    Robust regression(稳健回归) 语法 b=robustfit(X,y) b=robustfit(X,y,wfun,tune) b=robustfit(X,y,wfun,tune,con ...

  4. 理解javascript模块化(转)

    模块化是一个通用的编程最佳实践.程序的模块化使我们可以更方便地使用别人的代码,想要什么功能,就加载什么模块,从而提高代码的利用效率,增加开发速度. 模块就像积木,有了它,我们可以搭出各种各种功能样式的 ...

  5. 强大的API测试工具Hitchhiker v0.9 基于UI的断言测试,回顾2017

    v0.9是Hitchhiker在2017农历年的最后一个版本,而起点正是刚过完2016农历年,农历2018即将到来,一年轮回,今天写点东西稍微回顾下hitchhiker的2017. 先还是说v0.9, ...

  6. java配置context.xml文件

    2018-02-08   23:32:23 修改context.xml文件 自从学习了servlet后,每次修改里面的内容后,想要访问都要重启服务器,这样感觉很麻烦的,所以今天就教大家一个方法,只需要 ...

  7. SAP字符串处理

    拼接字符串 CONCATENATE t1 t2 INTO result. "直接拼接 CONCATENATE t1 t2 INTO result SEPARATED BY space. &q ...

  8. 全部用startssl生成的证书,配置Apache使其支持SSL

    Apache的编译安装见这篇: http://www.cnblogs.com/yjken/p/3921840.html 网上查阅了一大批资料,得知自己生成的证书是会被浏览器提示“证书不安全”的,我也就 ...

  9. BZOJ 1982: [Spoj 2021]Moving Pebbles [博弈论 对称]

    给你N堆Stone,两个人玩游戏. 每次任选一堆,首先拿掉至少一个石头,然后移动任意个石子到任意堆中. 谁不能移动了,谁就输了... 以前在poj做过已经忘记了... 构造对称,选最多的一堆往其他堆分 ...

  10. BZOJ 3620: 似乎在梦中见过的样子 [KMP 暴力]

    和我签订契约,成为魔法少女吧 题意:求所有形似于A+B+A 的子串的数量 , 且len(A)>=k,len(B)>=1 位置不同其他性质相同的子串算不同子串,位置相同但拆分不同的子串算同一 ...