# Matlab经典案例 # ### 小波消噪 ### ------------ 执行脚本: ```matlab % 适用于Matlab 2013及以上 clc; clear; targetList(1).Market = 'CFFEX'; targetList(1).Code = 'IF0000'; totalAmount=10000000; amountForEach=totalAmount/length(targetList); Freq=30; % 刷新频数 len=30; %数据有效长度限制 len1=3; %指数平滑周期1 len2=10; %指数平滑周期2 barlength =420;%取数据的回望长度 shareNum = 50; AccountList(1) = {'FutureBackReplay'}; % traderSetBacktest(1000000,0.000025,0.02,0,1,0,0); traderRunBacktestV2('wavV2',@wavV2,{Freq,len,len1,len2,shareNum,barlength,amountForEach},AccountList,targetList(1),'min',Freq,20170101,20181231,'NA'); ``` 主策略函数: ```matlab function wavV2( Int,beginD,cellPar ) global idexD global s1 global ShareNum multiple; freq=cellPar{1}; len=cellPar{2}; len1=cellPar{3};%指数平滑周期1 len2=cellPar{4};%指数平滑周期2 % shareNum=cellPar{5}; barlength =cellPar{6};%取数据的回望长度 tradeAmount=cellPar{7}; if Int %初始化 traderSetParalMode(false);%关闭并行 idexD=traderRegKData('min',freq);%注册数据 ShareNum=zeros(1,1); multiple=zeros(1,1); else iddexD = traderGetRegKData(idexD,barlength,false);%提取所有标的的非补齐数据 [marketposition,~,~] = traderGetAccountPositionV2(1,1); %持仓头寸 [barnum,~]= traderGetCurrentBarV2();%当前是第几根bar if (barnum(1)<=barlength) %确保数据长度满足回望长度要求 return; end %%%需要注意取数据按照标的每个标的八个特征,依次为 时间、开盘价、最高价、最低价、收盘价、成交量、成交金额、持仓量 ,而后每个标的依次下去 time=iddexD(1,:)';%第一行代表标的的时间 high=iddexD(3,:)';%第三行代表标的的最高价 low=iddexD(4,:)';% 第四行代表标的的最低价 close=iddexD(5,:)';%第五行代表标的的收盘价 if length(time(~isnan(time)))<len % 对有效数据长度做限制 return; end if isempty(s1)%第一次达到数据有效长度 barnum=420 for i=1:len-1 data=close(end-len+i-101:end-len+i);%收盘价数据滑动窗口,长度102 [c,l]=wavedec(data,2,'haar');%按2维对data进行小波分解,分成四段;c=小波分解系数;l=各组系数的长度 ca2=appcoef(c,l,'haar',2);%提取第二层低频部分系数 cd1=detcoef(c,l,1);%第一层高频系数提取 cd2=detcoef(c,l,2);%第二层高频系数提取 cdd1=zeros(1,length(cd1)); cdd2=zeros(1,length(cd2)); c1=[rot90(ca2,1),cdd2,cdd1]; ss=waverec(c1,l,'haar'); %重构第二层低频 s1(barnum(1)-len+i)=ss(end);%储存420重构后最后一个close end end data=close(end-101:end);%为行向量 [c,l]=wavedec(data,2,'haar');%按2维对data进行小波分解,分成四段;c=小波分解系数;l=各组系数的长度 ca2=appcoef(c,l,'haar',2);%提取第二层低频部分系数 cd1=detcoef(c,l,1);%第一层高频系数提取 cd2=detcoef(c,l,2);%第二层高频系数提取 cdd1=zeros(1,length(cd1)); cdd2=zeros(1,length(cd2)); c1=[rot90(ca2,1),cdd2,cdd1]; ss=waverec(c1,l,'haar');%重构第二层低频 s1(barnum(1))=ss(end);%储存重构后最后一个close [DIF,DEA,macd]=MACD(s1,12,26,9); ma1=EMA(s1,len1); ma2=EMA(s1,len2); con1= macd(end)>0 && ma1(end)>ma2(end)&&DIF(end)>DEA(end) ;%开多条件 con2= macd(end)<0 && ma1(end)<ma2(end)&&DIF(end)<DEA(end);%开空条件 con3= ma1(end)<ma2(end)&&macd(end)<0;%平多 con4=ma1(end)>ma2(end)&&macd(end)>0;%平空 %-----------------交易逻辑---------------------------% info=traderGetTargetInfoV2(1); multiple=info.Multiple; ShareNum=floor(tradeAmount/(close(end)*multiple)); if marketposition(1)==0 if con1 traderBuyV2(1,1,ShareNum,0,'market','buy');%开多单 elseif con2 traderSellShortV2(1,1,ShareNum,0,'market','sellshort');%开空单 end end if marketposition(1)>0 && con3 traderSellV2(1,1,ShareNum,0,'market','sell');%平多 end if marketposition(1)<0 && con4 traderBuyToCoverV2(1,1,ShareNum,0,'market','buytocover');%平空 end end end function [DIF,DEA,MACDValue]=MACD(Price,FastLength,SlowLength,DEALength) DIF=zeros(length(Price),1); DEA=zeros(length(Price),1); MACDValue=zeros(length(Price),1); DIF=EMA(Price,FastLength)-EMA(Price,SlowLength); DEA=EMA(DIF,DEALength); MACDValue=2*(DIF-DEA); end function EMAValue=EMA(Price,Length) EMAValue=zeros(length(Price),1); K=2/(Length+1); for i=1:length(Price) if i==1 EMAValue(i)=Price(i); else EMAValue(i)=Price(i)*K+EMAValue(i-1)*(1-K); end end end ``` <br> ### SVM机器学习 ### ------------ 执行脚本: ```matalb % 注:svm里面的函数仅可以用于matlab2017a及以前版本 clc; clear; warning('off'); targetList(1).Market = 'SHFE'; targetList(1).Code = 'RB0000'; totalAmount=1000000; amountForEach=totalAmount/length(targetList); lags=180; %训练样本长度 % shareNum=50; %固定下单手数 freq=30; %刷新频数 AccountList(1) = {'FutureBackReplay'}; traderSetBacktest(1000000,0.000025,0.02,0,1,0,0); traderRunBacktestV2('svmV2',@svmV2,{lags,freq,amountForEach},AccountList,targetList(1),'min',freq,20180702,20190101,'NA'); ``` 主策略函数: ```matalb function svmV2( Int,beginD,cellPar ) global idexD lags=cellPar{1}; freq=cellPar{2}; tradeAmount=cellPar{3}; barlength =270;%取数据的回望长度, 270根bar if Int %初始化 traderSetParalMode(false);%关闭并行 idexD=traderRegKData('min',freq);%注册数据 else iddexD = traderGetRegKData(idexD,barlength,false);%提取所有标的的非补齐数据 [marketposition,~,~] = traderGetAccountPositionV2(1,1); %记录第一个合约的头寸 %%%需要注意取数据按照标的每个标的八个特征,依次为 时间、开盘价、最高价、最低价、收盘价、成交量、成交金额、持仓量 ,而后每个标的依次下去 time=iddexD(1,:);%第一行代表标的的时间 high=iddexD(3,:);%第三行代表标的的最高价 low=iddexD(4,:);% 第四行代表标的的最低价 close=iddexD(5,:);%第五行代表标的的收盘价 open=iddexD(2,:);%第五行代表标的的收盘价 if length(time(~isnan(time)))<lags %数据长度限制, 必须大于lags根bar return; end statesData=diff(close)>0; %收盘价差分 trainData=[high',low',close']; trainData__=trainData(end-lags+1:end-1,:); %训练集大小,除去当前的前lags根bar statesData__=statesData(end-lags+2:end); %组标签,1=上涨,0=下跌 SVMstruct=fitcsvm(trainData__,statesData__,'KernelFunction','rbf'); %生成分组指标 ScoreSVMModel = fitPosterior(SVMstruct,trainData__,statesData__); [y,~]=predict(ScoreSVMModel,trainData(end,:)); con1=mean(close(end-4:end))>mean(close(end-29:end)); con2=mean(close(end-4:end))<mean(close(end-29:end)); info=traderGetTargetInfoV2(1); multiple=info.Multiple; shareNum=floor(tradeAmount/(open(end)*multiple)); %-----------------交易逻辑---------------------------% if marketposition<= 0 && con1&&y(end)>0 %开多,金叉且组标签=1(上涨) traderBuyV2(1,1,shareNum,0,'market','buy1'); end if marketposition>= 0 && con2&&y(end)<=0 %开多,死叉且组标签=0(下涨) traderSellShortV2(1,1,shareNum,0,'market','sellshort1'); end end end ``` <br> ### 海龟 ### ------------ 执行脚本: ```matalb % 适用于Matlab 2013及以上 clear; clc; targetList(1).Market = 'SHFE'; targetList(1).Code = 'rb0000'; targetList(2).Market = 'DCE'; targetList(2).Code = 'J0000'; global s; n=length(targetList); % len1=50; %长周期,用于进场信号 % len2=20; %短周期,用于离场信号 % len3=20; %ATR指标平滑周期 % Freq=30; %刷新频数 % ShareNum=1; %固定下单手数 len1=20; %长周期,用于进场信号 len2=10; %短周期,用于离场信号 len3=10; %ATR指标平滑周期 Freq=30; %刷新频数 ShareNum=1; %固定下单手数 for i=1:n s(i).openprice=[]; s(i).stoploss=[]; end % traderSetBacktest(10000000,0.000026,0.02,0,1,0,0); AccountList(1) = {'StockBackReplay'}; traderRunBacktestV2('TurtleV2',@TurtleV2,{1,len1,len2,len3,ShareNum},AccountList,targetList,'min',Freq,20180115,20181230,'FWard'); ``` 主策略函数: ```matlab function TurtleV2(Int,beginD,cellPar) global idexD TLen global s %开仓价,止损价 global share time0 %开仓手数 开仓次数 freq=cellPar{1}; len1=cellPar{2}; %长周期,用于进场信号 len2=cellPar{3}; %短周期,用于离场信号 len3=cellPar{4}; %ATR指标平滑周期 ShareNum=cellPar{5}; Plimit=4; %加仓次数限制 lens=60; %数据计算长度限制 if Int %初始化 traderSetParalMode(false);%关闭并行 idexD=traderRegKData('day',1);%注册数据 TLen=length(idexD(:,1));%品种个数 share=zeros(TLen,1);%记录开仓手数 time0=zeros(TLen,1);%记录开仓次数 else iddexD = traderGetRegKData(idexD,lens+3,false);%提取所有标的的非补齐数据 [mp,~,~] = traderGetAccountPositionV2(1,1:TLen); %记录第一个合约的头寸 [ValidCash,MarketCap,OrderFrozen,MarginFrozen] = traderGetAccountInfoV2(1); for i=1:TLen idddexD=iddexD(8*(i-1)+1:8*i,:);% 1.time 2.open 3.high 4.low 5.close 6.vol 7.turn 8.oi high=idddexD(3,:)'; low=idddexD(4,:)'; close=idddexD(5,:)'; %---------------------策略计算与基本逻辑---------------% if length(close(~isnan(close)))<lens %数据计算长度限制 continue; end %ATR指标 value1 = high(end-lens+1:end) - low(end-lens+1:end);% 当日最高价减去当日最低价 value2 = abs(high(end-lens+1:end)-close(end-lens:end-1));% 当日最高价减去前日收盘价的绝对值 value3 = abs(low(end-lens+1:end)-close(end-lens:end-1));% 当日最低价减去前日收盘价的绝对值 TRlist=max(value1,max(value2,value3)); ATR_len3=mean(TRlist(end-len3+1:end)); % 四条轨道 High_len1=max(high(end-len1:end-1)); Low_len1=min(low(end-len1:end-1)); High_len2=max(high(end-len2:end-1)); Low_len2=min(low(end-len2:end-1)); con1=close(end)>High_len1; con2=close(end)<Low_len1; con3=close(end)>High_len2; con4=close(end)<Low_len2; %% 出场 if mp(i)>0 %平多 if con4||(~isempty(s(i).stoploss)&&close(end)< s(i).stoploss) %收盘<短周期下轨 或者 收盘<止损价 OrderID=traderPositionToV2(1,i,0,0,'market','close'); if OrderID~=0 s(i).openprice=[]; %开仓记录信息清0 s(i).stoploss=[]; time0(i)=0; end end end if mp(i)<0 %平空 if con3||(~isempty(s(i).stoploss)&&close(end)> s(i).stoploss) %收盘>短周期上轨 或者 收盘>止损价 OrderID=traderPositionToV2(1,i,0,0,'market','close'); if OrderID~=0 s(i).openprice=[]; s(i).stoploss=[]; time0(i)=0; end end end %% 进场 if mp(i)==0 %无仓位下才开仓 [Multiple, ~, ~, ~, ~, ~, ~] = traderGetFutureInfoV2(i); %标的合约乘数 share(i)=10*floor(5000000/close(end)/Multiple/4);%计算下单手数 “/4意义不明” ShareNum=share(i); if con1 %开多 收盘>长周期上轨 OrderID=traderDirectBuyV2(1,i,ShareNum,0,'market','buy'); if OrderID~=0 s(i).openprice=close(end); %记录成交价 s(i).stoploss=s(i).openprice-1*ATR_len3; %1倍ATR波幅止损 time0(i)=1; %标记开仓次数 end elseif con2 %开空 收盘<长周期下轨 OrderID=traderDirectSellV2(1,i,ShareNum,0,'market','sellshort'); if OrderID~=0 s(i).openprice=close(end); s(i).stoploss=s(i).openprice+1*ATR_len3;%1倍ATR波幅止损 time0(i)=1; end end end if mp(i)>0&&time0(i)<Plimit %多单加仓 加仓次数<4次 if close(end)>=(s(i).openprice+4*ATR_len3) %收盘>开仓4倍ATR盈利 ShareNum=share(i); OrderID=traderDirectBuyV2(1,i,ShareNum,0,'market','buy'); if OrderID~=0 s(i).openprice=close(end); s(i).stoploss=s(i).openprice-1*ATR_len3; %加仓仓位按3倍ATR波幅止损 time0(i)=time0(i)+1; end end end if mp(i)<0&&time0(i)<Plimit %空单加仓 加仓次数<4次 if close(end)<=(s(i).openprice-4*ATR_len3) %收盘<开仓4倍ATR盈利 ShareNum=share(i); OrderID=traderDirectSellV2(1,i,ShareNum,0,'market','sellshort'); if OrderID~=0 s(i).openprice=close(end); s(i).stoploss=s(i).openprice+1*ATR_len3; %加仓仓位按3倍ATR波幅止损 time0(i)=time0(i)+1; end end end end end end ``` <br> ### 波动率放大均线突破 ### ------------ 执行脚本: ```matlab % 适用于Matlab 2013及以上 clear all; clc; % targetList(1).Market = 'CFFEX'; targetList(1).Code = 'IF0000'; targetList(2).Market = 'SHFE'; targetList(2).Code = 'RB0000'; len=30;%为滑动窗口大小 shareNum=[50 500];%为操作的手数 stoprate = 0.005;% 止损0.005 N = 20; Freq = 30;%刷新频率 AccountList(1) = {'FutureBackReplay'}; traderRunBacktestV2('MoneyManageMent',@Strategy,{stoprate,Freq,len,N,shareNum},AccountList,targetList,'min',Freq,20180102,20190101,'FWard'); ``` 主策略函数: ```matlab function Strategy(bInit,bDayBegin,cellPar)% %% 波动率放大+均线突破+日历过滤+滚动止损止盈 %进场只在每周的周一到周四进行,周五不进场开仓,但可以平仓。 %寻找波动率放大突破点,同时满足close > average (close,N)均线过滤的有条件下,做多 %寻找波动率放大突破点,同时满足close < average (close,N)均线过滤的有条件下,做空 %出场使用3:1的合约价值止盈止损比 %在沪深300股指期货与螺纹钢期货里面按照1:50的标准进行投资。 global g_idxK TLen; global record; stoprate = cellPar{1}; Freq = cellPar{2}; len = cellPar{3}; N = cellPar{4};%N shareNum = cellPar{5};%为操作的手数 if bInit g_idxK = traderRegKData('day',1); TLen = length(g_idxK(:,1)); record.entryP = nan(1,TLen); else mp=traderGetAccountPositionV2(1,1:TLen); %仓位读取 datas = traderGetRegKData(g_idxK,len,false); % 获取数据 for i=1:TLen data = datas(1+8*(i-1):8*i,:); time1 = data(1,:);%时间 open1 = data(2,:); %open价 high1 = data(3,:); %high价 low1 = data(4,:); %low价 close1 = data(5,:); %close价 if isnan(close1(1))%无数据跳过 return; end [position,Frozen,AvgPrice] = traderGetAccountPositionV2(1,1:TLen); %% 计算轨迹 %开仓 e = high1 - open1; f = open1 - low1; g = mean(e(end-5:end-1)); j = mean(f(end-5:end-1)); %% if mp(i) >0 if close1(end)>AvgPrice(i)*(1+3*stoprate) || close1(end)<AvgPrice(i)*(1-stoprate) traderPositionToV2(1,i,0,0,'market','buy1');%清仓 end elseif mp(i)<0 if close1(end)<AvgPrice(i)*(1-3*stoprate) || close1(end)>AvgPrice(i)*(1+stoprate) traderPositionToV2(1,i,0,0,'market','buy1');%清仓 end end if mp(i) == 0 if open1(end)+g<=close1(end)&&weekday(time1(end))~=5&& close1(end)>mean(close1(end-N+1:end))% traderDirectBuyV2(1,i,shareNum(i),0,'market','buy11');%开多 elseif open1(end)-j>=close1(end)&&weekday(time1(end))~=5&& close1(end)<mean(close1(end-N+1:end))% traderDirectSellV2(1,i,shareNum(i),0,'market','sell11');%开空 end end end end ``` <br> ### PivotPoint ### ------------ 执行脚本: ```matlab % 适用于Matlab 2013及以上 clear all; clc; targetList(1).Market = 'CZCE'; targetList(1).Code = 'MA000'; targetList(2).Market = 'CZCE'; targetList(2).Code = 'SR000'; len=3;%为滑动窗口大小 shareNum=[1200 1200];%为操作的手数 stoprate = [0.002 0.002];% 止损.2% Freq = 5;%刷新频率 AccountList(1) = {'FutureBackReplay'}; traderRunBacktestV2('PPoint',@PivotPoint,{stoprate,Freq,len,shareNum},AccountList,targetList,'min',Freq,20180106,20181221,'FWard'); ``` 主策略函数: ```matlab function PivotPoint(bInit,bDayBegin,cellPar)% %% PivotPoint %系统不断跟踪实时交易价格,根据前一交易日计算得到的六个价位作为触发条 %件,当当前价位达到触发价格时,产生交易信号,系统自行执行交易策略。因 %此我们的具体交易策略如下: %1) 趋势跟踪策略: %在空仓情况下,若盘中价格超过买入突破价,则认为出现上升趋势,开仓做多; %在空仓情况下,若盘中价格跌破卖出突破价,则认为出现下降趋势,开仓做空; %2) 反转趋势策略: %当日内最高价超过上观察价后,盘中价格出现回落,且进一步跌破卖出反转价 %构成的支撑线时,即在该点位(反手、开仓)做空; %当日内最低价跌破下观察价后,盘中价格出现反弹,且进一步超过反转买入价 %构成的阻力线时,即在该点位(反手、开仓)做多; global g_idxK TLen; global record; stoprate = cellPar{1};% 止损比率 Freq =cellPar{2};% 刷新频率 len=cellPar{3};% 为滑动窗口大小 shareNum=cellPar{4};% 为操作的手数 if bInit g_idxK = traderRegKData('day',1);%注册日数据 TLen = length(g_idxK(:,1));%标的个数 record.entryP = nan(1,TLen);%记录入场点位 record.reverse =zeros(1,TLen); else mp=traderGetAccountPositionV2(1,1:TLen); %仓位读取 datas = traderGetRegKData(g_idxK,len+1,true); % 获取len+1跟bar数据 [~,BarTime] = traderGetCurrentBarV2(); % 获取当下时间 dateVec = datevec(BarTime); % datevec将时间转化为序列。对应[年,月,日,时,分,...] nowTime = dateVec(4)*100 + dateVec(5);%分钟时间 for i=1:TLen %% 日内平仓 if nowTime>= 1445 record.reverse(i) =0;%每日反转标记归0 if mp(i)~=0%日内平仓 traderPositionToV2(1,i,0,0,'market','buy1');%日内平仓 end end %% 获取标的价格 data = datas(1+8*(i-1):8*i,:); open1 = data(2,:); %open价 high1 = data(3,:); %high价 low1 = data(4,:); %low价 close1 = data(5,:); %close价 if isnan(close1(1)) %无数据跳过 return; end if mp(i)==0 record.entryP(i)=nan; elseif isnan(record.entryP(i)) record.entryP(i) = open1(end);%记录入场价格 下根bar 开盘价 end %% ---------------------Pivot Point 策略计算与基本逻辑---------------% HH=high1(end-1);% 上一个交易日的最高价 LL=low1(end-1); % 上一个交易日的最低价 CC=close1(end-1); % 上一个交易日的收盘价 pivot=(HH+LL+2*CC)/4;%中轴价格 R3=pivot+(HH-LL)+(pivot-LL);%趋势多头阈值 R2=pivot+(HH-LL);%反转做空观察点位 R1=pivot+(HH-pivot);%反转做空阈值 S1=pivot-(HH-pivot);%反转做多阈值 S2=pivot-(HH-LL);%反转做多观察点位 S3=pivot-(HH-LL)-(HH-pivot);%趋势空头阈值 %% 判断平仓 移动止损出场 if mp(i) >0 if close1(end)-record.entryP(i)<-record.entryP(i)*stoprate(i) %多头止损 traderPositionToV2(1,i,0,0,'market','buy1');%清仓 elseif close1(end)-record.entryP(i)>record.entryP(i)*stoprate(i) %触发浮动止盈 record.entryP(i) = open1(end); end elseif mp(i)<0 if close1(end)-record.entryP(i)>record.entryP(i)*stoprate(i) %空头止损 traderPositionToV2(1,i,0,0,'market','buy1');%清仓 elseif close1(end)-record.entryP(i)<-record.entryP(i)*stoprate(i) %触发浮动止盈 record.entryP(i) = open1(end); end end %% 判断开仓 if mp(i) == 0&&nowTime<1445&&record.reverse(i) == 0 if close1(end)>R2 record.reverse(i) = 1;%反转标记 当日内最高价超过上观察价 elseif close1(end)<S2 record.reverse(i) = -1;%反转标记 当日内最低价跌破下观察价 end if close1(end)>R3||(record.reverse(i) == -1&&close1(end)>S1) %突破趋势做多阈值 | 在满足反转做多观察下,价格突破反转做多阈值 traderDirectBuyV2(1,i,shareNum(i),0,'market','buy11');%开多 elseif close1(end)<S3||(record.reverse(i) == 1&&close1(end)<R1) traderDirectSellV2(1,i,shareNum(i),0,'market','sell11');%开空 end end end end ``` <br> ### 超卖指标多因子 ### ------------ 执行脚本: ```matlab % 适用于Matlab 2013及以上 % 策略说明: % 选取HS300为备选资产池 % 每次从中选取最多30支股票进行操作; % 操作规则: % 首先择时:满足6日均线大于12日均线,则符合择时条件(个股) % 在满足择时的基础上,挑选所选择的因子的按照排序所得到的归一化后的值得大小。 % 由大至小选择。 % 满足30支股票后不再选择。 % 选择的股票在持有23个交易日后统一售出。 clear; clc; begintime = 20180102; %起止时间段 endtime = 20180930; targetList = traderGetCodeList('HS300'); %以沪深300为选股资产池。 Freq = 1; %每天的执行次数。 moneys = 10000000000; traderSetBacktest(moneys,0.001,0.02,0,1,0,0); AccountList(1) = {'StockBackReplay'}; traderRunBacktestV2('mul_factors',@mul_factors,{Freq,begintime,endtime,moneys},AccountList,targetList,'day',Freq,begintime,endtime,'FWard'); ``` 主策略函数: ```matlab function mul_factors(bInit,bDayBegin,cellPar) dbstop if error; global g_idxK; %获取的注册数据index global TLen; %股票支数 global beginopen; %每天第一根bar开盘价 % global beginclose; %每天第一根bar收盘价 global begin_day_time; %只获年月日数据; global TotalTime; %获取年月日时分秒数据 global beforetime; %上一次标记的交易时间 global numbers; global Tdays; global stopprice; global buyprice; global positions; global g_idxfactor_bias; global g_idxfactor_cci; global g_idxfactor_mfi; global g_idxfactor_mtm; global g_idxfactor_rsi; global g_idxfactor_dma; global points; % global g_idxfactor_emv; % global g_idxfactor_trix; global periods; global zlen; global ulen; Freq =cellPar{1}; %回测频率; begintime = cellPar{2}; %开始时间; endtime = cellPar{3}; %结束时间; if bInit points = 0; traderSetParalMode(false);%默认是true,因子计算函数并行执行,速度快,不能调试,false串行执行可以设断点调试 periods = 0; numbers = 0; g_idxK = traderRegKData('day',Freq); %注册数据,获得index; TLen = length(g_idxK(:,1)); %一共有多少条数据(股票数量) beforetime = zeros(TLen,1); %前一个交易时间的获取,若为第一次获取,则认为是0; Days=traderGetTradingDays(begintime,endtime); %交易时间段; les = length(Days); days = num2str(Days); stopprice = nan(TLen,1); buyprice = nan(TLen,1); buyprice = nan(TLen,1); for i = 1:length(Days) a1 = days(i,:); a2 = a1(1:4); a3 = a1(5:6); a4 = a1(7:8); Tdays1(i,1) = str2num(a2); Tdays1(i,2) = str2num(a3); Tdays1(i,3) = str2num(a4); Tdays1(i,4) = 0; Tdays1(i,5) = 0; Tdays1(i,6) = 0; end Tdays = datenum(Tdays1); %转换成为标准数据形式(datevec后是年月日形式,去掉了时分秒)。 g_idxfactor_bias = traderRegUserIndi(@factor_bias,{g_idxK,12,TLen});%注册因子bias g_idxfactor_cci = traderRegUserIndi(@factor_cci,{g_idxK,14,TLen});%注册因子cci g_idxfactor_mfi = traderRegUserIndi(@factor_mfi,{g_idxK,14,TLen}); %注册因子mfi g_idxfactor_mtm = traderRegUserIndi(@factor_mtm,{g_idxK,6,TLen}); %注册因子mtm g_idxfactor_rsi = traderRegUserIndi(@factor_rsi,{g_idxK,14,TLen}); %注册因子rsi g_idxfactor_dma = traderRegUserIndi(@factor_dma,{g_idxK,6,TLen,12}); %注册因子dma else if numbers < 50 %因为在之间的计算时间的时候,用于计算的 numbers = numbers + 1; return end [ValidCash,~,~,~,~]=traderGetAccountInfoV2(1); begindatas = traderGetRegKData(g_idxK,numbers,false); %在一天的开始时间获取开盘价,和一分钟的实时价格 [1.time 2.open 3.high 4.low 5.close 6.vol 7.turn 8.oi] for i = 1 : TLen beginclose(i) = begindatas(5 + 8 *i -8,end); begintime(i) = begindatas(1 + 8 *i -8,end); end % 超买超卖性指标 f_bias = traderGetRegUserIndi(g_idxfactor_bias,20); f_cci = traderGetRegUserIndi(g_idxfactor_cci,20); f_mfi = traderGetRegUserIndi(g_idxfactor_mfi,20); f_mtm = traderGetRegUserIndi(g_idxfactor_mtm,20); f_rsi = traderGetRegUserIndi(g_idxfactor_rsi,20); % 趋势型指标 f_dma = traderGetRegUserIndi(g_idxfactor_dma,1); % 标准化超买超卖性指标 normal_f_bias = normalizing(f_bias); normal_f_cci = normalizing(f_cci); normal_f_mfi = normalizing(f_mfi); normal_f_mtm = normalizing(f_mtm); normal_f_rsi = normalizing(f_rsi); ranking = mean([(1-normal_f_bias),(1-normal_f_cci),(1 - normal_f_mfi),(1 - normal_f_mtm),(1-normal_f_rsi)],2); sort_rank = sort(unique(ranking),'descend');%超卖超卖指标降序排列 pz = find(isnan(sort_rank)); sort_rank(pz) = [];%剔除nan if periods ~= 0 periods = periods + 1;%计算持有期 end %进行买入判断; if points == 0 && periods == 0 if length(sort_rank) < TLen * 0.1 %排序筛选出个数《30个 if isempty(sort_rank) return; else positions = []; zlen = length(sort_rank); coney = ValidCash /zlen; %平分仓位 % stopprice = zeros(TLen,1); % buyprice = zeros(TLen,1); for i = 1 : zlen p = find(sort_rank(i) == ranking); %排序定位 if length(p) == 1 if f_dma(p) > 0 %出于趋势状态 positions = [positions,p]; traderDirectBuyV2(1,p,10*floor(coney/beginclose(p)),0,'market','buys1'); %买入 stopprice(p) = floor(coney/beginclose(p)) * 0.9; %止损价 buyprice(p) = floor(coney/beginclose(p)); %开仓价 end else for ci = 1 : length(p) if f_dma(p(ci)) > 0 positions = [positions,p(ci)]; traderDirectBuyV2(1,p(ci),10*floor(coney/beginclose(p(ci))),0,'market','buys1'); stopprice(p(ci)) = floor(coney/beginclose(p(ci))) * 0.9; buyprice(p(ci)) = floor(coney/beginclose(p(ci))); end end end end if ~isempty(positions) periods = periods + 1;%计算持有期 points = 1; end end else zlen = length(sort_rank); coney = ValidCash / (TLen * 0.1); %平分30个仓位 plen = 0; for i = 1 : zlen pq = find(sort_rank(i) == ranking); if length(pq) > 1 for c1 = 1 : length(pq) if f_dma(pq(c1)) > 0 && plen < (TLen * 0.1) %只做因子排序前30个 positions = [positions,pq(c1)]; traderDirectBuyV2(1,pq(c1),10*floor(coney/beginclose(pq(c1))),0,'market','buys1'); stopprice(pq(c1)) = floor(coney/beginclose(pq(c1))) * 0.9; buyprice(pq(c1)) = floor(coney/beginclose(pq(c1))); plen = plen + 1; end end if plen >= (TLen * 0.1)%只做因子排序前30个 break; end else if f_dma(pq) > 0 && plen < (TLen * 0.1) positions = [positions,pq]; traderDirectBuyV2(1,pq,10*floor(coney/beginclose(pq)),0,'market','buys1'); stopprice(pq) = floor(coney/beginclose(pq)) * 0.9; buyprice(pq) = floor(coney/beginclose(pq)); plen = plen + 1; end if plen >= (TLen * 0.1) break; end end end if ~isempty(positions) periods = periods + 1;%计算持有期 points = 1; end end end %持有23天后,不管剩余多少持仓,均进行卖出操作; if points == 1 && periods == 23 mp = traderGetAccountPositionV2(1,1:TLen); for i = 1 : TLen if mp(i) ~= 0 traderDirectSellV2(1,i,mp(i),0,'market','sell2'); end end points = 0; periods = 0; buyprice = nan(TLen,1); stopprice = nan(TLen,1); zlen = []; ulen = []; end end end % 对于超买超卖型,应用法则较为一致,一般当指标数值高于一定阈值,如 80,认为处 % 于超买状态,预测股价未来会下跌,可以卖出;当指标数值小于一定阈值,如20,认 % 为处于超卖状态,预计价格未来会上升,可以买入。 % 标准化后,值越接近0则认为是买入信号,值越接近1,则认为是卖出信号; function value = factor_bias(cellPar,bpPFCell) %乖离率=[(当日收盘价-N 日平均价)/N 日平均价]*100%。 %参数N 有三组,默认值分别为6、12、24。 dbstop if error idxK = cellPar{1}; %数据获取;指标的数据 N=cellPar{2}; %滚动天数; 12 len = cellPar{3}; %一共计算了多少股票; hs300是300支股票 datas = traderGetRegKData(idxK,N,false,bpPFCell); %获取N天的数据,相应的天数获取相应的数据量; for i = 1 : len close(i,:) = datas(8*i + 5 - 8,:); end value = (close(:,end) - mean(close,2)) ./ mean(close,2); end function value = factor_cci(cellPar,bpPFCell) dbstop if error idxK = cellPar{1}; %数据获取;指标的数据 N=cellPar{2}; %滚动天数; 14 len = cellPar{3}; %一共计算了多少股票; datas = traderGetRegKData(idxK,2*N-1,false,bpPFCell); %获取N天的数据,相应的天数获取相应的数据量; for i = 1 : len high(i,:) = datas(8*i+3-8,:); low(i,:) = datas(8*i+4-8,:); close(i,:) = datas(8*i+5-8,:); end tp = (high(:,end) + low(:,end) + close(:,end)) / 3; means = nan(len,2*N-1); for i = N : 2*N-1 means(:,i) = mean(close(:,i-N+1:i),2); end ma = means(:,end); md = mean(means(:,N:end) - close(:,N:end),2); value = (tp - ma) ./ (md * 0.015); end function value = factor_mfi(cellPar,bpPFCell) % TYP := (HIGH + LOW + CLOSE)/3; % V1 := SUM(IF(TYP>REF(TYP,1),TYP*VOL,0),N)/SUM(IF(TYP<REF(TYP,1),TYP*VOL,0),N); % MFI := 100-(100/(1+V1)); dbstop if error idxK = cellPar{1}; %数据获取;指标的数据 N=cellPar{2}; %滚动天数;14 len = cellPar{3}; %一共计算了多少股票; datas1 = traderGetRegKData(idxK,N+1,false,bpPFCell); %获取N天的数据,相应的天数获取相应的数据量; value1 = []; value2 = []; for j = 1 : N datas = datas1(:,j:j+1); for i = 1 : len high(i,:) = datas(8*i+3-8,:); close(i,:) = datas(8*i+5-8,:); low(i,:) = datas(8*i+4-8,:); volume(i,:) = datas(8*i+6-8,:); end cal1 = zeros(len,1); cal2 = zeros(len,1); TYP = (high(:,end) + close(:,end) + low(:,end))/3; TYP1 = (high(:,end-1) + close(:,end-1) + low(:,end-1))/3; zz1 = TYP - TYP1; posit1 = find(zz1 > 0); posit2 = find(zz1 < 0); cal = TYP .* volume(:,end); cal1(posit1) = cal(posit1); cal2(posit2) = cal(posit2); value1 = [value1,cal1]; value2 = [value2,cal2]; end v1 = sum(value1,2) ./ sum(value2,2); value = 100-(100 ./ (1+v1)); end function value = factor_mtm(cellPar,bpPFCell) dbstop if error; idxK = cellPar{1}; %数据获取;指标的数据 N=cellPar{2}; %滚动天数; 6 len = cellPar{3}; %一共计算了多少股票; datas = traderGetRegKData(idxK,N,false,bpPFCell); %获取N天的数据,相应的天数获取相应的数据量; for i = 1 : len close(i,:) = datas(8*i+5-8,:); end value = close(:,end) - close(:,1); end function value = factor_rsi(cellPar,bpPFCell) dbstop if error; idxK = cellPar{1}; %数据获取;指标的数据 N=cellPar{2}; %滚动天数; 14 len = cellPar{3}; %一共计算了多少股票; datas = traderGetRegKData(idxK,N,false,bpPFCell); %获取N天的数据,相应的天数获取相应的数据量; for i = 1 : len open(i,:) = datas(8*i+2-8,:); close(i,:) = datas(8*i+5-8,:); end price = close - open; values = nan(len,1); for i = 1 : len prices = price(i,:); p1 = find(prices>0); p2 = find(prices<0); values(i) = sum(abs(prices(p1))) / sum(abs(prices(p2))); end value = 100 * values ./ (1 + values); end % 趋势型指标:一般而言,短期指标和长期(移动平均)指标两线交叉给出买卖信号。具体的说,短期 % 指标线上穿长期指标线,即黄金交叉,给出买入信号;长期指标线下穿短期指标线,即 % 死亡交叉,给出卖出信号 % 也即:值>0 买入信号 值<0 卖出信号 % 标准化后,值越接近1,则认为是买入信号,值越接近0,则认为是卖出信号。 function value = factor_dma(cellPar,bpPFCell) dbstop if error; idxK = cellPar{1}; %数据获取;指标的数据 N1 = cellPar{2}; %短期均值时间;6 N2 = cellPar{4}; %长期均值时间;12 len = cellPar{3}; %一共计算了多少股票; datas = traderGetRegKData(idxK,N2,false,bpPFCell); %获取N天的数据,相应的天数获取相应的数据量; for i = 1 : len close(i,:) = datas(8*i+5-8,:); end close1 = close(:,N2-N1+1:end); close2 = close(:,1:N2); value = mean(close1,2) - mean(close2,2); end function value = normalizing(data) %% %标准化指标值 = (当天指标值- N日指标最小值) /(N日指标最大值- N日指标最小值) maxium = max(data'); minium = min(data'); value = (data(:,end) - minium') ./ (maxium' - minium'); end ``` <br> ### EMDT ### ------------ 执行脚本: ```matlab % 适用于Matlab 2013及以上 clear all; clc; targetList(1).Market = 'CFFEX'; targetList(1).Code = 'IF0000'; len=100;%分钟数据长度 len1=10;%日数据长度 mlen=40;%开始交易时间 shareNum=[50 50];%为操作的手数 stoprate = 0.005;% 止损0.5% drawbackrate = 0.2;% 回调20% k2 = 1; %k2 反转交易模式阈值 Freq = 1;%刷新频率 AccountList(1) = {'FutureBackReplay'}; traderRunBacktestV2('MoneyManagement',@EMDT,{stoprate,drawbackrate ,Freq,len,len1,shareNum,mlen,k2},AccountList,targetList,'min',Freq,20170106,20170721,'FWard'); ``` 主策略函数: ```matlab function PivotPoint(bInit,bDayBegin,cellPar)% %% 策略名称:EMDT %根据开盘40分钟的数据,用EMDT方法识别今日采用趋势策略还是震荡策略, %趋势策略用突破,震荡策略用反转,固定一个百分点止损. global g_idxK g_idxKD TLen; global record; stoprate = cellPar{1};%止损0.5% 止盈0.5% drawbackrate = cellPar{2};% 回调20% Freq =cellPar{3};% 刷新频率 len=cellPar{4};% 为滑动窗口大小 100 len1=cellPar{5};%10根K线 shareNum=cellPar{6};% 为操作的手数 mlen = cellPar{7};%开盘40分钟观测 k2 = cellPar{8}; if bInit g_idxK = traderRegKData('min',Freq);%注册分钟数据 g_idxKD = traderRegKData('day',1);%注册日数据 TLen = length(g_idxK(:,1));%标的个数 record.entryP = nan(1,TLen);%记录入场价 record.PolePoint =zeros(1,TLen);% 记录移动止损价格 record.R =zeros(1,TLen); record.reverse =zeros(1,TLen); record.daynum =zeros(1,TLen); else mp=traderGetAccountPositionV2(1,1:TLen); %仓位读取 datas = traderGetRegKData(g_idxK,len+1,false);% 获取101跟bar 分钟数据 [1.time 2.open 3.high 4.low 5.close 6.vol 7.turn 8.oi] datasD = traderGetRegKData(g_idxKD,len1+1,false);% 获取11跟bar 日数据 [1.time 2.open 3.high 4.low 5.close 6.vol 7.turn 8.oi] [~,BarTime] = traderGetCurrentBarV2(); % 获取当下时间 dateVec = datevec(BarTime); % datevec将时间转化为序列。对应[年,月,日,时,分,...] nowTime = dateVec(4)*100 + dateVec(5);%分钟时间 for i=1:TLen %% 日内平仓 收盘平仓 if nowTime>= 1445 record.reverse(i) =0;%每日反转标记归0 if mp(i)~=0%日内平仓 traderPositionToV2(1,i,0,0,'market','buy1');%日内平仓 end end %% 获取标的价格 data = datas(1+8*(i-1):8*i,:); %分钟数据 time1 = data(1,:); %time open1 = data(2,:); %open价 high1 = data(3,:); %high价 low1 = data(4,:); %low价 close1 = data(5,:); %close价 %% 获取标的价格 dataD = datasD(1+8*(i-1):8*i,:); %日数据 openD1 = dataD(2,:); %open价 highD1 = dataD(3,:); %high价 lowD1 = dataD(4,:); %low价 closeD1 = dataD(5,:); %close价 if isnan(close1(1))||isnan(closeD1(1)) %无数据跳过 return; end %% 记录入场价格 下根bar 开盘价 if mp(i)==0 record.entryP(i)=nan; elseif isnan(record.entryP(i)) record.entryP(i) = open1(end); end %% 计算pivot HH = highD1(end-1); LL = lowD1(end-1); CC = closeD1(end-1); pivot=(HH+LL+2*CC)/4; R3=pivot+(HH-LL)+(pivot-LL);%趋势多头阈值 R2=pivot+(HH-LL);%反转做空观察点位 R1=pivot+(HH-pivot);%反转做空阈值 S1=pivot-(HH-pivot);%反转做多阈值 S2=pivot-(HH-LL);%反转做多观察点位 S3=pivot-(HH-LL)-(HH-pivot);%趋势空头阈值 if floor(time1(end))~=floor(time1(end-1)) record(i).daynum = record(i).daynum+1; %计算天数 end %% 开盘40分钟 if nowTime==915+mlen r = close1(end-mlen:end)'; %开盘前40分钟分钟数据收盘价 for j = 1:4 r = emdtvalue(r); %emdt处理 end %% 对数波动能量比 R = log(std(close1(end-mlen:end)'-r)/std(r)); record(i).R = record(i).R+R; Rmin = record(i).R/record(i).daynum; %每天均值 record(i).trend = 0; if R<=Rmin record(i).trend = 1; %趋势行情 elseif R>Rmin*k2 record(i).trend = -1; %反转行情 end if record(i).trend==1 && record(i).daynum>10 %今日属于趋势行情 if close1(end)>close1(end-mlen) && close1(end)>mean(closeD1(end-3:end)) %开多条件: 当前close》40分钟前close & 当前close》最近3日均值 traderDirectBuyV2(1,i,shareNum(i),0,'market','buy11');%开多 elseif close1(end)<close1(end-mlen) && close1(end)<mean(closeD1(end-3:end)) %开空条件: 当前close《40分钟前close & 当前close《最近3日均值 traderDirectSellV2(1,i,shareNum(i),0,'market','sell11');%开空 end end end %% 判断平仓 移动止损出场 if mp(i) >0 if close1(end)-record.entryP(i)<-record.entryP(i)*stoprate(i) %止损 traderPositionToV2(1,i,0,0,'market','buy1');%清仓 elseif close1(end)-record.entryP(i)>record.entryP(i)*stoprate(i) record.entryP(i) = close1(end); end elseif mp(i)<0 if close1(end)-record.entryP(i)>record.entryP(i)*stoprate(i) traderPositionToV2(1,i,0,0,'market','buy1');%清仓 elseif close1(end)-record.entryP(i)<-record.entryP(i)*stoprate(i) record.entryP(i) = close1(end); end end %% 开盘40分钟到收盘 if mp(i) == 0&&nowTime >915+mlen&&nowTime<1430&&record.reverse(i) == 0&&record(i).trend==-1 if close1(end)>R2 record.reverse(i) = 1;%反转标记 当日内最高价超过上观察价 elseif close1(end)<S2 record.reverse(i) = -1;%反转标记 当日内最低价跌破下观察价 end if (record.reverse(i) == -1&&close1(end)>S1) traderDirectBuyV2(1,i,shareNum(i),0,'market','buy11');%开多 elseif (record.reverse(i) == 1&&close1(end)<R1) traderDirectSellV2(1,i,shareNum(i),0,'market','sell11');%开空 end end end end end %% 计算EMD function value=emdtvalue(r) MaxIndex = find(diff(sign(diff(r)))==-2)+1;%头部拐点 MaxValue = r(MaxIndex); MinIndex = find(diff(sign(diff(r)))==+2)+1;%底部拐点 MinValue = r(MinIndex); %pp=csape(x,y,'变界类型','边界值'),生成各种边界条件的三次样条插值. 其中,(x,y)为数据向量 PPupper = csape(MaxIndex,MaxValue,'variational');%上包络线 PPlower = csape(MinIndex,MinValue,'variational');%下包络线 x = 1:length(r); value = r-0.5*(ppval(PPupper,x)+ppval(PPlower,x))';%原数据 - 上下包络线拟合值的均值 end ``` <br> ### Alpha001 ### ------------ 执行脚本: ```matlab % 适用于Matlab 2013及以上 % 1日调仓,计算标的alpha001值,标准化处理后做多因子值》0 clc; clear all; tic; %% 读取证券数据: targetList = traderGetCodeList('ZZ500'); %% 赋值: len=20; len1=5; freq = 1; initTime = 20160101; %开始时间 lastTime = 20170101; %结束时间 %% 在回测时设置初始资本10 000 000元、手续费率0.000026、无风险利率0.02、滑价0、默认1下一个bar的开盘价、默认0成交价、默认0直接成交 traderSetBacktest(10000000,0.000026,0.02,0,1,0,0); AccountList(1) = {'StockBackReplay'}; traderRunBacktestV2('alpha001BackTest',@alpha001,{len,len1,freq,initTime},AccountList,targetList,'day',freq,initTime,lastTime,'FWard'); toc; ``` 主策略函数: ```matlab function alpha001(bInit,bDayBegin,cellPar) %基于alpha001因子选股策略: %% %全局变量: global g_idxK; global g_idxAlpha; global TLen; global daynum; %赋值: len = cellPar{1}; len1 = cellPar{2}; freq = cellPar{3}; initTime = cellPar{4}; %% if bInit %初始化读取所有数据: traderSetParalMode(false); %并行执行时方便设置断点调试 g_idxK=traderRegKData('day',freq); %数据提取 TLen = length(g_idxK(:,1)); %股票总数量 g_idxAlpha=traderRegUserIndi(@MyAlpha001,{g_idxK,len,len1}); %策略逻辑实现 daynum = 0; else %主题策略部分: if bDayBegin daynum = daynum+1; %记录时间。 end if 0==mod(daynum,20) %整20天,调仓周期 %提取数据: getAlpha = traderGetRegUserIndi(g_idxAlpha,1); %alpha001因子值读取 if (sum(isnan(getAlpha))>0) %有nan跳过 return; else mp=traderGetAccountPositionV2(1,1:TLen); %仓位读取 [~,MarketCap,~,~,~] = traderGetAccountInfoV2(1); %获取动态权益 num = length(find(getAlpha>0)); %做多alpha》0的所有标的 Stock_flow = ((MarketCap)*0.4)/num; % 每只股票分配的资金 if (daynum==20) %第一笔交易开仓设置: for i=1:TLen if(getAlpha(i)>0) getKData = traderGetRegKData(g_idxK(i,:),2,false);%获取K线数据 shareNum = 500*floor((Stock_flow/getKData(5,end))/100); %计算购买股票数量; OrderID=traderPositionToV2(1,i,shareNum,0,'market','buy');%买入开仓操作; end end else %调仓操作: for i=1:TLen if(mp(i)>0) if(getAlpha(i)<0) OrderID=traderPositionToV2(1,i,0,0,'market','sell');%卖出调仓操作; end elseif(0==mp(i)) if(getAlpha(i)>0) getKData = traderGetRegKData(g_idxK(i,:),2,false);%获取K线数据 shareNum = 500*floor((Stock_flow/getKData(5,end))/100); %计算购买股票数量; OrderID=traderPositionToV2(1,i,shareNum,0,'market','buy');%买入开仓操作; end end end end end end end end %% %策略逻辑实现: function alpha = MyAlpha001(cellPar,bpPFCell) %赋值: idxK = cellPar{1}; %数据的一种存储格式变量 len = cellPar{2}; %回报率作标准差的数量 len1 = cellPar{3}; %天数 [targetNum,~] = size(idxK); %股票总数量 s = nan(1,targetNum); %每只股票最大值的索引(权重) alpha = nan(1,targetNum); for i=1:targetNum x1 = nan(1,len1); %收盘价或前20天的回报率的标准差 %提取数据: getKData = traderGetRegKData(idxK(i,:),len+len1+1,false,bpPFCell);%共8行数据,每行对应的数据为:(1)时间、(2)开盘价、(3)最高价、(4)最低价、(5)收盘价、(6)成交量、(7)成交金额、(8)持仓量。 [~,KLen] = size(getKData); %逻辑计算: if KLen>(len+len1) %step1:计算len1天的每天回报率: % disp(['打印输出第',num2str(i),'只证券的收盘价:']) %调试专用 getKData(5,:); %判断getKData(5,1)是否为空,如果为空,则调到下一层循环。 if (1==isnan(getKData(5,1))) continue; else returns = (getKData(5,2:end)-getKData(5,1:end-1))./(getKData(5,2:end)); %日收益率 %step2:判断: for j=1:len1 if(returns(end-j)<0) x1(j) = std(returns(end-j-len+1:end-j)); %前20天的回报率的标准差 else x1(j) = getKData(5,end-j); %收盘价 end end %step3:差异平方放大处理: x2 = sign(x1).*(abs(x1).^2); % disp(['打印输出第',num2str(i),'只证券的5天的收盘价或标准差:']) %调试专用 % x2 %step4:找出x2的最大值,并返回索引: p = find(max(x2)==x2); s(i) = p(end); % disp(['打印输出第',num2str(i),'只证券的5天的最大值索引序号:']) %调试专用 % s end else % disp('第',num2str(i),'只股票数据不够'); continue; end end %判断s是否存在nan,存在则退出: if (sum(isnan(s)) > targetNum/2) %过半数nan % disp(['打印输出第',num2str(i),'只证券的5天的最大值索引序号:']) %调试专用 % s return; else p =find(isnan(s)==1); s(p) = -inf;%nan赋值负无穷 %step5:排序并返回其对应排名的boolean值: [~,l] = sort(s); b = 0:length(s)-1; alpha(l) = b; %alpha001因子值 alpha = alpha/max(b); alpha = alpha - 0.5; % disp(['打印输出第',num2str(i),'只证券的alpha001因子值:']) %调试专用 % alpha end end %% ``` <br> ### Alpha009 ### ------------ 执行脚本: ```matlab % 适用于Matlab 2013及以上 % 1日调仓,计算股票因子alpha009,做多最大的前20个 clc; clear all; targetList = traderGetCodeList('ZZ500');%中证500 n = length(targetList); %n = 50; %股票数量定义(可自定义) Freq = 1; len = 20;%数据设置,数据量有达到一定天数才进行回测 len1 = 1;%len1设置调仓周期 len2 = 1; num = 5; %选排前num个股票 AccountList(1) = {'StockBackReplay'}; %股票 traderRunBacktestV2('alpha009',@alpha009,{len,len1,len2,num},AccountList,targetList,'day',Freq,20180102,20180630,'FWard'); ``` 主策略函数: ```matlab function alpha009(bInit,bDayBegin,cellPar) % alpha#9 % num 选股数量 %% 外部和全局参数声明 len = cellPar{1}; len1 = cellPar{2}; len2 = cellPar{3}; num = cellPar{4}; global g_idxKDay; global g_idxAlpha9; global TLen; % 注册日数据的index.日数据是分钟数据的合成.如在日中获取,当日的数据仅是当日已出现数据的合成,不包含之后的数据。 %% 初始化回测帐户 if bInit traderSetParalMode(false); %默认是true,因子计算函数并行执行,速度快,不能调试,false串行执行可以设断点调试 g_idxKDay = traderRegKData('day',1); % 只有注册之后才能获取数据。分钟数据的获取方法为 traderRegKData('min',1)。后面的数字是刷新频率。 g_idxAlpha9 = traderRegUserIndi(@getalpha9,{g_idxKDay,len,len1,len2,num}); %alpha9因子计算 TLen = length(g_idxKDay(:,1)); else alpha9 = traderGetRegUserIndi(g_idxAlpha9,1);%获取股票alpha值 [mp,~,~]=traderGetAccountPositionV2(1,1:TLen);%获取仓位信息 [~,MarketCap,~,~,~] = traderGetAccountInfoV2(1); %获取动态权益 nannum = sum(isnan(alpha9));%计算无效alpha值数量 if nannum+num>TLen return; %若未达到数据要求则跳过 end X = zeros(TLen,1); for i = 1:TLen X(i) = -alpha9(i); end [~,I] = sort(X); %排序 SelectedID = I(1:num); % 选出alpha最大的一组股票 Stock_flow = ((MarketCap)*1)/num; % 每只股票分配的资金 stock_list=1:500; a1=ismember(stock_list,SelectedID); target_position = zeros(TLen,1); %初始化目标仓位 target_position (a1) = Stock_flow; %得到最新一起目标仓位 for i=1:TLen dataDay = traderGetRegKData(g_idxKDay(i,:),1,false); % 取当日数据 dataDay(:,any(isnan(dataDay),1))=[]; % 数据长度足够;数据非空;当日的成交量不为0;当日高收不同----去除停牌,涨跌停情况 if isempty(dataDay) || dataDay(6,end) ==0 || dataDay(3,end)- dataDay(4,end)==0 continue; end shareNum = 500*floor((target_position(i)/dataDay(5,end))/100); order(i) = traderPositionToV2(1,i,shareNum,0,'market','rebalance'); end end end function alpha=getalpha9(cellPar,bpPFCell) %调用该函数的参数将会全部被赋给cellPar %bpPFCell为一个时间序列,标记特定的刷新时刻 %% %参数声明 idxK =cellPar{1}; len = cellPar{2}; len1 =cellPar{3}; len2 = cellPar{4}; num = cellPar{5}; %% %函数计算 %计算出alpha9 [targetNum,~]=size(idxK); %股票种类数量 alpha = nan(1,targetNum); %预载入 close = nan(targetNum ,len); volume= nan(targetNum ,len); for i=1:targetNum dataDay = traderGetRegKData(idxK(i,:),len,false,bpPFCell);%获取信息 close(i,:) = dataDay(5,:);%收盘价 volume(i,:) = dataDay(6,:);%成交量 end for i=1:targetNum %计算 if ((sum(isnan(close(i,:)))>0) || volume(i,end)==0) alpha(i) = nan; %disp(dataDay); else delta = diff(close(i,:));%相邻的两个收盘价 temp1 = min(delta(end-4:end));%5日最小值 if temp1>0 alpha(i) =delta(end);%顺趋势 else temp2 = max(delta(end-4:end)); if temp2<0 %5日最大值 alpha(i)=delta(end);%顺趋势 else alpha(i)=-delta(end);%反趋势 end end end end end ``` <br> ### 纯价指标选股 ### ------------ 执行脚本: ```matlab % 适用于Matlab 2013及以上 clear all; clc; begintime = 20160902; %起止时间段 20150102 endtime = 20181230; targetList = traderGetCodeList('HS300'); %以沪深300为选股资产池。 Freq = 1; %每天的执行次数。 moneys = 100000000; traderSetBacktest(moneys,0.001,0.02,0,1,0,0); AccountList(1) = {'StockBackReplay'}; traderRunBacktestV2('jjhx',@jjhx,{Freq,moneys},AccountList,targetList,'day',Freq,begintime,endtime,'FWard'); ``` 主策略函数: ```matlab function jjhx(bInit,bDayBegin,cellPar) global g_idxK; %获取的注册数据index global TLen; %股票支数 global s; Freq =cellPar{1}; %回测频率; moneys = cellPar{2}; if bInit traderSetParalMode(false);%默认是true,因子计算函数并行执行,速度快,不能调试,false串行执行可以设断点调试 g_idxK = traderRegKData('day',Freq); %注册数据,获得index; TLen = length(g_idxK(:,1)); %一共有多少条数据(股票数量) s.numbers = 0; % s.atr = traderRegUserIndi(@atr,{g_idxK,21,TLen});%计算ATR指标 % s.ma = traderRegUserIndi(@ma,{g_idxK,233,TLen});%计算MA指标 s.atr = traderRegUserIndi(@atr,{g_idxK,21,TLen});%计算ATR指标 s.ma = traderRegUserIndi(@ma,{g_idxK,233,TLen});%计算MA指标 s.weight_plus = traderRegUserIndi(@weight_plus,{g_idxK,34,TLen});% s.buyprice = zeros(TLen,1); s.times = zeros(TLen,1); s.stopprice = zeros(TLen,1); s.counts = zeros(TLen,1); s.portfolio = []; s.beixuan_portfolio = []; s.beixuan_times = nan(TLen,1); s.selltimes = zeros(TLen,1); else dbstop if error; if s.numbers < 233 %因为在之间的计算时间的时候,用于计算的 s.numbers = s.numbers + 1; return end begindatas = traderGetRegKData(g_idxK,9,false); %获取过去9天的日数据在一天的开始时间获取开盘价,和一分钟的实时价格 1.time 2.open 3.high 4.low 5.close 6.vol 7.turn 8.oi beginclose = begindatas(5:8:end,:);%收盘价 begintime = begindatas(1:8:end,end);%收盘时间 begintime = floor(max(unique(begintime(~isnan(begintime)))));%当前时间 value_weight_plus = traderGetRegUserIndi(s.weight_plus,21);%获取过去21天加权均价 value_atr = traderGetRegUserIndi(s.atr,1);%获取当天atr指标 value_ma = traderGetRegUserIndi(s.ma,3);%获取最近3天ma指标 ma_close55 = value_ma(1:TLen,:);%55日ma ma_close233 = value_ma(TLen + 1 : 2 * TLen,:);%233日ma %% 进行卖出判断和加仓判断。 coney = 200000; mp = traderGetAccountPositionV2(1,1:TLen);%获取各标的仓位信息 positions1 = find(mp~= 0);%已有仓位序号 if ~isempty(positions1) %存在已有仓位 p1000 = find(value_weight_plus(:,end) < value_weight_plus(:,end-1)); %当天加权均价下跌 p1001 = find(value_weight_plus(:,end-1) < value_weight_plus(:,end-2));%昨天加权均价下跌 p1002 = find(value_weight_plus(:,end-2) < value_weight_plus(:,end-3));%前天加权均价下跌 p1003 = find(cell2mat(arrayfun(@(n) (length(find((value_weight_plus(n,2:end) - value_weight_plus(n,1:end-1)) > 0)) / 20) < 0.4,1:TLen,'un',false)) == 1);%过去20天超过60%天是下跌的 p100 = intersect(intersect(intersect(p1000,p1001),p1002),p1003); p10 = intersect(find(beginclose(:,end) <= s.buyprice - 0.5 * value_atr),p100); p11 = union(find(beginclose(:,end) <= s.stopprice),p10);%卖出条件: 达到止损价 或者 (连续均价下跌 且 过去20天超过60%天是下跌的) positions12 = intersect(p11,positions1);% 需要平仓的仓位序号 % if ~isempty(positions12) % arrayfun(@(n) traderDirectSellV2(1,n,mp(n),0,'market','sell2'),positions12,'un',false);%平仓 % s.buyprice(positions12) = 0;%记录清0 % s.times(positions12) = begintime; % s.stopprice(positions12) = 0;%记录清0 % s.counts(positions12) = 0;%记录清0 % s.selltimes(positions12) = begintime; % end p15 = find(beginclose(:,end) >= 1.1 * s.buyprice);%符合加仓条件的股票,盈利超过10% p16 = intersect(intersect(intersect(p15,positions1),find(s.buyprice ~= 0)),find(s.times ~= begintime)); %加仓:已有仓位股票盈利超过10% 且 今日无操作 if ~isempty(p16) z1 = floor(coney ./ (100 * beginclose(p16,end))) * 100; %加仓数 p16 = p16(~isnan(z1)); z1 = z1(~isnan(z1)); s.stopprice(p16) = beginclose(p16,end) * 0.9618;%新止损价 s.buyprice(p16) = beginclose(p16,end);%新成本价 s.counts(p16) = s.counts(p16) + 1;%加仓次数+1 p161 = find(s.counts(p16) >= 2); if ~isempty(p161) s.stopprice(p16(p161)) = beginclose(p16(p161),end); end p16 = p16(z1 ~= 0); p17 = intersect(intersect(p16,find(s.times ~= begintime)),find(s.counts <= 4));%加仓次数不超过4次 if ~isempty(p17) arrayfun(@(n) traderDirectBuyV2(1,n,(floor(coney / beginclose(n,end)) - rem(floor(coney / beginclose(n,end)),100)),0,'market','buys1'),p17,'un',false); %加仓 end s.times(p16) = begintime; end end %% 加入备选池的判断; p31 = find(ma_close55(:,end-2) > ma_close233(:,end-2));%55日均线大于233日均线 p32 = find(ma_close55(:,end-1) > ma_close233(:,end-1)); p33 = find(ma_close55(:,end) > ma_close233(:,end)); p34 = intersect(intersect(p31,p32),p33); %符合进入备选池条件 s.beixuan_portfolio = union(s.beixuan_portfolio,p34); %总备选池 s.beixuan_times(p34) = begintime;% 记录进入备选池的时间 p35 = find(begintime - s.beixuan_times >= 150); %进入备选池超过150天 p36 = find(begintime - s.beixuan_times < 150); s.beixuan_portfolio = intersect(s.beixuan_portfolio,p36); %剔除超过150天的备选池股票 s.beixuan_times(p35) = []; %% 买入判断 positions2 = intersect(intersect(intersect(intersect(find(mp == 0),s.beixuan_portfolio),find(s.times ~= begintime)),find(s.counts == 0)),find(begintime - s.selltimes > 23));% 买入备选: 空仓&出于备选池中 & 距离上次卖出超过23天 if ~isempty(positions2) p2000 = find(value_weight_plus(:,end) > value_weight_plus(:,end-1));%加权均价上涨 p2001 = find(value_weight_plus(:,end-1) > value_weight_plus(:,end-2)); p2002 = find(value_weight_plus(:,end-2) > value_weight_plus(:,end-3)); p2003 = find(cell2mat(arrayfun(@(n) (length(find((value_weight_plus(n,2:end) - value_weight_plus(n,1:end-1)) > 0)) / 20) > 0.6,1:TLen,'un',false)) == 1);%过去20天超过60%天是上涨的 p20 = intersect(intersect(intersect(p2000,p2001),p2002),p2003); positions22 = intersect(positions2,p20);%符合买入条件的股票 if ~isempty(positions22) z2 = floor(coney ./(100 * beginclose(positions22,end))) * 100;%买入数量 positions23 = positions22(~isnan(z2)); z2 = z2(~isnan(z2)); positions24 = positions23(z2 ~= 0); if ~isempty(positions24) arrayfun(@(n) traderDirectBuyV2(1,n,500*(floor(coney / beginclose(n,end)) - rem(floor(coney / beginclose(n,end)),100)),0,'market','buys1'),positions24,'un',false);%买入股票 s.buyprice(positions24) = beginclose(positions24,end);%记录开仓价 s.stopprice(positions24) = beginclose(positions24,end) * 0.9382;%止损价 end end end end end function value = ma(cellPar,bpPFCell) idxK = cellPar{1}; %数据获取;指标的数据 N=cellPar{2}; %滚动天数; datas = traderGetRegKData(idxK,N,false,bpPFCell); %获取N天的数据,相应的天数获取相应的数据量; close55 = mean(datas(5:8:end,end-54:end),2); close233 = mean(datas(5:8:end,end-232:end),2); value = [close55;close233]; end function value = atr(cellPar,bpPFCell) % ATR取20日计算 idxK = cellPar{1}; %数据获取;指标的数据 N=cellPar{2}; %滚动天数; 20 len = cellPar{3}; %一共计算了多少股票; hs300是300支股票 datas = traderGetRegKData(idxK,N+1,false,bpPFCell); %获取N天的数据,相应的天数获取相应的数据量; high = datas(3:8:end,:); low = datas(4:8:end,:); close = datas(5:8:end,:); z1 = high(:,2:end) - low(:,2:end); z2 = abs(close(:,1:end-1) - high(:,2:end)); z3 = abs(close(:,1:end-1) - low(:,2:end)); tr = max(max(z1,z2),z3); value = mean(tr,2); end function value = weight_plus(cellPar,bpPFCell) idxK = cellPar{1}; %数据获取;指标的数据 N=cellPar{2}; %用于滚动的是34天。 TLen = cellPar{3}; %股票支数 datas = traderGetRegKData(idxK,N,false,bpPFCell); %获取N天的数据,相应的天数获取相应的数据量; high = datas(3:8:end,:); low = datas(4:8:end,:); close = datas(5:8:end,:); volume = datas(6:8:end,:); price = (high + low + close) /3 ; prices = price .* volume; values1 = cell2mat(arrayfun(@(n) prices(n,:),1 : TLen,'un',false)); values2 = cell2mat(arrayfun(@(n) volume(n,:),1 : TLen,'un',false)); value = values1 ./ values2 ; end ``` <br> ### 成分股多空占比指数择时 ### ------------ 执行脚本: ```matlab % 适用于Matlab 2013及以上 %对沪深300成分股进行价格观察,通过均线及均线差分情况判断成分股中多头信号及空头信号占比是否满足做多沪深300的阈值 clear all; clc; targetList(1).Market ='CFFEX'; targetList(1).Code ='IF0000'; A = traderGetCodeList('HS300'); for m = 1:length(A) targetList(m+1).Market = A(m).Market; targetList(m+1).Code = A(m).Code; end len = 3; %回顾周期 len2 = 3; %满足条件的个数 AccountList(1) = {'FutureBackReplay'}; traderRunBacktestV2('test_qianhai_renshou',@test,{len,len2},AccountList,targetList,'day',1,20170102,20180501,'FWard'); ``` 主策略函数: ```matlab function test(bInit,bDayBegin,cellPar)% global g_idxKDay; global g_idxSig; global TLen; len = cellPar{1};% 数值: 选择的历史对比周期数 len2 = cellPar{2} ``` # Python经典案例 # ### AberrationNew ### ------------ ```python # -*- coding: utf-8 -*- """ ------------------------------------------------- File Name: AberrationNew Description : Author : haoyuan.m date: 2018/10/23 ------------------------------------------------- Change Activity: 2018/10/23: ------------------------------------------------- """ __author__ = 'haoyuan.m' from atrader import * import numpy as np import pandas as pd def init(context): set_backtest(initial_cash=10000000) reg_kdata('min', 5) context.Tlen = len(context.target_list) # 标的个数 context.N = 50 # 窗口长度 context.initial = 10000000 def on_data(context): data = get_reg_kdata(reg_idx=context.reg_kdata[0], length=context.N + 1, fill_up=True, df=True) # 获取所有标的数据 if data['close'].isna().any(): return datalist = [data[data['target_idx'] == x] for x in pd.unique(data.target_idx)] long_positions = context.account().positions['volume_long'] short_positions = context.account().positions['volume_short'] for target in datalist: target_idx = target.target_idx.iloc[0] close = target.close.values std = close.std() mean = close.mean() condi_long = close[-1] > 2 * std + mean # 做多条件 向上突破2倍标准差 condi_short = close[-1] < mean - 2 * std # 做空条件 向下突破2倍标准差 long_close = close[-1] < mean # 平多条件 回落均值以下 short_close = close[-1] > mean # 平空条件 回升均值以上 if (long_positions.iloc[target_idx] > 0) and long_close: # 多出场 order_target_volume(account_idx=0, target_idx=target_idx, target_volume=0, side=1, order_type=2) if (short_positions.iloc[target_idx] > 0) and short_close: # 空出场 order_target_volume(account_idx=0, target_idx=target_idx, target_volume=0, side=2, order_type=2) if (long_positions.iloc[target_idx] == 0) and condi_long: # 多进场 order_target_value(account_idx=0, target_idx=target_idx, target_value=context.initial / context.Tlen, side=1, order_type=2) if (short_positions.iloc[target_idx] == 0) and condi_short: # 空进场 order_target_value(account_idx=0, target_idx=target_idx, target_value=context.initial / context.Tlen, side=2, order_type=2) if __name__ == '__main__': target = ['shfe.rb0000', 'shfe.ru0000'] run_backtest('aberration', 'AberrationNew.py', target_list=target, frequency='min', fre_num=10, begin_date='2018-03-01', end_date='2018-06-01', fq=1) ``` <br> ### BollBreaknew ### ------------ ```python # -*- coding: utf-8 -*- """ ------------------------------------------------- File Name: BollBreaknew Description : Author : haoyuan.m date: 2018/10/25 ------------------------------------------------- Change Activity: 2018/10/25: ------------------------------------------------- """ __author__ = 'haoyuan.m' """ 布林带突破趋势策略 多头入场:当前价格突破布林带上轨 空头入场:当前价格跌破布林带下轨 止损:开仓价格之下N倍ATR 止盈:开仓价格之上N倍ATR """ from atrader import * import numpy as np import pandas as pd import sys try: import talib except: print('请安装TA-Lib库') sys.exit(-1) def init(context): set_backtest(initial_cash=1e7) reg_kdata('day', 1) context.Tlen = len(context.target_list) context.entryP = np.zeros(context.Tlen) * np.nan context.Boll = 10 context.N = 20 context.Boll = 10 context.ATR = 10 context.ATRratio = 0.6 context.initial = 1e7 def on_data(context): data = get_reg_kdata(reg_idx=context.reg_kdata[0], length=context.N + 1, fill_up=True, df=True) if data['close'].isna().any(): return long_positions = context.account().positions['volume_long'] short_positions = context.account().positions['volume_short'] datalist = [data[data['target_idx'] == x] for x in pd.unique(data.target_idx)] for target in datalist: target_idx = target.target_idx.iloc[0] close = target.close.values.astype('float') high = target.high.values.astype('float') low = target.low.values.astype('float') openp = target.open.values.astype('float') upperband, _, lowerband = talib.BBANDS(close, timeperiod=context.Boll, nbdevup=2, nbdevdn=2, matype=0) atr = talib.ATR(high, low, close, timeperiod=context.ATR) if long_positions[target_idx] == 0 and short_positions[target_idx] == 0: context.entryP[target_idx] = np.nan elif np.isnan(context.entryP[target_idx]): context.entryP[target_idx] = openp[-1] # 多单止损 long_close = long_positions[target_idx] > 0 and \ (close[-1] - context.entryP[target_idx]) < -context.ATRratio * atr[-1] # 空单止损 short_close = short_positions[target_idx] > 0 and \ (close[-1] - context.entryP[target_idx]) > context.ATRratio * atr[-1] # 多单止盈 long_profit = long_positions[target_idx] > 0 and \ ((close[-1] - context.entryP[target_idx]) > context.ATRratio * atr[-1]) # 空单止盈 short_profit = short_positions[target_idx] > 0 and \ ((close[-1] - context.entryP[target_idx]) < -context.ATRratio * atr[-1]) # 开仓 long_open = long_positions[target_idx] == 0 and short_positions[target_idx] == 0 and close[-1] > upperband[-1] short_open = long_positions[target_idx] == 0 and short_positions[target_idx] == 0 and close[-1] < lowerband[-1] if long_open: order_target_value(account_idx=0, target_idx=target_idx, target_value=context.initial * 5 / context.Tlen, side=1, order_type=2) print('开多') elif short_open: order_target_value(account_idx=0, target_idx=target_idx, target_value=context.initial * 5 / context.Tlen, side=2, order_type=2) print('开空') # 平仓 if long_close: order_target_volume(account_idx=0, target_idx=target_idx, target_volume=0, side=1, order_type=2) print('多单止损') if long_profit: order_target_volume(account_idx=0, target_idx=target_idx, target_volume=0, side=1, order_type=2) context.entryP[target_idx] = close[-1] print('多单止盈') if short_close: order_target_volume(account_idx=0, target_idx=target_idx, target_volume=0, side=2, order_type=2) print('空单止损') if short_profit: order_target_volume(account_idx=0, target_idx=target_idx, target_volume=0, side=2, order_type=2) context.entryP[target_idx] = close[-1] print('空单止盈') if __name__ == '__main__': target = ['cffex.if0000', 'shfe.rb0000'] run_backtest('BollBreaknew', 'BollBreaknew.py', target_list=target, frequency='min', fre_num=30, begin_date='2018-02-01', end_date='2018-06-01', fq=1) ``` <br> ### Fama多因子### ------------ ```python # -*- coding: utf-8 -*- """ ------------------------------------------------- File Name: 多因子 Description : Author : haoyuan.m date: 2018/10/9 ------------------------------------------------- Change Activity: 2018/10/9: ------------------------------------------------- """ __author__ = 'haoyuan.m' from atrader import * import numpy as np import pandas as pd import datetime as dt ''' 本策略每隔1个月定时触发,根据Fama-French三因子模型对每只股票进行回归,得到其alpha值。 假设Fama-French三因子模型可以完全解释市场,则alpha为负表明市场低估该股,因此应该买入。 策略思路: 计算市场收益率、个股的账面市值比和市值,并对后两个进行了分类, 根据分类得到的组合分别计算其市值加权收益率、SMB和HML. 对各个股票进行回归(假设无风险收益率等于0)得到alpha值. 选取alpha值小于0并为最小的10只股票进入标的池 平掉不在标的池的股票并等权买入在标的池的股票 回测数据:上证50在回测日期前一天的成份股 回测时间:2017-09-01 到2018-05-31 ''' def init(context): set_backtest(initial_cash=10000000) # 设置回测初始信息 reg_kdata('day', 1) # 注册K线数据 reg_factor(['PB', 'NegMktValue']) days = get_trading_days('SSE', '2017-09-01', '2018-06-01') months = np.vectorize(lambda x: x.month)(days) month_begin = days[pd.Series(months) != pd.Series(months).shift(1)] context.month_begin = pd.Series(month_begin).dt.strftime('%Y-%m-%d').tolist() context.date = 20 # 设置开仓的最大资金量 context.ratio = 0.8 # 账面市值比的大/中/小分类 context.BM_BIG = 3.0 context.BM_MID = 2.0 context.BM_SMA = 1.0 # 市值大/小分类 context.MV_BIG = 2.0 context.MV_SMA = 1.0 # 计算市值加权的收益率,MV为市值的分类,BM为账目市值比的分类 def market_value_weighted(stocks, MV, BM): select = stocks[(stocks.NegMktValue_Class == MV) & (stocks.BM_Class == BM)] market_value = select['NegMktValue'].values mv_total = np.sum(market_value) mv_weighted = [mv / mv_total for mv in market_value] stock_return = select['return'].values # 返回市值加权的收益率的和 return_total = [] for i in range(len(mv_weighted)): return_total.append(mv_weighted[i] * stock_return[i]) return_total = np.sum(return_total) return return_total def on_data(context): if dt.datetime.strftime(context.now, '%Y-%m-%d') not in context.month_begin: # 调仓频率为月 return factor = get_reg_factor(context.reg_factor[0], target_indices=[x for x in range(50)], length=1, df=True) PB = factor[factor['factor'] == 'PB'].rename(columns={'value': 'PB'}).drop('factor', axis=1).set_index('target_idx') NegMktValue = factor[factor['factor'] == 'NegMktValue'].rename(columns={'value': 'NegMktValue'}). \ drop('factor', axis=1).set_index('target_idx') # 计算账面市值比,为P/B的倒数 PB['BM'] = 1 / PB.PB # 计算市值的50%的分位点,用于后面的分类 size_gate = NegMktValue['NegMktValue'].quantile(0.50) # 计算账面市值比的30%和70%分位点,用于后面的分类 bm_gate = [PB['BM'].quantile(0.30), PB['BM'].quantile(0.70)] x_return = [] kdata = get_reg_kdata(context.reg_kdata[0], target_indices=[x for x in range(50)], length=context.date + 1, fill_up=True, df=True) if kdata['close'].isna().any(): # 如果数据不满21天则跳过 return kdatalist = [kdata[kdata['target_idx'] == x] for x in pd.unique(kdata.target_idx)] for target in kdatalist: # 计算收益率 stock_return = target.close.iloc[-1] / target.close.iloc[0] - 1 target_idx = target.target_idx.iloc[0] BM = PB['BM'].loc[target_idx] market_value = NegMktValue['NegMktValue'].loc[target_idx] # 获取[股票代码. 股票收益率, 账面市值比的分类, 市值的分类, 流通市值] if BM < bm_gate[0]: if market_value < size_gate: label = [target_idx, stock_return, context.BM_SMA, context.MV_SMA, market_value] else: label = [target_idx, stock_return, context.BM_SMA, context.MV_BIG, market_value] elif BM < bm_gate[1]: if market_value < size_gate: label = [target_idx, stock_return, context.BM_MID, context.MV_SMA, market_value] else: label = [target_idx, stock_return, context.BM_MID, context.MV_BIG, market_value] elif market_value < size_gate: label = [target_idx, stock_return, context.BM_BIG, context.MV_SMA, market_value] else: label = [target_idx, stock_return, context.BM_BIG, context.MV_BIG, market_value] if len(x_return) == 0: x_return = label else: x_return = np.vstack([x_return, label]) stocks = pd.DataFrame(data=x_return, columns=['target_idx', 'return', 'BM_Class', 'NegMktValue_Class', 'NegMktValue']) stocks.set_index('target_idx', inplace=True) # 获取小市值组合的市值加权组合收益率 smb_s = (market_value_weighted(stocks, context.MV_SMA, context.BM_SMA) + market_value_weighted(stocks, context.MV_SMA, context.BM_MID) + market_value_weighted(stocks, context.MV_SMA, context.BM_BIG)) / 3 # 获取大市值组合的市值加权组合收益率 smb_b = (market_value_weighted(stocks, context.MV_BIG, context.BM_SMA) + market_value_weighted(stocks, context.MV_BIG, context.BM_MID) + market_value_weighted(stocks, context.MV_BIG, context.BM_BIG)) / 3 smb = smb_s - smb_b # 获取大账面市值比组合的市值加权组合收益率 hml_b = (market_value_weighted(stocks, context.MV_SMA, context.BM_BIG) + market_value_weighted(stocks, context.MV_BIG, context.BM_BIG)) / 2 # 获取小账面市值比组合的市值加权组合收益率 hml_s = (market_value_weighted(stocks, context.MV_SMA, context.BM_SMA) + market_value_weighted(stocks, context.MV_BIG, context.BM_SMA)) / 2 hml = hml_b - hml_s market_close = get_reg_kdata(context.reg_kdata[0], target_indices=[50], length=context.date + 1, fill_up=True, df=True).close market_return = market_close.iloc[-1] / market_close.iloc[0] - 1 coff_pool = [] # 对每只股票进行回归获取其alpha值 for stock in stocks.index: x_value = np.array([[market_return], [smb], [hml], [1.0]]) y_value = np.array([stocks['return'][stock]]) # OLS估计系数 coff = np.linalg.lstsq(x_value.T, y_value)[0][3] coff_pool.append(coff) # 获取alpha最小并且小于0的10只的股票进行操作(若少于10只则全部买入) stocks['alpha'] = coff_pool stocks = stocks[stocks.alpha < 0].sort_values(by='alpha').head(10) symbols_pool = stocks.index.tolist() positions = context.account().positions # 平不在标的池的股票 for target_idx in positions.target_idx.astype(int): if target_idx not in symbols_pool: if positions['volume_long'].iloc[target_idx] > 0: order_volume(account_idx=0, target_idx=target_idx, volume=int(positions['volume_long'].iloc[target_idx]), side=2, position_effect=2, order_type=2, price=0) # print('市价单平不在标的池的', context.target_list[target_idx]) # 获取股票的权重 percent = context.ratio / len(symbols_pool) # 买在标的池中的股票 for target_idx in symbols_pool: order_target_percent(account_idx=0, target_idx=int(target_idx), target_percent=percent, side=1, order_type=2, price=0) # print(context.target_list[int(target_idx)], '以市价单调多仓到仓位', percent) if __name__ == '__main__': begin = '2017-08-01' end = '2018-05-31' cons_date = dt.datetime.strptime(begin, '%Y-%m-%d') - dt.timedelta(days=1) hs300 = get_code_list('sz50', cons_date)[['code', 'weight']] targetlist = list(hs300['code']) targetlist.append('sse.000016') run_backtest(strategy_name='Fama多因子', file_path='FamaMultiFactors.py', target_list=targetlist, frequency='day', fre_num=1, begin_date=begin, end_date=end, fq=1) ``` <br> ### 指数增强### ------------ ```python # -*- coding: utf-8 -*- """ ------------------------------------------------- File Name: 指数增强 Description : Author : haoyuan.m date: 2018/10/11 ------------------------------------------------- Change Activity: 2018/10/11: ------------------------------------------------- """ __author__ = 'haoyuan.m' from atrader import * import numpy as np import pandas as pd import datetime as dt ''' 本策略以0.8为初始权重跟踪指数标的沪深300中权重大于0.35%的成份股. 个股所占的百分比为(0.8*成份股权重/所选股票占沪深300的总权重)*100%.然后根据个股是否 连续上涨5天;连续下跌5天 来判定个股是否为强势股/弱势股,并对其把权重由0.8调至1.0或0.6 回测数据为:HS300中权重大于0.35%的成份股 回测时间为: ''' def init(context): set_backtest(initial_cash=10000000) reg_kdata('day', 1) context.ratio = 0.8 context.cons_date = '2016-12-31' context.hs300 = get_code_list('hs300', context.cons_date)[['code', 'weight']] context.hs300 = context.hs300[context.hs300.weight > 0.35] context.sum_weight = context.hs300.weight.sum() print('选择的成分股权重总和为: ', context.sum_weight, '%') def on_data(context): positions = context.account().positions['volume_long'] #total_asset = context.account().cash['total_asset'].iloc[0] data = get_reg_kdata(reg_idx=context.reg_kdata[0], length=6, fill_up=True, df=True) if data['close'].isna().any(): return datalist = [data[data['target_idx'] == x] for x in pd.unique(data.target_idx)] for target in datalist: target_idx = target.target_idx.iloc[0] position = positions.iloc[target_idx] if position == 0: buy_percent = context.hs300.iloc[target_idx]['weight'] / context.sum_weight * context.ratio order_target_percent(account_idx=0, target_idx=target_idx, target_percent=buy_percent, side=1, order_type=2, price=0) # print(context.now, context.target_list[target_idx], '以市价单开多仓至仓位:', buy_percent * 100, '%') else: # 获取过去5天的价格数据,若连续上涨则为强势股,权重+0.2;若连续下跌则为弱势股,权重-0.2 recent_data = target['close'].tolist() if all(np.diff(recent_data) > 0): buy_percent = context.hs300.iloc[target_idx]['weight'] / context.sum_weight * (context.ratio + 0.2) order_target_percent(account_idx=0, target_idx=target_idx, target_percent=buy_percent, side=1, order_type=2, price=0) # print('强势股', context.target_list[target_idx], '以市价单调多仓至仓位:', buy_percent * 100, '%') elif all(np.diff(recent_data) < 0): buy_percent = context.hs300.iloc[target_idx]['weight'] / context.sum_weight * (context.ratio - 0.2) order_target_percent(account_idx=0, target_idx=target_idx, target_percent=buy_percent, side=1, order_type=2, price=0) # print('弱势股', context.target_list[target_idx], '以市价单调空仓至仓位:', buy_percent * 100, '%') if __name__ == '__main__': begin = '2017-01-01' end = '2018-01-01' cons_date = dt.datetime.strptime(begin, '%Y-%m-%d') - dt.timedelta(days=1) hs300 = get_code_list('hs300', cons_date)[['code', 'weight']] targetlist = hs300[hs300.weight > 0.35]['code'] run_backtest(strategy_name='指数增强', file_path='ExponentialEnhancement.py', target_list=targetlist, frequency='day', fre_num=1, begin_date=begin, end_date=end, fq=1) ``` <br> ### 日内回转交易### ------------ ```python # -*- coding: utf-8 -*- """ ------------------------------------------------- File Name: 日内回转交易 Description : Author : haoyuan.m date: 2018/10/16 ------------------------------------------------- Change Activity: 2018/10/16: ------------------------------------------------- """ __author__ = 'haoyuan.m' ''' 本策略首先买入SHSE.600000股票10000股 随后根据60s的数据计算MACD(12,26,9), 在MACD>0的时候买入100股;在MACD<0的时候卖出100股 但每日操作的股票数不超过原有仓位,并于收盘前把仓位调整至开盘前的仓位 回测数据为:SHSE.600000的60s数据 回测时间为:2018-08-01 到2018-10-01 ''' from atrader import * import numpy as np import pandas as pd import datetime as dt import sys try: import talib except: print('请安装TA-Lib库') sys.exit(-1) def init(context): # 用于判定第一个仓位是否成功开仓 context.first = 0 set_backtest(initial_cash=100000, stock_cost_fee=0.1) reg_kdata('min', 1) # 日内回转每次交易100股 context.trade_n = 100 # 获取昨今天的时间 context.day = [0, 0] # 用于判断是否触发了回转逻辑的计时 context.ending = 0 # 需要保持的总仓位 context.total = 10000 def on_data(context): bar = get_current_bar() if context.first == 0: # 最开始配置仓位 # 购买10000股浦发银行股票 order_volume(account_idx=0, target_idx=0, volume=context.total, side=1, position_effect=1, order_type=2, price=0) print(context.now, context.target_list[0], '以市价单开多仓10000股') context.first = 1. day = bar.time_bar.iloc[0] context.day[-1] = day.day # 每天的仓位操作 context.turnaround = [0, 0] return # 更新最新的日期 day = bar.time_bar.iloc[0] context.day[0] = day.day # 若为新的一天,则重置标记信息。 if context.day[0] != context.day[-1]: context.ending = 0 context.turnaround = [0, 0] # 如果一天结束,则 if context.ending == 1: return # 若有可用的昨仓则操作 if context.total >= 0: # 获取时间序列数据 recent_data = get_reg_kdata(reg_idx=context.reg_kdata[0], length=35, fill_up=True, df=True).close if recent_data.isna().any(): return macd = talib.MACD(recent_data.astype(float))[2].iloc[-1] # 根据MACD>0则开仓,小于0则平仓 if macd > 0: # 多空单向操作都不能超过昨仓位,否则最后无法调回原仓位 if (context.turnaround[0] + context.trade_n) < context.total: # 计算累计仓位 context.turnaround[0] += context.trade_n order_volume(account_idx=0, target_idx=0, volume=context.trade_n, side=1, position_effect=1, order_type=2, price=0) # print(context.now, context.target_list[0], '市价单开多仓', context.trade_n, '股') elif macd < 0: if (context.turnaround[1] + context.trade_n) < context.total: context.turnaround[1] += context.trade_n order_volume(account_idx=0, target_idx=0, volume=context.trade_n, side=2, position_effect=2, order_type=2, price=0) # print(context.now, context.target_list[0], '市价单平多仓', context.trade_n, '股') # 临近收盘时若仓位数不等于昨仓则回转所有仓位 if (day.strftime('%Y-%m-%d %H:%M:%S')[11:16] == '14:55') or ( day.strftime('%Y-%m-%d %H:%M:%S')[11:16] == '14:57'): position = context.account().positions['volume_long'][0] if position != context.total: order_target_volume(account_idx=0, target_idx=0, target_volume=context.total, side=1, order_type=2, price=0) # print('市价单回转仓位操作...') context.ending = 1 # 更新过去的日期数据 context.day[-1] = context.day[0] if __name__ == '__main__': recent_data = ['SSE.600000'] run_backtest('日内回转交易', 'IntradayTrading.py', target_list=recent_data, frequency='min', fre_num=5, begin_date='2018-08-01', end_date='2018-10-01', fq=1) ``` <br> ### 海龟### ------------ ```python #!/usr/bin/python # coding:utf-8 """ @author: Miaohua.L @contact: miaohua.l@bitpower.com.cn @file: turtle_strategy.py @time: 2018/9/21 20:54 """ import sys import numpy as np import pandas as pd try: import talib except: print('请安装TA-Lib库') sys.exit(-1) from atrader import * import numpy as np from datetime import datetime, date # 初始化设置 def init(context): set_backtest(initial_cash=1e5, margin_rate=1.0, slide_price=0.0, price_loc=1, deal_type=0, limit_type=0) # 设置回测初始资金信息 context.TLen = len(context.target_list) # 标的个数 reg_kdata('min', 1) # 注册k线数据 context.N = 101 # k线长度 # context.parameter分别为唐奇安开仓通道.唐奇安平仓通道.短ma.长ma.ATR的参数 context.parameter = [55, 20, 10, 60, 20] context.tar = context.parameter[4] # 策略逻辑 def on_data(context): mp = context.account().positions # 获取当前仓位 bar = get_current_bar() kdata = get_reg_kdata(reg_idx=context.reg_kdata[0], length=context.N, fill_up=True, df=True) # 获取k线数据 for i in range(context.TLen): df = kdata[kdata['target_idx'] == i] high1 = df['high'].astype(float) # high价 low1 = df['low'].astype(float) # low价 close1 = df['close'].astype(float) # close价 if np.isnan(close1.iloc[0]) == 1: return # 无数据跳过 close = close1.values[-1] # 计算ATR atr = talib.ATR(high1.values, low1.values, close1.values, timeperiod=context.tar)[-1] # 计算唐奇安开仓和平仓通道 context.don_open = context.parameter[0] + 1 upper_band = talib.MAX(close1.values[:-1], timeperiod=context.don_open)[-1] context.don_close = context.parameter[1] + 1 lower_band = talib.MIN(close1.values[:-1], timeperiod=context.don_close)[-1] # 若没有仓位则开仓 position_long = mp.loc[i, 'volume_long'] position_short = mp.loc[i, 'volume_short'] if not position_long and not position_short: # 计算长短ma线.DIF ma_short = talib.MA(close1.values, timeperiod=(context.parameter[2] + 1))[-1] ma_long = talib.MA(close1.values, timeperiod=(context.parameter[3] + 1))[-1] dif = ma_short - ma_long # 获取当前价格 # 上穿唐奇安通道且短ma在长ma上方则开多仓 if close > upper_band and (dif > 0): # order_target_volume(symbol=symbol, volume=8, position_side=PositionSide_Long, order_type=OrderType_Market) order_volume(account_idx=0, target_idx=i, volume=8, side=1, position_effect=1, order_type=2, price=0) # print(context.target_list[i], '市价单开多仓8手') # 下穿唐奇安通道且短ma在长ma下方则开空仓 if close < lower_band and (dif < 0): # order_target_volume(symbol=symbol, volume=8, position_side=PositionSide_Short, order_type=OrderType_Market) order_volume(account_idx=0, target_idx=i, volume=8, side=2, position_effect=1, order_type=2, price=0) # print(context.target_list[i], '市价单开空仓8手') elif position_long: # 价格跌破唐奇安平仓通道全平仓位止损 if close < lower_band: order_close_all() # print(context.target_list[i], '市价单全平仓位') else: vwap = mp.loc[i, 'holding_cost_long'] # 获取平仓的区间 band = vwap - np.array([200, 2, 1.5, 1, 0.5, -100]) * atr # 计算最新应持仓位 grid_volume = int(pd.cut(close1, band, labels=[0, 1, 2, 3, 4])[-1]) * 2 order_target_volume(account_idx=0, target_idx=i, target_volume=grid_volume, side=1, order_type=2, price=0) # print(context.target_list[i], '市价单平多仓到', grid_volume, '手') elif position_short: # 价格涨破唐奇安平仓通道或价格涨破持仓均价加两倍ATR平空仓 if close > upper_band: order_close_all() # print(context.target_list[i], '市价单全平仓位') else: # 获取持仓均价 vwap = mp.loc[i, 'holding_cost_short'] # 获取平仓的区间 band = vwap + np.array([-100, 0.5, 1, 1.5, 2, 200]) * atr # 计算最新应持仓位 grid_volume = int(pd.cut(close1, band, labels=[0, 1, 2, 3, 4])[-1]) * 2 order_target_volume(account_idx=0, target_idx=i, target_volume=grid_volume, side=2, order_type=2, price=0) # print(context.target_list[i], '市价单平空仓到', grid_volume, '手') if __name__ == '__main__': target_list = ['CZCE.FG000', 'SHFE.rb0000'] # 设置回测标的 frequency = 'min' # 设置刷新频率 fre_num = 5 # 设置刷新频率 begin_date = '2017-08-01' # 设置回测初始时间 end_date = '2017-08-25' # 设置回测结束时间 fq = 1 # 设置复权方式 run_backtest('海龟', 'test.py', target_list=target_list, frequency=frequency, fre_num=fre_num, begin_date=begin_date, end_date=end_date, fq=fq) ``` <br> ### 简单双均线 ### ------------ ```python # -*- coding: utf-8 -*- """ ------------------------------------------------- File Name: 简单双均线V Description : Author : haoyuan.m date: 2018/10/26 ------------------------------------------------- Change Activity: 2018/10/26: ------------------------------------------------- """ __author__ = 'haoyuan.m' from atrader import * import numpy as np # %% def init(context): set_backtest(initial_cash=1e6) reg_kdata('day', 1) # reg_userindi(indi_func=gen_signal) context.win = 21 context.long_win = 20 context.short_win = 5 context.Tlen = len(context.target_list) def on_data(context): positions = context.account().positions['volume_long'].values data = get_reg_kdata(reg_idx=context.reg_kdata[0], length=context.win, fill_up=True, df=True) if data['close'].isna().any(): return close = data.close.values.reshape(-1, context.win).astype(float) mashort = close[:, -context.short_win:].mean(axis=1) malong = close[:, -context.long_win:].mean(axis=1) target = np.array(range(context.Tlen)) long = np.logical_and(positions == 0, mashort > malong) short = np.logical_and(positions > 0, mashort < malong) target_long = target[long].tolist() target_short = target[short].tolist() for targets in target_long: order_target_value(account_idx=0, target_idx=targets, target_value=1e6/len(target_long), side=1, order_type=2, price=0) for targets in target_short: order_target_volume(account_idx=0, target_idx=targets, target_volume=0, side=1, order_type=2, price=0) if __name__ == '__main__': run_backtest(strategy_name='SMA', file_path='TwoLines.py', target_list=get_code_list('hs300')['code'], frequency='day', fre_num=1, begin_date='2017-09-01', end_date='2018-03-01', fq=1) ``` <br> ### 简单形态### ------------ ```python # -*- coding: utf-8 -*- """ ------------------------------------------------- File Name: 简单形态 Description : Author : haoyuan.m date: 2018/10/25 ------------------------------------------------- Change Activity: 2018/10/25: ------------------------------------------------- """ __author__ = 'haoyuan.m' ''' 简单形态与技术指标 统计过去一段时间连续上涨的bar的数目,满足连续上涨天数不小于bar的长度减2,同时使用均线进行简单过滤, 当前价格需要在均线价格之上,则进场做多,空头的条件与之相反。 出场使用3:1的合约价值止盈止损比,比如止盈3,止损1。 在沪深300股指期货与螺纹钢期货里面按照1:50的标准进行投资。测试时间段:2011年1月1日到2017年1月1日。 ''' from atrader import * import numpy as np import pandas as pd import datetime as dt import sys try: import talib except: print('请安装TA-Lib库') sys.exit(-1) def init(context): set_backtest(initial_cash=10000000) reg_kdata('day', 1) context.Tlen = len(context.target_list) context.stoprate = 0.02 context.N = 20 context.n = 4 context.initial = 10000000 def on_data(context): data = get_reg_kdata(reg_idx=context.reg_kdata[0], length=context.N + 3, fill_up=True, df=True) if data['close'].isna().any(): return long_positions = context.account().positions['volume_long'] long_hold_cost = context.account().positions['holding_cost_long'] short_positions = context.account().positions['volume_short'] short_hold_cost = context.account().positions['holding_cost_short'] datalist = [data[data['target_idx'] == x] for x in pd.unique(data.target_idx)] for target in datalist: target_idx = target.target_idx.iloc[0] close = target.close.values.astype('float')[-context.N:] # 平仓条件 if long_positions[target_idx] > 0: if (close[-1] > long_hold_cost[target_idx] * (1 + 3 * context.stoprate)) or \ (close[-1] < long_hold_cost[target_idx] * (1 - context.stoprate)): order_target_volume(account_idx=0, target_idx=target_idx, target_volume=0, side=1, order_type=2) if short_positions[target_idx] > 0: if (close[-1] < short_hold_cost[target_idx] * (1 - 3 * context.stoprate)) or \ (close[-1] > short_hold_cost[target_idx] * (1 + context.stoprate)): order_target_volume(account_idx=0, target_idx=target_idx, target_volume=0, side=2, order_type=2) # 开仓条件 if (long_positions[target_idx] == 0) and (short_positions[target_idx] == 0): if (sum(close[-context.n:] > close[-context.n-1:-1]) >= context.n-2) and close[-1] > close.mean(): order_target_value(account_idx=0, target_idx=target_idx, target_value=context.initial / context.Tlen, side=1, order_type=2) if (sum(close[-context.n:] < close[-context.n-1:-1]) >= context.n-2) and close[-1] < close.mean(): order_target_value(account_idx=0, target_idx=target_idx, target_value=context.initial / context.Tlen, side=2, order_type=2) if __name__ == '__main__': target = ['shfe.rb0000', 'DCE.j0000'] run_backtest('简单形态', 'SimpleForm.py', target_list=target, frequency='min', fre_num=60, begin_date='2016-01-01', end_date='2017-01-01', fq=1) ``` <br> ### 网格交易### ------------ ```python # -*- coding: utf-8 -*- """ ------------------------------------------------- File Name: 网格交易 Description : Author : haoyuan.m date: 2018/10/22 ------------------------------------------------- Change Activity: 2018/10/22: ------------------------------------------------- """ __author__ = 'haoyuan.m' ''' 本策略首先计算了rb1801过去200个1min收盘价的均值和标准差 并用均值加减2和3个标准差得到网格的区间分界线,分别配以0.3和0.5的仓位权重 然后根据价格所在的区间来配置仓位: (n+k1*std,n+k2*std],(n+k2*std,n+k3*std],(n+k3*std,n+k4*std],(n+k4*std,n+k5*std],(n+k5*std,n+k6*std] (n为收盘价的均值,std为收盘价的标准差,k1-k6分别为[-40, -3, -2, 2, 3, 40],其中-40和40为上下界,无实际意义) [-0.5, -0.3, 0.0, 0.3, 0.5](资金比例,此处负号表示开空仓) 回测数据为:rb1801的1min数据 ''' from atrader import * import numpy as np import pandas as pd import datetime as dt def init(context): set_backtest(initial_cash=10000000) # 订阅rb1801, bar频率为1min reg_kdata('min', 1) # 获取过去300个价格数据 hist_300bar = get_kdata_n(['shfe.rb1801'], 'min', 1, 200, end_date='2017-07-01', fill_up=True, df=True).close.values # 获取网格区间分界线 context.band = np.mean(hist_300bar) + np.array([-10000, -3, -2, 2, 3, 10000]) * np.std(hist_300bar) # 设置网格的仓位 context.weight = np.array([0.5, 0.3, 0.0, 0.3, 0.5]) def on_data(context): bar = get_current_bar() # 根据价格落在(-40,-3],(-3,-2],(-2,2],(2,3],(3,40]的区间范围来获取最新收盘价所在的价格区间 grid = pd.cut(bar.close.values, context.band, labels=[0, 1, 2, 3, 4])[0] mktvalue = context.account().cash['total_value'].iloc[0] # 获取多仓仓位 position_long = context.account().positions['volume_long'][0] # 获取空仓仓位 position_short = context.account().positions['volume_short'][0] # 若无仓位且价格突破则按照设置好的区间开仓 if not position_long and not position_short and grid != 2: # 大于3为在中间网格的上方,做多 if grid >= 3: # order_target_value(account_idx=0, target_idx=0, target_value=mktvalue * context.weight[grid], side=1, order_type=2) order_target_percent(account_idx=0, target_idx=0, target_percent=context.weight[grid], side=1, order_type=2) # print(context.now, context.target_list[0], '以市价单开多仓到仓位', context.weight[grid]) elif grid <= 1: # order_target_value(account_idx=0, target_idx=0, target_value=mktvalue * context.weight[grid], side=2, order_type=2) order_target_percent(account_idx=0, target_idx=0, target_percent=context.weight[grid], side=2, order_type=2) # print(context.now, context.target_list[0], '以市价单开空仓到仓位', context.weight[grid]) # 持有多仓的处理 elif position_long: if grid >= 3: # order_target_value(account_idx=0, target_idx=0, target_value=mktvalue * context.weight[grid], side=1, order_type=2) order_target_percent(account_idx=0, target_idx=0, target_percent=context.weight[grid], side=1, order_type=2) # print(context.now, context.target_list[0], '以市价单调多仓到仓位', context.weight[grid]) # 等于2为在中间网格,平仓 elif grid == 2: order_target_value(account_idx=0, target_idx=0, target_value=0, side=1, order_type=2) # print(context.now, context.target_list[0], '以市价单全平多仓') # 小于1为在中间网格的下方,做空 elif grid <= 1: order_target_value(account_idx=0, target_idx=0, target_value=0, side=1, order_type=2) # print(context.now, context.target_list[0], '以市价单全平多仓') # order_target_value(account_idx=0, target_idx=0, target_value=mktvalue * context.weight[grid], side=2, order_type=2) order_target_percent(account_idx=0, target_idx=0, target_percent=context.weight[grid], side=2, order_type=2) # print(context.now, context.target_list[0], '以市价单开空仓到仓位', context.weight[grid]) # 持有空仓的处理 elif position_short: # 小于1为在中间网格的下方,做空 if grid <= 1: order_target_value(account_idx=0, target_idx=0, target_value=mktvalue * context.weight[grid], side=2, order_type=2) # order_target_percent(account_idx=0, target_idx=0, target_percent=context.weight[grid], side=2, order_type=2) # print(context.now, context.target_list[0], '以市价单调空仓到仓位', context.weight[grid]) # 等于2为在中间网格,平仓 elif grid == 2: order_target_value(account_idx=0, target_idx=0, target_value=0, side=1, order_type=2) # print(context.now, context.target_list[0], '以市价单全平空仓') # 大于3为在中间网格的上方,做多 elif grid >= 3: order_target_value(account_idx=0, target_idx=0, target_value=0, side=2, order_type=2) # print(context.now, context.target_list[0], '以市价单全平空仓') # order_target_value(account_idx=0, target_idx=0, target_value=mktvalue * context.weight[grid], # side=1, order_type=2) order_target_percent(account_idx=0, target_idx=0, target_percent=context.weight[grid], side=1, order_type=2) # print(context.now, context.target_list[0], '以市价单开多仓到仓位', context.weight[grid]) if __name__ == '__main__': targetlist = ['shfe.rb1801'] run_backtest(strategy_name='网格交易', file_path='GridTrading.py', target_list=targetlist, frequency='min', fre_num=15, begin_date='2017-04-01', end_date='2017-07-01', fq=1) ``` <br> ### 跨期套利 ### ------------ ```python #!/usr/bin/python #coding:utf-8 """ @author: Miaohua.L @contact: miaohua.l@bitpower.com.cn @file: multifactors.py @time: 2018/9/5 14:13 """ from atrader import * import numpy as np import statsmodels.api as sm try: import statsmodels.tsa.stattools as ts except: print('请安装statsmodels库') sys.exit(-1) ''' 本策略根据EG两步法(1.序列同阶单整2.OLS残差平稳)判断序列具有协整关系后(若无协整关系则全平仓位不进行操作) 通过计算两个价格序列回归残差的均值和标准差并用均值加减0.9倍标准差得到上下轨 在价差突破上轨的时候做空价差;在价差突破下轨的时候做多价差 若有仓位,在残差回归至上下轨内的时候平仓 回测数据为:rb1801和rb1805的1min数据 回测时间为:2017-09-01 到2017-09-30 ''' # 协整检验的函数 def cointegration_test(series01, series02): target1 = ts.adfuller(np.array(series01), 1)[1] target2 = ts.adfuller(np.array(series02), 1)[1] # 同时平稳或不平稳则差分再次检验 if (target1 > 0.1 and target2 > 0.1) or (target1 < 0.1 and target2 < 0.1): urt_diff_1 = ts.adfuller(np.diff(np.array(series01)), 1)[1] urt_diff_2 = ts.adfuller(np.diff(np.array(series02)), 1)[1] # 同时差分平稳进行OLS回归的残差平稳检验 if urt_diff_1 < 0.1 and urt_diff_2 < 0.1: result = sm.OLS(np.array(series01.astype(float)), sm.add_constant(np.array(series02.astype(float))), missing='drop').fit() beta, c, resid = result.params[1], result.params[0], result.resid if ts.adfuller(np.array(resid), 1)[1] > 0.1: result = 0.0 else: result = 1.0 return beta, c, resid, result else: result = 0.0 return 0.0, 0.0, 0.0, result else: result = 0.0 return 0.0, 0.0, 0.0, result def init(context): set_backtest(initial_cash=1e7) # 订阅品种 reg_kdata('min', 1) context.initial = 1e7 def on_data(context): # 获取过去400个60s的收盘价数据 data = get_reg_kdata(reg_idx=context.reg_kdata[0], length=401, fill_up=True, df=True) if data['close'].isna().any(): return close_01 = data[data['target_idx'] == 0]['close'] close_02 = data[data['target_idx'] == 1]['close'] # 展示两个价格序列的协整检验的结果 beta, c, resid, result = cointegration_test(close_01, close_02) # 如果返回协整检验不通过的结果则全平仓位等待 if not result: print(context.now, '协整检验不通过,全平所有仓位') order_close_all() return # 计算残差的标准差上下轨 mean = np.mean(resid) up = mean + 1.5 * np.std(resid) down = mean - 1.5 * np.std(resid) # 计算新残差 resid_new = close_01.iloc[-1] - beta * close_02.iloc[-1] - c # 获取rb1801的多空仓位 positions = context.account().positions position_01_long = positions['volume_long'].iloc[0] position_01_short = positions['volume_short'].iloc[0] if not position_01_long and not position_01_short: # 上穿上轨时做空新残差 if resid_new > up: order_target_value(account_idx=0, target_idx=0, target_value=context.initial / 2 * 5, side=2, order_type=2, price=0) # order_volume(account_idx=0, target_idx=0, volume=2, side=2, # order_type=2, price=0) print(context.now, context.target_list[0] + '以市价单开空仓1手') order_target_value(account_idx=0, target_idx=1, target_value=context.initial / 2 * 5, side=1, order_type=2, price=0) # order_volume(account_idx=0, target_idx=1, volume=2, side=1, # order_type=2, price=0) print(context.now, context.target_list[1] + '以市价单开多仓1手') # 下穿下轨时做多新残差 if resid_new < down: order_target_value(account_idx=0, target_idx=0, target_value=context.initial / 2 * 5, side=1, order_type=2, price=0) print(context.now, context.target_list[0], '以市价单开多仓1手') order_target_value(account_idx=0, target_idx=1, target_value=context.initial / 2 * 5, side=2, order_type=2, price=0) print(context.now, context.target_list[1], '以市价单开空仓1手') # 新残差回归时平仓 elif position_01_short: if resid_new <= up: order_close_all() print(context.now, '价格回归,平掉所有仓位') # 突破下轨反向开仓 if resid_new < down: order_target_value(account_idx=0, target_idx=0, target_value=context.initial / 2 * 5, side=1, order_type=2, price=0) print(context.now, context.target_list[0], '以市价单开多仓1手') order_target_value(account_idx=0, target_idx=1, target_value=context.initial / 2 * 5, side=2, order_type=2, price=0) print(context.now, context.target_list[1], '以市价单开空仓1手') elif position_01_long: if resid_new >= down: order_close_all() print(context.now, '价格回归,平所有仓位') # 突破上轨反向开仓 if resid_new > up: order_target_value(account_idx=0, target_idx=0, target_value=context.initial / 2 * 5, side=2, order_type=2, price=0) print(context.now, context.target_list[0] + '以市价单开空仓1手') order_target_value(account_idx=0, target_idx=1, target_value=context.initial / 2 * 5, side=1, order_type=2, price=0) print(context.now, context.target_list[1] + '以市价单开多仓1手') if __name__ == '__main__': target = ['shfe.rb1801', 'shfe.rb1805'] run_backtest('跨期套利', 'CalendarSpread.py', target_list=target, frequency='min', fre_num=15, begin_date='2017-09-01', end_date='2017-09-30', fq=1) ```
contact@digquant.com.cn

联系

邮箱

0755-8695-2080

联系

电话

关注

微信

关注

QQ

回到

顶部