美文网首页程序员的量化世界量化交易投资理财
使用BotVS 快速迭代你的量化交易灵感(3)

使用BotVS 快速迭代你的量化交易灵感(3)

作者: 发明者量化FMZ | 来源:发表于2017-05-26 12:15 被阅读72次

    上一篇中完成了初步的开仓触发条件设计,有一点小衔接工作没有做完,就是在开仓时需要调用代码中自定义函数: CheckBalance_Unit 函数 去检查一下当前一份资金是否足够一手保证金金额。并且还要检查一下可用资金部分是否足够开1手。
    代码中增加一个全局变量 nowAccount 用于记录当前账户信息。


    var nowAccount = null; // 当前账户信息
    

    添加调用 CheckBalance_Unit 函数的代码:


    if(State == IDLE &&  Bar.Close > upTrack){  // LONG 
            // Log("触发!上轨:", upTrack, "当前Bar最新收盘价(Close)", Bar.Close, "lastDayATR:", lastDayATR, "lastDayClose:", lastDayClose);   // 测试代码 
            // throw "暂停!";                                                      // 测试代码
            nowAccount = CheckBalance_Unit(LONG);
    
        }else if(State == IDLE && Bar.Close < downTrack){  // SHORT
            // Log("触发!下轨:", downTrack, "当前Bar最新收盘价(Close)", Bar.Close, "lastDayATR:", lastDayATR, "lastDayClose:", lastDayClose); // 测试代码
            // throw "暂停!";                                                      // 测试代码
            nowAccount = CheckBalance_Unit(SHORT);
        }
    

    并且针对 CheckBalance_Unit 函数做一点小的修改。


    代码:

    if(nowAccount.Balance - initAccount.Balance * (1 - UsedRatio) < OneContractMargin * 1.2){
            throw "资金可用部分不足一手保证金金额。可用部分:" + (nowAccount.Balance - initAccount.Balance * (1 - UsedRatio)) + ",1手保证金" + OneContractMargin + ",系数:1.2";
        }
        return nowAccount;
    

    继续以下设计进度(设置止损止盈)

    (3) 以做多为例:设置当前初始线(入场价),按着一定规则(如:止损线=0.8 * 入场价)浮动止盈线和止损线,可以设置,(浮动止盈线-中线)= 1.2(中线-止损线)
    可以看到(3)号条件上止损止盈的计算是基于“入场价”的这个值的(条件中描述的 中线 = 入场价),所以 “入场价” 这个值需要是一个 全局变量可以持久保存的。同样 浮动止盈线和止损线也是需要设置为全局变量:

    var EnterPrice = null;        // 入场价
    var FloatProfitStop = null;   // 浮动止损
    var LossStop = null;          // 止损线
    

    根据条件中的计算公式,我们写一个自定义函数:


    function CalcFPS_LS(Para_EnterPrice, Direction){    // 计算 FloatProfitStop : FPS ,  LossStop : LS
        // 以做多为例:设置当前初始线(入场价),按着一定规则(如:止损线=0.8 * 入场价)浮动止盈线和止损线,可以设置,(浮动止盈线-中线)= 1.2(中线-止损线)
        if(Direction == LONG){
            LossStop = 0.8 * Para_EnterPrice;
            FloatProfitStop = 1.2 * (Para_EnterPrice - (0.8 * Para_EnterPrice)) + Para_EnterPrice;
        }else if(Direction == SHORT){
            LossStop = 1.2 * Para_EnterPrice;
            FloatProfitStop = Para_EnterPrice - 1.2 * (1.2 * Para_EnterPrice - Para_EnterPrice);
        }else{
            throw "错误的Direction参数:" + Direction;
        }
    }
    

    然后在 loop 函数中触发开仓的位置写上:


    可以看到我们预留了加仓、平仓的位置,在设计策略程序的时候局部观念和全局观念需要相互协调。有时候为了调整整体结构也需要频繁调整代码。
    可以看到:

    var tradeInfoLong =    // 商品期货交易类库 导出函数,处理交易细节。
    

    这行代码准备调用 《商品期货交易类库》 模块的接口,该模块的作用就是处理交易细节,相当于把BotVS的API 再次封装,对各个API 使用中的 容错方面进行了处理。适合想快速写出策略程序的开发者使用(直接使用BotVS API 开发者会更清楚程序流程,灵活性也更高,是否复用模块各有好处)。
    使用《商品期货交易类库》前如果没有复制过该模板的需要复制到自己BotVS账户的控制中心,地址:https://www.botvs.com/strategy/12961**
    复制后在引用该模板的策略中勾选该模板。

    在BotVS知乎专栏里面有关于这个模板的源代码注释,想学习的同学可以看看,欢迎关注。
    《商品期货交易类库》 模块,是通过创建一个 对象 来调用接口的,我们先声明一个全局变量用来引用生成的对象,
    var manager = null;  // 声明为全局变量
    

    调用导出函数生成对象:


    manager = $.NewPositionManager();  // 《商品期货交易类库》导出函数  ,返回一个控制对象。
    

    导出函数部分很好写:


    // LONG 
    manager.OpenLong(ContractType, _N(Balance_Unit / (ContractTypeInfo.VolumeMultiple * Bar.Close * ContractTypeInfo.LongMarginRatio), 0));
    
    // SHORT
    manager.OpenShort(ContractType, _N(Balance_Unit / (ContractTypeInfo.VolumeMultiple * Bar.Close * ContractTypeInfo.ShortMarginRatio), 0));
    

    回测试一下:

    本篇完整代码
    // 参数变量 (待填写)
    var ContractType = "rb1710";  // 标的物合约代码   ,螺纹钢 1710 合约 目前主力合约
    var UsedRatio = 0.5
    // 全局变量 (待填写)
    var Interval = 500;           // 轮询时间 , 毫秒  , 500 毫秒 = 0.5 秒
    var Balance_Unit = 0;
    var ContractTypeInfo = null;  // 合约信息
    var initAccount = null;       // 初始账户信息
    var nowAccount = null;        // 当前账户信息
    var LONG = 1;
    var SHORT = 2;
    var IDLE = 0;
    var State = IDLE;
    var EnterPrice = null;        // 入场价
    var FloatProfitStop = null;   // 浮动止损
    var LossStop = null;          // 止损线
    var manager = null;           // 用于引用《商品期货交易类库》 导出函数 生成的对象。 
    // 功能函数 (待填写)
    function loop(){              // 主循环函数
        /*入场:以前一日收盘价上下0.5*ATR为上下轨,突破上轨做多一份资金,突破下轨做空一份资金。*/
        var records = exchange.GetRecords(PERIOD_D1);
        if(!records || records.length < 21){
            return;
        }
        var atr = GetATR(records);
        if(atr.length < 2){
            return;
        }
        var Bar = records[records.length - 1];
        var lastDayClose = records[records.length - 2].Close;
        var lastDayATR = atr[atr.length - 2];
        var upTrack = lastDayClose + lastDayATR * 0.5;
        var downTrack = lastDayClose - lastDayATR * 0.5;
        
        // 开仓
        if(State == IDLE &&  Bar.Close > upTrack){  // LONG 
            // Log("触发!上轨:", upTrack, "当前Bar最新收盘价(Close)", Bar.Close, "lastDayATR:", lastDayATR, "lastDayClose:", lastDayClose);   // 测试代码 
            // throw "暂停!";                                                      // 测试代码
            nowAccount = CheckBalance_Unit(LONG);
            var tradeInfoLong = manager.OpenLong(ContractType, _N(Balance_Unit / (ContractTypeInfo.VolumeMultiple * Bar.Close * ContractTypeInfo.LongMarginRatio), 0));  // 商品期货交易类库 导出函数,处理交易细节。
            if(tradeInfoLong){
                EnterPrice = tradeInfoLong.price;
                CalcFPS_LS(EnterPrice, LONG);
                State = LONG;
            }
        }else if(State == IDLE && Bar.Close < downTrack){  // SHORT
            // Log("触发!下轨:", downTrack, "当前Bar最新收盘价(Close)", Bar.Close, "lastDayATR:", lastDayATR, "lastDayClose:", lastDayClose); // 测试代码
            // throw "暂停!";                                                      // 测试代码
            nowAccount = CheckBalance_Unit(SHORT);
            var tradeInfoShort = manager.OpenShort(ContractType, _N(Balance_Unit / (ContractTypeInfo.VolumeMultiple * Bar.Close * ContractTypeInfo.ShortMarginRatio), 0)); // 商品期货交易类库 导出函数,处理交易细节。
            if(tradeInfoShort){
                EnterPrice = tradeInfoShort.price;
                CalcFPS_LS(EnterPrice, SHORT);
                State = SHORT;
            }
        }
    
        // 加仓
    
    
        // 平仓
    }
    
    function CalcFPS_LS(Para_EnterPrice, Direction){    // 计算 FloatProfitStop : FPS ,  LossStop : LS
        // 以做多为例:设置当前初始线(入场价),按着一定规则(如:止损线=0.8 * 入场价)浮动止盈线和止损线,可以设置,(浮动止盈线-中线)= 1.2(中线-止损线)
        if(Direction == LONG){
            LossStop = 0.8 * Para_EnterPrice;
            FloatProfitStop = 1.2 * (Para_EnterPrice - (0.8 * Para_EnterPrice)) + Para_EnterPrice;
        }else if(Direction == SHORT){
            LossStop = 1.2 * Para_EnterPrice;
            FloatProfitStop = Para_EnterPrice - 1.2 * (1.2 * Para_EnterPrice - Para_EnterPrice);
        }else{
            throw "错误的Direction参数:" + Direction;
        }
    }
    
    function GetATR(records){  // 默认为 records 参数是有效值,传入,即: 不为null ,Bar 长度足够。
        var atr = TA.ATR(records);
        return atr;
    }
    
    function CheckBalance_Unit(Direction){
        ContractTypeInfo = exchange.SetContractType(ContractType);
        Log("标的物合约信息:", ContractTypeInfo);
        Balance_Unit = _N(initAccount.Balance * UsedRatio / 10, 2);
        Log("账户信息:", initAccount, "资金分配 10份,一份为:", Balance_Unit);
    
        var ticker = _C(exchange.GetTicker);
        var OneContractMargin = ContractTypeInfo.VolumeMultiple * ticker.Last * (Direction == LONG ? ContractTypeInfo.LongMarginRatio : ContractTypeInfo.ShortMarginRatio);
        if(Balance_Unit < OneContractMargin * 1.2){
            throw "最新价格:" + ticker.Last + "调整系数1.2 " + " ,资金可用部分的10分之一 不足 开" + (Direction == LONG ? "多" : "空") + "1手合约," + "1手合约需:" + OneContractMargin;
        }else{
            Log("最新价格:" + ticker.Last + "调整系数1.2 " + " 1份资金 可开:", Direction == LONG ? "多" : "空", _N(Balance_Unit / OneContractMargin, 0));
        }
        var nowAccount = _C(exchange.GetAccount);
        if(nowAccount.Balance < Balance_Unit){
            throw "当前账户资金已小于初始资金可用部分的十分之一。当前资金:" + nowAccount.Balance + ", 初始资金可用部分的十分之一为:" + Balance_Unit;
        }else if(nowAccount.Balance < OneContractMargin * 1.2){
            throw "资金不足:" + JSON.stringify(nowAccount) + ", 系数1.2,1手合约保证金:" + OneContractMargin;
        }
        if(nowAccount.Balance - initAccount.Balance * (1 - UsedRatio) < OneContractMargin * 1.2){
            throw "资金可用部分不足一手保证金金额。可用部分:" + (nowAccount.Balance - initAccount.Balance * (1 - UsedRatio)) + ",1手保证金" + OneContractMargin + ",系数:1.2";
        }
        return nowAccount;
    }
    
    // 入口函数 main 
    function main(){
        // 程序的初始化工作 (待填写)
        manager = $.NewPositionManager();  // 《商品期货交易类库》导出函数  ,返回一个控制对象。
        while(true){
            if(exchange.IO("status") == true && (initAccount = exchange.GetAccount()) !== null){
                break;
            }
            LogStatus("等待交易时间获取账户信息初始化!" + "时间:", new Date());
            Sleep(Interval);
        }
        CheckBalance_Unit(LONG);
        CheckBalance_Unit(SHORT);
        
        // 主循环, 程序完成初始化后在此 循环执行,直到手动关闭。
        var LoginState = null;
        var nowTimeStamp = 0;
        while(true){
            nowTimeStamp = new Date().getTime();
            if(exchange.IO("status") == true){
                LoginState = true;
                loop();
            }else{
                LoginState = false;
            }
            LogStatus("时间:", _D(nowTimeStamp), LoginState ? "已连接服务器" : "未连接服务器!"/*, 待显示的一些信息可以写在此处,如账户信息,实时行情,程序状态*/)
            Sleep(Interval);     //  暂停 0.5 秒, 避免轮询频率过高,访问交易所服务器过于频繁导致问题。
        }
    }
    
    function onexit(){
        // 做一些在程序停止时的 收尾工作。(待填写)
        
        Log("程序退出!");
    }
    

    视频地址: KSDD4(1).mov.mp4**KSDD4(2).mov.mp4**
    下一篇,我们一起来完善加仓和平仓的代码。

    相关文章

      网友评论

        本文标题:使用BotVS 快速迭代你的量化交易灵感(3)

        本文链接:https://www.haomeiwen.com/subject/vzitfxtx.html