}
SetErrorFilter("login|ready|流控|连接失败|初始|Timeout"); // 过滤常规错误
var mode = exchange.IO("mode", 0); // 设定行情模式 为立即返回模式 参看 API 文档: https://www.youquant.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.youquant.com/strategy/17289
##### 附个 simnow 模拟盘测试
![img](/upload/asset/ce365579e2fdb47105583a731f1c285927b459e3.png)
#### 欢迎读者给我留言!提出建议和意见,如果感觉好玩可以分享给更多热爱程序热爱交易的朋友
https://www.youquant.com/bbs-topic/745
![img](/upload/asset/87bdd9dbe7179f266adb17f17a30e90ac27be9ea.png)
### 程序员 littleDream 原创
haocow 詳細