Julia: 基于CTA策略的回测可视化分析的尝试

23 篇文章 1 订阅

相关库: Winston,Tk.

特点: 纯Julia打造,稀有,呵呵。

回测可视化的基本功能:

可以方便地看到当天日内交易每笔的买卖时点,当天的每笔盈亏情况。不用到行情中去查找和对照,那样太辛苦了。有了这个分析工具,可以大大提高了回测交易流水的分析效率。

可以前复盘,也可以向后复盘。可以自定义日期和标的。

这个分析工具主要是基于日内交易的,如果是基于跨日交易,其框架差不多,只需要做一部分小的修改就可以。

说明:

当Julia处在0.2时侯,这个东东已经投入使用。但有一阵子在Julia到0.4阶段,Wiston库也不跟着升级,导致用不上。这次看到Winston跟着Julia升到0.5了,甚喜。留做记念吧。

Winston画图功能有限,其实用PyPlot等其它的库替代 Winston也是可以的,我也试过。

代码如下:

using Tk
using Winston;
using StrategyData;

w     = Tk.Toplevel("策略复盘助手_日内策略",400,300)
f     = Tk.Frame(w);
Tk.pack(f, expand=true, fill="both")
ei    =  Tk.Entry(f) #输入标的
e     =  Tk.Entry(f) # 输入起始时间
rb    =  Tk.Radio(f, [ "向后复盘","向前复盘"])# 向前 或向后复盘
b     =  Tk.Button(f, "策略复盘请按=>Ok")
txt   =  Tk.Text(f, width=50,height=40)# 如果当日交易多,可以设置“height=40”,width=10,height=10
sc    =  Tk.Scrollbar(f,txt,"vertical")
map(u -> pack(u,side="left", anchor="w"), (ei,e, rb,b,txt,sc))     ## pack in left to right ,"left" : "top""n","w"
# 布局 : 问题
Tk.formlayout(ei, "选择标的:")
Tk.formlayout(e, "tradeDate:")
Tk.formlayout(rb,"复盘选择")
Tk.formlayout(b, nothing)
Tk.formlayout(txt,"tradeFlows")
#pack(txt, expand=true, fill="both")
Tk.formlayout(sc,nothing)
Tk.grid(sc, 5:20,200,sticky="news")  # 设定滚动条的位置
#grid(Slider(f, 1:10), 1  , 2:3, sticky="news")
Tk.focus(e)            ## put keyboard focus on widget
currentTrade ="" ;
nextDay = Dates.Date(2000,1,1)
function callback(path)
    instrument =  uppercase(get_value(ei))
    # pData :Dict{Date,Array{kBarData,1}}();
    !isdefined(:totalDictDictDictData_NMin) && error("totalDictDictDictData_NMin is not exist!")
    if haskey(totalDictDictDictData_NMin,instrument)
        pData  =  StrategyData.getMainContractContinousData(totalDictDictDictData_NMin,instrument)
    else
        msg =string("数据中不包含这个标的: ",instrument,"!")
        Tk.Messagebox(f,  msg)
    end
    tradeDates = sort(collect(keys(pData)))
    p      = Winston.FramedPlot(height=200,width=400);
    val    = get_value(e)
    dateStr= split(val,"-");
    yyear  = parse(Int64,dateStr[1]);
    mmonth = parse(Int64,dateStr[2]);
    dday   = parse(Int64,dateStr[3])
    ddate  = Dates.Date(yyear,mmonth,dday);
    #currentTrade =string("复盘策略: ",strategyName, "   复盘日期:",string(ddate),"\n");
    currentTrade =string( "   复盘日期:",string(ddate),"\n");
    totalPL = 0.0;
    nextDay = ddate;
    if get_value(rb)=="向后复盘"
        if  !haskey(pData,ddate)
            for i =1:length(tradeDates)
                if tradeDates[i]>ddate
                    ddate =tradeDates[i];
                    break;
                end
            end
            msg ="此日不是交易日!自动转移动下一个最近交易日!"
            Tk.Messagebox(f, msg)
        end

        for i =1:length(tradeDates)
            if tradeDates[i] > ddate
                nextDay =tradeDates[i];
                break;
            end
        end
        if nextDay==ddate
            msg ="已到最后交易日!"
            Tk.Messagebox(f, msg)
        end
    else
        if  !haskey(pData,ddate)
            for i =length(tradeDates):-1:1
                if tradeDates[i]<ddate
                    ddate =tradeDates[i];
                    break;
                end
            end
            msg ="此日不是交易日!自动转移动上一个最近交易日!"
            Tk.Messagebox(f,  msg)
        end

        for i =length(tradeDates):-1:1
            if tradeDates[i]<ddate
                nextDay =tradeDates[i];
                break;
            end
        end
        if nextDay==ddate
            msg ="已到第一个交易日!"
            Tk.Messagebox(f,  msg)
        end
    end
    #println(currentTrade);
    #println(string(nextDay));
    # 找到下一个交易日时间

    tradeData = pData[ddate]
    contract  = tradeData[1].Code;
    #setattr(p,draw_nothing=true);
    # 今天交易流水
    series =0;
    currentFlow=tradeFlow[];
    currSortFlows =tradeFlow[];
    # 交易明细部分  : 有顺序地输出交易流水
    for i =1: length(tradeFlows)
        if Dates.Date(tradeFlows[i].openDate)==ddate
            push!( currentFlow,tradeFlows[i])
        end
    end

    if !isempty( currentFlow)
        curOpenTime=DateTime[];
        for flow in  currentFlow
            if !in(flow.openDate,curOpenTime) # 保证不重复
                push!(curOpenTime,flow.openDate)
            end
        end
        sort!(curOpenTime) #排序
                # 把当天的交易按开仓时间顺序排列好
        for dt in curOpenTime
            for flow in  currentFlow
                if flow.openDate==dt
                    push!(currSortFlows,flow)
                end
            end
        end
        for flow in currSortFlows
            series =series+1;
            oTime =Dates.hour(flow.openDate)*100+Dates.minute(flow.openDate)
            if oTime>=1000
                openTime =string(oTime);
            else
                openTime =string("0",string(oTime))
            end
            openPrice =string(flow.openPrice);
            if length(openPrice)>7
                openPrice =openPrice[1:7]
            end
            cTime =Dates.hour(flow.closeDate)*100+Dates.minute(flow.closeDate)
            if cTime>=1000
                closeTime =string(cTime);
            else
                closeTime =string("0",string(cTime))
            end
            closePrice =string(flow.closePrice);
            if length(closePrice)>7
                closePrice =closePrice[1:7]
            end
            if flow.tradeType >0
                PL =flow.closePrice-flow.openPrice;
                direction ="L   "
            else
                PL =-flow.closePrice+flow.openPrice;
                direction ="S   "
            end
            totalPL =totalPL+PL;
            strPL =@sprintf("%0.1f",PL)
            str0 ="---------------------------------------"
            str1 =string("\n","第  ",string(series)," 次交易 =>   ",  flow.strategyName, "   \n" )
            str2 =string("开仓方向 : ",direction   , "       此笔盈亏 :   ", strPL      ,"   \n")
            str3 =string("开仓时间 : ", openTime, "       开仓价格 :   ",openPrice,"   \n")
            str4 =string("平仓时间 : ", closeTime, "       平仓价格 :   ",closePrice,"   \n")
            tempString =string(str0,str1,str2,str3,str4);
            currentTrade =string( currentTrade,tempString);
        end
    end

    strTotalPL =@sprintf("%0.1f",totalPL) # 总盈亏
    currCount  =length(currentFlow);
    # 画图
    Winston.setattr(p,title=string("复盘日期:", val,"  ","   复盘标的: ",contract,   "   复盘策略:", strategyName));
    Winston.setattr(p,title=string("复盘日期:", val,"  ","   复盘标的: ",contract,"  当日开仓次数:",currCount,"  累计盈亏:",strTotalPL ));
    #x =[ hour(tradeData[i].DateTime)*100+minute(tradeData[i].DateTime) for i =1:length(tradeData)]; # 横坐标,时间
    y =Float64[];
    for i=1: length(tradeData)
        push!(y,tradeData[i].Close)
    end
    x =Int64[];
    for i=1:length(tradeData)
        push!(x,i);
    end
    Winston.add(p,Curve(x,y))
    # 交易总结
    if !isempty(currentFlow)
        for i=1: length(currentFlow)
            opTime  =  Dates.hour(currentFlow[i].openDate)*100+Dates.minute(currentFlow[i].openDate);
            clTime  =  Dates.hour(currentFlow[i].closeDate)*100+Dates.minute(currentFlow[i].closeDate);
            LS      =  currentFlow[i].tradeType;
            for j=1: length(tradeData)
                mkTime = Dates.hour(tradeData[j].DateTime)*100+Dates.minute(tradeData[j].DateTime);
                if mkTime==opTime
                    buyP = Winston.Points(j,tradeData[j].Close*0.995,kind="filled circle",color="red")
                    # if LS>0
                          #Winston.setattr(buyP, ticklabels=["OpenBuy"])
                      #else
                          #Winston.setattr(buyP, ticklabels=["OpenSell"])
                    # end
                    Winston.add(p,buyP);
                elseif mkTime ==clTime
                    sellP = Winston.Points(j,tradeData[j].Close*1.005,kind="filled circle",color="green");
                    Winston.add(p,sellP)
                end
            end
        end
        totalStr1 =string("\n","---------------------------------------","\n")
        totalStr2 =string("当日交易总次数:   ", currCount, "   总盈亏:    ", strTotalPL,"\n")
        totalStr  =string(totalStr1,totalStr2)
        currentTrade =string(currentTrade,totalStr)
    else
        totalStr1 =string("---------------------------------------","\n")
        currentTrade =string(currentTrade,totalStr1,"今日策略无交易!")
    end
    # 更新相应控件的信息
    Tk.set_value(e,string(nextDay));
    Tk.set_value(txt,currentTrade);
    # 打点,进入点,退出点,一根直线  补充!
    Winston.display(p)
    #hold(true)
end
Tk.bind(b, "command", callback)
Tk.bind(b, "<Return>", callback)
Tk.bind(txt,"<Return>", callback)
Tk.bind(e, "<Return>", callback)
Tk.bind(ei, "<Return>", callback)
Tk.bind(sc,"<Return>", callback)

效果图:

这里写图片描述

改进1

关于箭头:

我用PyPlot库重新写了一遍,已经有了较大改进:

这里写图片描述

关于callback函数改写,没办法,动力不足,懒……。

现在的函数太长,最好要拆成多个小函数的组合。这个主要怪我有点懒。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值