和老白一起玩转JavaScript -- 创造一个会做买卖的小伙伴(8)实现一个多品种并发的商品期货策略

Author: 扫地僧, Created: 2017-03-18 17:20:46, Updated: 2017-10-11 10:38:05

} SetErrorFilter(“login|ready|流控|连接失败|初始|Timeout”); // 过滤常规错误 var mode = exchange.IO("mode", 0); // 设定行情模式 为立即返回模式 参看 API 文档: https://www.fmz.com/api if (typeof(mode) !== ‘number’) { // 如果 切换模式 的API 返回的 不是 数值,即切换失败。 throw “切换模式失败, 请更新到最新托管者!”; // 抛出异常 } while (!exchange.IO("status")) { // 检测 与 行情、交易服务器连接,直到 API 函数 exchange.IO("status") 返回true 连接上,退出循环 Sleep(3000); LogStatus("正在等待与交易服务器连接, " + new Date()); // 在未连接上时 输出 文本和 当前时间。 } var positions = _C(exchange.GetPosition); // 调用API GetPosition 函数 获取 持仓信息 if (positions.length > 0) { // 返回的数组不是空数组 ,即有持仓 Log(“检测到当前持有仓位, 系统将开始尝试恢复进度…”); Log(“持仓信息”, positions); } Log(“风险系数:”, RiskRatio, “N值周期:”, ATRLength, “系统1: 入市周期”, EnterPeriodA, “离市周期”, LeavePeriodA, “系统二: 入市周期”, EnterPeriodB, “离市周期”, LeavePeriodB, “加仓系数:”, IncSpace, “止损系数:”, StopLossRatio, “单品种最多开仓:”, MaxLots, “次”); // 输出 参数信息。 var initAccount = _bot.GetAccount(); // 获取账户信息 var initMargin = JSON.parse(exchange.GetRawJSON()).CurrMargin; // 调用 API GetRawJSON 函数 获取 : “CurrMargin”: “当前保证金总额”, var keepBalance = _N((initAccount.Balance + initMargin) * (KeepRatio/100), 3); // 根据预留保证金比例 计算出 需要预留的资金。 Log(“资产信息”, initAccount, “保留资金:”, keepBalance); // 输出信息

var tts = [];
var filter = [];                                                               // 过滤用数组
var arr = Instruments.split(',');                                              // 合约列表按照逗号分隔 成数组
for (var i = 0; i < arr.length; i++) {                                         // 遍历分隔后的数组
    var symbol = arr[i].replace(/^\s+/g, "").replace(/\s+$/g, "");             // 正则表达式 匹配 操作, 得出  合约代码
    if (typeof(filter[symbol]) !== 'undefined') {                              // 如果 在过滤数组中 存在 名为 symbol的属性,则显示信息 并跳过。
        Log(symbol, "已经存在, 系统已自动过滤");
        continue;
    }
    filter[symbol] = true;                                                     // 给过滤数组 添加 名为 symbol 的 属性,下次 同样的 合约代码 会被过滤
    var hasPosition = false;                                                   // 初始化 hasPosition 变量 false 代表没有持仓 
    for (var j = 0; j < positions.length; j++) {                               // 遍历 获取到的持仓信息
        if (positions[j].ContractType == symbol) {                             // 如果有持仓信息 合约 名称 和 symbol一样的, 给hasPosition 赋值true 代表有持仓
            hasPosition = true;
            break;
        }
    }
    var obj = TTManager.New(hasPosition, symbol, keepBalance, RiskRatio, ATRLength, EnterPeriodA, LeavePeriodA, EnterPeriodB, LeavePeriodB, UseEnterFilter, IncSpace, StopLossRatio, MaxLots);
    // 根据界面参数 使用 构造函数 New 构造  一个品种的海龟交易策略控制对象
    tts.push(obj);                                                             // 把该对象压入 tts 数组, 最终根据合约列表 ,生成了若干个品种的 控制对象储存在tts数组 
}


