当前位置: 首页 > news >正文

SPL 量化 回测

回测是一种评估交易策略的通用方法。它通过计算策略在历史数据上的表现来评估交易策略的可行性。如果回测结果良好,交易者和分析师可能会有信心在未来继续使用该策略。

1. 回测脚本

首先要编写回测脚本,将回测脚本保存为 backtest.splx。

脚本代码如下:

ABCD
1func bfee@m(amt)=max(amt*0.0003,5)+amt*0.00001
2func sfee@m(amt)=bfee(amt)+amt*0.0005
3func withdraw(R)=pma=R.pselect@a(~>~[-1]&&~>=~[1]),ma=R(pma),mi=pma.((idx=~,R.to(idx,).min())),((ma--mi)//(ma++1)).max()
4
5func Begin()>env@j(_TDS,[])>env@j(_TD, create( id, code, tdate, share, price, amt, lnk) )
6func Buy(K, S, P)=abs(_TD.pselect@bs( code-K.code) )=ifn(P,K.close)
7=_TDS.insert@n(B6, _TD.insert@n( 0, 0, K.code, K.tdate, S, C6, null, null) )
8if C6<K.lowreturn 0
9=B7.run(amt=price*share, amt += bfee(amt) )return B7.id=_TD.len()
10func Sell(K, I, P)=if(I, [_TD.m(I)], Holds(K.code, K.tdate))
11for B10if !B11 || B11.id<=0 || B11.lnk || K.code!=B11.codenext
12=abs(_TDS.pselect@bs(code-K.code) )=ifn(P,K.close)
13=_TDS.insert@n(C12, _TD.insert@n(0, -B11.id, K.code, K.tdate, B11.share, D12, null, null) )
14if D12>K.highreturn 0
15=B11.lnk=C13,C13.lnk=B11=C13.run(amt=price*share,amt-=sfee(amt))
16return B10.id
17func Holds(C, D)=if(!C,_TDS, _TDS.select@b( code-C) ).select( id > 0 && !lnk && tdate<D)
18func Sells(K, U, L, ND)=Holds(if( ifr(K), K.code ), K.tdate)=K.tdate-ND
19=B18.select((U>=0 && K.close>=price*(1+U)) || (L>=0 && K.close<=price*(1-L)) || (ND>0 && tdate<=C18) )
20if ifr(K)>B19.run(Sell(K, id, null) )
21elsefor B19.group(code)=K.select@b1(code-C21.code)
22>C21.run(Sell(D21, id, null) )
23func Check(KD,D)=ifn(D,_TD.max(tdate))=create(股票 _ 数量, ${[D28:D41].concat@c()})=[]
24for _TD.select(tdate<=B23).group(code)=B24.sort(tdate,-id)
25=KD.select@b(code-B24.code).select(tdate>=C24.tdate && tdate<=B23)
26=C24.groups(tdate;sum(sign(id)*share):share, sum(amt*sign(id)):amt)
27=join@1m(C25, tdate; C26, tdate).new(#1.tdate, cum(#2.share*#1.close-#2.amt,0):income)
28=C24.sum(if( id<0 && lnk, amt-lnk.amt) )现金收益
29=C24.sum(if( id>0 && !lnk, share) )* C25.m(-1).close持仓价值
30=C29-C24.sum(if(id>0 && !lnk, amt) )持仓收益
31=C24.max(iterate( ~~+sign(id)*amt, 0 ))占用资金
32=(C28+C30)/C31收益率
33=sqrt(var@s( x=C27.(income/C31) ) )波动率
34=withdraw(x)回撤率
35=C24.count(id>=0)买盘数
36=C24.count(id>0)买入数
37=C24.count(id<0)卖盘数
38=C24.count(id<0 && lnk && amt>=lnk.amt)赢利数
39=C24.count(id<0 && lnk && amt<lnk.amt)亏损数
40=C24.sum(if(id>0, amt) )买入资金
41=C24.sum(if(id<0, amt) )卖出资金
42>C23.record(B24.code | [C28:C41])>D23|=C27
43=C23.record@in(long(C23.count()) | to(2,C23.fno()).(C23.field(~).sum()),1)
44=D23.groups(tdate,sum(income):income).(income/B43.占用资金 )
45=B43.run( 收益率 =(现金收益 + 持仓收益)/ 占用资金, 波动率 =sqrt(var@s(B44)), 回撤率 =withdraw(B44))
46return C23
47func Trades(C)=_TD.select(amt && (!C || C==code || (ifa(C) && C.contain(code) )))
48=B47.groups(code, tdate, sign(id):flag, price; int(sum(share)):share, sum(amt):amt )
49func Display(R)=create(项目, 值 ).record( R.fno().conj(R.fname(~) | [R.field(~)] ) )
50=B49.run( 值 =string(值, if( typeof@x( 值)=="float", if(right( 项目,1)!="率","#0.00", "#0.00%"),"") ) )

2. 脚本函数解释

1. bfee(amt):计算买入手续费。

买入手续费 =max(交易金额 *0.03%,5)+ 交易金额 *0.001%

参数:amt 为交易金额

2. sfee(amt):计算卖出手续费。

卖出手续费 = bfee(amt)+amt*0.05%

参数:amt 为交易金额

3. withdraw(R):计算回撤率。

4. Begin():初始化交易数据。

5. Buy(K, S, P):单支买盘函数,计算买盘的买入资金。返回买盘序号(用于卖盘)

参数:

K:当日 K 线数据

S:share 交易股票数量

P:price 交易价格。P 值为空时将当日收盘价作为交易价格。

如果交易价格低于当日最低价则买入失败,记录买盘序号返回 0

6. Sell(K, I, P):单支卖盘函数,计算卖盘的卖出资金。返回对应的买盘序号。

参数:

K:当日 K 线数据

I:买盘序号 id 或记录。I 可以为空,表示将该股票全部卖出。

P:price 交易价格。P 值为空时将当日收盘价作为交易价格。

如果交易价格高于当日最高价则卖出失败,记录买盘序号返回 0

7. Holds(C,D):返回股票 C 当前持股数据。

参数:

C:code 股票代码,C 为空时,返回所有股票的持股数据。

D:tdate 交易日期

8. Sells(K, U, L, D)按条件批量卖出函数,当股票上涨或下跌到一定程度或持有天数大于 D 时,卖出。

参数:

K:当日 K 线数据,如果卖出涉及多支股票时将把当日所有股票 K 线形成集合作为参数。

U:up 上涨百分比

L:low 下跌百分比

D:持股天数

9. Check(KD,D):计算各种回测指标。

参数:

KD:所有股票的 K 线数据集合,

D:最后的统计日期

10. Trades(C):返回股票交易成功的买卖数据。

参数:

C:股票代码。可以为单值如 600000;也可以为股票代码列表,如 [600000,600015];也可以为空:返回所有股票

11. Display(R)纵向查看 Check() 中的计算结果。

参数:

R:记录。Check 函数中返回的某条记录。

3. 回测指标

股票 _ 数量:总共持有多少支股票。例如,买入股票 [600000,600015],则持有的股票 _ 数量就 2。

现金收益:指买卖股票所获得的价差收益,只计算已卖出的股票。例如,8 块买入,买入 100 股,买入手续费 5 元;15 元卖出,卖出手续费 5 元,那么现金收益就是 15*100-5-8*100-5=690。

持仓价值:当前持有的股票价值。例如持有 A 股票 100 股,当前股价 10 元,那么 A 股票的持仓价值就是 1000。

持仓收益:当前的持仓价值 - 持仓成本。

占用资金:在给定时间段内完成所有交易需要投入的现金综合(包括手续费)。比如某支股票一周内的买卖资金如下表。不难算出,要完成这些交易,需要投入 800 元,那么该股票的占用资金就是 800 元。

买入资金500
卖出资金700
买入资金1000
卖出资金900

收益率:指收益总额与投资额的比例。收益总额包括现金收益和持仓收益。投资额就是该股票的占用资金。

波动率:指股票价格在一定时间内的变动幅度,它反映了市场的不确定性和风险。波动率越高,金融资产价格的波动越剧烈,资产收益率的不确定性就越强;波动率越低,金融资产价格的波动越平缓,资产收益率的确定性就越强。波动率等于每日收益率的年化标准差。

回撤率:描述策略可能出现的最糟糕的情况,指在某一段时期内股票从最高点到最低点的百分比幅度。比如,某股票价格在一个月内的最高值为 20 元,其最低的价格为 10 元,则该股票在这一个月内的回撤率=(20-10)/20×100%=50%。一般来说回撤率越大,股票反弹的机会越大,投资风险也越高。

买盘数:指希望购买某支股票的订单数。

买入数:指成功购买某支股票的订单数。买盘数 >= 买入数

卖盘数:指希望售出某支股票的订单数。

赢利数:策略在给定时间段内交易次数中盈利的次数。

亏损数:策略在给定时间段内交易次数中亏损的次数。

买入资金:给定时间段内某支股票的总买入资金。

卖出资金:给定时间段内某支股票的总买入资金。

比如某支股票一周内的买卖资金如下表,那么一周内该股票的买入资金就是 1500 元,卖出资金是 1600 元。

买入资金500
卖出资金700
买入资金1000
卖出资金900

4. 回测举例

我们以 N 日均线策略为例,进行回测。

N 日均线策略内容为,当股价高于 N 日均价时买入,低于 N 日均价时卖出。

代码示例:

AB
1>call@f("backtest.splx")2024
2=date(B1,1,1)=date(B1,12,31)
3=call("adjustprice.splx", "", call("loadkday.splx", null, A2,B2) )
4
5>Begin()5
6for A3.group(code)=A6.derive(if(#>B5+1, sign(close[-1]-close[-B5:-1].avg()), 0):flag )
7=B6.group@o1(flag)
8=B7.run(if( flag==1:Buy(~,100,null), flag==-1:Sells(~, 0, 0, 0) ) )
9
10=Trades(null)=Check(A3,B2)
11=Display(B10(1))

A1 登记回测脚本中的函数。

A2 B2 回测起始和截止日期。

A3 读取前复权数据。

A5 初始化交易数据。

B5 均线策略的移动周期。

A6:B8 循环每支股票,进行回测计算。

B6 N 日均线策略,生成交易信号。1 表示买入,-1 卖出。

B7 按照一买一卖分组。

B8 对每次买卖进行回测计算。

A10 返回所有成功买卖的交易数据。

..

B10 返回回测指标。

..

A11 纵向查看回测指标。

..

SPL 可以在这里下载:免费下载。使用免费的桌面版就可以了,直接安装即可使用。专业程序员也可以使用这里的开源版,但需要自行配置部署。

SPL 配有系统的教程,这里有面向新手的课程(含视频):SPL程序设计语言 · 乾学院VIP课程 ,乾学院 也有更多学习资料。

相关文章:

  • 论文阅读:2024 arxiv Jailbreaking Black Box Large Language Models in Twenty Queries
  • python合并word中的run
  • Ubuntu ZLMediakit的标准配置文件(rtsp->rtmp->hls)
  • 《分词算法大揭秘:BPE、BBPE、WordPiece、ULM常见方法介绍》
  • 在原生代码(非webpack)里使用iview的注意事项
  • 回归分析丨基于R语言复杂数据回归与混合效应模型【多水平/分层/嵌套】技术与代码
  • AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年4月30日第68弹
  • mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz的下载安装和使用
  • PostgreSQL Patroni集群组件作用介绍:Patroni、etcd、HAProxy、Keepalived、Watchdog
  • 在Carla中构建自动驾驶:使用PID控制和ROS2进行路径跟踪
  • Android学习总结之自定义view设计模式理解
  • 尼日利亚slot游戏出海赛道借助本土网盟cpi流量广告投放优势
  • 企业数据合规实战:用API+AI构建备案核验系统
  • Python爬虫(11)Python数据存储实战:深入解析NoSQL数据库的核心应用与实战
  • WPF性能优化举例
  • python+echart绘制一个听力图
  • 第六章 QT基础:9、Qt中数据库的操作
  • 【Dify系列教程重置精品版】第四章:实现Dify的 hello world
  • Learning vtkjs之ImageCropFilter
  • C++负载均衡远程调用学习之自定义内存池管理
  • 是否进行了及时有效处置?伤者情况如何?辽阳市相关负责人就饭店火灾事故答问
  • 中国海油总裁:低油价短期影响利润,但也催生资产并购机会
  • 市场监管总局:2024年查办商标、专利等领域违法案件4.4万件
  • 昆明破获一起算命破灾诈骗案,民警:大师算不到自己的未来
  • 看展览|建造上海:1949年以来的建筑、城市与文化
  • 圆桌|特朗普上台百日未能结束俄乌冲突,若美国“退出”会发生什么?