contact@digquant.com.cn
400-1860-552
官方群:463071731
GFTD指标程序化实现
广发证券很早出过两篇研报。一篇名叫《基于修正 TD 指标的指数择时研究》、一篇名叫《基于GFTD的期指日内程序化交易策略》。今天编辑部就给大家进行实现。基于 Matlab 软件。希望大家有所收获。

TD指标的优势


TD 指标是大型投资基金 Tudor 的执行副总裁(Thomas R DeMark) 于 80 年代中期为了发现走势欲转折区域而设计的。由于其原理简单且预测精度高等特点而在近几十年内得到了广泛的应用。


TD指标基本原理


TD 指标通常可分为 TD 序列和 TD 组合。 TD 序列由启动、交叉和计数三个阶段组成,而 TD 组合则仅包括启动和计数两个阶段,两者的主要区别在于计数规则的不同,但其基本原理都是一致的,即:市场走势由买方和卖方共同作用形成,当买方的力量大于卖方时走势表现为上涨,反之为下跌,但买卖双方力量强弱的表象是动态的,当走势上涨一段时间后,买方力量必然面临衰竭,从而市场转为下跌, TD 指标正是为发现走势欲转折区域而设计的。


量化指标参数条件的调整


本报告对传统 TD 指标参数在 A 股市场进行实证,发现效果并不理想。我们尝试通过修正 TD 指标,找到最适合中国股市特征的指标参数和条件,进而对中国股市进行更有效的预测。最后,我们确定 TD 序列的启动阶段长度为 6,计数为 12; TD 组合的启动阶段长度为 6,计数为 8。指标的修正主要包括以下三个方面:( 1)改变计数起点;( 2)改变 TD 指标参数;( 3)改变计数规则。

择时策略的实证效果


基于修正后的 TD 序列和 TD 组合的量化择时策略应用于上证综指、深圳成指以及沪深 300 等三个不同指数,在 2000.1—— 2010.6 间均获得了较好的超额收益。其中,基于修正 TD 组合指标的效果较佳,预测的准确率同样均大于 70%。


比较三个指数,模型效果对于上证综指效果最好, 10 年期间发出 11 对信号,平均 1 年 1 至 2 对信号,成功 9 对,准确率达到 82%。仅有的两次失败发生在 2001—— 2002 年,成功躲过 2007 年的 5.30、 2008 全年大调整、2009 年的 8.5 调整以及今年以来的调整。抓住了 2005 年到 2007 年 5.30 的牛市主升浪行情、2007年下半年的牛尾行情、 2009 年初至 2009.8.4 的反弹行情。


目前信号


经过修正的 TD 组合模型对于上证综指过去 10 年检验效果最好,该模型从 2010 年 1 月 22 日发出卖出信号以来,在 2010 年 7 月 12 日发出买入信号,指示市场当前已经处于底部区域,轻仓投资者可以考虑择机进场。



附上部分源代码:

function GFTD2(bInit,bDayBegin,cellPar)%

global g_idxKDay;

global g_idxSUD;

global TLen;

global preCountClose;  % 前一个计数的收盘价

global pastCount;  %计数: >0表示买入计数;<0表示卖出计数。

global entryPrice; %记录开仓的价各

global histExtre; %记录历史最优价格,用于跟踪止损止盈

global pastTime; % 计数启动后计时,如果启动时间过长而始终没有形成买卖信号则取消计数

global benjin;

global coef;

global re; %保证金率

global histCap;

m=cellPar{1}; % 与前m日比较

N1=cellPar{2}; %连续下跌用来开启开多的条件

N2=cellPar{3}; %连续上涨用来开启开空的条件

T1=cellPar{4}; % 开多计数后大于该值则买入

T2=cellPar{5}; % 开空计数后大于该值则卖出

if bInit

    %traderSetParalMode(false);

    g_idxKDay = traderRegKData('day',1);

    g_idxSUD = traderRegUserIndi(@getSUD,{g_idxKDay,m,N1,N2});

    TLen = length(g_idxKDay(:,1));

    entryPrice = nan(1,TLen); % 记录入场价格

    histExtre = nan(1,TLen);  % 记录历史最优价

    preCountClose = nan(1,TLen); % 记录上次计数的价格,若是第一次则启动开空则此值设为较大的数,若为开多此值记为较小的一个数

    pastTime = zeros(1,TLen); % 记录计数开始后的时间长度。时间长度太长则计数取消

    pastCount= zeros(1,TLen); % 启动计数的计数。 >0 表示开多计数,<0表示开空计数

    [coef,~,~,~,~,LongMargin,ShortMargin] = traderGetFutureInfoV2(1:TLen);

    re = (LongMargin+ShortMargin)/2;