var preTotalHold = -1;
var lastStatus = '';
while (true) {                                                                 // 主要循环
    if (GetCommand() === "暂停/继续") {                                         // API GetCommand 函数 获取 程序界面上的 命令。此处 如果 点击了界面上的“暂停/继续”按钮
        Log("暂停交易中...");
        while (GetCommand() !== "暂停/继续") {                                  // 进入等待循环 ,直到再次点击  “暂停/继续” 按钮 退出 等待循环
            Sleep(1000);
        }
        Log("继续交易中...");
    }
    while (!exchange.IO("status")) {                                           // 一旦断开服务器的连接,则尝试重连 并等待。
        Sleep(3000);
        LogStatus("正在等待与交易服务器连接, " + new Date() + "\n" + lastStatus);  // 输出上一次的 状态栏 内容,并 更新时间。
    }
    var tblStatus = {                                                          // 用于显示在状态栏表格上的  持仓信息  对象
        type: "table",
        title: "持仓信息",
        cols: ["合约名称", "持仓方向", "持仓均价", "持仓数量", "持仓盈亏", "加仓次数", "开仓次数", "止损次数", "成功次数", "当前价格", "N"],
        rows: []
    };
    var tblMarket = {                                                          // 用于显示在状态栏表格上的 市场信息 对象
        type: "table",
        title: "运行状态",
        cols: ["合约名称", "合约乘数", "保证金率", "交易时间", "柱线长度", "上线", "下线", "止损价", "离市价", "异常描述", "发生时间"],
        rows: []
    };
    var totalHold = 0;
    var vmStatus = {};
    var ts = new Date().getTime();                                             // 当前时间戳
    var holdSymbol = 0;                                                        // 持有的合约量
    for (var i = 0; i < tts.length; i++) {                                     // 遍历tts数组
        tts[i].Poll();                                                         // 调用每个 合约的海龟管理对象的 Poll 函数
        var d = tts[i].Status();                                               // 更新每个 海龟管理对象的  状态 属性 status 并返回。
        if (d.holdAmount > 0) {                                                // 如果当前索引的对象 有 持仓
            vmStatus[d.symbol] = d.vm;                                         // 给空对象 vmStatus 添加合约名称 为属性名 的属性,并给其赋值 持仓信息vm
            holdSymbol++;                                                      // 给持有的合约品种数量  累计  
        }
        tblStatus.rows.push([d.symbolDetail.InstrumentName, d.holdAmount == 0 ? '--' : (d.marketPosition > 0 ? '多' : '空'), d.holdPrice, d.holdAmount, d.holdProfit, Math.abs(d.marketPosition), d.open, d.st, d.cover, d.lastPrice, d.N]);
        // 压入当前 索引 的 海龟管理对象 的信息 到状态分页表格
        tblMarket.rows.push([d.symbolDetail.InstrumentName, d.symbolDetail.VolumeMultiple, _N(d.symbolDetail.LongMarginRatio, 4) + '/' + _N(d.symbolDetail.ShortMarginRatio, 4), (d.isTrading ? '是#0000ff' : '否#ff0000'), d.recordsLen, d.upLine, d.downLine, d.stopPrice, d.leavePrice, d.lastErr, d.lastErrTime]);
        // 压入当前 索引 的 海龟管理对象 的信息 到行情分页表格
        totalHold += Math.abs(d.holdAmount);                                   // 值为回调函数 的参数ret 的属性 更新,可以参见 回调函数的 传入实参。processTask 函数中的 ret
        // 累计 总持仓手数
    }
    var now = new Date();                                                      // 获取最新时间
    var elapsed = now.getTime() - ts;                                          // 计算主要耗时代码 , 迭代 执行 Poll 函数的 开始与结束的 时间差。
    var tblAssets = _bot.GetAccount(true);                                     // 获取账户详细信息并返回一个表格对象。(因为参数传递的是true, 参见 模板的 GetAccount 函数的 getTable 参数)
    var nowAccount = _bot.Account();                                           // 获取账户信息
   
    if (tblAssets.rows.length > 10) {                                          // 如果获取的 表格的 行数 大于10
        // replace AccountId
        tblAssets.rows[0] = ["InitAccount", "初始资产", initAccount];           // 设置 索引 0 的行数 为 初始资金信息。
    } else {
        tblAssets.rows.unshift(["NowAccount", "当前可用", nowAccount], ["InitAccount", "初始资产", initAccount]); // 往 rows 数组 中开始的位置插入2个元素
    }
    lastStatus = '`' + JSON.stringify([tblStatus, tblMarket, tblAssets]) + '`\n轮询耗时: ' + elapsed + ' 毫秒, 当前时间: ' + now.toLocaleString() + ', 星期' + ['日', '一', '二', '三', '四', '五', '六'][now.getDay()] + ", 持有品种个数: " + holdSymbol;
    // 组合 各种 用于显示在界面的信息。
    if (totalHold > 0) {                                                       // 在有持仓时才 显示 手动恢复字符串(vmStatus JSON序列化)
        lastStatus += "\n手动恢复字符串: " + JSON.stringify(vmStatus);
    }
    LogStatus(lastStatus);                                                     // 调用API 显示在 状态栏
    if (preTotalHold > 0 && totalHold == 0) {                                  // 当全部持仓 平掉 没有持仓时
        LogProfit(nowAccount.Balance - initAccount.Balance - initMargin);      // 输出 盈利, 显示到收益曲线(此种情况 出现概率较低,很难有同时全部都未持仓的状态,所以收益都是 动态的,可以看 账户详细信息分析当前状况)
    }
    preTotalHold = totalHold;                                                  // 每次都更新  确保 输出收益只显示一次。 
    Sleep(LoopInterval * 1000);                                                // 轮询等待。避免API 访问过于频繁
}

}


源码地址 :https://www.fmz.com/strategy/17289

##### 附个 simnow 模拟盘测试
![img](/upload/asset/ce365579e2fdb47105583a731f1c285927b459e3.png) 

#### 欢迎读者给我留言!提出建议和意见,如果感觉好玩可以分享给更多热爱程序热爱交易的朋友
https://www.fmz.com/bbs-topic/745
![img](/upload/asset/87bdd9dbe7179f266adb17f17a30e90ac27be9ea.png) 
### 程序员 littleDream 原创

更多内容

haocow 詳細