海龟交易算法中的止损加仓taofv价和加仓价怎么确定

该用户的其他资料
在此可输入您对该资料的评论~
(window.slotbydup=window.slotbydup || []).push({
id: '4540180',
container: s,
size: '250,200',
display: 'inlay-fix'
资料评价:
所需积分:5EA 外汇交易 MT4平台指标编程
原版海龟交易法则
原版海龟交易法则简介
海龟交易法则本质上是一个趋势追随系统,建仓点位是根据唐迁通道(Donchian Channel,也称唐奇安通道)而来,加上对交易品种的筛选和仓位的调整,使得本是低胜率的趋势系统能够实现正收益的结果。
海龟交易法则通常应用于日线交易,但用于其他的交易周期也是可以的。
海龟交易品种的要求
市场的流动性足够好,规模足够大。
比如小麦期货同时满足上述两点要求,是海龟策略可以应用的交易品种,但国内的原油期货,由于存在价格控制,导致其流动性非常差,所以不能作为海龟交易的标的品种。
海龟交易头寸规模的计算
海龟将每一个品种的交易分成四份,称为头寸单位(Units),每份的头寸单位的计算公式为:
每单位头寸手数 = 账户余额 * 1% / ATR(20)
这样计算头寸规模的好处是根据 ATR 止损时,每个头寸单位损失的最大金额都是一样的,从而限制了风险。
“账户余额”定期更新一次即可,不一定非要用实时余额。
“ATR”是一个常用指标,中文名称为真实波动率,反映的是进行加权计算后的近期 K 线平均振幅。ATR(20) 表示 20 日的真实波动率,MT4 或其他交易软件中会有相应的指标可以直接使用。同样的,ATR 的值也可以同一批订单相同,不使用实时更新的值。
海龟交易持仓限制
海龟交易法则要求将交易品种按相关性划分类别,比如螺纹钢和铁矿石就是高度相关的交易品种,黄金和铜是松散相关的品种,欧元和豆粕是不相关的交易品种。
单个市场:4 个头寸单位。
高度相关:6 个头寸单位。
松散相关:10 个头寸单位。
单个方向:12 个头寸单位。
比如持有 4 份螺纹钢多单后,只能再加 2 份铁矿石的订单(共 6 份),可以再加 4 份黄金多单(共 10 份),最后还可以加 2 份欧元多单(共 12 份)。多头仓位满仓后,可以继续增加其他不相关品种的空头仓位,最多也是 12 份。理论上最多同时持有 24 份头寸。
海龟交易建仓策略
海龟策略分为长线版和短线版两个系统,区别仅是入场和离场的突破周期不一样。系统一选取 20 日入场,10 日离场的策略,系统二为 55 日入场,20 日离场。下面以较为常用的系统一为例:
以多单为例,向上突破 20 日最高点建立多单,如果价格向上跳空,按跳空价入市。
通常一波趋势结束后会进行进入震荡,为了避免这个几乎可以确定的震荡带来的假突破造成的损失,当最近一次盈利后,第一个突破不建仓。
以入市价为基础,价格每上涨 ATR(20) / 2,增加一份头寸。如加仓价遇到向上跳空,则下一份头寸的加仓价以当前头寸的实际成交价上涨 ATR(20) / 2 计算,空单同理。例子:
头寸 1 的建仓价 = 20 日最高点
头寸 2 的建仓价 = 头寸 1 的建仓价 + 1/2 * ATR(20)
头寸 3 的建仓价 = 头寸 2 的建仓价 + 1/2 * ATR(20)
头寸 4 的建仓价 = 头寸 3 的建仓价 + 1/2 * ATR(20)
海龟交易止损策略(一)
海龟交易法则有两个止损策略,第一个止损策略比较常用。
头寸 1 的止损价 = 头寸 1 的建仓价 – 2 * ATR(20)
头寸 1 的止损价 = 头寸 2 的建仓价 – 2 * ATR(20)
头寸 2 的止损价 = 头寸 2 的建仓价 – 2 * ATR(20)
头寸 1 的止损价 = 头寸 3 的建仓价 – 2 * ATR(20)
头寸 2 的止损价 = 头寸 3 的建仓价 – 2 * ATR(20)
头寸 3 的止损价 = 头寸 3 的建仓价 – 2 * ATR(20)
头寸 1 的止损价 = 头寸 4 的建仓价 – 2 * ATR(20)
头寸 2 的止损价 = 头寸 4 的建仓价 – 2 * ATR(20)
头寸 3 的止损价 = 头寸 4 的建仓价 – 2 * ATR(20)
头寸 4 的止损价 = 头寸 4 的建仓价 – 2 * ATR(20)
理想条件下,没有出现价格跳空,最终将为:
头寸 1 的止损价 = 头寸 1 的建仓价 &# * ATR(20)
头寸 2 的止损价 = 头寸 2 的建仓价 – 1 * ATR(20)
头寸 3 的止损价 = 头寸 3 的建仓价 &# * ATR(20)
头寸 4 的止损价 = 头寸 4 的建仓价 – 2 * ATR(20)
海龟交易止损策略(二)
相对于海龟止损策略一,止损策略二更加激进:
每笔头寸的止损价 = 本头寸的建仓价 &# * ATR(20)
加仓后不调整之前头寸的止损价。
海龟交易退出策略
以 20 日突破入场的系统一,价格反向突破 10 日最高或最低点离场,系统二是 20 日顶点。
海龟交易法则适应的市场
海龟交易法则是一个集趋势判断和风险管理于一身的完整策略:
通过“品种划分”和“持仓上限”将市场中所有品种趋势性最强劲的前几名筛选出来。
通过“头寸规模限制”和“品种流动性要求”锁定了账户总资金的最大回撤风险。
通过“入场策略”和“退出策略”的突破来判断趋势的启动和结束。
由此可见海龟交易法则更适合期货市场(品种多,但国内期货的涨跌停限制导致流动性稍差),海龟策略的成名也是应用在期货市场当中的。
外汇市场虽然流动性最好,但是主要直盘品种不到十种,按海龟的交易交易理念,是高度相关的品种,可操作空间比较窄。
由于国内 A 股的采取了涨跌停限制(流动性差)、T+0 限制(流动性差)、融券制(流动性差)等特殊制度(导致流动性非常差),海龟策略并不适合国内股票市场。
海龟交易法则的实际应用
配图是 USD/CAD 日线,实线是 20 日高低点,虚线是 10 日高低点,碰巧加元连着走了两波非常大的趋势。
我使用海龟交易法则在 MT4 平台上进行了复盘回测,回测报告可以看这里:
海龟交易策略适用于国内股票市场吗、海龟交易系统有效吗、海龟法则的头寸规模怎么计算、海龟交易法则是什么、原版海龟交易策略的内容、海龟策略可不可以在外汇市场上用。
版权声明:本站所有原创文章,作者保留版权。转载必须包含本声明,不得修改任何内容(包括文章标题),并以超链接的形式注明作者“Bary”和本文原始地址。和老白一起玩转JavaScript -- 创造一个会做买卖的小伙伴(8)实现一个多品种并发的商品期货策略 - 知乎专栏
{"debug":false,"apiRoot":"","paySDK":"/api/js","wechatConfigAPI":"/api/wechat/jssdkconfig","name":"production","instance":"column","tokens":{"X-XSRF-TOKEN":null,"X-UDID":null,"Authorization":"oauth c3cef7c66aa9e6a1e3160e20"}}
{"database":{"Post":{"":{"title":"和老白一起玩转JavaScript -- 创造一个会做买卖的小伙伴(8)实现一个多品种并发的商品期货策略","author":"littledream-XX","content":"实现一个多品种并发的商品期货策略(8)& 当一把造物主,做个程序员真好!实现 《商品期货多品种海龟策略》机器人 海龟交易法起源
这个交易系统的诞生来源与两个交易老手的争论,一方觉得交易这种技能是后天习得的,另一方觉得这是先天所决定的。1983年,在新加坡海龟农场,两位神一样的交易员Dennis、Eckhardt有了分歧。E神说,交易员是天生的;D神说,就像这一大缸海龟一样,交易员可以培养。
于是,D神掏出来白花花的银子,要做实验,要打赌。他们在华尔街日报、纽约时报等等放出广告,说D神要搞培训班了,给每个人100万美元的账户,手把手地教,不限专业,无须经验,皆可报名。有千余人投了简历,40人进入面试,23人被留下考察,13人进入培训班。
这13个人来自各行各业,多数都没有交易经验,是一群尚未成功的普通人。他们被培训了两个星期,然后放出去交易,在接下来的四年半里,创出了80%的年均收益率。培训内容,叫做《海龟交易法则》;培训学员,被称为“海龟”。
尽管有人质疑样本的随机性,这场试验应该算D神胜利了。- #### 海龟交易系统是一个完整的交易系统,它有一个完整的交易系统所应该有的所有成分,涵盖了成功交易中的每一个必要决策:
市场:买卖什么?
头寸规模:买卖多少?
Unit=(1%*Account)/N
入市:什么时候买卖?
止损:什么时候放弃一个亏损的头寸?
退出:什么时候退出一个盈利的头寸?
战术:怎么买卖?
核心围绕: N值,海龟的止损、加仓、头寸规模 都是基于N值计算, 有些海龟交易系统用的是ATR来代替N值,ATR为真实波幅的20日平均。
“海龟们从不去预测市场的动向,而是会寻找市场处于某种特定状态的指示信号。优秀的交易者不会试着预测市场下一步会怎么样;相反,他们会观察指示信号,判断市场现在正处于什么样的状态中。”
对于 “海龟交易法”感到陌生的读者可以看这篇文章:
也可以 知乎 或者 百度 搜索,有很多文章介绍,老白就不做赘述了。
- #### 作为 本系列文章的最后收尾一篇,我们就来动手实践一个海龟策略,当然我们要有创新,我们实现一个“海龟群”。
说点题外的,之前的几篇文章记录的都是老白当时学习时的心路历程,学习量化、程序化没办法一蹴而就,只能脚踏实地,耐住性子一点点进步。老白开始的时候也感觉思考问题、找BUG 、写程序晕头转向的。但是,慢慢的我发现学习是个加速度,开始很慢,积累越多越轻松。一个完全零基础的朋友经常和我说:“越是感觉自己要放弃的时候,越是应该跟困难死磕的时候!”
言归正传, 为什么我们要使用海龟群呢?
当然是为了尽可能的分散风险,即使是大名鼎鼎的海龟策略,当年也曾经有过大幅回撤,甚至亏损本金。任何交易系统都是有一定风险的。多品种的好处就是“把鸡蛋放在不同的篮子里”。当然也有缺点,那就是需要不小的资金量。资金量小了,可能只能交易几个品种,降低了分散风险的能力。
还有一点需要牢记:任何时候都可能飞出一只黑天鹅! (如商品期货 16年底黑色星期五 全线暴跌。)注释版源代码:/*
\n\n参数:\n\nInstruments
字符串(string)
MA701,CF701,zn1701,SR701,pp,hc1610,ni,v1701,rb1610,jm1701,ag1612,al1701,jd1701,cs\n\nLoopInterval
轮询周期(秒)
数字型(number)
3\n\nRiskRatio
% Risk Per N ( 0 - 100) 数字型(number)
1\n\nATRLength
ATR计算周期
数字型(number)
20\n\nEnterPeriodA
系统一入市周期
数字型(number)
20\n\nLeavePeriodA
系统一离市周期
数字型(number)
10\n\nEnterPeriodB
系统二入市周期
数字型(number)
55\n\nLeavePeriodB
系统二离市周期
数字型(number)
20\n\nUseEnterFilter
使用入市过滤
布尔型(true/false)
true\n\nIncSpace
加仓间隔(N的倍数)
数字型(number)
0.5\n\nStopLossRatio
止损系数(N的倍数)
数字型(number)
2\n\nMaxLots
单品种加仓次数
数字型(number)
4\n\nRMode
进度恢复模式
下拉框(selected)
自动|手动\n\nVMStatus@RMode==1
手动恢复字符串
字符串(string)
{}\n\nWXPush
推送交易信息
布尔型(true/false)
true\n\nMaxTaskRetry
开仓最多重试次数
数字型(number)
5\n\nKeepRatio
预留保证金比例
数字型(number)
10 \n\n*/\n\n\n\nvar _bot = $.NewPositionManager();
// 调用CTP商品期货交易类库 的导出函数 生成一个用于单个品种交易的对象 \n\n\n\nvar TTManager = {
// 海龟策略 控制器\n\n
New: function(needRestore, symbol, keepBalance, riskRatio, atrLen, enterPeriodA, leavePeriodA, enterPeriodB, leavePeriodB, useFilter,\n\n
multiplierN, multiplierS, maxLots) {\n\n
// 该控制器对象 TTManager 的属性 New 赋值一个 匿名函数(构造海龟的函数,即:构造函数),用于创建 海龟任务,参数分别是:\n\n
// needRestore: 是否需要恢复,symbol:合约代码,keepBalance:必要的预留的资金,riskRatio:风险系数, atrLen:ATR指标(参数)周期。enterPeriodA:入市周期A\n\n
// leavePeriodA:离市周期A , enterPeriodB:入市周期B, leavePeriodB:离市周期B,useFilter:使用过滤,multiplierN:加仓系数,multiplierS:止损系数,maxLots:最大加仓次数\n\n\n\n
// subscribe\n\n
var symbolDetail = _C(exchange.SetContractType, symbol);
// 声明一个局部变量 symbolDetail 用于接受API SetContractType 函数的返回值(值为symbol的合约的详细信息,symbol 是 \"MA709\",返回的就是甲醇709合约的详细信息),\n\n
// 调用API SetContractType 订阅并切换合约为 symbol 变量值的合约。 _C() 函数的作用是 对 SetContractType 合约容错处理,即如果 SetContractType返回null 会循环重试。\n\n
if (symbolDetail.VolumeMultiple == 0 || symbolDetail.MaxLimitOrderVolume == 0 || symbolDetail.MinLimitOrderVolume == 0 || symbolDetail.LongMarginRatio == 0 || symbolDetail.ShortMarginRatio == 0) {\n\n
// 如果 返回的合约信息对象symbolDetail 中 VolumeMultiple、MaxLimitOrderVolume 等数据异常,则调用 throw 抛出错误,终止程序。\n\n
Log(symbolDetail);\n\n
throw \"合约信息异常\";\n\n
// 检索的数据没有异常则,输出部分合约信息。\n\n
Log(\"合约\", symbolDetail.InstrumentName, \"一手\", symbolDetail.VolumeMultiple, \"份, 最大下单量\", symbolDetail.MaxLimitOrderVolume, \"保证金率:\", _N(symbolDetail.LongMarginRatio), _N(symbolDetail.ShortMarginRatio), \"交割日期\", symbolDetail.StartDelivDate);\n\n
var ACT_IDLE = 0;
// 定义一些宏 (标记)\n\n
var ACT_LONG = 1;\n\n
var ACT_SHORT = 2;\n\n
var ACT_COVER = 3;
// 动作宏\n\n\n\n\n\n
var ERR_SUCCESS = 0;
// 错误宏\n\n
var ERR_SET_SYMBOL = 1;\n\n
var ERR_GET_ORDERS = 2;\n\n
var ERR_GET_POS = 3;\n\n
var ERR_TRADE = 4;\n\n
var ERR_GET_DEPTH = 5;\n\n
var ERR_NOT_TRADING = 6;\n\n
var errMsg = [\"成功\", \"切换合约失败\", \"获取订单失败\", \"获取持仓失败\", \"交易下单失败\", \"获取深度失败\", \"不在交易时间\"];
// 错误宏的值 对应该数组的索引,对应索引的值就是翻译\n\n\n\n
var obj = {
// 声明一个对象,构造完成后返回。单个的海龟策略控制对象。\n\n
symbol: symbol,
// 合约代码
构造函数执行时的参数传入\n\n
keepBalance: keepBalance,
// 预留的资金
构造函数执行时的参数传入\n\n
riskRatio: riskRatio,
// 风险系数
构造函数执行时的参数传入\n\n
atrLen: atrLen,
// ATR 长度
构造函数执行时的参数传入\n\n
enterPeriodA: enterPeriodA,
// 入市周期A
构造函数执行时的参数传入\n\n
leavePeriodA: leavePeriodA,
// 离市周期A
构造函数执行时的参数传入\n\n
enterPeriodB: enterPeriodB,
// 入市周期B
构造函数执行时的参数传入\n\n
leavePeriodB: leavePeriodB,
// 离市周期B
构造函数执行时的参数传入\n\n
useFilter: useFilter,
// 使用入市过滤条件
构造函数执行时的参数传入\n\n
multiplierN: multiplierN,
// 加仓系数 基于N
构造函数执行时的参数传入\n\n
multiplierS: multiplierS
// 止损系数 基于N
构造函数执行时的参数传入\n\n
obj.task = {
// 给 obj对象添加一个 task 属性(值也是一个对象),用来保存 海龟的任务状态数据。\n\n
action: ACT_IDLE,
// 执行动作\n\n
amount: 0,
// 操作量\n\n
dealAmount: 0,
// 已经处理的操作量 \n\n
avgPrice: 0,
// 成交均价\n\n
preCost: 0,
// 前一次交易成交的额度\n\n
preAmount: 0,
// 前一次成交的量\n\n
init: false,
// 是否初始化\n\n
// 重试次数\n\n
desc: \"空闲\",
// 描述信息\n\n
onFinish: null
// 处理完成时的 回调函数,即可以自行设定一个 回调函数在完成当前 action 记录的任务后执行的代码。\n\n
obj.maxLots = maxL
// 赋值 最大加仓次数
构造函数执行时的参数传入\n\n
obj.lastPrice = 0;
// 最近成交价,用于计算 持仓盈亏。\n\n
obj.symbolDetail = symbolD
// 储存 合约的详细信息 到obj 对象的 symbolDetail 属性\n\n
obj.status = {
// 状态数据\n\n
symbol: symbol,
// 合约代码\n\n
recordsLen: 0,
// K线长度\n\n
// 持仓状态 , 用来储存 每个品种的 ,手动恢复字符串。\n\n
// 开仓次数\n\n
// 平仓次数\n\n
// 止损平仓次数\n\n
marketPosition: 0,
// 加仓次数\n\n
lastPrice: 0,
// 最近成交价价格\n\n
holdPrice: 0,
// 持仓均价\n\n
holdAmount: 0,
// 持仓数量\n\n
holdProfit: 0,
// 浮动持仓盈亏\n\n
upLine: 0,
// 上线\n\n
downLine: 0,
// 下线\n\n
symbolDetail: symbolDetail,
// 合约详细信息\n\n
lastErr: \"\",
// 上次错误\n\n
lastErrTime: \"\",
// 上次错误时间信息\n\n
stopPrice: '',
// 止损价格\n\n
leavePrice: '',
isTrading: false
// 是否在交易时间\n\n
};\n\n\n\n
obj.setLastError = function(err) {
// 给obj对象添加方法,设置 最近一次的错误信息\n\n
if (typeof(err) === 'undefined' || err === '') {
// 如果参数未传入,或者 错误信息为 空字符串\n\n
obj.status.lastErr = \"\";
// 清空 obj 对象的 status 属性的 对象的lastErr属性\n\n
obj.status.lastErrTime = \"\";
// 清空\n\n
// 返回\n\n
var t = new Date();
// 获取新时间\n\n
obj.status.lastErr =
// 设置错误信息\n\n
obj.status.lastErrTime = t.toLocaleString();
// toLocaleString()
根据本地时间格式,把 Date 对象转换为字符串。\n\n
obj.reset = function(marketPosition, openPrice, N, leavePeriod, preBreakoutFailure) {
// 给obj对象添加方法,恢复仓位。\n\n
// 参数,marketPosition:加仓次数,openPrice:最后一次加仓价, N:N值, leavePeriod:离市周期,preBreakoutFailure:是否上次突破失败\n\n
if (typeof(marketPosition) !== 'undefined') {
// 如果 第一个参数不是未定义 ,传入参数\n\n
obj.marketPosition = marketP
// 给obj 添加属性 marketPosition : 加仓次数 正数为多仓,负数为空仓\n\n
obj.openPrice = openP
// 最后一次加仓价\n\n
obj.preBreakoutFailure = preBreakoutF
// 是否上次突破失败\n\n
obj.N = N;
// N值\n\n
obj.leavePeriod = leaveP
// 离市周期\n\n
var pos = _bot.GetPosition(obj.symbol, marketPosition & 0 ? PD_LONG : PD_SHORT); // 调用 模板类库生成的 交易控制对象的成员函数GetPosition 获取 持仓信息\n\n
if (pos) {
// 如果获取到持仓信息\n\n
obj.holdPrice = pos.P
// 根据获取的持仓信息 给obj 属性赋值\n\n
obj.holdAmount = pos.A
// 同上\n\n
Log(obj.symbol, \"仓位\", pos);
// 输出显示当前仓位\n\n
// 如果GetPosition 返回null ,没有找到持仓信息。\n\n
throw \"恢复\" + obj.symbol + \"的持仓状态出错, 没有找到仓位信息\";
// 抛出异常\n\n
Log(\"恢复\", obj.symbol, \"加仓次数\", obj.marketPosition, \"持仓均价:\", obj.holdPrice, \"持仓数量:\", obj.holdAmount, \"最后一次加仓价\", obj.openPrice, \"N值\", obj.N, \"离市周期:\", leavePeriod, \"上次突破:\", obj.preBreakoutFailure ? \"失败\" : \"成功\");\n\n
// 输出恢复的 相关参数,数据。\n\n
obj.status.open = 1;
// 设置 开仓 计数为1\n\n
obj.status.vm = [obj.marketPosition, obj.openPrice, obj.N, obj.leavePeriod, obj.preBreakoutFailure];
// 储存 手动恢复字符串 数据。\n\n
// 没有传入参数,即不恢复, 全部初始化。\n\n
obj.marketPosition = 0;
// 初始化各项变量\n\n
obj.holdPrice = 0;\n\n
obj.openPrice = 0;\n\n
obj.holdAmount = 0;\n\n
obj.holdProfit = 0;\n\n
obj.preBreakoutFailure = // test system A
// 此处设置true
会使策略 尝试 突破系统A \n\n
obj.N = 0;\n\n
obj.leavePeriod = leavePeriodA;
// 用系统A 的离市周期 赋值\n\n
obj.holdProfit = 0;
// 初始化\n\n
obj.lastErr = \"\";\n\n
obj.lastErrTime = \"\";\n\n
};\n\n\n\n
obj.Status = function() {
// 给Obj 添加 Status 函数, 把Obj 的一些属性值 赋值给 Obj.status 同样意义的属性\n\n
obj.status.N = obj.N;
// 给 obj.status 赋值\n\n
obj.status.marketPosition = obj.marketP\n\n
obj.status.holdPrice = obj.holdP\n\n
obj.status.holdAmount = obj.holdA\n\n
obj.status.lastPrice = obj.lastP\n\n
if (obj.lastPrice & 0 && obj.holdAmount & 0 && obj.marketPosition !== 0) {
// 如果有持仓\n\n
obj.status.holdProfit = _N((obj.lastPrice - obj.holdPrice) * obj.holdAmount * symbolDetail.VolumeMultiple, 4) * (obj.marketPosition & 0 ? 1 : -1);\n\n
// 计算持仓盈亏 = (最近成交价 - 持仓价格)* 持仓量 * 一手合约份数 , 计算出来 保留4位小数, 用 obj.marketPosition(加仓次数) 属性的 正负 去修正,计算结果的正负(做空按照这个算法是相反的负数,所以要用-1修正)。\n\n
} else {\n\n
// 如果没有持仓,浮动盈亏赋值为0\n\n
obj.status.holdProfit = 0;\n\n
return obj.
// 返回这个 obj.status 对象(用于显示在界面状态栏?)\n\n
obj.setTask = function(action, amount, onFinish) {
// 给obj 对象添加 方法,设置任务\n\n
// 参数,action:执行动作,amount:数量,onFinish: 回调函数\n\n
obj.task.init =
// 重置 初次执行标记 为false \n\n
obj.task.retry = 0;
// 重置..\n\n
obj.task.action =
// 参数传来的 动作指令 赋值\n\n
obj.task.preAmount = 0;
// 重置\n\n
obj.task.preCost = 0;\n\n
obj.task.amount = typeof(amount) === 'number' ? amount : 0;
// 如果没传入参数 ,设置 0\n\n
obj.task.onFinish = onF\n\n
if (action == ACT_IDLE) {
// 如果 动作指令是 空闲\n\n
obj.task.desc = \"空闲\";
// 描述变量
“空闲”\n\n
obj.task.onFinish =
// 赋值为 null\n\n
// 其他动作\n\n
if (action !== ACT_COVER) {
// 如果不等于 平仓动作\n\n
obj.task.desc = (action == ACT_LONG ? \"加多仓\" : \"加空仓\") + \"(\" + amount + \")\"; // 根据 action 设置描述 信息\n\n
// 如果是平仓 动作 设置描述信息为 “平仓”\n\n
obj.task.desc = \"平仓\";\n\n
Log(\"接收到任务\", obj.symbol, obj.task.desc);
// 输出日志 显示 接收到任务。\n\n
// process immediately\n\n
obj.Poll(true);
// 调用 obj 对象的方法 处理 任务,参数是 true , 参数为true ,控制Poll 只执行 一部分(子过程)\n\n
obj.processTask = function() {
// 处理 交易任务 \n\n
var insDetail = exchange.SetContractType(obj.symbol);
// 切换 要操作的合约\n\n
if (!insDetail) {
// 切换失败 返回错误\n\n
return ERR_SET_SYMBOL;\n\n
var SlideTick = 1;
// 滑价设置为1 个 PriceTick\n\n
// 声明返回值
初始false\n\n
if (obj.task.action == ACT_COVER) {
// 处理 指令为全平的 任务,这部分处理 类似 商品期货交易类库 不再赘述,可以参见 商品期货交易类库注释版\n\n
var hasPosition =\n\n
if (!$.IsTrading(obj.symbol)) {\n\n
return ERR_NOT_TRADING;\n\n
hasPosition =\n\n
var positions = exchange.GetPosition();\n\n
if (!positions) {\n\n
return ERR_GET_POS;\n\n
var depth = exchange.GetDepth();\n\n
if (!depth) {\n\n
return ERR_GET_DEPTH;\n\n
var orderId =\n\n
for (var i = 0; i & positions. i++) {\n\n
if (positions[i].ContractType !== obj.symbol) {\n\n
var amount = Math.min(insDetail.MaxLimitOrderVolume, positions[i].Amount);\n\n
if (positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) {\n\n
exchange.SetDirection(positions[i].Type == PD_LONG ? \"closebuy_today\" : \"closebuy\");\n\n
orderId = exchange.Sell(_N(depth.Bids[0].Price - (insDetail.PriceTick * SlideTick), 2), Math.min(amount, depth.Bids[0].Amount), obj.symbol, positions[i].Type == PD_LONG ? \"平今\" : \"平昨\", 'Bid', depth.Bids[0]);\n\n
hasPosition =\n\n
} else if (positions[i].Type == PD_SHORT || positions[i].Type == PD_SHORT_YD) {\n\n
exchange.SetDirection(positions[i].Type == PD_SHORT ? \"closesell_today\" : \"closesell\");\n\n
orderId = exchange.Buy(_N(depth.Asks[0].Price + (insDetail.PriceTick * SlideTick), 2), Math.min(amount, depth.Asks[0].Amount), obj.symbol, positions[i].Type == PD_SHORT ? \"平今\" : \"平昨\", 'Ask', depth.Asks[0]);\n\n
hasPosition =\n\n
if (hasPosition) {\n\n
if (!orderId) {\n\n
return ERR_TRADE;\n\n
Sleep(1000);\n\n
while (true) {\n\n
// Wait order, not retry\n\n
var orders = exchange.GetOrders();\n\n
if (!orders) {\n\n
return ERR_GET_ORDERS;\n\n
if (orders.length == 0) {\n\n
for (var i = 0; i & orders. i++) {\n\n
exchange.CancelOrder(orders[i].Id);\n\n
Sleep(500);\n\n
} while (hasPosition);\n\n
} else if (obj.task.action == ACT_LONG || obj.task.action == ACT_SHORT) {
// 处理 建/加多仓 任务
处理 建/加空仓 任务,这部分处理 类似 商品期货交易类库 不再赘述,可以参见 商品期货交易类库注释版。(此策略没有使用商品期货交易类库的交易功能,在次直接植入了处理代码)\n\n
if (!$.IsTrading(obj.symbol)) {\n\n
return ERR_NOT_TRADING;\n\n
Sleep(1000);\n\n
while (true) {\n\n
// Wait order, not retry\n\n
var orders = exchange.GetOrders();\n\n
if (!orders) {\n\n
return ERR_GET_ORDERS;\n\n
if (orders.length == 0) {\n\n
for (var i = 0; i & orders. i++) {\n\n
exchange.CancelOrder(orders[i].Id);\n\n
Sleep(500);\n\n
var positions = exchange.GetPosition();\n\n
// Error\n\n
if (!positions) {\n\n
return ERR_GET_POS;\n\n
// search position\n\n
var pos =\n\n
for (var i = 0; i & positions. i++) {\n\n
if (positions[i].ContractType == obj.symbol && (((positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) && obj.task.action == ACT_LONG) || ((positions[i].Type == PD_SHORT || positions[i].Type == PD_SHORT_YD) && obj.task.action == ACT_SHORT))) {\n\n
if (!pos) {\n\n
pos = positions[i];\n\n
pos.Cost = positions[i].Price * positions[i].A\n\n
} else {\n\n
pos.Amount += positions[i].A\n\n
pos.Profit += positions[i].P\n\n
pos.Cost += positions[i].Price * positions[i].A\n\n
// record pre position\n\n
if (!obj.task.init) {\n\n
obj.task.init =\n\n
if (pos) {\n\n
obj.task.preAmount = pos.A\n\n
obj.task.preCost = pos.C\n\n
} else {\n\n
obj.task.preAmount = 0;\n\n
obj.task.preCost = 0;\n\n
var remain = obj.task.\n\n
if (pos) {\n\n
obj.task.dealAmount = pos.Amount - obj.task.preA\n\n
remain = parseInt(obj.task.amount - obj.task.dealAmount);\n\n
if (remain &= 0 || obj.task.retry &= MaxTaskRetry) {\n\n
ret = {\n\n
price: (pos.Cost - obj.task.preCost) / (pos.Amount - obj.task.preAmount),\n\n
amount: (pos.Amount - obj.task.preAmount),\n\n
position: pos\n\n
} else if (obj.task.retry &= MaxTaskRetry) {\n\n
var depth = exchange.GetDepth();\n\n
if (!depth) {\n\n
return ERR_GET_DEPTH;\n\n
var orderId =\n\n
if (obj.task.action == ACT_LONG) {\n\n
exchange.SetDirection(\"buy\");\n\n
orderId = exchange.Buy(_N(depth.Asks[0].Price + (insDetail.PriceTick * SlideTick), 2), Math.min(remain, depth.Asks[0].Amount), obj.symbol, 'Ask', depth.Asks[0]);\n\n
} else {\n\n
exchange.SetDirection(\"sell\");\n\n
orderId = exchange.Sell(_N(depth.Bids[0].Price - (insDetail.PriceTick * SlideTick), 2), Math.min(remain, depth.Bids[0].Amount), obj.symbol, 'Bid', depth.Bids[0]);\n\n
// symbol not in trading or other else happend\n\n
if (!orderId) {\n\n
obj.task.retry++;\n\n
return ERR_TRADE;\n\n
} while (true);\n\n
if (obj.task.onFinish) {\n\n
obj.task.onFinish(ret);\n\n
obj.setTask(ACT_IDLE);
// 任务执行完成(中间没有被 错误 return),重设为 空闲任务\n\n
return ERR_SUCCESS;\n\n
obj.Poll = function(subroutine) {
// 处理海龟交易法
策略逻辑, 参数:
子程序?\n\n
obj.status.isTrading = $.IsTrading(obj.symbol);
// 调用 模板的导出函数 $.IsTrading 检测 obj.symbol 记录的品种是否在交易时间,结果赋值给obj.status.isTrading\n\n
if (!obj.status.isTrading) {
// 如果 obj.status.isTrading 是 false 即 不在交易时间内, return 返回\n\n
if (obj.task.action != ACT_IDLE) {
// 如果 任务属性的 执行动作属性 不等于 等待标记(宏)\n\n
var retCode = obj.processTask();
// 就调用 当前obj 对象的processTask函数 执行 task 记录的任务。 \n\n
if (obj.task.action != ACT_IDLE) {
// 如果 调用 processTask 函数后 task属性的action 属性不等于 等待标记,即证明任务没有处理成功。\n\n
obj.setLastError(\"任务没有处理成功: \" + errMsg[retCode] + \", \" + obj.task.desc + \", 重试: \" + obj.task.retry);\n\n
// 此时调用 setLastError 记录 并 显示 任务 没有处理成功, 错误代码, 任务描述、重试次数\n\n
} else {\n\n
obj.setLastError();
// 调用 setLastError 不传参数, 不传参数 用空内容(字符串,详见函数setLastError)刷新。\n\n
// 执行完 任务 返回\n\n
if (typeof(subroutine) !== 'undefined' && subroutine) {
// 参数 subroutine 不为null 且 已定义, 比如在调用 setTask 后会执行Poll,到此就返回\n\n
// 返回\n\n
// Loop\n\n
var suffix = WXPush ? '@' : '';
// 界面参数如果开启 微信推送, suffix 会被赋值 \"@\"(微信推送功能 只用在API: Log函数后加 \"@\"字符即可), 否则空字符。\n\n
// switch symbol\n\n
_C(exchange.SetContractType, obj.symbol);
// 切换 合约 为 obj.symbol 记录的合约代码\n\n
var records = exchange.GetRecords();
// 获取K线数据\n\n
if (!records) {
// 如果 K线获取到
null 值\n\n
obj.setLastError(\"获取K线失败\");
// 设置失败信息,并返回。\n\n
obj.status.recordsLen = records.
// 记录K线长度\n\n
if (records.length & obj.atrLen) {
// 如果 K线长度小于
ATR指标参数(小于的话 无法计算出ATR指标 即N值)\n\n
obj.setLastError(\"K线长度小于 \" + obj.atrLen);
// 设置错误信息,并返回。\n\n
var opCode = 0; // 0: IDLE, 1: LONG, 2: SHORT, 3: CoverALL
// 声明一个临时变量
操作代码 有4种操作\n\n
var lastPrice = records[records.length - 1].C
// 声明一个临时变量
用K线 最后一个柱 的收盘价给其赋值,(K线最后一个柱的收盘价是实时更新的是最新价格)\n\n
obj.lastPrice = lastP
obj.lastPrice\n\n
if (obj.marketPosition === 0) {
// 如果当前 海龟策略 控制对象的加仓次数 为0 ,即没持仓。\n\n
obj.status.stopPrice = '--';
// 给止损价 赋值 '--'\n\n
obj.status.leavePrice = '--';
// 用于显示 状态的表格 对象 status的 leavePrice属性赋值 \"--\"
(因为没有持仓,所以没有 离市价)\n\n
obj.status.upLine = 0;
// 赋值 上线,(这里如果不明白 这些变量控制那些显示,可以实际运行一个模拟盘 ,看下界面对比分析更好理解。)\n\n
obj.status.downLine = 0;
// 赋值 下线\n\n
for (var i = 0; i & 2; i++) {
// 在当前的分支条件内,是没有持仓的,这里循环两次,用来检测2个突破系统的触发。\n\n
if (i == 0 && obj.useFilter && !obj.preBreakoutFailure) {
// 如果是第一次循环,并且启用了入市条件过滤,并且上次突破没有失败。\n\n
// 跳过本次循环\n\n
var enterPeriod = i == 0 ? obj.enterPeriodA : obj.enterPeriodB;
三元条件表达式,选择使用的
突破系统 参数,即当 i == 0 时 使用 系统A\n\n
if (records.length & (enterPeriod + 1)) {
// 限制 当前 K线周期 bar 长度 必须大于 突破系统的入市周期加1\n\n
// 跳过本次循环\n\n
var highest = TA.Highest(records, enterPeriod, 'High');
// 计算enterPeriod周期内所有最高价的 最大值\n\n
var lowest = TA.Lowest(records, enterPeriod, 'Low');
// 计算enterPeriod周期内所有最低价的 最小值\n\n
obj.status.upLine = obj.status.upLine == 0 ? highest : Math.min(obj.status.upLine, highest);
// 取两次 系统A 和系统B 获取的 highest中 最小的值\n\n
obj.status.downLine = obj.status.downLine == 0 ? lowest : Math.max(obj.status.downLine, lowest);
// 取两次 系统A 和系统B 获取的 lowest中 最大的值\n\n
if (lastPrice & highest) {
// 最新的 价格 如果向上突破 对应周期内的最高价\n\n
opCode = 1;
// 操作值 赋值1\n\n
} else if (lastPrice & lowest) {
// 最新的 价格 如果向下突破 对应周期内的最低价\n\n
opCode = 2;
// 操作值 赋值2\n\n
obj.leavePeriod = (enterPeriod == obj.enterPeriodA) ? obj.leavePeriodA : obj.leavePeriodB;
if (lastPrice & highest) {
// 修改以上注释\n\n
opCode = 1;\n\n
} else if (lastPrice & lowest) {\n\n
opCode = 2;\n\n
if (opCode != 0) {\n\n
obj.leavePeriod = (enterPeriod == obj.enterPeriodA) ? obj.leavePeriodA : obj.leavePeriodB;\n\n
// 如果持有仓位\n\n
var spread = obj.marketPosition & 0 ? (obj.openPrice - lastPrice) : (lastPrice - obj.openPrice);
// 计算单价盈亏 做多 盈利是负值 亏损是正值,因为要做和止损单价的对比,所以取反, 做空同理\n\n
obj.status.stopPrice = _N(obj.openPrice + (obj.N * StopLossRatio * (obj.marketPosition & 0 ? -1 : 1)));
// 计算止损价 做多的时候: 用开仓价 减去 N值 乘 止损系数, 做空: 用开仓价 加上 N值 乘止 损系数。\n\n
if (spread & (obj.N * StopLossRatio)) {
// 检测 单价盈亏 是否大于 设定的 盈亏限制(即 止损系数 * N值)\n\n
opCode = 3;
// 触发 止损 操作代码 赋值 3\n\n
obj.preBreakoutFailure =
// 触发止损
,标记 上次突破失败为真\n\n
Log(obj.symbolDetail.InstrumentName, \"止损平仓\", suffix);
// 打印 该品种 合约名 止损, 如果开启微信推送,则推送到微信。\n\n
obj.status.st++;
// 止损计数 累计\n\n
} else if (-spread & (IncSpace * obj.N)) {
// 如果单价盈亏(取反 得 正 盈利数,负亏损数) 大于加仓系数 * N值, 触发加仓操作\n\n
opCode = obj.marketPosition & 0 ? 1 : 2;
// 0: IDLE, 1: LONG, 2: SHORT, 3: CoverALL \n\n
} else if (records.length & obj.leavePeriod) {
// 只要 K线周期 长度大于 离市 周期,可以计算离市价格\n\n
// obj.status.leavePrice = TA.Lowest(records, obj.leavePeriod, obj.marketPosition & 0 ? 'Low' : 'High') // 问题2\n\n
obj.status.leavePrice = obj.marketPosition & 0 ? TA.Lowest(records, obj.leavePeriod, 'Low') : TA.Highest(records, obj.leavePeriod, 'High');\n\n
if ((obj.marketPosition & 0 && lastPrice & obj.status.leavePrice) ||
// 做多 或者 做空 如果触发了
离市价\n\n
(obj.marketPosition & 0 && lastPrice & obj.status.leavePrice)) {\n\n
obj.preBreakoutFailure =
// 上次突破失败 赋值为 false ,即 没失败\n\n
Log(obj.symbolDetail.InstrumentName, \"正常平仓\", suffix);
打印信息 平仓,可微信推送\n\n
opCode = 3;
// 给操作 赋值 3 \n\n
obj.status.cover++;
// 平仓计数累计\n\n
if (opCode == 0) {
则返回\n\n
if (opCode == 3) {
// 如果是 全平仓
obj.setTask(ACT_COVER, 0, function(ret) {
// 调用 obj 海龟控制对象的成员函数 setTask 设置任务 (全平仓)并自定义一个回调函数(第三个参数 function(ret){...} 就是匿名函数。)\n\n
obj.reset();
// 回调函数 会在setTask 函数中 设置任务后 调用的 Poll 的函数中 通过 processTask 函数 执行该任务完成后 ,触发回调函数。\n\n
_G(obj.symbol, null);
// 回调函数 调用了 不传参数的 reset函数,执行控制对象 变量重置工作,清空 _G 保存的 本地永久 数据(用于恢复,因为已经平仓了,所以需要清空)\n\n
// 回调函数是在任务完成后(即 全部海龟头寸 平仓后 才触发,此处只是预设)\n\n
// Open\n\n
if (Math.abs(obj.marketPosition) &= obj.maxLots) {
// 建仓 或者 加仓处理, 这里判断如果 加仓次数 大于等于 最大允许加仓次数\n\n
obj.setLastError(\"禁止开仓, 超过最大持仓 \" + obj.maxLots);
// 设置错误信息,然后返回。\n\n
var atrs = TA.ATR(records, atrLen);
// 计算ATR 指标\n\n
var N = _N(atrs[atrs.length - 1], 4);
// 获取 当前ATR指标值 ,即 N值\n\n\n\n
var account = _bot.GetAccount();
// 调用 模板 生成的 交易控制对象的 成员函数 GetAccount\n\n
var currMargin = JSON.parse(exchange.GetRawJSON()).CurrM
// 获取当前 保证金数值\n\n
var unit = parseInt((account.Balance+currMargin-obj.keepBalance) * (obj.riskRatio / 100) / N / obj.symbolDetail.VolumeMultiple);
// 计算 总 可用资金对应 N值 计算出的
一个头寸的 大小(手数)。可以看原版的海龟交易法
关于 unit 的计算,知乎上也有相关文章。 \n\n
var canOpen = parseInt((account.Balance-obj.keepBalance) / (opCode == 1 ? obj.symbolDetail.LongMarginRatio : obj.symbolDetail.ShortMarginRatio) / (lastPrice * 1.2) / obj.symbolDetail.VolumeMultiple);\n\n
// 根据 要做 多仓 或者 空仓 的保证金率 计算 可用资金 可以开 的手数,可开量。\n\n
unit = Math.min(unit, canOpen);
// 最终头寸大小 取 unit, canOpen 中最小值\n\n
if (unit & obj.symbolDetail.MinLimitOrderVolume) {
// 如果 计算出的 头寸大小
小于 合约规定的限价单 最小下单量,则\n\n
obj.setLastError(\"可开 \" + unit + \" 手 无法开仓, \" + (canOpen &= obj.symbolDetail.MinLimitOrderVolume ? \"风控触发\" : \"资金限制\"));
// 设置最新错误信息\n\n
// 返回\n\n
obj.setTask((opCode == 1 ? ACT_LONG : ACT_SHORT), unit, function(ret) {
opCode 设定,
调用 setTask 函数 设定任务\n\n
if (!ret) {
// 同样 第三个参数 是回调函数,回调函数中 ret 是触发 调用回调函数时传入的参数,任务的执行返回值。 \n\n
obj.setLastError(\"下单失败\");\n\n
Log(obj.symbolDetail.InstrumentName, obj.marketPosition == 0 ? \"开仓\" : \"加仓\", \"离市周期\", obj.leavePeriod, suffix);
// 任务成功完成,回调函数会执行此 输出\n\n
obj.N = N;
// 开仓 或者 加仓后 更新N值 \n\n
obj.openPrice = ret.
// 更新 开仓价格\n\n
obj.holdPrice = ret.position.P
// 更新持仓均价,根据 任务执行的ret。\n\n
if (obj.marketPosition == 0) {
// 如果此时 加仓次数是0, 即代表本次是 建仓\n\n
obj.status.open++;
// 开仓计数 累计\n\n
obj.holdAmount = ret.position.A
// 更新持仓量\n\n
obj.marketPosition += opCode == 1 ? 1 : -1;
// 根据 做多 或者 做空 累计 加仓次数\n\n
obj.status.vm = [obj.marketPosition, obj.openPrice, N, obj.leavePeriod, obj.preBreakoutFailure];
// 更新 用于恢复的 字符串 ,属性vm\n\n
_G(obj.symbol, obj.status.vm);
// 本地持久化储存 当前持仓信息。\n\n
// Poll 函数结束\n\n
// 在New 构造函数中 声明一个 局部变量 vm 区别于obj.vm\n\n
if (RMode === 0) {
// 如果进度恢复模式为 自动,下拉框第一个索引是0 ,设置为第一个时 下拉框参数就返回0 ,第二个 返回下一个索引1,以此类推。\n\n
vm = _G(obj.symbol);
// 取回 持久化储存的数据 赋值给 局部变量vm\n\n
// 否则 恢复模式为 手动\n\n
vm = JSON.parse(VMStatus)[obj.symbol];
// 取手动恢复字符串 JSON解析后的数组中的对应于合约类型 obj.symbol 的 数据。\n\n
// 如果获取的有 数据\n\n
Log(\"准备恢复进度, 当前合约状态为\", vm);
// 输出恢复的 合约状态\n\n
obj.reset(vm[0], vm[1], vm[2], vm[3], vm[4]);
// 调用重设 函数 重新设置 恢复状态\n\n
// 如果vm 没有数据\n\n
if (needRestore) {
// 需要恢复 则输出 没找到进度的信息, (有可能是 合约列表 中 有新的合约代码,则不需要恢复)\n\n
Log(\"没有找到\" + obj.symbol + \"的进度恢复信息\");\n\n
obj.reset();
// reset 不传参数 ,即重置\n\n
// 返回 构造完成的对象。\n\n
}\n\n};\n\n\n\nfunction onexit() {
// 策略程序 退出时执行。\n\n
Log(\"已退出策略...\");\n\n}\n\n\n\nfunction main() {\n\n
if (exchange.GetName().indexOf('CTP') == -1) {
// 限定 连接的交易所 必须是 CTP 商品期货 \n\n
throw \"只支持商品期货CTP\";\n\n
SetErrorFilter(\"login|ready|流控|连接失败|初始|Timeout\");
// 过滤常规错误\n\n
var mode = exchange.IO(\"mode\", 0);
// 设定行情模式 为立即返回模式 参看 API 文档: BotVS\n\n
if (typeof(mode) !== 'number') {
// 如果 切换模式 的API 返回的 不是 数值,即切换失败。\n\n
throw \"切换模式失败, 请更新到最新托管者!\";
// 抛出异常\n\n
while (!exchange.IO(\"status\")) {
// 检测 与 行情、交易服务器连接,直到
exchange.IO(\"status\") 返回true 连接上,退出循环\n\n
Sleep(3000);\n\n
LogStatus(\"正在等待与交易服务器连接, \" + new Date());
// 在未连接上时
输出 文本和 当前时间。\n\n
var positions = _C(exchange.GetPosition);
// 调用API
GetPosition 函数 获取 持仓信息\n\n
if (positions.length & 0) {
// 返回的数组不是空数组 ,即有持仓\n\n
Log(\"检测到当前持有仓位, 系统将开始尝试恢复进度...\");\n\n
Log(\"持仓信息\", positions);\n\n
Log(\"风险系数:\", RiskRatio, \"N值周期:\", ATRLength, \"系统1: 入市周期\", EnterPeriodA, \"离市周期\", LeavePeriodA, \"系统二: 入市周期\", EnterPeriodB, \"离市周期\", LeavePeriodB, \"加仓系数:\", IncSpace, \"止损系数:\", StopLossRatio, \"单品种最多开仓:\", MaxLots, \"次\");\n\n
// 输出 参数信息。\n\n
var initAccount = _bot.GetAccount();
// 获取账户信息\n\n
var initMargin = JSON.parse(exchange.GetRawJSON()).CurrM
// 调用 API GetRawJSON 函数 获取
: \"CurrMargin\": \"当前保证金总额\",\n\n
var keepBalance = _N((initAccount.Balance + initMargin) * (KeepRatio/100), 3); // 根据预留保证金比例 计算出 需要预留的资金。\n\n
Log(\"资产信息\", initAccount, \"保留资金:\", keepBalance);
// 输出信息\n\n
var tts = [];\n\n
var filter = [];
// 过滤用数组\n\n
var arr = Instruments.split(',');
// 合约列表按照逗号分隔 成数组\n\n
for (var i = 0; i & arr. i++) {
// 遍历分隔后的数组\n\n
var symbol = arr[i].replace(/^\\s+/g, \"\").replace(/\\s+$/g, \"\");
// 正则表达式 匹配 操作, 得出
合约代码\n\n
if (typeof(filter[symbol]) !== 'undefined') {
// 如果 在过滤数组中 存在 名为 symbol的属性,则显示信息 并跳过。\n\n
Log(symbol, \"已经存在, 系统已自动过滤\");\n\n\n\n
filter[symbol] =
// 给过滤数组 添加 名为 symbol 的 属性,下次 同样的 合约代码 会被过滤\n\n
var hasPosition =
// 初始化 hasPosition 变量 false 代表没有持仓 \n\n
for (var j = 0; j & positions. j++) {
// 遍历 获取到的持仓信息\n\n
if (positions[j].ContractType == symbol) {
// 如果有持仓信息 合约 名称 和 symbol一样的, 给hasPosition 赋值true 代表有持仓\n\n
hasPosition =\n\n \n\n
var obj = TTManager.New(hasPosition, symbol, keepBalance, RiskRatio, ATRLength, EnterPeriodA, LeavePeriodA, EnterPeriodB, LeavePeriodB, UseEnterFilter, IncSpace, StopLossRatio, MaxLots);\n\n
// 根据界面参数 使用 构造函数 New 构造
一个品种的海龟交易策略控制对象\n\n
tts.push(obj);
// 把该对象压入 tts 数组, 最终根据合约列表 ,生成了若干个品种的 控制对象储存在tts数组 \n\n
var preTotalHold = -1;\n\n
var lastStatus = '';\n\n
while (true) {
// 主要循环\n\n
if (GetCommand() === \"暂停/继续\") {
// API GetCommand 函数 获取 程序界面上的 命令。此处 如果 点击了界面上的“暂停/继续”按钮\n\n
Log(\"暂停交易中...\");\n\n
while (GetCommand() !== \"暂停/继续\") {
// 进入等待循环 ,直到再次点击
“暂停/继续” 按钮 退出 等待循环\n\n
Sleep(1000);\n\n
Log(\"继续交易中...\");\n\n
while (!exchange.IO(\"status\")) {
// 一旦断开服务器的连接,则尝试重连 并等待。\n\n
Sleep(3000);\n\n
LogStatus(\"正在等待与交易服务器连接, \" + new Date() + \"\\n\" + lastStatus);
// 输出上一次的 状态栏 内容,并 更新时间。\n\n
var tblStatus = {
// 用于显示在状态栏表格上的
type: \"table\",\n\n
title: \"持仓信息\",\n\n
cols: [\"合约名称\", \"持仓方向\", \"持仓均价\", \"持仓数量\", \"持仓盈亏\", \"加仓次数\", \"开仓次数\", \"止损次数\", \"成功次数\", \"当前价格\", \"N\"],\n\n
rows: []\n\n
var tblMarket = {
// 用于显示在状态栏表格上的 市场信息 对象\n\n
type: \"table\",\n\n
title: \"运行状态\",\n\n
cols: [\"合约名称\", \"合约乘数\", \"保证金率\", \"交易时间\", \"柱线长度\", \"上线\", \"下线\", \"止损价\", \"离市价\", \"异常描述\", \"发生时间\"],\n\n
rows: []\n\n
var totalHold = 0;\n\n
var vmStatus = {};\n\n
var ts = new Date().getTime();
// 当前时间戳\n\n
var holdSymbol = 0;
// 持有的合约量\n\n
for (var i = 0; i & tts. i++) {
// 遍历tts数组\n\n
tts[i].Poll();
// 调用每个 合约的海龟管理对象的 Poll 函数\n\n
var d = tts[i].Status();
// 更新每个 海龟管理对象的
状态 属性 status 并返回。\n\n
if (d.holdAmount & 0) {
// 如果当前索引的对象 有 持仓\n\n
vmStatus[d.symbol] = d.
// 给空对象 vmStatus 添加合约名称 为属性名 的属性,并给其赋值 持仓信息vm\n\n
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]);\n\n
// 压入当前 索引 的 海龟管理对象 的信息 到状态分页表格\n\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]);\n\n
// 压入当前 索引 的 海龟管理对象 的信息 到行情分页表格\n\n
totalHold += Math.abs(d.holdAmount);
// 值为回调函数 的参数ret 的属性 更新,可以参见 回调函数的 传入实参。processTask 函数中的 ret\n\n
// 累计 总持仓手数\n\n
var now = new Date();
// 获取最新时间\n\n
var elapsed = now.getTime() -
// 计算主要耗时代码 , 迭代 执行 Poll 函数的 开始与结束的 时间差。\n\n
var tblAssets = _bot.GetAccount(true);
// 获取账户详细信息并返回一个表格对象。(因为参数传递的是true, 参见 模板的 GetAccount 函数的 getTable 参数)\n\n
var nowAccount = _bot.Account();
// 获取账户信息\n\n
if (tblAssets.rows.length & 10) {
// 如果获取的 表格的 行数 大于10\n\n
// replace AccountId\n\n
tblAssets.rows[0] = [\"InitAccount\", \"初始资产\", initAccount];
// 设置 索引 0 的行数 为 初始资金信息。\n\n
} else {\n\n
tblAssets.rows.unshift([\"NowAccount\", \"当前可用\", nowAccount], [\"InitAccount\", \"初始资产\", initAccount]); // 往 rows 数组 中开始的位置插入2个元素\n\n
lastStatus = '`' + JSON.stringify([tblStatus, tblMarket, tblAssets]) + '`\\n轮询耗时: ' + elapsed + ' 毫秒, 当前时间: ' + now.toLocaleString() + ', 星期' + ['日', '一', '二', '三', '四', '五', '六'][now.getDay()] + \", 持有品种个数: \" + holdS\n\n
// 组合 各种 用于显示在界面的信息。\n\n
if (totalHold & 0) {
// 在有持仓时才 显示 手动恢复字符串(vmStatus JSON序列化)\n\n
lastStatus += \"\\n手动恢复字符串: \" + JSON.stringify(vmStatus);\n\n
LogStatus(lastStatus);
// 调用API 显示在 状态栏\n\n
if (preTotalHold & 0 && totalHold == 0) {
// 当全部持仓 平掉 没有持仓时\n\n
LogProfit(nowAccount.Balance - initAccount.Balance - initMargin);
// 输出 盈利, 显示到收益曲线(此种情况 出现概率较低,很难有同时全部都未持仓的状态,所以收益都是 动态的,可以看 账户详细信息分析当前状况)\n\n
preTotalHold = totalH
// 每次都更新
确保 输出收益只显示一次。 \n\n
Sleep(LoopInterval * 1000);
// 轮询等待。避免API 访问过于频繁\n\n
}\n\n}\n源码地址 :##### 附个 simnow 模拟盘测试#### 欢迎读者给我留言!提出建议和意见,如果感觉好玩可以分享给更多热爱程序热爱交易的朋友### 程序员 littleDream 原创","updated":"T01:05:41.000Z","canComment":false,"commentPermission":"anyone","commentCount":0,"collapsedCount":0,"likeCount":0,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":true,"rating":"none","titleImage":"/v2-eff865fa483_r.jpg","links":{"comments":"/api/posts//comments"},"reviewers":[],"topics":[{"url":"/topic/","id":"","name":"量化交易"},{"url":"/topic/","id":"","name":"期货"},{"url":"/topic/","id":"","name":"程序化交易"}],"adminClosedComment":false,"titleImageSize":{"width":1393,"height":782},"href":"/api/posts/","excerptTitle":"","tipjarState":"closed","annotationAction":[],"sourceUrl":"","pageCommentsCount":0,"hasPublishingDraft":false,"snapshotUrl":"","publishedTime":"T09:05:41+08:00","url":"/p/","lastestLikers":[],"summary":"实现一个多品种并发的商品期货策略(8) & 当一把造物主,做个程序员真好!实现 《商品期货多品种海龟策略》机器人 海龟交易法起源 这个交易系统的诞生来源与两个交易老手的争论,一方觉得交易这种技能是后天习得的,另一方觉得这是先天所决定的。1983年,…","reviewingCommentsCount":0,"meta":{"previous":null,"next":null},"annotationDetail":null,"commentsCount":0,"likesCount":0,"FULLINFO":true}},"User":{"littledream-XX":{"isFollowed":false,"name":"littledream","headline":"I love coder!","avatarUrl":"/7e91f15b6c_s.jpg","isFollowing":false,"type":"people","slug":"littledream-XX","bio":"程序员","hash":"35dbc5f4a258fb73f28066a","uid":321700,"isOrg":false,"description":"I love coder!","profileUrl":"/people/littledream-XX","avatar":{"id":"7e91f15b6c","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false,"badge":{"identity":null,"bestAnswerer":null}}},"Comment":{},"favlists":{}},"me":{},"global":{},"columns":{"next":{}},"columnPosts":{},"columnSettings":{"colomnAuthor":[],"uploadAvatarDetails":"","contributeRequests":[],"contributeRequestsTotalCount":0,"inviteAuthor":""},"postComments":{},"postReviewComments":{"comments":[],"newComments":[],"hasMore":true},"favlistsByUser":{},"favlistRelations":{},"promotions":{},"switches":{"couldAddVideo":false},"draft":{"titleImage":"","titleImageSize":{},"isTitleImageFullScreen":false,"canTitleImageFullScreen":false,"title":"","titleImageUploading":false,"error":"","content":"","draftLoading":false,"globalLoading":false,"pendingVideo":{"resource":null,"error":null}},"drafts":{"draftsList":[],"next":{}},"config":{"userNotBindPhoneTipString":{}},"recommendPosts":{"articleRecommendations":[],"columnRecommendations":[]},"env":{"isAppView":false,"appViewConfig":{"content_padding_top":128,"content_padding_bottom":56,"content_padding_left":16,"content_padding_right":16,"title_font_size":22,"body_font_size":16,"is_dark_theme":false,"can_auto_load_image":true,"app_info":"OS=iOS"},"isApp":false},"sys":{}}

我要回帖

更多关于 海龟交易法则加仓 的文章

 

随机推荐