七天打造一套量化交易系统:Day1-数据分类、获取、清洗与存储
数据是量化交易的基础,重要性不言而喻。无论是股票、期货、期权、基金、ETF等等,甚至包括比特币,这些投资标的历史行情数据都可以用作回测分析,本篇将分享如何选择可靠的数据源,如何进行数据获取,如何清洗常见的错误数据,如何进行有效地存储,方便后期使用。
本系列搭建的量化交易系统主要针对的投资标的是股票和期货,其他的投资标的后边有机会我们再进行分享。那么接下来的内容都是围绕这两个投资标的进行展开。
数据的分类
行情数据是量化中使用最多的数据,从价、量的变化中寻找规律,我们拿到的一般是交易所下发的切片数据,国内股票行情3秒1次,期货行情一般是1秒2次。对一段时间内的切片数据进行汇总形成k线图(开、高、低、收、成交量等),再加入一些算法就形成了各种技术指标(MA、MACD、BOLL、KDJ等)。
针对股票还需要关注的数据有:年报季报、龙虎榜单、资金流向等数据,这些数据可以用在后面的多因子选股模型中。
针对大宗商品期货需要关注的数据有:仓单数据、现货报价、席位持仓、席位盈亏,对龙虎榜单上的数据进行分析也很有意思,能够发现很多正向指标和反向指标。
数据的获取
获取这些数据,方式也很多,下面分享几个中常用的方式:
1、通达信接口
from mootdx.reader import Reader
# 读取通达信行历史情数据
# market: 参数 `std` 为标准市场(就是股票), `ext` 为扩展市场(期货,黄金等)
# tdxdir: 是通达信的数据目录, 根据自己的情况修改
reader = Reader.factory(market='std', tdxdir='D:/software/new_tdx')
# 读取日线数据
r = reader.daily(symbol='sh000001')
# 读取分钟数据
r = reader.minute(symbol='sh000001')
# 获取通达信中的实时行情
from mootdx.quotes import Quotes
# 标准市场
client = Quotes.factory(market='std', multithread=True, heartbeat=True, bestip=True, timeout=15)
# k 线数据
r = client.bars(symbol='000001', frequency=0, offset=10)
2、聚宽接口
上述代码用于获取股票的财务数据,字段的含义以及其他数据的获取方式可以参考聚宽的接口文档。
3、期货CTP接口
def OnRtnDepthMarketData(self, pDepthMarketData: 'CThostFtdcDepthMarketDataField') -> "void":
log.info(api_struct_serialize(pDepthMarketData))
具体代码参考之前的文章 CTP-API开发系列之九:行情登录及订阅代码
4、爬虫脚本
如果部分数据没有标准的接口可以直接使用,就需要我们编写爬虫脚本,定时拉取。比如龙虎榜数据,可以从交易所官网、东方财富等网站拉取。
之前写过一篇爬虫的文章,可以参考一下。期货仓单数据获取
数据的清洗
对数据的清洗是一项细活,需要不断积累完善,一般需要多个数据源进行校准,保证数据的准确性,对空值、极大值的处理,时间序列对齐等等。
数据的存储
在我们的量化系统中,MarketDataEngine 行情管理引擎的作用就是,通过 ExchangeMarketDataListener 接收不同交易接口的行情 ,在onMarketData 函数中进行统一处理,并实时转发给所有需要用到的模块中(MarketDataReceivers)。同时,存储在内存中的行情数据也可以进行落地存储。
class MarketDataEngine
{
private:
class ExchangeMarketDataListener : public MarketDataListener
{
private:
Exchange* const exchange;
MarketDataDispatcher* const dispatcher;
public:
ExchangeMarketDataListener(Exchange* ex, MarketDataDispatcher* disp)
: exchange(ex), dispatcher(disp)
{}
void onMarketData(MarketData& data);
};
TradeEngine* const tradeEngine;
const char* const tradeEngineHomeDir;
std::unique_ptr<MarketDataDispatcher> dispatcher;
std::set<std::shared_ptr<ExchangeMarketDataListener> > listeners;
typedef std::set<std::shared_ptr<MarketDataReceiver> > MarketDataReceivers_t;
MarketDataReceivers_t receivers;
std::shared_ptr<MarketDataReceiver> createMarketDatareceiver(const char* exchangeID, const char* brokerID, const char* userID,
const char* password, const std::vector<std::string>& uris, const char* path, const char* channel);
void init();
public:
MarketDataEngine(TradeEngine* te, const char* homeDir)
: tradeEngine(te), tradeEngineHomeDir(homeDir)
{
init();
}
~MarketDataEngine()
{
receivers.clear();
}
};
数据存储的目的是方便后期的使用,主要是用于回测、数据模型的训练。在后面的章节中,我们再继续使用这些数据。