全文8w字,先带大家认识量化交易和量化回测框架,最后带你手把手实现第一个股票策略。
量化交易
1定义
量化交易是指借助现代统计学和数学的方法,利用计算机技术来进行交易的证券投资方式。
量化交易从庞大的历史数据中海选能带来超额收益的多种“大概率”事件以制定策略,用数量模型验证及固化这些规律和策略,然后严格执行已固化的策略来指导投资,以求获得可以持续的、稳定且高于平均收益的超额回报。
2掌握技能
量化交易分类
分类
关于这三种分类,不用记忆它们的定义。我们课程主要的量化投资方法是市场中性的策略。
三种分类特点以及要求
趋势性交易适合一些主观交易的高手,用技术指标作为辅助工具在市场中如鱼得水的,但如果只用各种技术指标或指标组合作为核心算法构建模型,从未见过能长期盈利的。一般也会做一些量化分析操作,使用编程如python/matlab。市场中性在任何市场环境下风险更低,收益稳定性更高,资金容量更大。适合一些量化交易者,发现市场中的alpha因子赚取额外收益,例如股票与股指期货的对冲策略等。会做一些量化分析操作,使用编程如python/matlab。高频交易在极短的时间内频繁买进卖出,完成多次大量的交易,此类交易方式对硬件系统以及市场环境的要求极高,所以只有在成熟市场中的专业机构才会得到应用适合一些算法高手,使用C/C++编程语言,去进行算法交易,对软硬件条件要求比较高。
金融专业出生,对金融市场环境非深入了解基本了解金融基础、投资知识,对数据挖掘、机器学习方法擅长,挖掘股票等的价值非常擅长算法,C/C++,编写程序化的一些交易方法
金融产品以及衍生品的常用投资技术
投资策略
注:比特币不属于衍生品,通常使用的也是趋势策略,少部分使用高频策略
量化交易的优势
严格的纪律性完备的系统性完备的系统性具体表现为“三多”。首先表现在多层次,包括在大类资产配置、行业选择、精选个股三个层次上我们都有模型;其次是多角度,量化交易的核心投资思想包括宏观周期、市场结构、估值、成长、盈利质量、分析师盈利预测、市场情绪等多个角度;再者就是多数据,就是海量数据的处理。人脑处理信息的能力是有限的,当一个资本市场只有100只股票,这对定性投资基金经理是有优势的,他可以深刻分析这100家公司。但在一个很大的资本市场,比如有成千上万只股票的时候,强大的定量化交易的信息处理能力能反映它的优势,能捕捉更多的投资机会,拓展更大的投资机会。靠数学模型取胜股票实际操作过程中,运用概率分析,提高买卖成功的概率
量化交易历史
量化交易全球的发展历史
量化投资的产生1969年,爱德华·索普利用他发明的“科学股票市场系统”,成立了第一个量化投资基金。索普也被称之为量化投资的鼻祖量化投资的兴起1988年,詹姆斯·西蒙斯成立了大奖章基金,从事高频交易和多策略交易。基金成立20多年来收益率达到了年化70%左右,除去报酬后达到40%以上。西蒙斯也因此被称为"量化对冲之王"。量化交易的繁荣1991年,彼得·穆勒发明了alpha系统策略等,开始用计算机+金融数据来设计模型,构建组合
国内量化交易的发展历史
我们通过一张来对比国内国外的发展历史
2012年到2016年量化对冲策略管理的资金规模增长了20倍,管理期货策略更是增长了30倍,增长的速度是所有策略中最快的。相比美国量化基金发展历程,中国现在基本处于美国90年代至21世纪之间的阶段。
量化投资元年2010年,沪深300股指期货上市,此时的量化基金终于具备了可行的对冲工具,各种量化投资策略如alpha策略、股指期货套利策略才真正有了大展拳脚的空间,可以说2010年是中国量化投资元年。量化投资高速发展、多元化发展2013-2015年股指新政之前可以说是国内量化基金有史以来最风光的一段时期。国内量化投资机构成批涌现,国内量化投资高速发展。
量化交易项目做什么?
量化交易研究流程
量化回测框架提供完整的数据,以及回测机制进行策略评估研究,并能够实时进行模拟交易。为实盘交易提供选择。我们的研究一般在回测平台当中做
1分析结果
我们最终想要的结果就是在回测当中表现的较好的分析方法和策略。比如:
演示结果
2什么是策略
量化策略是指使用计算机作为工具,通过一套固定的逻辑来分析、判断和决策。量化策略既可以自动执行,也可以人工执行。其实策略也可以理解为,分析数据之后,决策买什么以及交易时间。
3流程包含的内容
获取数据:公司财务、新闻数据基本行情数据数据分析挖掘:传统分析方法、机器学习,数据挖掘方法数据处理,标准化,去极值,中性化分组回测,行业分布构建策略:获取历史行情,历史持仓信息,调仓记录等止盈止损单,限价单,市价单回测:股票涨跌停、停复牌处理市场冲击,交易滑点,手续费策略分析:订单分析,成交分析,持仓分析模拟交易:接入实时行情,实时获取成交回报实时监控,实时归因分析实盘交易:接入真实券商账户
量化开发和研究岗位要求
基于交易市场数据,研究、开发交易策略,进行基础建模负责对交易策略进行回测、跟踪、分析、优化定期对交易策略的运行结果进行总结,给出分析报告,评估市场适用度负责数据挖掘、处理,数据统计分析,从数据中发现规律,为量化分析提供支持,开发量化模型策略与基金经理合作跟踪优化股票市场量化策略在实盘的表现
基础回测框架
Zipline本身只支持美国的证券,无法更好的使用数据,本地运行速度慢
云端的框架
提供部分满足需求的数据策略运行在远端服务器
这些线上平台提供了本地专业版,但是需要收费
不去实现一个回测框架的原因
没有完整的股票行情和基本面数据回测平台是载体,重点在于快速验证策略证券投资机构各自使用回测框架不同,没有通用的框架
注册账号
策略创建运行流程
体验创建策略、运行策略流程
1创建策略
2策略界面
1一个完整的策略需要做的事情
选择策略的运行信息:选择运行区间和初始资金选择回测频率选择股票池编写策略的逻辑获取股票行情、基本面数据选择哪些股票、以及交易时间分析结果策略指标分析
3策略主体运行流程分析
在init方法中实现策略初始化逻辑策略的股票池:在那些股票中进行交易判断可以选择在before_trading进行一些每日开盘之前的操作,比如获取历史行情做一些数据预处理,获取当前账户资金等。在handle_bar方法中实现策略具体逻辑,包括交易信号的产生、订单的创建。handle_bar内的逻辑会在每次bar数据更新的时候被触发。
调用的顺序为:
4策略结果分析
回测完成后,在"回测结果"页面会展示回测的仓位、盈亏、交易、风险等信息
数据获取接口
数据接口种类
获取指定行业、板块股票列表history_bars-指定股票合约历史数据get_fundamentals-查询财务数据
获取行业、板块以及概念股票列表
1关于股票代码以及代码补齐
RiceQuant上的股票代码标记
股票自动搜索及补全
Windows用户:输入ctrl+iMac用户:输入command+iLinux用户:输入ctrl+i
当您输入了这个组合键之后,Ricequant在线IDE就会进入股票代码搜索和自动完成模式,接着您可以输入任何一种进行搜索和自动补全:
股票数字代码-自动补全为股票数字代码,比如"00002XSHE":股票中文全称-自动补全为股票中文全称,比如"招商地产"股票拼音缩写-这里比较特殊,自动补全为股票中文全称,因为股票拼音缩写并不是独一无二的,比如ZSDC补全为"招商地产"
2获取行业
industry-行业股票列表
industry(code)
获得属于某一行业的所有股票列表。
参数 | 类型 | 注释 |
---|---|---|
code | str OR industry_code item | 行业名称或行业代码。例如,农业可填写industry_code.A01 或 "A01" |
获得属于某一行业的所有股票的order_book_idlist。
def init(context):
stock_list = industry("A01")
logger.info("农业股票列表:" + str(stock_list))
3获取板块
sector-板块股票列表
sector(code)
获得属于某一板块的所有股票列表。
参数 | 类型 | 注释 |
---|---|---|
code | str OR sector_code items | 板块名称或板块代码。例如,能源板块可填写"Energy"、"能源"或sector_code.Energy |
属于该板块的股票order_book_id或order_book_idlist.
def init(context):
ids1 = sector("consumer discretionary")
ids2 = sector("非必需消费品")
ids3 = sector("ConsumerDiscretionary")
assert ids1 == ids2 and ids1 == ids3
logger.info(ids1)
板块代码 | 中文板块名称 | 英文板块名称 |
---|---|---|
Energy | 能源 | energy |
Materials | 原材料 | materials |
ConsumerDiscretionary | 非必需消费品 | consumer discretionary |
ConsumerStaples | 必需消费品 | consumer staples |
HealthCare | 医疗保健 | health care |
Financials | 金融 | financials |
InformationTechnology | 信息技术 | information technology |
TelecommunicationServices | 电信服务 | telecommunication services |
Utilities | 公共服务 | utilities |
Industrials | 工业 | industrials |
4获取概念
5获取指数成分股
index_components-指数成分股
index_components(order_book_id, date=None)
获取某一指数的股票构成列表,也支持指数的历史构成查询。
参数 | 类型 | 说明 |
---|---|---|
order_book_id | str | 指数代码,可传入order_book_id |
date | str, date, datetime, pandas Timestamp | 查询日期,默认为策略当前日期。如指定,则应保证该日期不晚于策略当前日期 |
构成该指数股票的order_book_idlist
常见的指数获取代码为
6自定义股票池,提供给handle_bar使用
我们可以通过context的参数,相当于提供一个全局变量来获取
def init(context):
# 在context中保存全局变量
context.s1 = "000001.XSHE"
# context.s2 = "601390.XSHG"
# 获取行业
# context.stock_list = industry("C39")
# 获取指数成分股
context.hs300 = index_components("000300.XSHG")
def before_trading(context):
logger.info(context.hs300)
logger.info("before_trading")
获取股票合约数据
1history_bars-某一合约历史数据
history_bars(order_book_id, bar_count, frequency, fields=None, skip_suspended=True, include_now=False)
获取指定合约的历史行情,同时支持日以及分钟历史数据。不能在init中调用。
参数 | 类型 | 注释 |
---|---|---|
order_book_id | str | 合约代码,必填项 |
bar_count | int | 获取的历史数据数量,必填项 |
frequency | str | 获取数据什么样的频率进行。"1d"或"1m"分别表示每日和每分钟,必填项。您可以指定不同的分钟频率,例如"5m"代表5分钟线 |
fields | strOR str list | 返回数据字段。必填项。见下方列表 |
skip_suspended | bool | 是否跳过停牌,默认True,跳过停牌 |
include_now | bool | 是否包括不完整的bar数据。默认为False,不包括。举例来说,在09:39的时候获取上一个5分钟线,默认将获取到09:31~09:35合成的5分钟线。如果设置为True,则将获取到09:36~09:39之间合成的"不完整"5分钟线 |
ndarray,方便直接与talib等计算库对接,效率较history返回的DataFrame更高。
获取的字段内容如下
fields | 字段名 |
---|---|
datetime | 时间戳 |
open | 开盘价 |
high | 最高价 |
low | 最低价 |
close | 收盘价 |
volume | 成交量 |
total_turnover | 成交额 |
datetime | int类型时间戳 |
open_interest | 持仓量(期货专用) |
basis_spread | 期现差(股指期货专用) |
settlement | 结算价(期货日线专用) |
prev_settlement | 结算价(期货日线专用) |
2代码以及注意的问题
因为撮合逻辑是当前bar收盘或者下一个bar开盘,所以history_bars可以获取到包含当前bar及之前所有的bar数据获取当天的数据获取前十天的数据获取每天的每分钟分钟的数据?获取每分钟之前的几分钟数据?
# 如果想在今天运行,获取从几天开始前几天一些数据
# 获取前5天的收盘价,开盘价
# 股票代号,间隔,频率,交易指标
data = history_bars(context.s1, 5, "1d", "close")
# 获取多个指标
data = history_bars(context.s1, 5, "1d", ["close", "open"])
# 如果回测是每日的,不支持获取分钟数据
data = history_bars(context.s1, 5, "1m", ["close", "open"])
问题:这里的频率跟回测的频率区别?
3其它-通过bar_dict获取
获取合约当前价格的bar_dict,
Bar对象
属性 | 类型 | 注释 |
---|---|---|
order_book_id | str | 合约代码 |
symbol | str | 合约简称 |
datetime | datetime.datetime | 时间戳 |
open | float | 开盘价 |
close | float | 收盘价 |
high | float | 最高价 |
low | float | 最低价 |
volume | float | 成交量 |
total_turnover | float | 成交额 |
prev_close | float | 昨日收盘价 |
limit_up | float | 涨停价 |
limit_down | float | 跌停价 |
isnan | bool | 当前bar数据是否有行情。例如,获取已经到期的合约数据,isnan此时为True |
suspended | bool | 是否全天停牌 |
prev_settlement | float | 昨结算(期货日线数据专用) |
settlement | float | 结算(期货日线数据专用) |
注意,在股票策略中bar对象可以拿到所有股票合约的bar信息
# 只能获取当前的交易信息
logger.info(bar_dict[context.s1].close)
注:只能获取当前运行日期的,不能获取之前日期
获取财务数据
1get_fundamentals-查询财务数据
get_fundamentals(query, entry_date=None, interval="1d", report_quarter=False)
注意这里的数据指标类别虽然有400多种,但是RQ平台的这些指标数据质量不高,很多指标没有经过运算处理成需要的指标,跟我们在讲金融数据处理的时候列出来的那些财务指标差别比较大。
pandasDataPanel如果查询结果为空,返回空pandasDataFrame如果给定间隔为1d,1m,1q,1y,返回pandasDataFrame
2如何获取指标-query查询
通过fundamentals获取以上的属性
q = query(fundamentals.eod_derivative_indicator.pe_ratio)
3过滤指标条件
query.filter:过滤大小query.order_by:排序query.limit:限制数量fundamentals.stockcode.in_:在指定的股票池当中过滤
# 增加条件过滤掉不符合的股票代码
# 默认直接获取A股是所有的股票这个指标数据
# order_by默认是升序
# limit:选择固定数量的股票,获取20个股票交易
q = query(fundamentals.eod_derivative_indicator.pe_ratio,
fundamentals.eod_derivative_indicator.pcf_ratio).filter(
fundamentals.eod_derivative_indicator.pe_ratio > 20,
fundamentals.eod_derivative_indicator.pcf_ratio > 15,
).order_by(
fundamentals.eod_derivative_indicator.pe_ratio
).limit(20)
# 想要从沪深300指数的一些股票去进行筛选
# 通过fundamentals.stockcode去限定股票池
q = query(fundamentals.eod_derivative_indicator.pe_ratio,
fundamentals.eod_derivative_indicator.pcf_ratio).filter(
fundamentals.eod_derivative_indicator.pe_ratio > 20,
).order_by(
fundamentals.eod_derivative_indicator.pe_ratio
).filter(
fundamentals.stockcode.in_(context.hs300)
).limit(20)
# 获取财务数据,默认获取的是dataframe,entry_date在回测当中不去要提供
fund = get_fundamentals(q)
# 注释:每个表都有一个stockcode在用来方便通过股票代码来过滤掉查询的数据
问题:一般选择一些满足财务数据的股票时间并不是每天去获取,而是间隔一周、一个月去获取一次?怎么取获取呢?
scheduler定时器定时数据获取
scheduler.run_daily-每天运行scheduler.run_weekly-每周运行scheduler.run_monthly-每月运行
scheduler.run_daily(function)
每日运行一次指定的函数,只能在init内使用。
注意,schedule一定在其对应时间点的handle_bar之前执行,如果定时运行函数运行时间较长,则中间的handle_bar事件将会被略过。
参数 | 类型 | 注释 |
---|---|---|
function | function | 使传入的function每日运行。注意,function函数一定要包含(并且只能包含)context, bar_dict两个输入参数 |
scheduler.run_monthly(function,tradingday=t)
每月运行一次指定的函数,只能在init内使用。
注意:
tradingday的负数表示倒数。tradingday表示交易日,如某月只有三个交易日,则此月的tradingday=3与tradingday=-1表示同
参数 | 类型 | 注释 |
---|---|---|
function | function | 使传入的function每日交易开始前运行。注意,function函数一定要包含(并且只能包含)context, bar_dict两个输入参数 |
tradingday | int | 范围为[-23,1], [1,23] ,例如,1代表每月第一个交易日,-1代表每月倒数第一个交易日,用户必须指定 |
2添加定时器之后的策略运行顺序
比如我们添加了这样一段代码:
def init(context):
# 定义一个每天运行一个定时器
scheduler.run_daily(get_data)
# 每个一个月去获取财务数据,每隔一周去获取财务数据
scheduler.run_monthly(get_data, tradingday=1)
3代码
def get_data(context, bar_dict):
# logger.info("-------")
# 进行每月的第一天去调整要买卖的股票
q = query(fundamentals.eod_derivative_indicator.pe_ratio,
fundamentals.eod_derivative_indicator.pcf_ratio).filter(
fundamentals.eod_derivative_indicator.pe_ratio > 20,
).order_by(
fundamentals.eod_derivative_indicator.pe_ratio
).filter(
fundamentals.stockcode.in_(context.hs300)
).limit(20)
# 获取财务数据
data = get_fundamentals(q)
logger.info("这个月更新的股票池")
logger.info(data.T)
回测交易接口
用于股票的交易函数
order_shares-指定股数交易order_lots-指定手数交易order_value-指定价值交易order_percent-一定比例下单order_target_value-目标价值下单
1交易函数API
1order_shares-指定股数交易
order_shares(id_or_ins, amount, style=MarketOrder())
落指定股数的买/卖单,最常见的落单方式之如有需要落单类型当做一个参量传入,如果忽略掉落单类型,那么默认是市价单。
参数 | 类型 | 注释 |
---|---|---|
id_or_ins | str或instrument对象 | order_book_id或symbol或instrument对象,用户必须指定 |
amount | float-required | 需要落单的股数。正数代表买入,负数代表卖出。将会根据一手xx股来向下调整到一手的倍数,比如中国A股就是调整成100股的倍数。 |
style | OrderType | 订单类型,默认是市价单。目前支持的订单类型有:style=MarketOrder() and style=LimitOrder(limit_price) |
Order对象
购买Buy2000股的平安银行股票,并以市价单发送:
order_shares("000001.XSHE", 2000)
卖出2000股的平安银行股票,并以市价单发送:
order_shares("000001.XSHE", -2000)
购买1000股的平安银行股票,并以限价单发送,价格为¥
order_shares("000001.XSHE", 1000, style=LimitOrder(10))
2order_target_value-目标价值下单
order_target_value(id_or_ins, cash_amount, style=OrderType)
买入/卖出并且自动调整该证券的仓位到一个目标价值。如果还没有任何该证券的仓位,那么会买入全部目标价值的证券。如果已经有了该证券的仓位,则会买入/卖出调整该证券的现在仓位和目标仓位的价值差值的数目的证券。需要注意,如果资金不足,该API将不会创建发送订单。
参数 | 类型 | 注释 |
---|---|---|
id_or_ins | str或instrument对象 | order_book_id或symbol或instrument object,用户必须指定 |
cash_amount | float-required | 最终的该证券的仓位目标价值 |
style | OrderType | 订单类型,默认是市价单。目前支持的订单类型有:style=MarketOrder()style=LimitOrder(limit_price) |
Order对象
如果现在的投资组合中持有价值¥3000的平安银行股票的仓位并且设置其目标价值为¥10000,以下代码范例会发送价值¥7000的平安银行的买单到市场。:
order_target_value("000001.XSHE", 10000)
3order_target_percent-目标比例下单
order_target_percent(id_or_ins, percent, style=OrderType)
买入/卖出证券以自动调整该证券的仓位到占有一个指定的投资组合的目标百分比。
其实我们需要计算一个position_to_adjust
position_to_adjust = target_position - current_position
投资组合价值等于所有已有仓位的价值和剩余现金的总和。买/卖单会被下舍入一手股数的倍数。目标百分比应该是一个小数,并且最大值应该<=比如0.5表示50%。
如果position_to_adjust计算之后是正的,那么会买入该证券,否则会卖出该证券。需要注意,如果资金不足,该API将不会创建发送订单。
参数 | 类型 | 注释 |
---|---|---|
id_or_ins | str或instrument对象 | order_book_id或symbol或instrument object,用户必须指定 |
percent | float-required | 仓位最终所占投资组合总价值的目标百分比。 |
style | OrderType | 订单类型,默认是市价单。目前支持的订单类型有:style=MarketOrder()style=LimitOrder(limit_price) |
order对象
如果投资组合中已经有了平安银行股票的仓位,并且占据目前投资组合的10%的价值,那么以下代码会买入平安银行股票最终使其占据投资组合价值的15%:
order_target_percent("000001.XSHE", 0.15)
2交易注意事项
出现以下情况,我们的交易会被回测平台自动拒单
portfolio内可用资金不足
下单数量不足一手(股票为100股)
下单价格超过当日涨跌停板限制
当前可卖(可平)仓位不足
股票当日停牌
合约已经退市(到期)或尚未上市
3何为市价单和限价单
撮合机制
我们加入了允许用户自定义撮合机制的功能。您可以在策略编辑页面"更多"选项下选择不同的撮合机制。目前提供的撮合方式有以下两种:
市价单与限价单
成交数量都不超过当前bar成交量的25%。某一分钟成交量10000股,那么回测的时候我们做限制成交不能超过2500股。一旦超过,市价单会在部分成交之后被自动撤单;限价单会一直在订单队列中等待下一个bar数据撮合成交,直到当日收盘。当日收盘后,所有未成交限价单都将被系统自动撤单。
拓展:需要注意,在当前的分钟回测撮合模式下,用户在回测中无法通过在scheduler调用的函数中一次性实现卖出->资金释放->买入这种先卖后买的逻辑的。因为在分钟回测中,卖出并不能立刻成交。
分钟回测及实盘模拟:在一个handle_bar内下单,在该handle_bar结束时统一撮合成交。日回测:在一个handelbar内下单,下单时立刻撮合成交。举例来说,策略A设置每周一开盘进行调仓操作,先卖后买。以下这种方式在分钟回测中无法实现卖出资金立刻释放的:#scheduler调用的函数需要包括context,bar_dict两个参数defrebalance:order_sharesorder_sharesdefinit:scheduler.run_weekly
为了更好模拟实际交易中订单对市场的冲击,我们引入滑点的设置。您可以在策略编辑页面"更多"选项下进行滑点设置,允许设置的范围是[0,。该设置将在一定程度上使最后的成交价"恶化",也就是买得更贵,卖得更便宜。我们的滑点方式是按照最后成交价的一定比例进行恶化。例如,设置滑点为0.那么如果原本买入交易的成交价为10元,则设置之后成交价将变成11元,即买得更贵。
注:真是交易不需要
4交易的费用
投资组合
我们看一张
1定义
投资组合是由投资人或金融机构所持有的股票、债券、金融衍生产品等组成的集合,目的是分散风险。
2如何查看投资组合的信息
还记得我们之前提到的一个叫context的参数吗,这个参数当中就包含了投资组合的信息
context属性
now-当前时间
context.now
使用以上的方式就可以在handle_bar中拿到当前的bar的时间,比如daybar的话就是那天的时间,minutebar的话就是这一分钟的时间点。
返回数据类型为datetime.datetime
portfolio-投资组合信息
context.portfolio
该投资组合在单一股票或期货策略中分别为股票投资组合和期货投资组合。在股票+期货的混合策略中代表汇总之后的总投资组合。
stock_account-股票资金账户信息
context.stock_account
获取股票资金账户信息。
属性 | 类型 | 注释 |
---|---|---|
cash | float | 可用资金,为子账户可用资金的加总 |
frozen_cash | float | 冻结资金,为子账户冻结资金加总 |
total_returns | float | 投资组合至今的累积收益率 |
daily_returns | float | 投资组合每日收益率 |
daily_pnl | float | 当日盈亏,子账户当日盈亏的加总 |
market_value | float | 投资组合当前的市场价值,为子账户市场价值的加总 |
total_value | float | 总权益,为子账户总权益加总 |
units | float | 份额。在没有出入金的情况下,策略的初始资金 |
unit_net_value | float | 单位净值 |
static_unit_net_value | float | 静态单位权益 |
transaction_cost | float | 当日费用 |
pnl | float | 当前投资组合的累计盈亏 |
start_date | datetime.datetime | 策略投资组合的回测/实时模拟交易的开始日期 |
annualized_returns | float | 投资组合的年化收益率 |
positions | dict | 一个包含所有仓位的字典,以order_book_id作为键,position对象作为值,关于position的更多的信息可以在下面的部分找到。 |
Position对象
position就代表着当前我们的仓位中有哪些股票正持有,position.keys可以获取
股票position对象
属性 | 类型 | 注释 |
---|---|---|
order_book_id | str | 合约代码 |
quantity | int | 当前持仓股数 |
pnl | float | 持仓累计盈亏 |
sellable | int | 该仓位可卖出股数。T+1的市场中sellable = 所有持仓-今日买入的仓位 |
market_value | float | 获得该持仓的实时市场价值 |
value_percent | float | 获得该持仓的实时市场价值在总投资组合价值中所占比例,取值范围[0, 1] |
avg_price | float | 平均建仓成本 |
3代码
# 查看我们的投资组合信息,仓位、资金
# 查看股票账户信息
logger.info("股票账户信息:")
logger.info(context.stock_account)
# 卖出股票就要从持有的这些股票当中去选择
logger.info(context.portfolio.positions)
# 交易的价格计算
# 当日的:close * 股数
logger.info("投资组合的资金:%f" % context.portfolio.cash)
logger.info("投资组合的市场价值:%f" % context.portfolio.market_value)
logger.info("投资组合的总价值:%f" % context.portfolio.total_value)
4查看交易情况界面
策略评价指标
策略评价指标
收益指标风险指标
收益指标
1回测收益率
策略在期限内的收益率。
2年化收益率
采用了复利累积以及Actual/365Fixed的年化方式计算得到的年化收益。
我们更加注重年化收益率,对于股票来讲,年化达到15~30%已经算是比较好的策略。年化收益率越高越好
3基准收益率
相同条件下,一个简单的买入并持有基准合约策略的收益率。
基准收益率拿来对比我们的策略收益率,策略的表现期望超过基准收益率才获得了比较好的收益
风险指标
风险指标指的是在获得收益的时候,承担一些风险值
1最大回撤
最大回撤越小越好,最大回撤最好保持10~30%之间
单位风险收益指标
1夏普比率
举例而言,假如国债的回报是4%,而您的投资组合预期回报是16%,您的投资组合的标准偏差是5%,那么用16%-4%,可以得出12%,再用12%÷5%=代表投资者风险每增长1%,换来的是4%的多余收益。夏普比率越大,说明单位风险所获得的风险回报越高。最终夏普比率越高越好,达到5以上已经是很好的结果
实现第一个股票策略
不管是技术分析还是基本面分析,我们在进行投资的时候都会选择某些表现较好的股票来作为一个股票池,从中进行交易的判断或者直接购买。
需求
选股:获得市盈率大于50且小于6营业总收入前10的股票调仓:每日调仓,将所有资金平摊到这10个股票的购买策略,卖出一次性卖出所有不符合条件的
代码
# 可以自己import我们平台支持的第三方python模块,比如pandas、numpy等。
# 每日选股:获得市盈率大于50且小于65,营业总收入前10的股票
# 买卖:买入每天选出来的10只,卖出不符合条件
# 调仓按照月调仓,投资对象HS300
# 在这个方法中编写任何的初始化逻辑。context对象将会在你的算法策略的任何方法之间做传递。
def init(context):
# 在context中保存全局变量
# context.s1 = "000001.XSHE"
# 实时打印日志
# logger.info("RunInfo: {}".format(context.run_info))
# 定义一个选股的范围
context.hs300 = index_components("000300.XSHG")
scheduler.run_monthly(get_data, tradingday=1)
def get_data(context, bar_dict):
# 删掉两个条件
# .filter(
# fundamentals.eod_derivative_indicator.pe_ratio > 50
# ).filter(
# fundamentals.eod_derivative_indicator.pe_ratio < 65
# )
# 选股
q = query(fundamentals.eod_derivative_indicator.pe_ratio,
fundamentals.income_statement.revenue
).order_by(
fundamentals.income_statement.revenue.desc()
).filter(
fundamentals.stockcode.in_(context.hs300)
).limit(10)
fund = get_fundamentals(q)
# 行列内容以及索引一起进行转置
# print(fund.T)
context.stock_list = fund.T.index
# before_trading此函数会在每天策略交易开始前被调用,当天只会被调用一次
def before_trading(context):
pass
# 你选择的证券的数据更新将会触发此段逻辑,例如日或分钟历史数据切片或者是实时数据切片更新
def handle_bar(context, bar_dict):
# 开始编写你的主要的算法逻辑
# bar_dict[order_book_id] 可以拿到某个证券的bar信息
# context.portfolio 可以拿到现在的投资组合信息
# 使用order_shares(id_or_ins, amount)方法进行落单
# TODO: 开始编写你的算法吧!
# order_shares(context.s1, 1000)
# 在这里才能进行交易
# 先判断仓位是否有股票,如果有,卖出(判断在不在新的股票池当中)
if len(context.portfolio.positions.keys()) != 0:
for stock in context.portfolio.positions.keys():
# 如果旧的持有的股票不在新的股票池当中,卖出
if stock not in context.stock_list:
order_target_percent(stock, 0)
# 买入最新的每日更新的股票池当中的股票
# 等比例资金买入,投资组合总价值的百分比平分10份
weight = 1.0 / len(context.stock_list)
for stock in context.stock_list:
order_target_percent(stock, weight)
# after_trading函数会在每天交易结束后被调用,当天只会被调用一次
def after_trading(context):
pass
文章为作者独立观点,不代表 股票程序化软件自动交易接口观点