else

    dUD = traderGetRegUserIndi(g_idxSUD,1); %获取计算的指标

    mp=traderGetAccountPositionV2(1,1:TLen); %仓位读取

    pastTime = pastTime+1;

   

    non = dUD(3*TLen+1:end,end);

    lens = length(non(non>0));

    datas = traderGetRegKData(g_idxKDay,20,false); % 获取11个数据

    realTime = max(datas(1:8:end,end));  % 记录当下的实际时间。若标的的最后时间和这个不同时,说明该标的此时不在交易状态

   

    [ValidCash,MarketCap,~,~,~] =traderGetAccountInfoV2(1);

   

    prices = datas(5:8:end,end);

    MarketCap = max(sum(prices(mp~=0).*abs(mp(mp~=0)).*re(mp~=0).*coef(mp~=0))+ValidCash,MarketCap);

    percash = min(ValidCash/max(1,length(mp(mp==0))-lens),MarketCap/(TLen-lens));

    percash = min(percash,MarketCap*.15);

    % ——————————————————调仓———————————————————

   

   

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

        %省略,总资金止损;需要自己加入

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%

   

    for i=1:TLen

        data = datas(1+8*(i-1):8*i,:);

        if  data(6,end) ==0 || data(1,end)~=realTime

            continue;

        end

       

        if mp(i)~=0

            % 记录开盘价,并在真实的交易发生后,将计数归零。注意不是在开盘命令时,因为如果发生撤单,将不会再次发出订单

            if isnan(entryPrice(i))

                entryPrice(i) = data(2,end);

            end

           

            % 记录最优价格

            if mp(i)>0

                histExtre(i) = max(histExtre(i),data(3,end));

            else

                histExtre(i) = min(histExtre(i),data(4,end));

            end

           

            % 限定最大本金损失 和 最大回撤限制。

        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

        %省略,止损及跟踪止损;需要自己加入

        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%

        end

       

        % 计数归零,启动时间过长,则启动计数归零

        if pastTime(i) >60 %

            pastCount(i)=0;

        end

        % 计数增加

       

        if pastCount(i)>0 && data(5,end)>preCountClose(i) && dUD(i+TLen,end)==1

            pastCount(i) = pastCount(i) +1;

            preCountClose(i) = data(5,end);

        elseif pastCount(i)<0 && data(5,end)

            pastCount(i) = pastCount(i) - 1;

            preCountClose(i) = data(5,end);

        end

       

        %计数启动, 每次新的启动都会使得计数重新开始。

        % 同时记录时间,使得启动在一段时间后如果还是没有产生信号就取消。

        if dUD(i,end)==1

            pastTime(i) = 0;

            pastCount(i) = 1;

            preCountClose(i) = 0;

        elseif dUD(i,end)==-1

            pastTime(i) = 0;

            pastCount(i) = -1;

            preCountClose(i) = data(5,end)*1000;

        end

       

        % 开仓(包括反手开仓)

        amount =floor( percash/coef(i)/data(5,end));

        if mp(i) <= 0 && pastCount(i)>T1

            if data(5,end)<=mean(data(5,:))

                pastCount(i)=1;

                continue;

            end

            traderBuyV2(1,i,amount,0,'market','buy1');%开多单

            entryPrice(i) = nan;

            histExtre(i)=nan;

        elseif  mp(i) >= 0 && pastCount(i)<-T2

            if data(5,end)>=mean(data(5,:))

                pastCount(i)=-1;

                continue;

            end

            traderSellShortV2(1,i,amount,0,'market','sell1');%开空单

            entryPrice(i) = nan;

            histExtre(i) = nan;

        end

    end

end

end

 

 

 

function value=getSUD(cellPar,bpPFCell)

idxK =cellPar{1};

m = cellPar{2};

N1 = cellPar{3};

N2 = cellPar{4};

 

lags = max([m+max(N1,N2),20]);

 

[targetNum,~]=size(idxK);

value = zeros(1,targetNum*4);

regKMatrix = traderGetRegKData(idxK,lags ,false,bpPFCell);

for i=1:targetNum

    data = regKMatrix(8*(i-1)+1:8*i,:);

    [~,KLen]=size(data);

    if KLen>=lags

        % 计算连续上涨或下跌。连续上涨则看空启动;连续下跌则看多启动, 成交量太低就不要交易了

        range=mean(data(3,1:end-1)-data(4,1:end-1));

        diff1 = data(5,end-N1+1:end)-data(5,end-N1-m+1:end-m);

        diff2 = data(5,end-N2+1:end)-data(5,end-N2-m+1:end-m);

        if sum(diff1<0)==length(diff1) && mean(data(6,:))>2000

            value(i)=1;

        elseif sum(diff2>0)==length(diff2) && mean(data(6,:))>2000

            value(i) = -1;

        end

       

        %启动计数后,计算是否满足计数增加条件;

        if data(5,end)>data(3,end-2) && data(3,end)>data(3,end-1)

            value(i+targetNum) =1;

        elseif data(5,end)

            value(i+targetNum) =-1;

        end

       

        % 获取日波幅变化,用于限定最低损失和最大回撤

        value1 = abs(data(3,end-19:end-1) - data(4,end-19:end-1));

        value(i+2*targetNum) = mean(value1(value1>0));

       

        %不满足条件的标的不交易

        if mean(data(6,:))<2000

            value(i+3*targetNum)=1;

        else

            value(i+3*targetNum)=0;

        end

    end

end

end



回复 4/27 15:12

回复 4/20 20:13