Meta 交易者持仓报告 - 在 MetaTrader 4 中进行美国商品期货交易委员会报告分析的新领域

Vasiliy Sokolov | 8 四月, 2016

简介

市场价格波动并非由主观的艾略特波浪、江恩线和斐波那契水平或者技术指标导致。 跟开盘价、最高价、最低价、收盘价和交易量(OHLCV)格式的价格图表相比,它们并没有包含任何一点新的信息。 价格波动由供求基本规律决定。 尽管看起来简单,但供求关系决定了价格。

价格是卖家和买家互动的结果。 很多交易者了解这一点,但只有少数对其加以利用。 这种全面的市场法则的实际应用,对于敢于预测未来价格的人来说,可谓极其有用。 本文是从经济理论假设到在期货和商品市场进行交易应用的某种“导体”。

经济理论表示,市场中存在“超买”和“超卖”状态的可能性。 这些状态对市场至关重要。 在“超买”区域价格继续上升会变得更加困难,因为供给的竞争性增加。 而在“超卖”区域价格继续下降也会变得愈加困难,因为面临的需求压力增加。 无论哪种情形,市场都接近其长期反转点。 这些表述似乎简单无用,因为经济理论没有提出任何确定这些市场状态的方法。 但是,这些方法是存在的,而且往往在很久之前已经由成功的交易者所建立。

本文作为理论和实践之间的桥梁,基于 Trade Stocks and Commodities With the Insiders: Secrets of the COT Report 一书中描述的简单指标,为读者介绍了确定市场状态的方法。该书的作者为著名的交易者 - 拉瑞 威廉姆斯。 然而,这本书未得以流行,其方法也未能被广泛利用。 其中的一个主要原因是,书中表达的所有概念,均停留在理论水平。

自这本书的俄语翻译版本出现后两年多时间里,没有出现任何通用、有用和公开的软件以在 MetaTrader4 中利用这种概念。MetaTrader4 是俄罗斯最流行的交易平台。 本文将填补该空白。 现在,任何人都可以使用自己的 MetaTrader 4 客户端在实践中测试和使用拉瑞 威廉姆斯的理论。

使用交易者持仓报告进行当前市场分析的方法可以视为基本分析。 基本分析本身在交易者中并未大范围的使用。 大多数交易者利用技术分析进行真实交易已不是秘密。 这是因为基本分析通常跟经济新闻发布相关,而预测市场对新闻的反应是不可能的。

第二,这种分析是非参数化的,因此非常主观。

第三,基本分析可能是无限的。 它的意思是,你可能分析会影响市场的经济效果的某个部分,而其他部分交易者一般不予考虑,但市场却会给予反应。 使用 CFTC 报告可以自动消除所有的基本分析的弱点,拉瑞 威廉姆斯对这些报告分析的方法对该技术提供了最好的技术分析功能。 这意味着,首先,所有的重大经济影响可以通过观察三组市场参与者的活动进行间接考虑。

其次,所有这些观察可以归结为简单的技术指标,降低主观性和模糊性。

无法创建一个使用简单的基本分析规则进行交易的 Expert Advisor。 下面给出了开发方式之一。 对于那些决定利用 CFTC 报告进行交易的人,我认为会非常有用。

文章的第一部分是关于供求经济理论。 利用简单的示例,对市场模型进行了描述,证明了不同市场阶段的存在。 第二部分是关于数据分析的方法,用来确定当前市场的状态。 它包含了需要用来分析买家和买家交互的一组指标的描述。 它主要是基于拉瑞 威廉姆斯的书籍,书中对这些指标的实际应用进行了详细的说明。 第三部分是关于 Meta COT 项目的技术实现。 它包含了关于 Expert Advisor 的使用、项目中包含的指标和脚本的使用以及下载所需信息的方法的详细信息。 第四部分是最后一部分。 我们将检验该 Expert Advisor 以及文章所述概念的有效性。 该部分给出了最终的总结,提出了一些建设性的意见。



1. 市场是一个价格的平衡函数

1.1 供求规律

市场是一个将某种商品、服务或资源的买家(“需求”)和卖家(“供给”)集中起来的机构或机制。 [3]

关于市场的定义,没有比这个更加精确的了。 事实上,任何公开市场的主要任务都是集合卖家和买家。 卖家和买家之间的关系由供求规律调节。 这个基本规则使股票价格上升或下降,从而使交易者因未来和现在价格的差价获利或损失。 交易者应该了解供求之间平衡变化的机制,所以我们来考虑以下的表格 [3]:

总供给

每蒲式耳的价格(美元)

每周总需求

过剩(+)或不足(-)

12 000

5

2 000

+10 000

10 000

4

4 000

+6 000

7 000

3

7 000

0

4 000

2

11 000

-7 000

1 000

1

16 000

-15 000


表 1-1 玉米的市场供给和需求(以千蒲式耳计)

下面是玉米卖家和买家之间的关系,以价格和交易量来表示。 卖家希望以尽可能高的价格卖出商品,买家希望以尽可能低的价格买入商品。 本例中,在玉米价格为 5 美元每蒲式耳时,很多卖家都想卖出。 其总供给是 12000 蒲式耳。 但问题是没有那么多愿意在这么高的价格下购买的买家。 愿意在这个高价购买的总量才 2000 蒲式耳。 剩下的 10000 蒲式耳(12000 - 2000 = 10000)没有卖出去,导致卖家商品过剩,而买家商品不足。 该价格在市场上无法维持。 很多卖家将降价以消除商品过剩,尽管赚的钱少了,却拿到了实实在在的金钱。 而其他卖家也希望在竞争卖家之前先发制人。 卖家之间的竞争即将开始,从而降低了价格 [3]

现在我们来考虑相反的情形。 假设此时玉米价格很低 - 只有 1 美元每蒲式耳。 该价格对于买家很有诱惑力,很多买家希望买入。 其总需求为 16000 蒲式耳。 但打算在这个价格卖出的卖家非常少。 总供给(卖出)量只有 1000 蒲式耳,从而导致买家商品短缺达 15000 蒲式耳,而卖家商品过剩。 同样,该价格在市场上无法维持。 一些买家会在较高价格买入,以得到所需的商品。 这些买家会加入买家队伍中,预期价格的继续上升,希望成为第一批在相对低价买入的买家。 买家之间的竞争即将开始,从而提升了价格 [3]。

为了清晰起见,我们在图表上绘制供求函数:

图 1-2 供求和价格的函数



目前我们只考虑了供求之间极端分配的情形。 在第一种情形中,价格将下降;在第二种情形中,价格将上升。 两种情形都会达到某个供求相等的平衡点。 在这一点,不会出现买家商品短缺或卖家商品过剩。 在表格中,该点位于 3 美元处。 在该价格下,总供给和总需求是 7000 蒲式耳,此时卖家和买家的交换达到平衡,没有未售出的商品或出现亏损。

在现实生活中,供求关系在很多因素的影响下是不断变化的,包括从基本原因到随机事件的影响。 无论哪种情形,供求关系只有一个交点。 该点将是当前的市场价格。

考虑一个示例,在需求稳定的情况下,供给量增加(图 1-3a,点 A-B)。 在该情况下,价格将下降(p1<p2),因为商品变得更加便宜,卖家之间开始竞争。 另一方面,希望在优惠的价格买入的买家数量开始增多(q3>q2)。

现在考虑供给量减小的情况(图 1-3a,点 A-C)。 这会导致价格上升,因为商品量变少,买家之间开始竞争(p3>p2)。 当然,希望在更高价格买入的买家变少。



图 1-3a 需求稳定时的供给量变化

需求的情况也一样 - 当需求增加时(图 1-3b,点 A-B),价格开始上升,很多人希望在高价卖出,从而导致总供给上升(q3>q2)。 与其相反,当需求开始减小时,价格将下降,希望在低价卖出的卖家减少,因此,总供给量将下降(q1<q2)。


图 1-3b 供给稳定时的需求量变化

现在我们考虑供给和需求同时变化的情形。 很明显,这样的情形只有四种 [3]:


1. 需求减少,供给增加。 每种因素都会导致价格降低,最终,总的价格下降比单因素作用的价格下降幅度更大。

2. 需求增加,供给增加。 两种因素互相补偿,因为需求增加导致价格上升,而供给增加导致价格下降。 总体而言,价格波动将取决于较强的因素。

3. 需求减少,供给减少。 跟上面的情形类似,结果是不确定的。 需求减少导致价格下降,而供给减少将导致价格上升。 总体而言,价格波动将取决于较强的因素。

4. 需求增加,供给减少。 需求增加导致价格上升,同样,供给减少也导致价格上升。 因此,总的价格上升比单因素影响下的幅度更大。

在现实市场中,供求关系始终是变化的,所有这四种情形都反映在价值的实际市场价格中。 因此,价量图上的供求走势的交点,在不同的时间阶段处于不同的位置。

图 1-4 供求的动态变化

但最重要的是,供求关系的交点不会出现在低价高量区(即图表的左上角),同样,也不会出现高价低量区(即图表的右下角)。 为了说明的更加清楚,我们来看图 1-5:

图 1-5 市场阶段

如上所述,供给方拒绝在低价提供大量商品(在低价大量卖出),而买家拒绝在高价提供大量需求(在高价大量买入)。 因此,可以构建出一个任意市场的模型。 图 1-5 显示了一个模型。 虚线显示了供求关系的平均交点位置。 完整的市场周期可以分为三个期间或阶段:

1. 低价阶段。 其特征是低价低量。 来自买家的真实和未实现的需求很大,而市场供给受限。 在买家中存在商品短缺,而卖家未实现的商品则过剩。 这会导致买家竞争,从而最终导致价格上升。

2.平均价格阶段。 在该阶段,出现了可以接受的价格和较高的交易量。 这里没有买家的商品短缺,也没有卖家的商品过剩。 这里是价量的平衡点。 在图 1.5 中由一个点表示。

3. 高价阶段。 其特征是高价高量。 来自商品生产商的实际供给很大,但买家需求受限。 由于买家不愿意买入高价商品,生产商卖出高价商品的愿望导致他们开始竞争,最终导致价格下降。

换言之,供求关系决定了市场的来回波动。 在中期(时间范围约为 1-1.5 年),市场将从低价阶段进入高价阶段,或刚好相反,所以会出现趋势。 从长期来看(一年或以上),市场将进入高价或低价阶段,遇到买家或卖家的阻力,并在其影响下反转,所以会出现横盘。 市场波动可以比作呼吸。 在长呼出一口气后,肺内几乎没有气体可以继续呼气了。

同时,膈膜和肺之间的压力低于大气压,因此空气很容易进入肺内。 在长吸 一口气后, 肺的容量被空气充满,不可能再继续吸入了。 此时,膈膜和肺之间的压力大于大气压力,这种压差使空气从肺流出。 该过程反复循环。 市场中的过程与其类似。 买家之间的日益激烈的竞争(负压)导致市场价格上升(吸入空气),而卖家之间的竞争(正压)导致市场价格下降(呼出空气)。

从上面可以看出,市场上可能出现商品短缺和过剩的情形。 但是,市场上每次买卖的量也是相等的。 因此,一些技术分析者在看到上升趋势时就宣称是牛市而看到下降趋势时就宣称熊市是不正确的。 市场上看涨(买家)和看空(合约)的合约数量始终是相等的。 但是,在低价阶段,市场上待售的商品量有限。 大多数商品拥有者等待低价周期结束。


换言之,如果商品是 小麦,则会待在 粮仓< ,如果是货币则待在银行。 因此,买家无法买到他们所希望的商品量。 使用标准的术语,可以将这种情形称为“超卖”。 与之相反的情形是,价格很高,卖家的所有商品都涌入市场,这种情形被称为“超买”。 后面我们将其称为超买和超卖情形。



1.2 价格是供求等式的第一个变量

为了展示高低价的示例,我们来考虑白糖的周线柱状图。

图 1-6 白糖,周线柱状图


我们知道,如果价格相对较低,则要卖出的人就少,因此市场上提供的商品量也相对较少。 与之相反,如果市场价格较高,商品量会增加。 价格本身就可以说明商品过剩或短缺的可能性。 但是,这种假设往往是粗糙和估计的。 尽管商品价格和商品量彼此相关,然而两者之间没有直接的线性关系。

在较高的价格下可能出现商品量较少的情况,而商品量较多时商品可能出现较低的价格。 这是由于供求关系的函数极少呈现线性关系,几乎始终受到需求价格弹性和供给价格弹性的影响 [5]。 利用价格确定市场交易量的第二个问题是这种方法的“相对性”。 我们如何才能确定什么价格算高和什么价格算低?

我们知道,市场在长期来看是遵从趋势的,那何不创建一个反应市场超买和超卖状态的指标,在市场价格达到高点时建立空头头寸,而在市场价格达到低点时建立多头头寸呢? 而获得利润则要利用市场的全部属性 - 在形成趋势时和趋势反转时。

在过去的五十年间,已经开发了很多技术指标,试图确定市场的超买和超卖状态。 但它们都有一个严重的问题 - 即仅基于供求关系的一个变量,这个变量就是价格。 价格的变化是供求平衡变化后市场的最终反映。

因此,使用基于价格的任何指标或价格本身的交易者,最多能够追随价格的变化,但永远不能领先于价格变化。 简单的计算机测试显示了这种方法的不一致性。

下面是基于 RSI 指标的基本系统的利润图:

图 1-7 基于 RSI 指标的 Expert Advisor 的利润变化。 EURUSD,日线柱状图,2000 年至 2009 年

该系统是最基本的。 如果 7 日周期的 RSI 达到 80%,则进行卖出。 如果 RSI 达到 20%,则进行买入。通过选择自定义周期进行了更多交易。 止损位 100 点,从交易条件退出是在 200 点的获利下。 测试在 EURUSD 上进行,时间为 2000 年至 2009 年,采用日线柱状图。 所有交易的交易手数恒定,等于 0.1。

得到的结论并不明确:仅使用价格数据不足以确定市场的阶段。 我们需要供求等式的第二个变量 - 市场交易量。



1.3 未平仓量是供求等式的第二个变量

令人惊奇的是,对于很多市场,可以了解一种合约的交易量,分析未平仓量数值。

未平 仓量 是期货和/期权市场上的合约数量,不受交易、交割等补偿。未平仓量是所有做多或做空头寸的总和 [1]。 例如,如果市场上的所有做多头寸之和等于 1000 手合约,则意味着相同市场上的做空头寸之和也等于 1000 手,1000 手合约的未平仓量是相同的。 下面给出了未平仓量的公式:

买入和卖出小麦的一个期货合约将增加一个单位的未平仓量数值。

理解未平仓量和市场交易量的区别很关键。 例如,一天内可以交易 1000000 手合约,但并不意味着有 1000000 手合约的商品。 它可能小得多,在日间不断换手,增加了日交易量。 在一天结束时,并非所有的头寸都会关闭,其中很多会滚动到下一天。 未平仓合约的数量是未平仓量的指标。 商品的生产商通过买入多头期货来对冲风险,于是他们进行商品实际交付的委托。 它反映在未平仓量上。 这样,在某种程度上,未平仓量可以是市场上商品量的衡量手段。

在所有的期货市场上,未平仓量是每天在每个交易时段结束时进行计算。 该信息是公开的,通常可以在期货交易所的官方网站上找到。 关于未平仓量的相同数据可以通过订阅付费提醒,例如 e-Signal,获得。 对于 MetaTrader 用户,可以使用“Meta COT:Net Position”指标获得超过 40 个市场的未平仓量数据, 该指标是本文所述的 Meta COT 项目的一部分。 关于其使用的更多信息,请参阅文章的第三部分。

不幸的是,由于现货和股票市场的结构和性质,未平仓量(以及本文所述的其他所有数据类型)均不可用。 但是,由于现货和期货市场的高度关联,可以根据期货市场上的供求变化,指导现货市场上的交易。 例如,你可以分析欧元期货市场上的未平仓量变化,而在 EURUSD 现货市场上进行交易。 但是,为了表达的更加准确,我们仅使用期货图表。

现在,我们来看一下黄金在较长时间周期上的未平仓量变化:

图 1-8 黄金,周线图表

在图 1-8 上,我们看到黄金期货从 2003 年到 2009 年的价格图表(上)和未平仓量(下)图表。 可以看出,当未平仓量值跟之前的值相比较高时,市场开始下跌。 同时,当未平仓量值较低时,市场开始上涨。 由于时间周期较长,包含了最近的高波动性,在周线图表上不太容易看。

适用于黄金的规律也同样适用于其他商品,供求规律适用于任何市场:


图 1-9 大豆,周线图表

图 1-10 美国国债,周线图表

可以看出,未平仓量值相对较低说明市场具有上涨的潜力,而相对较高的未平仓量值说明具有价格下降的可能。 当未平仓量出现最低或最高值时,市场并非总会反转,未平仓量的极值并不意味着市场立即反转。 未平仓量并非神奇的市场“转换开关”,而是可以测量市场的第二维度 - 市场交易量并评估市场的当前阶段。


下面是可以从供求规律得出的第一个规则:

当未平仓量极高时,尝试建立做空头寸。

当未平仓量极低时,尝试建立做多头寸。



2. 市场结构

我们发现,市场是一个将某种商品或服务的买家和卖家集中起来的机构或机制。 现在是时候详细讨论这些群体了。

2.1 套期保值者和投机者是市场结构的一部分

市场总会找到供求关系的交点。 该点是商品价格达成一致的点,即在这个价格上,卖家愿意卖出一定量的商品,而买家愿意买入这些量的商品。 供求关系在基本、政治、随机和其他因素的影响下不断变化。 根据供求规律,商品价格也是始终变化的。 因此,未来价格可能高于或低于当前的价格。 既然价格可能随时间变化,那么未来价格就具有不确定性。 这种未来价格的不确定性被称为 风险 [6]。

因此,任何商品所有者都面临未来出现不利的价格变化的风险。 但是,价格变化可以是向上或向下。 如果价格下降,则意味着未来以相对于当前价格的较低价格卖出造成的损失。 与之相反,如果商品价格上升,则会给商品所有者提供额外的利润。 商品的主要拥有者,通常是它们的生产商。

如果产品是小麦,其所有者可能是 General Mills 公司,如果是黄金 - 其所有者可能是 Barrick Gold 公司,诸如此类。但价格变化的风险很高,这些公司的主要收入来自于销售价格和产品成本的差价,它们对于降低拥有商品的风险很感兴趣。 为此,制造商在商品和金融市场采用套期保值操作。 实际上,制造商将其风险卖给了想买入的人。 除了风险之外,在出现有利的价格变化时,买家也具有获得额外利润(风险溢价)的机会。

因此,交易所不只是一个集中买家和卖家的市场。 也是一个集中买卖风险和溢价的地方。

商品及其风险的买家被称为投机者。 投机者的主要目的是从当前和未来价格的差价中获得利润。 投机者就像产品制造商及其最终消费者之间的一层“胶”。 他们提供了高度的市场流动性以及价格的平滑变化 [4]。

套期保值操作不能将风险降低到零,只是一种在风险和利润之间找到合理平衡的方法。 你会进一步发现,适当的套期保值可以用来进行投机。 通过监测套期保值的头寸变化,可以跟踪大型制造公司的投资团队,而他们对其所在的市场几乎了如指掌。

实际上,这种观点并非全无意义。 这些公司是直接的商品供应商,在交易所进行交易。 如果不是这些制造商,谁会必须了解这一商业领域发生的所有事情呢? 他们掌握着不为大众所知的信息,了解他人所不知的内容。 否则,对于他们在几十年间进行很多数百万美元的日常交易,却仍是交易所的成功交易者该如何解释呢?

对投机者的监测也并非全无意义。 投机者的购买水平说明市场处于“过热”的时刻,即市场在第三阶段,或者相反,是市场冷淡的时刻(第一阶段)(请参阅第一部分)。 最后,投机者往往是商品的主要买家,这意味着当他们将其多头头寸累积到最大时,不再具有买入能力,市场反转进入下跌趋势。


2.2 美国商品期货交易委员会报告分析

多亏了被称为“商品期货交易委员会”(CFTC)的美国政府组织,任何人都有机会了解套期保值者和投机者的头寸。 实际上,对于任何个人或法人,如果其交易量达到或超过了委员会规定的水平,就要提交一份商品交易的持仓报告。 每隔一周,委员会就会对交易者的综合头寸进行报告。

每个报告都会公布在官方网站上:www.cftc.gov. 报告将给出周二的数据,发布在周五深夜(莫斯科时间)。 报告本身提供了多种格式: Excel 表格、CSV 文本文件格式以及简单的文本格式。 你还可以下载 Excel 和 CSV 格式的长时间的历史记录。 报告分为期货交易和期货和期权交易两种。 报告具有简要格式和扩展格式。 扩展格式跟简要格式的不同之处在于存在一些农产品产量的额外统计和数据。 在实践中,Excel 或 CSV 文件用于 CFTC 报告分析。 现在我们来看一下 2009 年 8 月 4 日的小麦的简要报告。


表 2-1 2009 年 8 月 4 日的小麦报告(仅限期货)

在报告的顶端是产品名称,本例中是 小麦 ,在 芝加哥交易所行交易,报告类型是 仅限期货头寸,日期是 2009 年 8 月 4 日。 表格中包含了 四个 主要的列。 每个都代表了三种交易者群体各自的综合做空和做多头寸。 第一个交易者群体包含了主要的投机者。 在报告中被称为 非商业交易者 (NON-COMMERCIAL)。 我们可以看到,在 2009 年 8 月 4 日,大型投机者有 75 933 手多头合约,有 97 574 手空头合约。 这说明他们的总头寸或净头寸为空,结果为 -21 641 手空头合约。 通常,对于投机者来说这并非是典型的情形。

在大多数(并非所有)市场上,他们是纯粹的买家,也就是说,他们的多头头寸始终高于空头头寸。 只利用一篇报告进行分析,是不可能确定这种情形是否是市场的典型情形。 无论如何,你必须记住,这里最重要的参数是 该交易者群体的净头寸,而不是单独的多头头寸或空头头寸。 在计算交易者报告分析的多数指标时,需要使用这种净头寸。 再说一次,总头寸或净头寸等于多头头寸和空头头寸的差值,可以为正,也可以为负,下面是它的公式:

where NetPosition 是交易者的净或总净头寸, i 是交易者的类别,比如主要的非商业交易者或大型商业交易者。

下一个是非商业交易者的主要合约数量,为所谓的套利或双向持仓。

拉瑞 威廉姆斯对其描述如下: “如果非商业交易者拥有 euro/dollar 的期货合约,其中 2000 手长仓合约和 1500 手短仓合约,则 500 手合约将包含在“长”类别,1500 手合约则为“双向持仓”[1]。 简而言之,双向持仓显示了长仓和短仓合约的数量。

需要注意的是,我们指的不是锁仓。 例如,交易者可以保留同一种商品在期货合约中相反的头寸,以在不同的月份交割,或者在相同商品的期货和期权合约中具有相反的头寸。 在报告中,包含了期权的头寸,这些情形被纳入考虑。

第二组反映了 套期保值者 的头寸。 他们也被称为 经营者业交易者 (COMMERCIAL)。 通常来讲(但并不总是如此),经营者是纯粹的卖家,因为他们中大多数是类似小麦、黄金或猪肉等商品的生产商。 商业交易者也是其他货物作为其产品基础的制造商,例如,棉花糖或面包。 一般来说,他们是卖家,即他们的合约为做空。

通常,当价格下降到某个低水平时,经营者会将销量降到最低,因为在这种低价卖出商品并不获利。 同时,正在生产自己产品的经营者,反而增加其做多头寸。 因此,经营者的净头寸可以是多头,基于经营者净头寸的指标将位于上面的区域。


通过对小麦的报告示例,我们来考虑具体的情形。 经营者的多头具有 166 518 手合约,而空头有 130 979 手空头合约。 他们的净头寸为 35 539 手合约,即为多头。 通常,这意味着商品价格处于低位,因为经营者卖出的少于买入的。 无论如何,要确定准确的情形,需要使用其长期的净头寸图表。

“Total”列包含了一组商业和非商业交易者的多头和空头头寸。 这没有什么关系。

“Nonreportable Positions”列包含了可以不予计算的交易者的多头和空头头寸。 实际上,该列反映了小型投机者的头寸,他们的头寸太小,不足以纳入投机者的范畴。 这些头寸的计算采用综合计算 - 从未平仓量值减去所报告的交易者的总多头头寸和总空头头寸。 在示例中,未平仓量是 322 431 手合约,多头是 279 239 手合约,这意味着 29194 手合约是可不予计算的散户的多头头寸(322 431 - 279 239)。 对空头可以进行相同的计算: 322 431 - 279 339 = 43 092 合约。

下面是跟之前报告相比,每组合约的数量发生了变化。 接下来是每种类别在未平仓量中的比例,在最下面的是每种类别的交易者的数量。 这些数据并非在实际中使用。

作为对该报告的补充,如上所述,还有包含期权头寸的报告。 所有的期权头寸都转换为期货等价物,并直接添加到期货头寸。 同样是对小麦的报告,但期权和期货综合头寸的显示如下:

表 2-2 2009 年 8 月 4 日的小麦简要报告(期货和期权)

尽管数量不同,两种类型的报告将给出头寸变化动态的类似图表。 然而,最好使用包含期权头寸信息的报告,因为它们考虑了更加完整的市场交易量。 看一下“套利”列增加的值。 现在这一行的值由和非商业交易者的多头和空头头寸对比主导。 这是因为商业交易者使用期权来掩盖其期货头寸。


现在我们来检验报告的扩展格式,它包含了小麦的期货和期权头寸:

图 2-3 2009 年 8 月 4 日,小麦的完整期货和期权报告

可以看出,报告包含了三组交易者的原有头寸的额外统计数据。 由于所有这些报告是长期过程中的动态分析,所以该信息是客观的。

2.3 观察大型套期保值者的头寸

现在,可以开始编制套期保值者的买卖统计数据和长期监测他们的动作了。 例如,我们将使用已经展示的小麦价格图表,其中的指标显示了经营者的多头和空头头寸。 图表包含了周线图,涵盖自 2001 年年中至 2009 年年中期间。

图 2-4 小麦,经营者的多头和空头头寸,周线柱状图

上图的绿线表明了交易者的多头头寸,红线对应着经营者的空头头寸。 所有的头寸都以绝对值指定,没有正负号。 有必要进行假设,红线代表着空头头寸的绝对量,而绿线代表着多头头寸的绝对量。 总体来说,经营者的空头头寸在 2004 年春季之前处于主导地位(红线在绿线之上),其后经营者进行了较多的买入操作(绿线在红线之上)。 这种图表不显示关键的转折点。 现在我们来看一个同样的图表,只不过多了一个经营者总净头寸的指标。

图 2-5 经营者的总净头寸,周线柱状图

这里你会看到更加有趣的现象。 注意:当经营者的净头寸较高时,市场过一会就会上涨。 同时,当经营者的净头寸较低时,市场过一会就会下跌。 当然,并非所有的指标预测都会成真,但即使它碰巧对了哪怕一次,也会获利颇丰。

我们来注意 2005 年年底经营者净头寸的极限高点! 刚好不前也不后,经营者出现如此高的净头寸值。 现在看一下随后市场发生了什么。 它开始了势不可挡的上涨过程,持续了两年多。 因此,价格上涨了超过 700(!) 美元。 也有其他一些时刻,显示了非常好的投机机会。

例如,在 2007 年年底的价格猛烈下跌以后,很多人认为牛市即将结束。 显然,经营者不这样认为。 就在其他人试图降低多头头寸时(我们从经营者报告了解),经营者开始买入! 很快就出现了上涨。 最高价格被刷新,然后开始出现恐慌。 在这四个月里,价格又增长了 400 美元! 经营者给出的卖出信号不是那么令人信服。 然而,在很多情况下,经营者使用简单的跟踪止损技巧,可以安全的逃离市场。 无论如何,所有的损失可以经一次完美的交易进行补偿。

这里的关键词是“紧随其后”。 不要忘记,该指标并非计算价格得出。 而是完全独立于价格。 我们可以移除价格图表,但经营者的净头寸指标仍会显示相同的数值。 价格是最近的市场变化。 最先开始改变的是供求力量的平衡,在此之前是主要市场参与者的思想变化。 观察经营者的所作所为,我们就能在价格变化尚未出现之前,见证市场的早期进化。 它给予我们独一无二的优势,在未来趋势的火车头的一节半空的车厢里获得一席之地。 当走势出现时,很多人开始操作已经太迟,但使用交易委员会报告的交易者就不会!

让我们通过经营者的棱镜来观察黄金和白银。

图 2-6 黄金和经营者的净头寸。 周线柱状图

黄金的价格图表充满了投机的大好机会。 并非所有的机会都能导致超额利润,但很多能带来可观的收入。 如果交易者将自己的进入点跟持续很多年的大牛市相关联,将会是非常成功的赢家。 需要提醒你的是,我们所考虑的是当经营者的净头寸很高的时刻,这些点位是潜在的买入点。 这同样适用于卖出:我们寻找净头寸的较低水平,尝试做空,寻找适宜的时机卖出。

注意 2008 年年底黄金所出现的疯狂。 在 9 月初暴跌结束后,开始出现意外的上涨。 事实上,黄金价格在一天之内增长了 90 美元! 然后直到 9 月底价格一直处于高位,但时间不算很长。 在此之后又出现下跌,在 10 月底,黄金刷新了其年度最低价。

我们是否能够预测这种剧变,哪怕只是预测一部分? 答案是肯定的,我们可以使用交易者头寸报告进行预测。 请注意,在周五的牛市(即黄金价格“突然”上涨 90 美元那天)不久之前,经营者已经停止卖出黄金。

他们是市场上的生产商,这些年来没有哪一周的买入量会超过卖出量。 实际上,他们的拒绝卖出在黄金看涨者中产生了恐慌。 黄金的短缺导致了价格的上涨。

尽管价格在高位没有持续很长时间,经营者将一些黄金卖给了黄金看涨者,他们是对价格峰值的后知后觉者。 之后黄金价格开始下跌,经营者随后将其黄金卖出量降低到四年的最低点(他们的净头寸增加(请参阅图表))。 然后我们看到了卖家之间竞争的效果。 商品的短缺开始导致价格上升,这是一个赚钱的好机会。

现在我们来看白银的图表。


图 2-7 白银和经营者的净头寸。 周线柱状图

这里我们所看到的跟黄金一样。 当净头寸达到相对高点时,白银价格开始上涨。 相对低位说明白银的上涨趋势开始衰退。

结果如下: 贵金属市场基本上由一小群专业人士控制。 任何想在这些市场交易获利的交易者,应该考虑这些力量的影响。


2.4 净头寸的指数

但是,我们如何了解什么是“相对”高的净头寸以及什么是相对低的净头寸呢? 如拉瑞 威廉姆斯所言,此之地板及彼之天花板。 因此,有必要使用可以清楚的表明我们的头寸阶段的标准化的指标。 这种指标的确存在,被称为 COT 指数。 该指数是一个常见的用于计算头寸值的随机指数。

我们回忆一下它的公式 [1]:

当前价格跟三年内的最高价相比较。 该指数可以计算任何时间周期,不仅仅是三年。 使用的时间周期最好不少于 26 周,即半年的指数。 对于长期的交易,使用 156 周(三年)的指数。 该指数以百分比的形式显示了当前和选定周期的相对强度。


下面是所列出的经营者的指数计算示例,右边的数字表示合约数量 [1]:


当前周的数值

350

过去 3 年的最小值

-150

差值

200

过去 3 年的最大值

750

过去 3 年的最小值

-150

差值

600

指数 = (200/600)x100=0,33x100=33%;

在这种情况下,经营者是看跌而非看涨。 如果指数达到极限低位,即位于底部 20% 的范围,市场趋向于下跌。 如果指数达到极限高位,即位于顶部 80% 的范围,市场趋向于反转上涨。

为了进行确认,我们来看一下同一个白银图表,但换成了 156 周的经营者指数:

图 2-8 白银和 156 周的经营者指数,周线柱状图

你可以使用其他水平,例如,75% 和 25%。 其含义不会改变,指标将显示潜在的超买和超卖区域。

尽管非常奇怪,但指数周期并不改变所监测头寸的动态。 下面是白银在不同平均周期的多个指数的示例:


图 2-9 不同周期的白银经营者指数图表,周线柱状图

对于经营者指数,我们在上图分别使用了(从上向下)156 周、104 周、52 周、26 周的平均周期。 只有 26 周指数显示了较频繁的波动幅度。 其他的平均周期看起来非常相似,没有改变指标的外形。 使用的平均周期取决于个人喜好。 例如,你可以使用 156 周的平均周期,它显示了较宽范围内的头寸变化动态,同时给出了看涨头寸的清晰兴趣结果。

COT 指数不仅用于监测看涨的经营者,还适用于非商业交易者以及小型投机者。


2.5 未平仓量的结构

在第一部分,我们已经分析了商品量影响价格的示例。 为了衡量市场上的商品量,使用未平仓量是非常有效的方式。 经济理论预测,如果市场的未平仓量处于相对高位,则很可能转为下跌。 对于未平仓量相对较低的市场,适用相同的规则。 在本例中,价格很有可能开始上涨。

现在来研究其结构。 从交易者持仓报告,我们得知期货市场上的主要参与者是套期保值者(也被称为经营者)和代表了主要大型商品基金的“非商业交易者”。 还有第三种较小的交易者群体 - 剥头皮交易者或所谓的“散户”。 因为他们的交易量很小,不能影响到市场价格。 该类别的操作交易量进行间接计算,表示未平仓量和经营者及非商业交易者的未平仓头寸总量的差值。

CFTC 报告中给出的是周未平仓量值。 由于未平仓量(OI)是未平仓多头和空头头寸的累积数量,它的水平可以通过两种方式计算,即计算总多头头寸或计算总空头头寸。

下面是其公式:

未平仓量 = 非商业交易者多头头寸+ 非商业交易者套利 + 经营者多头头寸 + 非报告多头头寸;
未平仓量 = 非商业交易者空头头寸 + 非商业交易者套利 + 经营者空头头寸 + 非报告空头头寸;

现在我们来看一下 2009 年 8 月 4 日的欧元期货报告,并使用这两个公式计算市场的未平仓量:

表 2-10 2009 年 8 月 4 日的欧元期货报告

OI = 61 443 + 946 + 22 984 + 52 864 = 138 237;
OI = 34 337 + 946 + 72 454 + 30 500 = 138 237;

无论哪个未平仓量计算公式,结果是一样的。 但是,可以看到,多头头寸主要由大型非商业交易者持有(61 337 手合约),但经营者偏好空头头寸(52 864 手合约)。 三个群体的未平仓量各自所占比例的计算可以说是未平仓量结构分析的逻辑延续。 例如,空头头寸在这一周期的比例达到未平仓量的 52.4%(78454 / 138237 x 100%)。 例如,经营者在所述周期的空头头寸比例达到未平仓量的 52.4%(78454 / 138237 x 100 %)。 它的值在“Percent of Open Interest for each category of traders”部分给出。

但是,考虑三组交易者各自的比例变化动态,会非常有趣。 如果我们收集较长时间内的这种信息,就可以构建相应的图表。 “Meta Cot: Percent Position in OI”指标。 它为每组交易者计算该数据。

图 2-14 显示了日元期货的长期图表。 每当经营者的空头头寸比例达到未平仓量的 70% 及以上时,日元就趋于开始下跌。 几乎每当经营者的空头头寸比例低于未平仓量的 30% 以下时,市场就接近底部,通常在之后开始长期的上涨过程。 这种情形用红色虚线表示。 转为上涨趋势的道理相同。 每当空头头寸比例低于未平仓量的 30 % 时,日元就趋于开始下跌。 每当经营者的空头头寸低于未平仓量的 30% 时,市场就接近底部,通常在之后开始长期的上涨过程。

图 2-11 经营者空头头寸在未平仓量的比例。 日元,周线柱状图

拉瑞 威廉姆斯在 Trade Stocks & Commodities with the Insiders: Secrets of the COT Report [1] 一书中,提出将未平仓量值和经营者的净头寸融合到一个指标。 实际上,如果经营者的相对净头寸足够低,同时又持有较大的市场份额,可以认为市场接近其顶点,很快就会转折。

该指标的计算公式为:

随机震荡指标(净头寸/未平仓量);

也就是用经营者的净头寸除以未平仓量,这些数据是在较长时间内收集,随机指标将使用这些数据进行计算。 这些指标被称为 威廉姆斯商业指数(WILLCO)。 拉瑞 威廉姆斯建议使用 26 周或 6 个月的平均周期,但你可以使用其他类型的平均周期,比如一年(52 周)或三年(156 周)。

它的使用方法跟 COT 指数相同,当它的值超过 80% 时,可以预测市场将转为下跌,当它的值低于 20% 时,可以预测市场将开始上涨。 在图 2-15 中显示了日元的 WILLCO 指标。 红色虚线显示了跟拉瑞 威廉姆斯在同一个的图表上所绘制的相同的点位 [1]:

图 2-12 日元的平均 WILLCO 指标(26 周),周线柱状图

可以看出,该指标后续的建议并非那么准确。 但是,时间周期可以消除这个问题。 看一下美国国债的图表,指标相同,但平均周期为 156 周:

图 2-13 美国国债的平均 WILLCO 指标(156 周),周线柱状图

现在我们已经标记了最低水平和最高水平。 考虑到指标的数据并非基于价格数据,它的准确性令人惊讶! 毫无疑问,美国国债对经营者的动作非常敏感。

2.6 动量指标

该指标由 Stefan Brice 在他的 The Commitments of Traders Bible 一书中提出。 它的理念很简单,反映了当前的 COT 指数和 6 个周期前的同一个指数的差值,其公式为:

COT-Index (p) – COT-Index(p-n);

其中,p 是指数的当前值,n 是等于 6 的周期数。

指数周期数可以是任意的,不仅限于 6 个周期。 另外,指数可以用来计算期货市场中的所有参与者,甚至包括未平仓量本身。 这些指标被称为 运动指数。 它主要用来确认长期趋势的修正完成。 理解起来很简单。 如果运动指数上升超过 40% - 当前的下跌态势即将结束。 预计价格将上涨。 如果运动指数下降超过 40% - 当前的上涨态势即将结束。 预计价格将下降。

我们尝试将该指标应用到欧元期货。

图 2-14 欧元期货的运动指数

在过去的几年里,欧元呈现稳定的上涨趋势。 蓝色箭头显示其越过 40% 阻力位的时间。 来看一下指标令人难以置信的准确度,每当它越过 40% 的水平时,价格修正已经完成,欧元继续上涨。 指标的卖出信号没有那么准确。 但是,通常在运动指数越过 -40% 的下边界后,修正 已经开始。 应该将这种指标的反应纳入考虑。 它相当于 COT 动量的晴雨表。 清晰的显示了市场的动荡。 它对于激进型和中短期交易尤其有用。

下一个指标是实验性的,尚未在其他地方进行描述。 拉瑞 威廉姆斯在其书的第九章探讨了根据经营者的活动研究未平仓量的变化。 对于如何利用这种关系,还不是十分清楚。 对于一些示例,拉瑞 威廉姆斯显示了聚合/分离。 对于其他示例,他总结为:经营者的卖出或买入都增加了未平仓量水平。 对运动指数的研究,使我认识到,无法通过未平仓量的变化监测经营者头寸的改变。

最有趣的交互模型如下:

1. 未平仓量水平在下降 - 经营者的净头寸水平在增长。

2. 未平仓量水平在增长 - 经营者的净头寸水平在下降。

也就是说,在所有的市场参与者(未平仓量)和经营者活动之间有一些差异。 最好基于为未平仓量和经营者指数而计算的运动指数来观察这些变化。 通过对比它们的差异,我们可以确定套期保值者的活动和其他市场参与者活动的分离。

该指标被称为 扩散运动指数,其公式为:

运动指数(经营者) – 运动指数(未平仓量);

它看起来跟简单运动指数基本相似,就像 WILLCO 和 COT 指标相似一样。 大致的临界水平值分别为 60% 和 -60%。 按照跟普通的运动指数相同的规则进行分析。

我们来考虑指数在欧元期货上的表现示例:

图 2-15 欧元的扩散运动指数

可以看到,它的值也指示了重要的转折点和修整结束的时刻。 但是,应该谨慎的使用该指标 - 它的 效度还 没有经过证明。


2.7. 观察大型套期保值者的头寸

大型商品基金的主要目的是在商品市场上获得投机利润。 他们使用的交易方法基于一般的趋势跟随。 一旦价格超过某个 n 周的高点,其中一些基金就会建立多头头寸。 这样就导致了又一波价格上涨运动。 或许,从中期来看,市场趋势的主要原因之一就是大型商品基金活动。

通过估算,基金在 26 周的最高价/最低价平仓最为频繁。 基金是逐步入市的,使用建筑产品的技巧,此外,很多基金使用更加长期的策略进入市场。 这些基金是逐步入市的,使用其他技巧建立头寸,此外,很多基金使用更加长期的策略进入市场。 例如,如果价格达到其 26 周的最高价,一些基金就会建立多头头寸。 然后价格继续上涨,达到其 52 周的最高价。 更加谨慎的基金开始加入进来,建立多头头寸,同时,之前已经建立多头头寸的基金再次增加其头寸。 最终,价格达到了 156 周的最高价。 此时,想买的人都已经买进。 游戏结束。 现已没有买家。

所有基金的趋势策略已经达到极限。 市场变得极度的紧张。 由于没有进一步的买家(从交易报告可以看出),价格应该很快开始下跌,这很快就应验了。 价格开始了下跌的过程。 此时,市场上有非常多的参与者(从未平仓量得知),他们开始恐慌。

首先,进入市场较晚的买家开始关闭其多头头寸。 他们还没有能够得到长期持仓才能获得的足够利润。 这导致了额外的价格下降加速度,开始下降的更快。 恐慌很快蔓延,越来越多的参与者试图关闭其多头头寸。 每个人都试图夺门而逃。 恐慌的增长使得价格在很短的时间内急剧下跌。

在散户离开市场后,价格处于低点,然后经营者开始了他们的游戏,再次进入市场,增加其多头头寸。 他们的理念很简单 - 使用商品来生产其产品的经营者对于低价买入商品很感兴趣。 同时,经营者将其销售量降到最低,因为在低价卖出产品并不获利。 因此,经营者的净头寸将会很高。 指标会相应的反映这种现象。 需求竞争即将开始,价格又开始上升。 循环会结束,历史将会重演。

我们使用应用于非商业交易者净头寸的三年指数,基于以上的概念来验证大型非商业交易者的活动。

图 2-16 活牛期货价格和非商业交易者的总净头寸

从活牛期货示例中可见,非商业交易者的活动跟当前价格趋势类似。 如果价格上涨,基金就会买入,如果价格下跌,他们就会卖出,非常简单。 需要注意的是,每当基金将其头寸增加到最大值时,活牛价格已经开始了下跌过程。 当基金头寸接近其最小值时,价格已经开始上涨。

我认为,已经决定将资金投资这些基金的投资者的经济成功,跟这个问题有关。 非商业交易者的类似活动在其他市场上相同:


图 2-17 棉花图表和非商业交易者 156 周的平均净头寸

当指数的高位清晰的显示了即将到来的市场转折时,它的低值往往不够成熟。 无论如何,该图表表明,最好在“大型买家”买入时做空。


2.8 观察小型投机者的头寸

根据 CFTC 的规定,小型投机者是那些头寸过小,远不及以上两个群体的散户。 他们的准确值未知,尽管可以假设他们的总数量相当惊人。 例如,你可以在 CFTC 对小麦在芝加哥商业交易所的简要报告上进行检验:

表 2-18 小麦的简要报告中小型投机者的比例

从报告中可以看出,主要的市场参与者总数量很小。 做空的为 286 家,做多的为 304 家。 市场的参与者中有 286 家拥有所有多头头寸的 91.8%,有 304 家拥有所有空头头寸的 88.5%。 我们是否可以将这些主要的市场参与者称为散户呢? 我认为不可以。 在交易委员会报告中没有纳入计算的很多交易者是非常小型的投机者,他们产生了一个市场群体。

本例中,散户只控制了 8.2% 的多头头寸和 11.5% 的空头头寸。 观察该类别的交易者的买入和卖出历史记录,将会非常惊人。 我们来看一下在较长时间周期上的 GBP 图表,但这次我们考虑小型投机者的总净头寸:

图 2-19 小型投机者在 GBP 期货上的总净头寸:


注意:每当散户的总净头寸达到其相对高位时,市场价格往往开始下跌过程。 反之,当散户对 GBP 感到失望时,就开始卖出,GBP 价格开始上涨。 尤其需要注意的是红线标出的最后两个点。 在英镑出现巨大下跌后,小型投机者认为价格已经达到底部,接下来将是上涨过程。 于是在一周之内,他们从净卖家变成净买家。 但是,价格还尚未到达底部。 英镑继续下跌了两个月。 这时,散户的情绪已经改变,再次变成了主卖出。 结果散户又错了。 英镑开始上涨。

对该群体的交易活动分析的基本原则很简单。 尝试跟散户的活动反着来。 如果可不予计算的交易者突然增加卖出 - 则尝试买入,如果他们开始买入 - 则尝试卖出。



3. 技术解决方案

3.1 MetaCOT 项目的目标和结构

目前,我们已经考虑了所有的项目指标,现在来检验其结构。 对其工作原理的清晰理解可以帮助你避免很多数据更新和设置的问题。

首先,我们来考虑软件的原则,如下所示:

1. 透明性 所有的项目源代码对所有人都是开放和可用的。 任何人都可以下载并编译这些工具。 此外,本文描述了它的工作原理,所以它对任何人都会是透明和可理解的。

2. 综合性。 软件包含了所拉瑞 威廉姆斯在其书中描述的所有 CFTC 数据分析工具,包括其他项目中所没有的 WILLCO 指标。 此外,软件包含一个特殊的脚本,它以特殊的方式对信息进行分组。 因此,你可以自动组合不同的工具,甚至创建一个新的工具! 另外,项目架构的设计是为了方便基于 COT 项目创建新的指标。 基本的 COT 数据可以使用单函数获得(对于所需的数据)。 这些数据可以轻松用于在另一个指标的计算。

3. 自动化 CFTC 数据非常大。 它们包含了成百上千个市场的信息,每个市场的信息都分散在不同的文件和年份。 使用 Meta COT 脚本可以解决这个问题。 现在你所需要做的只是从 CFTC 下载(每周一次)更新文件并运行脚本。 所有的数据都会自动提取、分组和准备使用。

4. 简易性。 所有的指标和脚本均使用 MQL 编程语言创建,没有使用任何第三方 DLL。 我们采用了进行数据组织和计算的最简单的算法。 我们对问题进行了分离。 这些基于脚本的程序开发是为了用于新数据分组、统一、输出和创建的程序。 所有这些数据都用于指标的构建。

5.独立性。 获得信息的最重要的因素之一是其传输“节点”的编号。 如果信息从源头直接传递到终点,则相对于经过一些源头和终点之间的额外中介,信息失真的可能性要小得多。 整个项目的实施过程中,所需的信息从源头直接获取,不经过第三方供应商。

项目包含了几个尚未编译的程序文件的集合。 每个文件必须放在特定的目录并进行编译。 下面的表格包含了一个文件列表、文件的简要描述和安装位置。

文件名称<

类型

安装目录

说明

Meta COT Script Build.mq4

脚本<

..\Meta Trader\experts\scripts\

用于数据准备的主要独立脚本。 从 CFTC 政府服务器上的可用标准 CSV 文件创建一组文件。 每个新文件都包含关于工具的信息。 创建的文件名称跟工具名称一致。

Meta COT Script Concatenate.mq4

脚本

..\Meta Trader\experts\scripts\

这是一个独立的脚本。 它根据历史记录将多个文件集中到一个文件。 例如,“COT - SUGAR NO. 11 - NEW YORK BOARD OF TRADE. CSV”文件(数据期间为 2005.01.04-2007.08.28)和“COT - SUGAR NO. 11 - ICE FUTURES U.S. .CSV”文件(数据期间为 2007.09.04-2009.09.01)将转换为一个文件 - “SUGAR CONCATENATE”,包含 2005.01.04 至 2009.09.01 期间的数据。

Meta COT Script Agregation.mq4

<脚本<

..\Meta Trader\experts\scripts\

这是 一个独立的 脚本。 根据它们的数值之和,它将多个文件集中为一个文件。 例如,“COT - WHEAT - CHICAGO BOARD OF TRADE. CSV”、“COT - WHEAT - KANSAS CITY BOARD OF TRADE. CSV”、“COT - WHEAT - MINNEAPOLIS GRAIN EXCHANGE. CSV”文件将转换为一个“WHEAT AGREGATION”文件,包含了所有三个文件的数值之和。

Meta COT Script Report.mq4

<脚本<

..\Meta Trader\experts\scripts\

该脚本需要“cotlib.mq4”库。 它创建一个包含用于所有指标运算的 CSV 报告文件。 平均周期和工具名称在脚本设置中进行定义。 它或可以在其他程序中用于数据分析。

Meta COT Absolute Position.mq4

指标

..\Meta Trader\experts\indicators\

该脚本需要“cotlib.mq4”库。 它显示了 所有类别的交易者的绝对头寸,包含未平仓量在内。

Meta COT Net Position.mq4

指标

..\Meta Trader\experts\indicators\

该脚本需要“cotlib.mq4”库。 它显示了 所有类别的交易者的净头寸,包含未平仓量在内。

Meta COT Index.mq4

指标

..\Meta Trader\experts\indicators\

该脚本需要“cotlib.mq4”库。 它显示了所有类别的交易者的 COT 指数,包含未平仓量指数在内。 计算周期在脚本设置中定义。

Meta COT Percent Position in OI.mq4

指标

..\Meta Trader\experts\indicators\

该脚本需要“cotlib.mq4”库。 它显示了每类交易者的净头寸除以未平仓量的结果。

Meta COT WILLCO.mq4

指标

..\Meta Trader\experts\indicators\

该脚本需要“cotlib.mq4”库。 它显示了 所有类别 的交易者的 WILLCO 指数。 计算周期在脚本设置中定义。

Meta COT Movement Index.mq4

指标

..\Meta Trader\experts\indicators\

该脚本需要“cotlib.mq4”库。 它显示了每类交易者的运动指数和未平仓量。 平均和动量周期在脚本设置中进行定义。

Meta COT Spread Movement Index.mq4

指标

..\Meta Trader\experts\indicators\

指标需要“cotlib.mq4”库 它显示了每类交易者的运动指数除以未平仓量的结果。<

Meta COT Expert.mq4

Expert advisor

..\Meta Trader\experts\

Expert advisor 需要“cotlib.mq4”库。 它在 历史数据上测试 COT 指标。

cotlib.mq4

..\Meta Trader\experts\libraries\

它是系统的核心。 它包含了一个 COT 数据及其处理方法。 它有一个用于计算所有使用的指标的大型的数组、定义和函数的集合。

ONCATENATE.ini

列表文件<

..\Meta Trader\experts\files\

它是一个文件列表,其中的列表按时间进行组合。

COT - * CONCATENATE.ini

文件列表

..\Meta Trader\experts\files\<

它是一个文件列表,其中的列表按时间进行组合。

AGREGATION.ini

列表文件<

..\Meta Trader\experts\files\

它是一个文件列表,其中的列表按总和进行组合。

COT - * AGREGATION.ini

文件列表

..\Meta Trader\experts\files\<

它是一个文件列表,其中的列表按总和进行组合。

表 3-1 Meta COT 项目文件及其安装路径

你需要将这些文件安装在适当的目录下,然后进行编译。 在这些步骤之后,终端必须接收到适当的 MetaTrader 自定义指标、脚本和引导。

3.2 加载数据和创建报告

如你所知,所有的指标数据均由非营利组织 CFTC 提供。 这些数据每周公布在 CFTC 的官方网站上。 报告分为几种类型。 第一组被称为 “期货报告”,仅含有期货头寸的数据。 第二种报告被称为 “期货和期权报告”,它包含了期货和期权的数据。

它提供了更加完整的市场信息。 最好使用第二种报告。 还有一种特殊类型的报告,即“商品指数交易者附录”。 主要区别在于它是为了有限范围的农产品而准备,更重要的是,它包含了第四种交易者类别,即所谓的商品指数交易者 (CIT)。

这些交易者是中间头寸的持有者。 一方面,他们的头寸属于套期保值者,包含在“期货报告”和“期货和期权报告”中,另一方面,他们的行为跟大型套保基金类似。 总之,跟作为纯卖家的典型套期保值者相比,他们是纯买家。 有观点认为,这种类别的交易者在市场中制造恐慌。 急剧的下跌和上涨大部分是由他们引起。 他们具有足够的能量影响市场上涨或下跌,同时,他们的主要目的是 - 获得投机利润。 可以找到 2007 年以来该类交易者的数据,格式有 Exel 和 CSV。 由于他们出现的历史时间较短,因此对他们行为进行研究成为日后的任务。 目前,该项目不支持这种报告。

报告以多种格式发布。 首先,是交易表格。 这种格式不包含 “商品指数交易者附录”,它只限于 Excel 和 CSV 格式。

对于这些表格的格式你已经非常熟悉:


图 3-1 Excel 格式的 COT 报告的一部分

另外,也有常规的 Excel 表格。 它们包含的数据跟传统报告相同,唯一的区别在于收集数据的时间周期较长。 图 3-2 显示了这种表格的一部分。

图 3-2 Excel 格式的 COT 报告的一部分

委员会以 CSV 格式发布的报告。 该格式是扩展名为“txt”的文本文件,数据用逗号隔开。 这是 Meta COT 使用的唯一格式,所以对该格式进行详细的描述。 图 3.4 显示了该文件的一部分:

图 3-4 CSV 格式的 COT 报告示例

其结构可能显得有些乱,实则不然。 文件包含行和列。 列数是 128,行数取决于工具的数量和报告的周期。 一般来说,一个报告文件包含一年的数据。 例如,在 2009 年 9 月下载的这种格式的报告,会包含 2009 年 1 月至 2009 年 9 月(编写日期)的数据。 第一行包含 128 列,是列名称。 CSV 文件是构建项目数据的基础。

现在我们来创建一个图表。 考虑长期的 COT 数据非常方便。 我们来准备自 2000 年至今的数据。 于是,我们输入地址 http://cftc.gov/marketreports/commitmentsoftraders/CFTC009781.html 或在 http://cftc.gov 网站前往 Home> Market Reports > Commitments of Traders 部分。

我们将看到以下的图像:

图 3-5 数据档案

有两种类型的报告:期货报告,期货和期权报告。 我们使用自 2000 年以来的数据,最好使用第二种类型的报告,即期货和期权报告(注:1995 年以前的数据仅限于期货报告)。 但是,它们具有相同的报告格式,你可以使用两者中的任何一种报告类型。 下载自 2009 年至 2000 年的所有 9 个文本文件。 随后,你还可以下载一个包含自 1995 年至 2008 年的单文件和一个 2009 年的文件,但这样的话,工具的数量会非常多。 在图 3.5,其中一个参考文献以红色圈出。 下载的文件应在以下目录解压:..\ Meta Trader folder \ experts \ files \。

档案中的所有文件具有相同的名称 annualof.txt,所以必须对其重命名。 文件名称可以随意,但这些文件名称必须在 names.ini 中列出,它是一个特殊的项目配置文件。 它非常简单,显示了要处理的文件的简单列表。

例如,如果我们将 2009 年的文件命名为“2009_Futures-and-Options.txt”,将 2008 年的文件命名为“2008_Futures-and-Options.txt”,以此类推,则处理文件列表显示如下:

图 3-6 COT 项目的处理文件列表示例

如果你对下载文件的重命名方式跟示例相同,就不必编辑 names.ini 文件,脚本将默认处理该文件集。 如果出于某种原因,你需要使用其他名称,则在 names.ini中指定新的文件名称,以替换默认指定的名称。

这样,在 \files 目录下你必须具有 10 个文件,如图 3-6 所示:

图 3-7 MetaTrader\experts\files 文件夹的目录

names.ini 目录 应该跟图 3-6 中一致。 注意:names.ini 文件中的文件名称顺序非常重要。 文件名称应该以降序排列。 例如,第一个应该是 2009 年的文件,最后一个是 2000 年的文件。

现在,已经准备好了数据,可以运行 Meta COT Script Build 脚本。 它以自动模式工作,只有一个选项,即文件列表名称。 本例中,文件名称为“names.ini”,但可以对其进行更改。 对一些问题,需要灵活解决。

例如,假设对于一些工具,你只想分析期货数据,而不包含期权数据。 同时,对于其他工具,你想使用更加完整的报告 - 期货和期权报告。 你可以下载两种类型的报告至计算机并创建两个文件列表,例如 Names_option.ini names_futures.ini

首先,你可以运行一个脚本获得期货数据,脚本的参数“names_list”等于 “names_futures.ini”。 之后,这些数据应保存在另一个文件夹。 再次运行脚本可以获得所需的数据,不过,这次参数“names_list” 等于 “names_option.ini”。 将产生期货和期权的数据。

在运行脚本后不久,“\files”文件夹将生成很多 CSV 格式的报告文件。 每一个这种文件的格式为 “COT - 市场名称 - 交易所名称 CSV"

很多文件会不完整。 一些市场的 COT 报告有的内容会短时出现,然后永久消失。 不过,工具的数量足够显示一份常规的报告。 稍后我们将仔细考虑所包含的报告工具,但现在我们来关注生成的文件。 使用其中一个文件进行考虑: “COT - NEW ZEALAND DOLLAR - CHICAGO MERCANTILE EXCHANGE. CSV"。 不难猜到,它包含了我们所需的新西兰元期货的所有信息。


我们用记事本打开,查看其内容:

图 3-8 新西兰元

可以看出,该文件包含一种工具(本例中为新西兰元)在较长时间内的统计数据。没有列名称,该文件用于不需要列名称的指标。 但是,为了理解这些数字的含义,我们逐列查看:

I - 工具名称;
II - 报告日期;
III - 未平仓量;
IV - 非商业交易者的多头头寸;
V - 非商业交易者的空头头寸;
VI - 非商业交易者的套利(双向持仓);
VII - 经营者的多头头寸;
VIII - 经营者的空头头寸;
IX - 交易报告中多头头寸的累计数量;
X - 交易报告中空头头寸的累计数量;
XI - 可不予计算的交易者的多头头寸;

XII - 可不予计算的交易者的空头头寸。

这就是你需要用于构建 COT 指标的全部内容。

在该阶段,相同的市场信息位于不同的文件中。 原因在于:首先,证券交易所的名称随时间而变化,其次在于名称本身,委员会可能更改工具名称,或很有可能弄错标题。 工具名称的变化,哪怕只有一个字符变化,对于提取数据的程序都意味着很大的差异。

例如,工具“COT - SUGAR NO. 11 - COFFEE SUGAR”和“COCOA EXCHANGECOT - SUGAR NO. 11 - COFFEESUGAR AND COCOA EXCHANGE”对于程序来说是完全不同的,它会为工具文件创建两个不同的标题。 我们来看一下白糖的示例。 在运行 Meta COT Script Build 脚本后,\files 目录下将出现很多文件,包括:

COT - SUGAR NO. 11 - COFFEE SUGAR AND COCOA EXCHANGE .csv
COT - SUGAR NO. 11 - COFFEESUGAR AND COCOA EXCHANGE .csv
COT - SUGAR NO. 11 - ICE FUTURES U.S. .csv
COT - SUGAR NO. 11 - NEW YORK BOARD OF TRADE .csv
COT - SUGAR NO. 14 - COFFEE SUGAR AND COCOA EXCHANGE .csv

最后一个文件属于白糖的另一个类别,包含的数据过少,无法进行使用,所以该文件可以立即删除。 从名称可以看出,第一个和第二个文件对应着同一个工具,委员会有时创建的工具报告中有语法错误,从而影响到第二个文件的创建。 第三个和第四个包含的也是白糖的文件,但交易所不同: 分别是“ICE期货交易所”和“纽约期货交易所”。

如果你打开这些文件,可以得到白糖交易的时间表:

白糖文件名称

交易开始日期

交易结束日期

COT - SUGAR NO. 11 - ICE FUTURES U.S. .csv

2007.09.04

2009.09.01

COT - SUGAR NO. 11 - NEW YORK BOARD OF TRADE .csv

2005.01.04

2007.08.28

COT - SUGAR NO. 11 - COFFEE SUGAR AND COCOA EXCHANGE .csv

2003.02.25

2004.12.28

COT - SUGAR NO. 11 - COFFEESUGAR AND COCOA EXCHANGE .csv

2003.01.07

2003.02.14

表 3-9 白糖报告的文件

从交易时间表可以看出,所有的数据文件描述了相同的产品,因此,可以将所有这些文件组合到一个连续的报告文件。 为了实现该目的,有一个 Meta COT Script Concatenate 脚本。 该脚本需要一个带有清单的特殊文件,例如 SUGAR CONCATENATE.ini。 所有需要集中到单个时间表的白糖文件,都应该在这里列出。 为了利用白糖文件创建一个特殊文件,例如 SUGAR CONCATENATE.ini。 它应该列出要组合到单个时间序列中的所有白糖文件。

带文件列表的文件名称应包含到中央文件列表 CONCATENATE.ini。 该文件包含所有要组合的年份的文件名称。 MetaCOT 项目具有相应的文件,它们的格式如下: COT - NAME TOOL CONCATENATE.ini。 这些名称在文件列表 CONCATENATE.ini 中列出。

在准备好所有的文件列表后,你可以运行 Meta COT Script Concatenate 脚本。 短暂运行后,它将创建组合的文件,名称跟文件相同,但是为 CSV 格式。

为了更好的理解处理的逻辑,我们来看一下图 3-10。 CONCATENATE.ini、SUGAR CONCATENATE.ini 和其他文件的层次显示如下:


3.3 作为新分析工具的组合报告

有很多彼此相关的商品,它们都有 CFTC 报告。 例如,育肥牛和活牛的市场价格高度相关。 小麦在三个美国交易所进行交易,其价格彼此紧密关联。

自然也可以认为,这些市场上的所有参与者也大致相同。 那么为什么将这些市场上的报告组合为一个报告呢? 例如,如果经营者在活牛市场有 20100 手合约,在育肥牛市场有 15200 手合约,那么他们在牛市场的组合头寸达到 35300 手合约。 这种加法可以应用到所有的市场参与者,无论是空头头寸还是多头头寸。

我们可以更进一步,用一个共同的变体将所有市场参与方的活动组合起来。 何不将所有货币期货的报告组合到一个报告,从而得出我们自己的美元指数版本? 或者可以将来自标普 500 和道琼斯 30 的所有证券指数的报告组合起来。 得到的工具将包含更大的交易量,而且包含了不仅仅是某个市场而是整个行业的参与者的观点!

这个想法非常完美,但尚未实现。 假设,MetaCOT 项目有一个这种工具。 它被称作 Meta COT Script Agregation。 其工作原理跟 Meta COT Script Concatenate类似,差别在于组合报告的算法不同。 它需要一个要组合的指定的工具列表,这些工具的文件名称应在单文件中列出,文件的文件名称应添加到所有类似文件的文件列表。

我们来创建一个自己的美元指数。 首先我们得创建“Dollar Index Agregation.ini”文件,并写入所有货币期货的报告的名称。

COT - EURO FX CONCATENATE.csv
COT - BRITISH POUND STERLING CONCATENATE.csv
COT - JAPANESE YEN CONCATENETE.csv
COT - AUSTRALIAN DOLLAR CONCATENATE.csv
COT - CANADIAN DOLLAR CONCATENATE.csv
COT - SWISS FRANC CONCATENATE.csv

注意:使用 Meta COT Script Concatenate 脚本合并这些文件。 列出这些名称后,你可以将“COT - DOLLAR INDEX AGREGATION.ini”文件保存到 MetaTrader/expert/files 目录下。 名称可以随意,但我们采用了以上名称。 然后你应创建 AGREGATION.ini 文件。 它指定了关联文件的名称: COT - DOLLAR INDEX AGREGATION.ini。

现在你可以保存文件并运行 Meta COT Script Agregator 脚本。 该脚本只有一个参数 - 它实际上是文件列表的名称,需要进行处理。 因为我们示例中的文件列表名称为“AGREGATION.ini”,所以它的参数名称应该设置为相同的文件名称。 脚本将对“COT - DOLLAR INDEX AGREGATION.ini”文件列表中包含的所有工具的值求和,在本例中,它将使用含有所有货币的报告的六个文件,并创建一个名称为“COT - DOLLAR INDEX AGREGATION.csv”的新文件。 该文件将包含所有货币期货的报告的数值总和。

可以进行求和的文件-报告列表是无限的。 利用这个强大的工具,你可以创建一个新的报告,一种全新的信息。 最大的好处在于所有的操作均为自动进行,你不必对数值手动求和。

但是,一些报告可能不包含其他报告中属于一般处理列表的给定周期的数据。 在这种情况下,程序会识别这种时间差,并通知以如下所示的信息: «--> Time Gap has Found: 21.04.2004.». 这并非是出错,而是非常典型和经常出现的事件。 在本例中,程序将对其他所有工具的数据求和,并创建一份包含日期为 21.04.2004 的报告。 对于缺失日期的数据,它将包含距离最近的上一个周期的数据。 这将是处理的第一个日期。 例如,如果 4 月 21 号的数据缺失,21/04/2004 的组合报告将包含 14/04/2004 的数据。

因此,在组合报告时需加以注意。 同一组的工具可以有相同的合约价格,不同的组可以不同。 举一个简单的例子,S&P500 指数和它的两个期货合约: E-迷你 S&P500 和完整 S&P500。 一手电子期货合约等于指数值 *5$,而完整期货合约等于指数值 *25$。 尽管大型双重市场在合约数量方面让步于小型电子双重市场,它们的市值可以是相等的,甚至完整 S&P500 的资本化可以更强。关于各种市场的数值有效相加的问题仍未解决,但基本的相加工具已经具备,可以审慎使用。

3.4 指标

所有的指标基于同一个原则安排,且包含相同的内核。 指标内核加载所有的数据并计算 MetaCOT 项目的所有指标的值。 每个指标只显示其任务所定义的值,但实际上其代码内部包含了所有可能指标的值。 这种方法证明及其有效,尤其在设计交易机器人时。 因为 cotlib.mq4 库计算所有可能指标的值,可以将其指定为要优化的参数!

这意味着在每次新的运行之后,机器人将使用新的指标及其计算周期。 所以这种方法可以确定最好的指标和最佳周期。 这对于找到最适宜的指标和交易者群体非常有用。

现在,已经准备好了所有必要的报告文件,需要将它们加载到指标。 所有的指标都类似,具有相似的参数结构。

例如,我们来考虑 Meta COT 净头寸指标:

变量类型

变量名称

默认值

说明

intperiod

156

指标计算的周期。 它用于需要周期进行计算的指标(COT 指数,WILLCO 等)。 通常,使用 156 周平均周期。

oolShowNoncomm

<

如果为真,指标将显示非商业交易者的总净头寸。

boolShowOperators

如果为,指标将显示经营者的总净头寸。

boolShowNonrep

如果为,指标将显示非报告交易者的总净头寸。
<<

boolShowOI

如果为,指标将显示未平仓量指数。

boolload_cot_file

如果为,指标将加载准备好的报告文件,文件名称在 <cot_file 变量中指定。

stringcot_file

cot-sample.txt

变量包含报告文件的名称,如果 <load_cot_file 变量为真则需要加载文件。<

stringsettings

settings.txt

指标和报告之间一致性的设置文件。 程序找到文件中当前图表的名称,并使用具有相同名称的报告。

图 3-11 Meta COT 净头寸指标的变量

第一,所有使用平均周期的指标,都有一个外部变量 period。 它的默认设置是 156 周(3 年平均周期)。

第二,所有的指标都有布尔变量,它允许自定义指标的绘制(例如,你可以定义一组市场参与者)。

第三,指标具有三个变量:load_cot_file、cot_file 和 settings。 我们对其仔细考量。 对于所有的指标,都有一个“一致性文件”,默认名称是“settings.ini”。 每次手动选择报告名称时极其方便。为了分析选中的图表必须下载该报告。 每一种工具都需要自己的报告。 settings.ini 文件决定了你需要为当前图表下载的报告类型。 例如,如果变量 load_cot_file 为假,而你在 GBPUSD 图表上启动了 Meta COT 净头寸指标,指标会自动选择名称为“COT - BRITISH POUND STERLING CONCATENATE.csv”的报告并将其上传到图表。 这一切都得感谢文件名称跟工具名称一致的简单的报告文件。

Meta COT 在默认情况下已经拥有这样的文件。 这是典型的 CSV 文件,如下所示:

报告文件名称;指标名称;第一个工具的第一个有效字符;第 N 个工具的第一个有效字符

例如,如果你要自动下载 COT - SWISS FRANC CONCATENATE.csv 报告,每次将指标附加到 6S 和 USDCHF 图表后,你需要添加下面的行至文件
COT - SWISS FRANC CONCATENATE.csv;SWISS FRANC;6S;USDCHF

本例中,符号“6S”是具有不同执行日期的各种瑞士法郎期货的总称。 例如,在合约 6S_CONT;6SU9;6SU9#I;6SZ9;6SZ9#I 上交易的瑞士法郎;其中第一个是用于策略回测的连续期货。 对于该系统,仅指定工具的第一个有效字符就足够了(本例中为 6S),可以对指标的所有瑞士法郎期货使用相同的报告。 最后一行是一种“结尾”。 如果已经搜索了所有的文件目录,仍未找到所需的报告,则指标显示最后报告的名称。 在这种情况下,不会出现所用报告的名称,而是一条警告信息: “REPORT FOR THE INSTRUMENT WASN'T FOUND!”。 这种情况下,将上传一个由 cot_file 变量定义其名称的报告,而不考虑变量 load_cot_file

注意:在行的末尾没有分号。 此时,指标将下载 COT - SWISS FRANC CONCATENATE.csv 报告,指标名称将更改为: Meta COT Index (156): SWISS FRANC。 “SWISS FRANC”这个名称取自于列的第二行,平均周期由周期变量在括号中指定。 为特定的经纪人配置的 setting.ini 文件的完整目录可见于图 3-8。 注意:你的经纪人的工具可能跟这些名称不同。 你需要在你经纪人的工具里找到对应的名称,根据以上描述的规则,将其添加到 settings.ini 文件。

在一些情况下,需要下载一个报告名称跟默认值不一致的特定文件,以进行工具分析。 在这种情况下,你必须将变量 load_cot_file 设置为真,并在变量 cot_file 中指定所需报告文件的名称。 无论该工具的名称是什么,指标将下载所需的报告文件。

图 3-8 settings.ini 文件的示例

对于各种不同的任务,你可以使用第三方程序,比如 Microsoft Excel,进行数据分析。 在这种情况下,使用由 Meta COT 为指定工具计算的值非常方便,然后将指标值下载到第三方程序。 对于这种情形,创建了一个特殊的脚本 - Meta COT Script Report 。 它需要一个 cotlib.mq4 库才能正常工作。 它的参数跟指标相同:计算周期(period)和运动指数(movement_index)。 在为当前工具运行之后,它将在 \files 目录下创建一个 Meta COT Report REPORT TITLE.csv 文件。 该文件包含所有指标的值,包括当前工具的基本值。

现在,所有的指标都已配置和正常工作,可以删除所有报告了。 Meta COT 不更新数据,每次都会从无到有创建一组数据或附加到现有的数据。 这意味着,在运行脚本前,必须移除旧的数据。 不幸的是,MetaTrader 没有删除文件的内置功能,我们只能手动进行。 最便捷的方法是:创建一个批文件命令或 bat-file。

该文件已经存在,名称是 erase_cot.bat。 它只包含一行:

erase COT*.csv

它在运行后将删除所有名称以 COT 开始且以 CSV 为扩展名的文件。

删除之后,可以在此重复文件创建过程。

3.5 源代码

我们已经开发了一个容易管理和修改的项目,它可以轻松的适应于任何机械交易系统。

从 CFTC 委员会报告获得数据的机制在单独的程序( Meta COT Script Build。 算法非常简单,该程序确保了高度的可管理性和后续修改的能力。 最初,程序打开需要处理的文件名称的列表。 文件中包含的文件名称在其单参数中指定(默认为 names.ini)。 当它读取了第一个文件的名称时,脚本试图打开目录中具有相同名称的文件。 如果文件存在,则脚本开始逐列读取 CSV 文件,在行的末尾重置列计数器。 当列计数器达到所需的列时(由专门的函数进行检查),它的值立即记录下目标文件,并在其后面写入分号。

有必要仔细描述最后文件的创建过程。 如果列计数器表明当前程序在处理第一列(工具名称),它会读取该名称并尝试打开具有相同名称的文件。 如果文件不存在,程序将创建该文件,否则会将数据附加到文件末尾。 因此,每次在数据实现之前需要删除现有的文件。 后面的列值将会加载并添加到文件。 在清空列计数器后,程序关闭当前的输出文件。 在处理了委员会报告之后,程序尝试打开文件列表中指定的下一个文件。

结果显示,程序对于期货名称或其组合的顺序一无所知。 一般而言,工具的名称可以随机排列,可以放在不同的文件内。 最终的结果始终是相同的:一个工具对应一个报告文件。

Meta COT Script Build 的源代码 如下:

#property copyright "Copyright © 2009, C-4, All Rights Reserved."
#property link      "vs-box@mail.ru"
#property show_inputs
// Definitions
#define Market_and_Exchange_Names 1
#define As_of_Date_in_Form_YYMMDD 2
#define As_of_Date_in_Form_YYYY_MM_DD 3
#define CFTC_Contract_Market_Code 4
#define CFTC_Market_Code_in_Initials 5
#define CFTC_Region_Code 6
#define CFTC_Commodity_Code 7
#define Open_Interest_All 8
#define Nonc_Positions_Long_All 9
#define Nonc_Positions_Short_All 10
#define Nonc_Positions_Spreading_All 11
#define Commercial_Positions_Long_All 12
#define Commercial_Positions_Short_All 13
#define Total_Reportable_Pos_Long 14
#define Total_Reportable_Pos_Short 15
#define Nonrep_Positions_Long_All 16
#define Nonrep_Positions_Short_All 17

extern string name_list="names.ini";
//string normalize_name;
int a;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int start()
  {
   int CFileNames,
   cot_file,
   tools_data;

   int column,
   column_now,
   column_string;
   int cot;
   bool IsOldInfo=true;
   string name_cotfile;
   string data,data2,
   normalize_name,
   string_cot;

   CFileNames=FileOpen(name_list,FILE_READ|FILE_CSV);
   if(TrueFileName(CFileNames,name_list)==false)return(1);
   Print("The file list: ",CFileNames);
// Open a file list with reports
   while(FileIsEnding(CFileNames)==false)
     {
      cot++;
      name_cotfile=FileReadString(CFileNames);
      cot_file=FileOpen(name_cotfile,FILE_READ|FILE_CSV,",");
      if(TrueFileName(cot_file,name_cotfile)==false)return(1);
      else Print("File not found");

      // Skip first string
      while(FileIsLineEnding(cot_file)==false)
        {
         column++;
         FileReadString(cot_file);
        }

      while(FileIsEnding(cot_file)==false)
        {
         data=FileReadString(cot_file);
         column_string++;
         if(FileIsLineEnding(cot_file)==true || FileIsEnding(cot_file)==true)
           {
            column_string=0;
            FileWrite(tools_data,string_cot);
            FileClose(tools_data);
            string_cot="";
           }
         else
           {
            if(TrueDataNew(column_string)==true)
              {// check for the comma in the instrument name
               data=NormalizeData(data);
               if(column_string==Market_and_Exchange_Names)
                 {

                  data2=FileReadString(cot_file);        // see comment1!
                  if(CheckValidColumn(data2)==true)      //
                     data=data+NormalizeColumn(data2);   //
                  else column_string++;
                  //data=NormalizeNames(data);
                  tools_data=FileOpen("COT - "+data+".csv",FILE_READ|FILE_WRITE|FILE_CSV);
                  TrueFileName(tools_data,data);
                  FileSeek(tools_data,0,SEEK_END);
                  string_cot=string_cot+data;
                 }
               else
                 {
                  if(column_string==As_of_Date_in_Form_YYYY_MM_DD)data=ConvertData(data);
                  string_cot=string_cot+";"+data;
                 }
              }
           }
        }
      Print("Total lines: ",column);
     }
   Print(a);
   return(0);
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string ConvertData(string data)
  {
   string c_data="";
   int strlen=StringLen(data);
   int char;
   for(int i=0;i<strlen;i++)
     {
      char=StringGetChar(data,i);
      if(char=='-')c_data=c_data+".";
      else c_data=StringConcatenate(c_data,CharToStr(char));
     }
   return(c_data);
  }
<<// If the current cell is necessary for combined COT report, it returns true,
// Overwise it returns false. The list of the cells used are set as defines
<bool TrueDataNew(int column_string)
  {
   switch(column_string)
     {
      case Market_and_Exchange_Names:
      case As_of_Date_in_Form_YYYY_MM_DD:
      case Open_Interest_All:
      case Nonc_Positions_Long_All:
      case Nonc_Positions_Short_All:
      case Nonc_Positions_Spreading_All:
      case Commercial_Positions_Long_All:
      case Commercial_Positions_Short_All:
      case Total_Reportable_Pos_Long:
      case Total_Reportable_Pos_Short:
      case Nonrep_Positions_Long_All:
      case Nonrep_Positions_Short_All:
         return(true);
      default:
         return(false);
     }
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string NormalizeData(string file_name)
  {

   int char;
   int strlen;
   string normalize_name="";
   string normalize_name_p="";
   strlen=StringLen(file_name);
   for(int i=0;i<strlen;i++)
     {
      char=StringGetChar(file_name,i);
      switch(char)
        {
         case '/':
         case '\\':
         case ':':
         case '*':
         case '\"':
         case '<':
         case '>':
         case '|':
         case ';':
            break;
         default:
            // If the last symbol of the string is space, delete it.
            //if(char==" "&&i==strlen-1)break;
            normalize_name=StringConcatenate(normalize_name,CharToStr(char));
        }
     }
   return(normalize_name);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string NormalizeNames(string data)
  {
   int strlen=StringLen(data);
   string n_data;
   data=StringTrimRight(data);
   if(CharToStr(StringGetChar(data,strlen-1))==".")StringSetChar(data,strlen-1,"");
   return(data);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool TrueFileName(int handle,string filename)
  {
   if(handle==-1)
     {
      Print("Can't open the file: ",filename," Last Error: ",GetLastEr());
      return(false);
     }
   else return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CheckValidColumn(string column_name)
  {
   int strlen=StringLen(column_name);
   if(StringFind(column_name,"\"")!=-1){a++;return(true);}
   return(false);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string NormalizeColumn(string column)
  {
   string n_column;
   int char;
   int strlen=StringLen(column);
   for(int i=0;i<strlen;i++)
     {
      char=StringGetChar(column,i);
      if(char=='\"')continue;
      else n_column=StringConcatenate(n_column,CharToStr(char));
     }
   return(n_column);
  }
//+------------------------------------------------------------------+


指标部分更加困难。 几乎所有的指标都包含同一个代码:cotlib.mq4 库。 其结构可以分为几个部分:

1. 定义 - 对预处理程序常量和数据表格(数组集)的描述; 2.

2. 初始化 - 计算所下载数据的大小并重新分配数组的大小(init_data () 函数); 3.

3. 从报告文件加载数据 (load_data () 函数);

4. 使用报告数据计算指标值 (count_data () 函数);

为了访问指标值,使用了 get_data (int type, int bar) 函数。 它有两个参数:必须返回的指标数量和你要获得的指标值的柱指数。 例如,如果你要获得经营者在当前柱的 WILLCO 指标的值(bar = 0),则函数调用应该是:get_data (WILLCO_OPERATORS, 0); 这里的“WILLCO_OPERATORS”是为预处理程序指定的已命名的整数常量的定义。

所有计算指标的平均周期都相同,它们应该在指标初始化之前选定(在输入参数变化后,会自动进行指标初始化)。 需要读取的文件由 void settings_load (void) 函数自动确定。 它打开配置文件(默认为 settings.ini)并尝试在所需报告的工具列表中找到当前的工具(Symbol ())。 如果报告符合工具的名称,函数将会下载报告。如果没有当前工具的报告,它会默认加载工具(参数名称)。

#property copyright "Copyright © 2009, C-4, All Rights Reserved."#property link      "vs-box@mail.ru"// Possible "type" of parameter values for function get_data()
#define OI                    0#define NONCOMM_LONG          1#define NONCOMM_SHORT         2#define OPERATORS_LONG        3#define OPERATORS_SHORT       4#define NONREP_LONG           5#define NONREP_SHORT          6#define NET_NONCOMM           7#define NET_OPERATORS         8#define NET_NONREP            9#define OI_NONCOMM_LONG       10#define OI_NONCOMM_SHORT      11#define OI_OPERATORS_LONG     12#define OI_OPERATORS_SHORT    13#define OI_NONREP_LONG        14#define OI_NONREP_SHORT       15#define WILLCO_NONCOMM        16#define WILLCO_OPERATORS      17#define WILLCO_NONREP         18#define INDEX_OI              19#define INDEX_NONCOMM         20#define INDEX_OPERATORS       21#define INDEX_NONREP          22#define MOVEMENT_NONCOMM      23#define MOVEMENT_OPERATORS    24#define MOVEMENT_NONREP       25#define MOVEMENT_OI           26#define OI_NET_NONCOMM        27#define OI_NET_OPERATORS      28#define OI_NET_NONREP         29extern bool   load_cot_file=false;
extern string cot_file="COT - U.S. DOLLAR CONCATENATE.csv";
extern string settings="settings.ini";
string name;
bool error=false;
int column;
bool DrawData=true;
bool LoadData=true;


//**********************************************************************************************************
//                                     COT DATA TABLE
int n_str;                    // number of strings

                              // Array with dates
datetime realize_data[];
// Arrays with absolute positions
double open_interest[];       // Open Interest value
double noncomm_long[];        // Long positions of non-commercial traders
double noncomm_short[];       // Short positions of non-commercial traders
double noncomm_spread[];      // Spread of non-commercial traders
double operators_long[];      // Long positions of operators
double operators_short[];     // Short positions of operators
double nonrep_long[];         // <Long positions of unreported traders (crowd)
double nonrep_short[];        // Shortpositions of unreported traders< (crowd)

                              // An arrays contain the result of division of the absolute long position 
                              // by short position for each of the category of traders
<
double oi_noncomm_long[];     // Open Interest / Long positions of noncommercial traders
double oi_noncomm_short[];    // Open Interest / Short positions of noncommercial traders
double oi_operators_long[];   // Open Interest / Long positions of noncommercial traders (operators)
double oi_operators_short[];  // Open Interest / Short positions of noncommercial traders (operators)
double oi_nonrep_long[];      // Open Interest / Long positions of unreported traders (crowd)
double oi_nonrep_short[];     // Open Interest / Short positions of unreported traders (crowd)

                              // An arrays contain the result of division of the total net position by Open Interest
                              // for each of the category of traders, it used for WILLCO calculation
<double oi_net_noncomm[];
double oi_net_operators[];
double oi_net_nonrep[];

// Arrays with net positions of several groups of traders
double net_noncomm[];         // Net position of noncommercial traders
double net_operators[];       // Net position of commercial traders
double net_nonrep[];          // Net position of unreported traders

double index_oi[];            // Index of Open Interest
double index_ncomm[];         // Index of noncommercial traders
double index_operators[];     // Index of commercial traders (operators)
double index_nonrep[];        // Index of unreported traders (crowd)

                              // Arrays with Stochastic, calculated on division of
                              // Open Interest by Total net position for each category of traders
                              // Stohastic(OI/NET_POSITION)
double willco_ncomm[];        // 
double willco_operators[];    //
double willco_nonrep[];       //

                              //MOVEMENT INDEX
double movement_oi[];
double movement_ncomm[];
double movement_operators[];
double movement_nonrep[];
<//**********************************************************************************************************
<//
bool init_data()
  {
   string data;
   int handle_cotfile;
   int str;
   settings_load();
   handle_cotfile=FileOpen(cot_file,FILE_READ|FILE_CSV);
//handle_cotfile=FileOpen("COT - U.S. DOLLAR CONCATENATE.csv",FILE_READ|FILE_CSV);
   if(handle_cotfile==-1)
     {
      Print("Can't load file. The further work is not possible ",cot_file);
      Print(GetLastError());
      return(false);
     }
   while(FileIsEnding(handle_cotfile)==false)
     {
      data=FileReadString(handle_cotfile);
      if(FileIsLineEnding(handle_cotfile) && data!="")str++;
     }
   ArrayResize(realize_data,str);

   ArrayResize(open_interest,str);
   ArrayResize(noncomm_long,str);
   ArrayResize(noncomm_short,str);
   ArrayResize(noncomm_spread,str);
   ArrayResize(operators_long,str);
   ArrayResize(operators_short,str);
   ArrayResize(nonrep_long,str);
   ArrayResize(nonrep_short,str);

   ArrayResize(oi_noncomm_long,str);
   ArrayResize(oi_noncomm_short,str);
   ArrayResize(oi_operators_long,str);
   ArrayResize(oi_operators_short,str);
   ArrayResize(oi_nonrep_long,str);
   ArrayResize(oi_nonrep_short,str);

   ArrayResize(oi_net_noncomm,str);
   ArrayResize(oi_net_operators,str);
   ArrayResize(oi_net_nonrep,str);

   ArrayResize(net_noncomm,str);
   ArrayResize(net_operators,str);
   ArrayResize(net_nonrep,str);

   ArrayResize(index_oi,str);
   ArrayResize(index_ncomm,str);
   ArrayResize(index_operators,str);
   ArrayResize(index_nonrep,str);

   ArrayResize(willco_ncomm,str);
   ArrayResize(willco_operators,str);
   ArrayResize(willco_nonrep,str);

   ArrayResize(movement_oi,str);
   ArrayResize(movement_ncomm,str);
   ArrayResize(movement_operators,str);
   ArrayResize(movement_nonrep,str);

   FileClose(handle_cotfile);
   return(true);
  }
<
void settings_load()
  {
   int h_set,_column;
   string set,sname;
   if(load_cot_file==true)return;
   h_set=FileOpen(settings,FILE_READ|FILE_CSV,";");
//FileOpen("\Temp\Cot_file.txt",FILE_WRITE,FILE_CSV);
   if(h_set==-1)return;
   while(FileIsEnding(h_set)==false)
     {
      set=FileReadString(h_set);
      _column++;
      if(set==Symbol() && _column!=1){cot_file=sname;return;}
      if(_column==2 && set!="")name=set;    // The second column is the desired indicator name
      if(FileIsLineEnding(h_set)==true)_column=0;
      if(_column==1)sname=set;

     }
   FileClose(h_set);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void load_data()
  {
   datetime data_realize;
   int handle_cotfile;
   string data;
   handle_cotfile=FileOpen(cot_file,FILE_READ|FILE_CSV,";");
   Print("Файл найден");
   while(FileIsEnding(handle_cotfile)==false)
     {
      data=FileReadString(handle_cotfile);
      column++;
      if(FileIsLineEnding(handle_cotfile)==true || FileIsEnding(handle_cotfile)==true)
        {//column=12 - special case
         if(column==12)
            nonrep_short[n_str]=StrToDouble(data);
         column=0;
         if(data!="")n_str++;
        }
      else
        {
         switch(column)
           {
            case 2:
               realize_data[n_str]=StrToTime(data);
               break;
            case 3:
               open_interest[n_str]=StrToDouble(data);
               //Print(open_interest[n_str]);
               break;
            case 4:
               noncomm_long[n_str]=StrToDouble(data);
               break;
            case 5:
               noncomm_short[n_str]=StrToDouble(data);
               break;
            case 6:
               noncomm_spread[n_str]=StrToDouble(data);
               break;
            case 7:
               operators_long[n_str]=StrToDouble(data);
               break;
            case 8:
               operators_short[n_str]=StrToDouble(data);
               break;
            case 11:
               nonrep_long[n_str]=StrToDouble(data);
               break;
           }
        }
     }
   FileClose(handle_cotfile);
   Print("Fil loaded");
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void count_data()
  {
   int    max,
   min;
   double delta;

   for(int i=0;i<n_str;i++)
     {
      index_oi[i]=EMPTY_VALUE;
      index_ncomm[i]=EMPTY_VALUE;
      index_operators[i]=EMPTY_VALUE;
      index_nonrep[i]=EMPTY_VALUE;
      willco_ncomm[i]=EMPTY_VALUE;
      willco_operators[i]=EMPTY_VALUE;
      willco_nonrep[i]=EMPTY_VALUE;
      movement_ncomm[i]=EMPTY_VALUE;
      movement_operators[i]=EMPTY_VALUE;
      movement_nonrep[i]=EMPTY_VALUE;
     }
   for(i=0;i<n_str;i++)
     {
      if(open_interest[i]==0)
        {
         oi_noncomm_long[i]=0;
         oi_noncomm_short[i]=0;
         oi_operators_long[i]=0;
         oi_operators_short[i]=0;
         oi_nonrep_long[i]=0;
         oi_nonrep_short[i]=0;
        }
      else
        {
         oi_noncomm_long[i]=noncomm_long[i]/open_interest[i];
         oi_noncomm_short[i]=noncomm_short[i]/open_interest[i];
         oi_operators_long[i]=operators_long[i]/open_interest[i];
         oi_operators_short[i]=operators_short[i]/open_interest[i];
         oi_nonrep_long[i]=nonrep_long[i]/open_interest[i];
         oi_nonrep_short[i]=nonrep_short[i]/open_interest[i];
        }

      net_noncomm[i]=noncomm_long[i]-noncomm_short[i];
      net_operators[i]=operators_long[i]-operators_short[i];
      net_nonrep[i]=nonrep_long[i]-nonrep_short[i];

      if(open_interest[i]==0)
        {
         oi_net_noncomm[i]=0;
         oi_net_operators[i]=0;
         oi_net_nonrep[i]=0;
        }
      else
        {
         oi_net_noncomm[i]=net_noncomm[i]/open_interest[i];
         oi_net_operators[i]=net_operators[i]/open_interest[i];
         oi_net_nonrep[i]=net_nonrep[i]/open_interest[i];
        }
     }

   for(i=0;i<n_str-period;i++)
     {

      max=ArrayMaximum(open_interest,period,i);
      min=ArrayMinimum(open_interest,period,i);
      delta=open_interest[max]-open_interest[min];
      if(delta==0)delta=1;
      index_oi[i]=(open_interest[i]-open_interest[min])/delta*100;

      max=ArrayMaximum(net_noncomm,period,i);
      min=ArrayMinimum(net_noncomm,period,i);
      delta=net_noncomm[max]-net_noncomm[min];
      if(delta==0)delta=1;
      index_ncomm[i]=(net_noncomm[i]-net_noncomm[min])/delta*100;

      max=ArrayMaximum(net_operators,period,i);
      min=ArrayMinimum(net_operators,period,i);
      delta=net_operators[max]-net_operators[min];
      if(delta==0)delta=1;
      index_operators[i]=(net_operators[i]-net_operators[min])/delta*100;

      max=ArrayMaximum(net_nonrep,period,i);
      min=ArrayMinimum(net_nonrep,period,i);
      delta=net_nonrep[max]-net_nonrep[min];
      if(delta==0)delta=1;
      index_nonrep[i]=(net_nonrep[i]-net_nonrep[min])/delta*100;

      max=ArrayMaximum(oi_net_noncomm,period,i);
      min=ArrayMinimum(oi_net_noncomm,period,i);
      delta=oi_net_noncomm[max]-oi_net_noncomm[min];
      if(delta==0)delta=1;
      willco_ncomm[i]=(oi_net_noncomm[i]-oi_net_noncomm[min])/delta*100;

      max=ArrayMaximum(oi_net_operators,period,i);
      min=ArrayMinimum(oi_net_operators,period,i);
      delta=oi_net_operators[max]-oi_net_operators[min];
      if(delta==0)delta=1;
      willco_operators[i]=(oi_net_operators[i]-oi_net_operators[min])/delta*100;

      max=ArrayMaximum(oi_net_nonrep,period,i);
      min=ArrayMinimum(oi_net_nonrep,period,i);
      delta=oi_net_nonrep[max]-oi_net_nonrep[min];
      if(delta==0)delta=1;
      willco_nonrep[i]=(oi_net_nonrep[i]-oi_net_nonrep[min])/delta*100;
     }
   for(i=0;i<n_str-period-movement_index;i++)
     {
      movement_oi[i]=index_oi[i]-index_oi[i-movement_index];
      movement_ncomm[i]=index_ncomm[i]-index_ncomm[i-movement_index];
      movement_operators[i]=index_operators[i]-index_operators[i-movement_index];
      movement_nonrep[i]=index_nonrep[i]-index_nonrep[i-movement_index];
     }
  }
<// Returns one of the COT table values
// defined by "type" variable and bar defined by "bar"
<double get_data(int type,int bar)
  {
   double data;
   int i_data=get_cot(bar);
   if(i_data==EMPTY_VALUE)return(EMPTY_VALUE);
   switch(type)
     {
      case OI:
         return(open_interest[i_data]);
      case NONCOMM_LONG:
         return(noncomm_long[i_data]+noncomm_spread[i_data]);
         //return(noncomm_long[i_data]);
      case NONCOMM_SHORT:
         return(noncomm_short[i_data]+noncomm_spread[i_data]);
         //return(noncomm_short[i_data]);
      case OPERATORS_LONG:
         return(operators_long[i_data]);
      case OPERATORS_SHORT:
         return(operators_short[i_data]);
      case NONREP_LONG:
         return(nonrep_long[i_data]);
      case NONREP_SHORT:
         return(nonrep_short[i_data]);
      case NET_NONCOMM:
         return(net_noncomm[i_data]);
      case NET_OPERATORS:
         return(net_operators[i_data]);
      case NET_NONREP:
         return(net_nonrep[i_data]);
      case INDEX_OI:
         return(index_oi[i_data]);
      case INDEX_NONCOMM:
         return(index_ncomm[i_data]);
      case INDEX_OPERATORS:
         return(index_operators[i_data]);
      case INDEX_NONREP:
         return(index_nonrep[i_data]);
      case OI_NONCOMM_LONG:
         return(oi_noncomm_long[i_data]);
      case OI_NONCOMM_SHORT:
         return(oi_noncomm_short[i_data]);
      case OI_OPERATORS_LONG:
         return(oi_operators_long[i_data]);
      case OI_OPERATORS_SHORT:
         return(oi_operators_short[i_data]);
      case OI_NONREP_LONG:
         return(oi_nonrep_long[i_data]);
      case OI_NONREP_SHORT:
         return(oi_nonrep_short[i_data]);
      case WILLCO_NONCOMM:
         return(willco_ncomm[i_data]);
      case WILLCO_OPERATORS:
         return(willco_operators[i_data]);
      case WILLCO_NONREP:
         return(willco_nonrep[i_data]);
      case OI_NET_NONCOMM:
         return(oi_net_nonrep[i_data]);
      case OI_NET_OPERATORS:
         return(oi_net_operators[i_data]);
      case OI_NET_NONREP:
         return(oi_net_nonrep[i_data]);
      case MOVEMENT_NONCOMM:
         return(movement_ncomm[i_data]);
      case MOVEMENT_OPERATORS:
         return(movement_operators[i_data]);
      case MOVEMENT_NONREP:
         return(movement_nonrep[i_data]);
      case MOVEMENT_OI:
         return(movement_oi[i_data]);
     }
   return(EMPTY_VALUE);
  }
<// Returns the cell number in array
// It returns an EMPTY_VALUE, if bar hasn't been found
<<<int get_cot(int i)
  {
   datetime tbar=iTime(Symbol(),0,i);
   for(int k=0;k<n_str;k++)
      if(realize_data[k]<tbar)return(k);
   return(EMPTY_VALUE);
  }
// Returns bar with the last COT data exist
int get_lastdata()
  {
   return(iBarShift(Symbol(),0,realize_data[n_str-1],false));
  }
<// Returns a string without extension and first 6 symbols
// (before was "COT - FileName.csv", it became "FileName")
<string str_trim(string fname)
  {
   int strlen=StringLen(fname)-1;
   int char;
   string str;
   for(int i=strlen;i>0;i--)
     {
      char=StringGetChar(fname,i);
      if(char=='.')
        {
         for(int b=6;b<i;b++) // change to b=0, if it is not necessary 
                              // to delete the first of 6 symbols<<
            str=str+CharToStr(StringGetChar(fname,b));
         return(str);
        }
     }
   return(fname);
  }
//+------------------------------------------------------------------+

指标的代码本身非常繁琐,它们的差别仅在于初始化的缓冲器编号和 get_data () 函数的第一个参数。 所有的数值都在初始化阶段提前进行计算,所以他们不使用 IndicatorCounted () 函数。 在开始时的初始化过程中,调用 cotlib.mq4 库的基本函数,它配置图表,然后启动 start () 函数。 它立即运行 build_data () 函数,该函数在指标窗口显示报告数据中时间周期的数据。

作为示例,下面是 WILLCO 指标的源代码:

#property copyright "Copyright © 2009, C-4, All Rights Reserved."#property link      "vs-box@mail.ru"#property indicator_separate_window
#property  indicator_buffers 3#property  indicator_color1  Green
#property  indicator_color2  Red
#property  indicator_color3  Blue

extern int  period=52;
int movement_index=6;
extern bool Show_iNoncomm=false;
extern bool Show_iOperators=true;
extern bool Show_iNonrep=false;

double i_willco_noncomm[];
double i_willco_operators[];
double i_willco_nonrep[];

#include <cotlib.mq4>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int init()
  {
   if(init_data()==false)error=true;
   if(error==false)load_data();
   if(error==false)count_data();
//if(error==false)count_index(period);
   SetParam();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int start()
  {
   if(error==true)Print("Can't load data. The further work is impossible");
   if(DrawData==true)build_data();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void SetParam()
  {
   SetIndexStyle(0,DRAW_LINE);
   SetIndexStyle(1,DRAW_LINE);
   SetIndexStyle(2,DRAW_LINE);
   IndicatorDigits(2);
   SetIndexEmptyValue(0,EMPTY_VALUE);
   SetIndexEmptyValue(1,EMPTY_VALUE);
   SetIndexEmptyValue(2,EMPTY_VALUE);
   SetLevelValue(0,0.0);
   SetLevelValue(1,20.0);
   SetLevelValue(2,80.0);
   SetLevelValue(3,100.0);
   if(load_cot_file==true)IndicatorShortName(StringConcatenate("Meta COT WILLCO (",period,"): ",str_trim(cot_file)));
   else IndicatorShortName(StringConcatenate("Meta COT WILLCO (",period,"): ",name));
   if(Show_iNoncomm==true)
     {
      SetIndexBuffer(0,i_willco_noncomm);
      SetIndexLabel(0,"WILLCO: Noncommercial Traders");
     }
   if(Show_iOperators==true)
     {
      SetIndexBuffer(1,i_willco_operators);
      SetIndexLabel(1,"WILLCO: Operators Traders");
     }
   if(Show_iNonrep==true)
     {
      SetIndexBuffer(2,i_willco_nonrep);
      SetIndexLabel(2,"WILLCO: Nonrep Traders");
     }
  }

void build_data()
  {
   int end_data=get_lastdata();
   for(int i=0;i<end_data;i++)
     {
      if(Show_iNoncomm)
        {
         i_willco_noncomm[i]=get_data(WILLCO_NONCOMM,i);
         if(Show_iOperators)i_willco_operators[i]=get_data(WILLCO_OPERATORS,i);
         if(Show_iNonrep)i_willco_nonrep[i]=get_data(WILLCO_NONREP,i);
        }
      DrawData=false;
     }
//+------------------------------------------------------------------+

程序中最困难的部分在于创建一个对报告中的数据求和的脚本。 已经解决了很多算法问题。 程序以旧的 C 语言规范编写。 它包含一个没有函数调用的单一单元。 这种方法可能显得效率低甚至不正确,但在这种情况下它是立即解决问题的最佳方案,不需要将其分解为不同的“子任务”。

首先,程序打开文件列表,读取要组合的文件名称并逐个打开文件。 每个文件都有一个需要组合的报告的列表。 程序立即打开这些文件,读取它们的值并逐行进行处理(在 CSV 格式中行就是列)。 如果这些值具有相同的日期(第二列的值),则程序将这些值一起相加。 这些值的和被记录到最终文件,它跟要组合的文件的名称相同,但扩展名为 CSV。

#property copyright "Copyright © 2009, C-4, All Rights Reserved."#property link      "vs-box@mail.ru"#define EQU_NO           0#define EQU_YES          1#define DATA            1#define OI              2#define NONCOMM_LONG    3#define NONCOMM_SHORT   4#define SPREADING       5#define OPERATORS_LONG  6#define OPERATORS_SHORT 7#define ALL_LONG        8#define ALL_SHORT       9#define NONREP_LONG     10#define NONREP_SHORT    11extern string s_list="AGREGATION.ini";
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int start()
  {
   int      h_list;                    // File list descriptor
                                       // (it contain a file list)
                                       // string   s_list;                    // File name

   int      h_enumeration;             // Descriptor of the enumeration file
   string   s_enumeration;             // Filename of the enumeraton file

   int      h_rezult;                  // Description of the output file
   string   s_rezult;                  // Filename of the output file

   int      h_temp;                    // Temporary descriptor (one for all)
                                       // used for the file counting
   string   s_temp;                    // Temporary string (one for all), 
                                       // used for the file counting

   int      h_file[];                  // Array of descriptors for the output files 
   string   s_file[];                  // Array with the names for the output files

   datetime data[],data_all;
   int      oi[],oi_all,
   noncomm_long[],noncomm_long_all,
   noncomm_short[],noncomm_short_all,
   spreading[],spreading_all,
   operators_long[],operators_long_all,
   operators_short[],operators_short_all,
   all_long[],all_long_all,
   all_short[],all_short_all,
   nonrep_long[],nonrep_long_all,
   nonrep_short[],nonrep_short_all;

   int      str;                       // Count of the result files
   int      n;                         // The current index of the result files
   int      column;                    // Column number
   int      frnz_str[];                // Frozen descriptiors
   double   count;                     // Count the string count for the result file
   string   tmp;                       // A text string for the result file

   h_list=FileOpen(s_list,FILE_READ|FILE_CSV);                    // Открываем список файлов
   if(h_list==-1){Print("File list not found");return(0);}
   while(FileIsEnding(h_list)==false)
     {                            // Reading it string by string
      s_enumeration=FileReadString(h_list);                       // Each string has a filename
      Print("---------------<<",s_enumeration,">>---------------");
      h_enumeration=FileOpen(s_enumeration,FILE_READ|FILE_CSV);   // Try to open enumeration file list
      if(h_enumeration==-1)
        {
         Print("The enumeration file not found ",s_enumeration);
         continue;                                                // If it does not exist, skip it
        }

      s_rezult=StringConcatenate(ConvertName(s_enumeration),".csv");
      h_rezult=FileOpen(s_rezult,FILE_WRITE|FILE_CSV);
      if(h_rezult==-1)
        {
         Print("Can't create a result file: ",s_rezult,"; ",GetLastError());
         continue;
        }
      while(FileIsEnding(h_enumeration)==false)
        {
         s_temp=FileReadString(h_enumeration);                  // Reading string by string enumeration file
                                                                // each string is the name of the final file
         h_temp=FileOpen(s_temp,FILE_READ|FILE_CSV);            // Try to open the result file
         if(h_temp==-1)continue;        // If result file does not exists, skip it
         str++;                                                 // Overwise we increasing by 1 the count of the files processed.
                                                                // Print("--> ",s_temp);
         while(FileIsEnding(h_temp)==false)
           {
            tmp=FileReadString(h_temp);
            if(tmp!="")count++;
           }
         Print("--> ",s_temp," ",count/12);
         count=0;
         FileClose(h_temp);                                    // Closing result file
        }//End While (counting the files with enumerations)
      FileSeek(h_enumeration,0,SEEK_SET);

      ArrayResize(h_file,str);
      ArrayResize(s_file,str);
      ArrayResize(data,str);
      ArrayResize(oi,str);
      ArrayResize(noncomm_long,str);
      ArrayResize(noncomm_short,str);
      ArrayResize(spreading,str);
      ArrayResize(operators_long,str);
      ArrayResize(operators_short,str);
      ArrayResize(all_long,str);
      ArrayResize(all_short,str);
      ArrayResize(nonrep_long,str);
      ArrayResize(nonrep_short,str);
      ArrayResize(frnz_str,str);
      while(FileIsEnding(h_enumeration)==false)
        {
         s_file[n]=FileReadString(h_enumeration);              // Reading enumeration file string by string
                                                               // each string is the name of the result file
                                                               // Print(str,"; ",s_file[n]);
         h_file[n]=FileOpen(s_file[n],FILE_READ|FILE_CSV);     // Try to open the final file
         if(h_file[n]==-1){Print("File not found");continue;}  // It it does not exist, skip it
         n++;
        }// End While( open enumerations file)   
      //for(n=0;n<str;n++){
      //   Print(n,"; ",s_file[n]);
      //}
      // At present time we have the list of the result files(s_file[]) 
      // and their descriptors (h_file[]) which are
      // in the single enumerations file. Now lets combine these files to a single file:
      while(FileIsEnding(h_file[0])==false)
        {
                                 // As a signal of the end of the file there is a end of the first file
                                 // (we assume theat the files are the same)

         for(int i=0;i<str;i++)
           {                     // Reading column by column for each of the file included in the enumerations file.
            if(frnz_str[i]==1)continue;
            tmp=FileReadString(h_file[i]);
            switch(column)
              {
               case DATA:            data[i]=StrToTime(tmp);
               case OI:              oi[i]=StrToInteger(tmp);
               case NONCOMM_LONG:    noncomm_long[i]=StrToInteger(tmp);
               case NONCOMM_SHORT:   noncomm_short[i]=StrToInteger(tmp);
               case SPREADING:       spreading[i]=StrToInteger(tmp);
               case OPERATORS_LONG:  operators_long[i]=StrToInteger(tmp);
               case OPERATORS_SHORT: operators_short[i]=StrToInteger(tmp);
               case ALL_LONG:        all_long[i]=StrToInteger(tmp);
               case ALL_SHORT:       all_short[i]=StrToInteger(tmp);
               case NONREP_LONG:     nonrep_long[i]=StrToInteger(tmp);
               case NONREP_SHORT:    nonrep_short[i]=StrToInteger(tmp);
              }
           }// End For()
         //Print(h_file[0], ";  ",s_file[0]);
         column++;
         if(FileIsLineEnding(h_file[0])==true || FileIsEnding(h_file[0])==true)
           {  < // If string has finished - compare the dates for all of the files, is they are equal, combine them
            column=0;
            if(tmp=="")continue;
            for(int k=0;k<str;k++)
              {
               if(data[0]>data[k])
                  frnz_str[k]=1;
               if(data[0]<data[k])
                  frnz_str[0]=1;
               if(data[0]==data[k])frnz_str[k]=0;
               if(data[0]!=data[k])
                 {
                  Print("Time Gap was found: ",TimeToStr(data[0],TIME_DATE),"; ",TimeToStr(data[k],TIME_DATE));
                  continue;
                 }
              }

            for(k=0;k<str;k++)
              {
               data_all=data[0];
               oi_all+=oi[k];
               noncomm_long_all+=noncomm_long[k];
               noncomm_short_all+=noncomm_short[k];
               spreading_all+=spreading[k];
               operators_long_all+=operators_long[k];
               operators_short_all+=operators_short[k];
               all_long_all+=all_long[k];
               all_short_all+=all_short[k];
               nonrep_long_all+=nonrep_long[k];
               nonrep_short_all+=nonrep_short[k];
              }
            FileWrite(h_rezult,ConvertName(s_enumeration),
                      TimeToStr(data_all,TIME_DATE),
                      oi_all,
                      noncomm_long_all,
                      noncomm_short_all,
                      spreading_all,
                      operators_long_all,
                      operators_short_all,
                      all_long_all,
                      all_short_all,
                      nonrep_long_all,
                      nonrep_short_all);
            //Print(TimeToStr(data_all,TIME_DATE),";  ",oi_all);
            data_all=0;
            oi_all=0;
            noncomm_long_all=0;
            noncomm_short_all=0;
            spreading_all=0;
            operators_long_all=0;
            operators_short_all=0;
            all_long_all=0;
            all_short_all=0;
            nonrep_long_all=0;
            nonrep_short_all=0;
           }

        }// End While(summation)

      // Zero results
      FileClose(h_enumeration);
      FileClose(h_rezult);
/*for(int j=0;j<str;j++){
         FileClose(h_file[j]);
         noncomm_long[j]=0;
         noncomm_short[j]=0;
         spreading[j]=0;
         operators_long[j]=0;
         operators_short[j]=0;
         all_long[j]=0;
         all_short[j]=0;
         nonrep_long[j]=0;
         nonrep_short[j]=0;
      }*/

      n=0;str=0;
     } //End While( file enumeration list)

  }// End Programm   
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string ConvertName(string str)
  {
   string str_cnv;
   int strlen=StringLen(str);
   int char;
   for(int i=0;i<strlen-4;i++)
     {
      char=StringGetChar(str,i);
      str_cnv=StringConcatenate(str_cnv,CharToStr(char));
     }
   return(str_cnv);
  }
//+------------------------------------------------------------------+

将报告按日期组合的程序 Meta COT Script Concatenates 使用类似的方式,但其算法更加简单。

#property copyright "Copyright © 2009, C-4, All Rights Reserved."#property link      "vs-box@mail.ru"extern string FilesList="CONCATENATE.ini";
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int start()
  {
   int    h_list,h_file,h_cont,h_rezult;   // File descriptors
   int    column;
   string s_list,s_file,s_cont,s_rezult,   // Strings with file names
   string_cont,string_cont_f;
   h_list=FileOpen(FilesList,FILE_READ|FILE_CSV);      // Open the file list
   if(h_list==-1){Print("File list ("+FilesList+") not found");return(0);}
   while(FileIsEnding(h_list)==false)
     {                  // Read it string by string
      s_list=FileReadString(h_list);                      // Each string is a filename
      Print(s_list);
      h_file=FileOpen(s_list,FILE_READ|FILE_CSV);         // Try open this file
      if(h_list==-1){Print("File "+s_list+" not found");continue;}  // If it does not exists
                                                                    // we skip it
                                                                    // s_rezult="COT - CONT - "+s_list;
      s_rezult=get_rezultname(s_list);
      h_rezult=FileOpen(s_rezult,FILE_WRITE|FILE_READ|FILE_CSV);
      while(FileIsEnding(h_file)==false)
        {
         s_file=FileReadString(h_file);
         //Print(s_file);
         h_cont=FileOpen(s_file,FILE_READ|FILE_CSV,';');
         if(h_cont==-1){Print("Data file "+s_file+" not found");continue;}
         else Print("Loading data file "+s_file+"...");

         while(FileIsEnding(h_cont)==false)
           {
            string_cont=FileReadString(h_cont);
            if(string_cont=="")continue;
            if(FileIsLineEnding(h_cont)==true)
              {
               string_cont_f=string_cont_f+";"+string_cont;
               FileWrite(h_rezult,string_cont_f);
               string_cont_f="";
               column=0;
              }
            else
              {
               //if(string_cont=="")continue;
               if(column==0)string_cont_f=string_cont_f+string_cont;
               else string_cont_f=string_cont_f+";"+string_cont;
               column++;
              }
           }
         FileClose(h_cont);
        }
      FileClose(h_file);
      FileClose(h_rezult);
     }
   return(0);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string get_rezultname(string fname)
  {
   int strlen=StringLen(fname)-1;
   int char;
   string str;
   for(int i=strlen;i>0;i--)
     {
      char=StringGetChar(fname,i);
      if(char=='.')
        {
         for(int b=0;b<i;b++)
            str=str+CharToStr(StringGetChar(fname,b));
         str=str+".csv";
         return(str);
        }
     }
   return("COT - CONT - "+fname);
  }
//+------------------------------------------------------------------+


4. 从理论到实践!

4.1 只要一分钟,商品交易者就做好了工作准备

现在是对所有内容进行总结的时候了。 期货市场主要有三组参与者:套期保值者、大型商业基金和散户。 第一和第二组是最资本化的。 散户仅控制市场的很小一部分,通常小于 10%,所以他们的活动不会影响到市场价格。 剩下的两组交易者数量上很少,但能量巨大,他们按照供求规律定义着两者的交互作用。

非商业交易者和经营者在本质上是对立的。 其中一组的活动几乎是另一组的映像。 CFTC 报告的第一个可能的应用是基于供求规律观察两组交易者之间的平衡。

相比市场上的其他任何人,套期保值者是信息更加灵通的交易者。 他们跟产品生产最为接近,所以他们对隐藏的市场趋势的了解比其他任何人更加提前。 因此,相对于其他市场参与者,他们始终是内幕人士。 这种幕后的信息使他们调整其套保水平,这会在 CFTC 报告上反映出来。 第二个可能的应用是观察经营者头寸的快速变化。

散户被认为是所有类别的交易者中消息最不灵通的一组。 通常,这意味着散户的活动由荒谬的传言、大众心理、贪婪和恐惧导致。 散户净头寸的剧烈变化通常反映了散户情绪在所有这些因素作用下的巨大改变,最好为当前情绪的反向转折做好准备。 第三个可能的应用是观察散户头寸的剧烈变化

CFTC 报告本身不能给出进入或退出市场的具体时机。 这一点对于所有的基本指标都是一样的。 你必须使用有效的技术分析方法以更加准确的确定进入和退出时机。


为了有效的使用 CFTC 报告信息,下面是交易系统应该具有的一些特性:

1. 即使在不使用 CFTC 过滤器的情况下,系统必须是获利或至少是不赚不亏的。

2. 系统不应该具有某些参数,这些参数的优化会改变交易系统的表现和最终获利。 系统的主要功能应该是确认交易信号。 辅助单元的开发不应该是策略开发的重点。

3. 系统应该是完全可逆的。 卖出信号必须是买入信号的映像。

4. 技巧应该是对当前局部趋势的反趋势。 CFTC 的卖出信号将在长期上涨趋势中出现或至少在主运动的强势反转时出现。 买入信号也跟基本趋势相反。

5. 技巧应该保证捕捉到趋势逆转点。 系统应该将所有的转折点视为进入市场的时点。 对于交易系统来说,这是最困难同时又是最重要的条件。 CFTC 信号很少出现,一些指标产生的信号一年不要超过 1-2 次。 如果在这些时刻技巧没有给出进入市场的信号,那么对 CFTC 报告的使用变得毫无意义。

6. 当使用 CFTC 报告信息时,主要的获利来仅来自于几次交易,交易期间很长,多达几个月。 相应的,交易策略应允许头寸的长期浮动。

7. 系统不应该利用某个市场的特性。 CFTC 报告可用于很多市场,所以系统应该同样好的应用于生成报告的所有市场。

对技术的最有价值的利用是作为过滤器使用。 如此,交易的数量和最大值会显著下降,而盈利的预期和最终利润则会提高。

现在我们来考虑需要关注的最重要的信号。 这种信号有两种:未平仓量水平和三种类别的交易者之一的净头寸。假设交易者为经营者。 我们将所有的报告信息归结为这两个参数,通过 3 年指数进行表达。

这消除了主观性,使得市场分析更加容易。 因此,买入的有利情形应该是经营者卖出量较低的冷淡市场。 冷淡的市场说明此时的未平仓量水平非常低,因此具有增长潜力。 我们还要找出与之对立的卖出情形:市场的热度很高(未平仓量水平很高),同时,经营者的卖出量非常大。

我们现在来看 JPY 指标以找出这些时刻。

图 4-1 JPY 期货的经营者的未平仓量和指数

蓝线显示了卖出的有利时机,橙线为买入时机。 当未平仓量足够大(绿线在上),而经营者做空(红线在下)时,是卖出日元的有利时机。 买入的道理相同:当未平仓量相对较低(绿线在下),而经营者做多(红线在上)时,是买入日元的有利时机。 该数据可以用于初步市场分析,不需要价格水平。 只有在找到有利的投机时间后,你才能利用技术分析找到更加合适的进入市场的时机。 下面的图表显示的是同一个指标,但加上了市场价格:

图 4-2 日元价格图表的经营者指数和未平仓量

可以看出,并非所有的卖出和买入信号都会导致巨大的获利。 很多甚至会导致严重的亏损。 但是,任何系统都应该使用强制止损,这是没有例外的。

图表上有些信号的值不会达到 20% 和 80% 的水平。 这些水平并非是 完美的。 每种市场都有自己的理想水平。 指标同样如此,没有一个对所有市场都理想的通用水平值。

现在,从市场作为交易者的交易记录的角度,我们来更加仔细的检验。 应该注意的是,很多市场具有定期的报告。 其中有些具有报告的市场存在了一段时间(几个月或几年),然后就永久消失了。 也有一些市场,早已没有交易,但它们的信息仍存于历史数据中。 从这些市场中,可以找到 60 个定期和清晰发布报告的市场。 下面是具有最完整的报告的市场(数据文件的名称,未标注 CSV 扩展名):

编号

工具

CSV 数据文件

商品
1瘦猪肉COT - LEAN HOGS - CHICAGO MERCANTILE EXCHANGE
2育肥牛COT - FEEDER CATTLE - CHICAGO MERCANTILE EXCHANGE
3活牛COT - LIVE CATTLE - CHICAGO MERCANTILE EXCHANGE
4牛奶COT - MILK CONCATENATE
5小麦 - 芝加哥交易所COT - WHEAT - CHICAGO BOARD OF TRADE
6小麦 - 堪萨斯州 堪萨斯市期货交易所COT - WHEAT - KANSAS CITY BOARD OF TRADE
7小麦 - 明尼阿波利斯谷物交易所COT - WHEAT - MINNEAPOLIS GRAIN EXCHANGE
8小麦 - 芝加哥交易所COT - OATS - CHICAGO BOARD OF TRADE
9稻谷COT - ROUGH RICE - CHICAGO BOARD OF TRADE
10白糖 COT - SUGAR CONCATENATE
11咖啡 COT - COFFEE CONCATENATE
12可可 COT - COCOA CONCATENATE
132 号棉花 COT - COTTON 2 CONCATENATE
14豆粕COT - SOYBEAN MEAL - CHICAGO BOARD OF TRADE
15小型大豆COT - MINI SOYBEANS - CHICAGO BOARD OF TRADE
16豆油COT - SOYBEAN OIL - CHICAGO BOARD OF TRADE
17大豆COT - SOYBEANS - CHICAGO BOARD OF TRADE
18黄油(现金结算)COT - BUTTER (CASH SETTLED) - CHICAGO MERCANTILE EXCHANGE
19冷藏浓缩橙汁 COT - FRZN CONCENTRATED ORANGE JUICE CONCATENATE
20木材 COT - LUMBER CONCATENATE
金属
21铜 - 等级 #1COT - COPPER-GRADE #1 - COMMODITY EXCHANGE INC.
22白银COT - SILVER - COMMODITY EXCHANGE INC.
23GOLDCOT - GOLD - COMMODITY EXCHANGE INC.
24白金COT - PLATINUM - NEW YORK MERCANTILE EXCHANGE
25COT - PALLADIUM - NEW YORK MERCANTILE EXCHANGE
货币
26澳元COT - AUSTRALIAN DOLLAR CONCATENATE
27加拿大元COT - CANADIAN DOLLAR CONCATENATE
28英镑COT - BRITISH POUND STERLING CONCATENATE
29欧元期货COT - EURO FX CONCATENATE
30日元COT - JAPANESE YEN CONCATENETE
31墨西哥比索COT - MEXICAN PESO CONCATENATE
32 新西兰元COT - NEW ZEALAND DOLLAR - CHICAGO MERCANTILE EXCHANGE
33瑞士法郎COT - SWISS FRANC CONCATENATE
34 俄罗斯卢布COT - RUSSIAN RUBLE - CHICAGO MERCANTILE EXCHANGE
35美元指数COT - U.S. DOLLAR CONCATENATE
指数
36道琼斯工业指数COT - DOW JONES INDUSTRIAL AVERAGE - CHICAGO BOARD OF TRADE
37道琼斯工业指数 x5$COT - DOW JONES INDUSTRIAL AVG- x $5 - CHICAGO BOARD OF TRADE
38E-迷你标准普尔 400COT - E-MINI S&P 400 STOCK INDEX - CHICAGO MERCANTILE EXCHANGE
39E-迷你标准普尔 500COT - E-MINI S&P500 CONCATENATE
40标准普尔 500 证券指数COT - S&P500 STOCK INDEX CONCATENATE
41标准普尔 400 中盘股证券指数COT - S&P400 MIDCAP STOCK INDEX CONCATENATE
42NASDAQ-100COT - NASDAQ-100 STOCK INDEX CONCATENATE
43纳斯达克-100 迷你COT - NASDAQ-100 INDEX (MINI) CONCATENATE
44日经指数COT - NIKKEI STOCK AVERAGE CONCATENATE
45罗素指数-2000 迷你COT - RUSSELL 2000 CONCATENATE
债券工具
461 年期利率互换COT - INTEREST RATE SWAPS 10YR CONCATENATE
475 年期利率互换COT - INTEREST RATE SWAPS 5YR CONCATENATE
48美国国库债券COT - U.S. TREASURY BONDS - CHICAGO BOARD OF TRADE
4910 年期美国国库券COT - 10-YEAR U.S. TREASURY NOTES - CHICAGO BOARD OF TRADE
505 年期美国国库券 COT - 5-YEAR U.S. TREASURY NOTES - CHICAGO BOARD OF TRADE
512 年期美国国库券COT - 2-YEAR U.S. TREASURY NOTES - CHICAGO BOARD OF TRADE
52月伦敦同业拆借利率COT - 1-MONTH LIBOR RATE CONCATENATE
5330 天联邦基金COT - 30-DAY FEDERAL FUNDS - CHICAGO BOARD OF TRADE
543 个月欧洲日元COT - 3-MONTH EUROYEN TIBOR CONCATENATE
553 个月欧洲美元COT - 3-MONTH EURODOLLARS CONCATENATE
能源
56低硫轻质原油COT - CRUDE OIL LIGHT SWEET - NEW YORK MERCANTILE EXCHANGE
57天然气COT - NATURAL GAS - NEW YORK MERCANTILE EXCHANGE
582 号 2 HEATING OIL N.Y. 港COT - NO. 2 HEATING OIL N.Y. HARBOR - NEW YORK MERCANTILE EXCHANGE
波动指数
59ISO 新英格兰 LMP 互换COT - ISO NEW ENGLAND LMP SWAP - NEW YORK MERCANTILE EXCHANGE

表 4-3 长时间定期发布报告的工具列表

可以看到,委员会目前收集的数据覆盖面很广,包含农产品、货币、金融和其他市场。 表格中描述的所有文件将会在运行 Meta COT Script BuildMeta COT Script Concatenate脚本获得。

4.2 创建研究机器人


CFTC 报告的效度研究是一个艰难的任务,需要技术领域的真实有效的开发以及多工具的优化。 很多有经验的交易者具备这个能力,但那时他们自己的专业能力,不对外公开。 技术分析是一个宽广的领域,超越了本文的研究范围,因此我们将限定在可用的技术指标上,尽管原始但却广为人知的市场分析的技术方法。

我们将创建一个简单的研究决策机器人,在分析交易者报告的基础上确定进入市场的时机。 如果你是 MQL4 的开发人员,可以轻松使用 CFTC 数据作为基于技术分析的交易策略的补充过滤器。 如果你不是开发人员,但对该技术感兴趣,你也会发现这种机器人的可用性是非常宝贵的。

使用策略优化程序可以选择指标及其优化的平均周期。 实践显示,对于不同的市场,最为获利和准确的指标是那些基于不同交易者组的动态的指标。 或许,对于某些市场,非报告交易者的总净头寸的使用比较有效,而对于其他市场,经营者的活动最为有效。

所以,开始时,让我们求助于技术分析来找到更加准确的市场进入时机。 拉瑞 威廉姆斯在其书中建议使用基于移动平均线的经典技术分析。 我们按照他的说法,生成两条简单移动平均线,其中一条基于长期趋势,另一条基于短期趋势。 我们将长期移动平均线应用于周线图,将短期移动平均线应用于日线图。 慢速平均线周期等于指标的计算,快速平均线为常量,等于 5 天。

如果慢速平均线当前值大于其两周前的值,则长期趋势向上,反之则向下。 我们用同样的方法来分析 5 周期移动平均线,如果当前值大于两天前的值,我们预期会出现短期上涨趋势,反之则是短期下跌趋势。 一旦短期趋势线和长期趋势线重叠,我们将着手解决。 我们将使用 Meta COT 的指标之一作为过滤器。 只有在指标处于当前有利值的范围时,才会进行建仓。

看一下图 4-2:

图 4-4 建仓的技术分析信号

图 4.4 给出了 EURUSD 的日线图。 左上角显示了 EURUSD 在同一时期周线图的一部分。 长期简单平均线(蓝线)的周期跟 COT 经营者指数指标相同,本例中为 26 周。 短期平均线(红线)始终为 5 个周期。 可以看出,在 2006 年 10 月 15 日,红线的平均值(Ma1)大于两天前的平均值(MA2),同时,长期平均值大于两周前的平均值。 趋势是相同的,经营者指数指标也达到 100% 的水平,说明这是一个很好的买入机会(或严格地讲,该条件跟前一天一致,旦我们使用该组合进行图解说明)

机器人使用止损和获利值执行操作(这是唯一可能的退出点)。 这些值的大小取决于周期指标,周期越长对应的止损和获利值就越大。 指标的 26 个周期内,就有 100 个止损点和 300 个获利点。 例如,对于 156 周的平均周期,我们将使用等于 600 点的止损值和 1800 点的获利值(这是简单的估算)。 指定的获利和止损水平是平仓的唯一方法,我们可以假设交易系统彻底完成。

头寸的大小是固定的,等于工具合约的 0.1。

虽然说系统不算原始,但也谈不上完美。 一般来说,它是走向失败的方式,但如果使用 Meta COT 过滤器,事情是可以改变的。

现在我们来看一下机器人的源代码。 它的运行跟指标非常类似。 机器人并非是只知道如何下订单的指标。 在初始化过程中,机器人为当前图表加载数据(cotlib.mq4 库),计算所有指标的值,然后从其取一个值。 所选的指标将取决于 indicator_type 整数,如果我们将其设置为 17,说明它将使用经营者的 WILLCO 指标,如果将其设置为 22,说明它将是非报告经营者的指数。

找到指标编号很简单,它们列在 cotlib.mq4 库的开始部分。 为了定义平均周期,需要设置指标的 period 的值。 指标的选择及其周期使用策略优化程序来组织。 两个值都标记为要优化的值。 周期值从 26 开始,步长为 26,终值为 156。 indicator_type 值从 16 开始,步长为 1,终值为 26。 利用这种方法,我们将尝试所有的指标和不同的平均周期。


图 4-4 Expert 输入参数窗口

一些指标未包含在历史回测中。 这些指标包括没有明确的水平的指标,例如经营者的净头寸或绝对头寸,以及指标值必须根据市场进行选择的指标,例如交易者的多头头寸除以未平仓量的商。 无论如何,这些指标足以用来评估其有效性。 如果要用来构建真实的交易机器人,则所考虑的策略远非完美。 但是,该交易系统示例将帮助你利用项目提供的大量信息,并在提供的方法之中找到最有效的一个。 表 4.5 显示了所用指标的最佳测试结果。

下表中所列的结果非常接近,测试的主要目的是找到最适宜的指标,而不是将利润最大化。 需要注意的是,最佳结果并不意味着获利最多。 最佳结果由交易数量(越多越好)、最大亏损(越少越好)、获利能力和最终获利决定。

你可以在你的计算机上进行回测。 表格中包含了以下工具集的历史数据回测:

工具

指标

计算周期

交易总数

获利系数

数学 预期

亏损 %

利润 $

欧元

经营者指标

26

77

1.79

62.31

12.57

4798

英镑

非商业 WILLCO 指标

26

140

1.44

18.64

7.12

2600

日元

非报告交易者指标

52

35

1.37

63.29

14.85

2200

瑞士法郎

经营者运动指标

26

21

3.96

159.71

4.63

3354

澳元

非报告交易者指标

26

59

1.98

57.50

5.67

3392

新西兰元

非商业交易者指标

26

20

1.97

59.00

4.13

1180

美元指数

非报告交易者运动指标

26

144

1.20

1.50

1.98

216

GOLD

非报告交易者指标

26

81

1.84

52.09

5.70

4219

白银

WILLCO 算子

26

165

1.85

26.58

4.76

4385

白金

非报告交易者指标

26

233

1.77

24.54

6.73

5717

非商业交易者指标

26

142

1.23

8.15

10.61

1158

白糖

经营者 WILLCO 指标

26

18

1.89

61.22

6.67

1102

可可

经营者指标

26

29

1.96

56.59

6.08

1641

咖啡

经营者指标

52

113

1.38

19.58

7.59

2212

燕麦

经营者指标

26

15

2.24

83.50

8.25

1252

小麦

未平仓量指数指标

26

53

1.66

53.25

8.91

2822

大豆

非商业交易者指标

26

66

2.88

118.72

6.46

7965

橙汁

经营者指标

26

29

2.75

68.83

2.73

1996

<低硫轻质原油

经营者指标

26

194

1.23

16.53

12.68

3206

E-迷你标准普尔指数

经营者运动指标

26

32

2.01

75.21

6.66

2406

表 4-5 固定手数为 0.1 的 COT 过滤器的最佳历史数据回测结果


指标的测试结果非常有趣。 首先,对于非报告交易者指数和 WILLCO 指标,向获利区有着明显的偏移。 运动指数指标的表现更加不确定。 对于某些工具,它获得了最大的利润(通常是为经营者计算的指标),对同一指标的某些值则产生巨大亏损。 非商业交易者使用运动指数指标在所有工具上导致了巨大的亏损(图 4-6 的 No.43 点)。 或许,对该现象的深入研究可以使我们利用其信号。

优化图表可以表达如下:



图 4-6 指标检验及其参数有效性的基本规划

红线对应着获利能力为零的水平。 从点 43 开始获利开始变得愈加不稳定。

一般来说,最大的利润是通过使用指标的长平均周期获得(104-156 周),但由于交易数量很少,它们未包含在报告中。 长平均周期的指标非常准确,使用也非常有效,尤其是在数十个市场上同时使用。 尽管经营者和非商业交易者具有镜像般的相似性,但一些市场偏好使用经营者的头寸,而另一些市场偏好使用非商业交易者的头寸。 散户的活动也很有趣。

值得注意的是,在黄金市场长跟追随经营者相比,采取和散户相反的操作带来更多的利润。

尽管 WILLCO 指标包含了未平仓量,并且非常有效,但未平仓量本身没那么有效。 最好的情况是,它的使用带来较少的利润。 未平仓量的应用被视为一个补充信号,而不是它的主要信号。

一般来讲,这些指标已经证明了自己的有效性,如果将其整合到良好的交易系统中,无疑会带来更加丰厚的利润。

下面给出了典型的欧元优化图表:

可以看出,图中有向获利区的明显偏移(红线以上)。 并非所有图表都有这么明显的位移,由于一些技术原因,其中很多都无法适当的进行测试。

现在我们来比较过滤器自身的有效性。 为此,我们将分别在具有 COT 过滤器和不具有 COT 过滤器的同一个价格图表上进行测试。 我们将使用欧元期货作为工具。

没有 COT 过滤器的交易系统的交易结果显示在图 4.8 中:

图 4-8 没有 COT 过滤器的交易结果

可以看出,最终利润为 1897 美元,大致等于使用手数总量 0.1 的相同的点数。 同时,最大亏损为 30%,不得不说是非常大的。 我们来看一下图 4-9:

使用的策略相同,但经过了周期为 26 周的经营者指数值的过滤:

图 4-9 使用 COT 过滤器的交易结果


可以看出,交易数量大幅下降,获利的数学预期增加。 最终利润比第一次测试并没有高多少,但最大亏损的水平却发生了很大的变化。 现在它变成 8.6%,比之前的水平小得多。 毫无疑问,COT 过滤器非常有效,可以改善策略。

为了确定使用 CFTC 信息的最大有效性,需要进行详细的调查,本文无法完成这项任务。 这部分的主要任务是,阐明基本内容,使用简单的示例表明利用 CFTC 数据的效果非常明显。

而在真实交易中是否采用该方法,则取决于你。


4.3 结语和续篇

如果你现在对这个话题仍然没有问题,则说明你没有认真读这篇文章,或者对讨论的概念不感兴趣。 我希望,此时你有很多问题。下面给出了其中一些问题的答案。 实际上,这只是简短的常见问题及其回答,利用这些概念可以帮助你解决很多问题。


1. 我是否应该将所描述的交易系统的建仓逻辑应用到实践中去?

当然不能。 你可以轻松的找到更适宜的技巧来确定进入市场的准确时机。 或许你已经在技术分析领域拥有了自己的经验。 尝试将 CFTC 过滤器应用到交易策略中。 你可以将自己的头寸管理技巧添加到 Meta COT 研究机器人中。

2. 观察和跟随哪一组交易者的头寸更好一些?

没有确定的答案。 使用 Meta COT Expert 你可以发现哪一组交易者在特定的市场上最为有效。 尝试在每种市场上使用三组交易者,然后选择一个预测较为准确的组。

3. 使用哪个指标更好一些?

没有确定的答案。 初步测试显示,WILLCO 或运动指数指标是非常不错的选择。 再次强调,必须选择的指标有两个特点,第一,跟市场高度关联,第二,跟使用的交易策略配合良好。 无论如何,研究机器人必须确定交易系统提供的最适宜的指标。

4. 最好选择哪种时间周期?

没有确定的答案。 对于短期交易者来说,最好的选择可能是 26 平均周期的指标或运动指数指标。 对于希望维持头寸的交易者来说,具有较长计算周期的指标更加适合。 跟其他 26 周期的计算非常不同。 较长的周期之间没有差别。 另外,不要使用少于 26 周的计算周期。 即使上半年的指标,也非常不稳定和极度紊乱。 理想的时间周期根据市场而不同,它们应该跟所选的交易策略良好关联。 可以通过研究机器人的暴力计算找到理想的计算周期。

5. 指标的临界水平在哪里?

拉瑞 威廉姆斯在其书中展示了不同临界水平的图表。 一些图表的临界水平为 25%-75%,而其他则为 20%-80%。 在某些市场上,有效临界水平或许更宽,而在另一些市场上则更窄。 无论如何,有效水平将处于0-30% 和 70%-100% 的范围。 你可以使用研究机器人确定理想的水平。 创建两个变量,分别具有较低和较高的临界水平。 然后对这些变量进行优化。 尝试改变它们的值,以找到理想的结果。

6. 为什么在某些市场上,经营者(套保基金、散户)有异常的表现?

某组交易者的异常表现的一个可能原因在于其内部结构以及它们在商品市场上交易时解决的问题。 例如,套期保值者在黄金市场上的结构和任务跟在外汇市场上的任务可能大不相同。 总之,你应该使用最有效的交易者群体。

7. 我想使用 Meta COT Aggregation 将多个工具组合为一个,但我不确定合并是否正确。 如何正确的检验是否成功组合了工具?

首先,你需要将产生的文件下载到指标,以直观确定市场转折点的值。 应该坚持基本的原则:在市场下跌之前,经营者应该具有最高的卖出水平,而其他人此时必须可能买入。 与之相反的情形是市场处于底部。 如果直观检查显示了正确的值,则这些值可以在研究机器人中进行测试。 如果结构显示良好,超过了使用标准报告而获得的结果,则组合应视为可靠,可以在交易中使用。

8. 报告在某些市场上不适用。 所有的指标都给出负面的结果。

在这些市场上,尝试使用没有 CFTC 过滤器的交易系统。 然后比较结果。 如果没有过滤器的系统比有过滤器的系统表现差距较大,可能是系统本身有问题。 在这种情况下,你需要改善自己的交易系统,至少达到不赚不亏,然后添加 CFTC 过滤器并再次测试。 尝试、测试和优化 - 这是查找和解决问题的一般方法。

9. 我无法找到市场的报告。

报告并非面向所有市场。 可能你选择的市场没有整理报告。 可以尝试在本文第四部分所给出的表格中查找所需的市场。 如果表格中没有,那很可能这种市场报告不存在,或它们不完整而无法使用。 如果市场出现在表格中,则查看跟其名称对应的报告文件。 手动加载该文件至指标。

10. 指标(脚本、自动交易系统)不工作。 我该怎么办?

你应该更加认真的阅读本文的第三部分。 很可能你忘记了某些操作或者做法不正确。 请认真阅读并尝试自己解决问题。

11. 不显示指标。

主要原因是报告文件缺失。 确认报告文件是否已经下载。 为此,打开“终端”,选择“日志”选项卡并再次下载指标。 如果指标没有找到报告文件,你会收到警告信息: “下载 File_name.scv 文件出现错误。 无法继续运行”。 前往终端的 \files 目录,检查所需的文件是否存在。 如果文件不存在,使用批处理程序 Erase_cot.bat 删除所有的记录,然后重新创建。 所需的文件应该会显示。

12. 指标能够显示,但跟指定的不同。 显示的不是指标名称,而是“未找到工具”。

很有可能是当前工具的名称跟所有报告都不匹配。 打开 settings.ini 文件,找到应该自动加载的报告。 如果你没有找到报告,则自己创建设置(参阅章节 3.4)。 将工具名称添加到文件列表。 重新启动指标,现在应该没问题。

13. 自动交易系统不交易。

该问题的可能原因是,自动交易系统所需的报告文件未复制到 \tester\files 目录。 策略测试程序使用该目录,所以它需要那里有相应的文件。 将包括配置文件在内的所有文件复制到 \tester\ 文件目录。 现在自动交易系统应该可以正常工作了。

14. 是否可以基于 Meta COT 创建我自己的指标?

肯定可以。 你所需要的主要函数是 get_data (int type, int bar)。 它返回由类型参数指定的指标的值。 文章的第三部分有很多关于主代码和示例的信息。

15. 如何创建一个投资组合的自动交易系统?

我们还没有提供多工具的交易功能。 你必须开发自己的方法,进行数据下载。 或者,你可以进行有序文件加载,这种情况下,数据可以存储在每个工具的单独部分。 但所有这些都需要进一步的开发。

16. 脚本的运行没法进行设置,每次启动脚本前都显示设置窗口,尽管并不需要。

有一个 #property 指令,负责定义设置的调用。 如果你需要更改设置,就对其进行注释。 相反,如果你需要自定义设置,则移除注释。 然后重新编译脚本。

17. 即便读了文章之后,我还有很多问题...

你可以多读几遍拉瑞 威廉姆斯的书籍:Trade Stocks & Commodities with the Insiders: Secrets of the COT Report。 CTFC 报告分析是一个很大的话题。



参考文献

1. 拉瑞 威廉姆斯 Trade Stocks & Commodities with the Insiders: Secrets of the COT Report (Wiley Trading)

2. Stephen Briese The Commitments of Traders Bible

3. Campbell R. McConnell, Stanley L. Brue Economics: Principles, Problems, and Policies, 16th edition

4. Todd Lofton Getting Started in Futures

5. http://www.procapital.ru/forumdisplay.php?f=267 关于 CFTC 报告分析应用的讨论(俄语)。

6. http://www.aup.ru/articles/finance/1.htm 经济活动中风险的概念,文章作者是 Valery Romanov。

7. http://www.timingcharts.com 具有 COT 图表的网站。

8. http://www.ireallytrade.com 拉瑞 威廉姆斯的网站。 它包含关于 CFTC 报告的最新信息。 在其主页上,拉瑞 威廉姆斯给出了关于最近交易机会的有用建议。

9. http://commitmentsoftraders.org Steve Briese 的网站。 大多跟他的有关。 它包含了有用的分析和其他指标,基本为 PDF 格式。

10. http://cftc.gov 美国商品期货交易委员会网站。 它包含关于交易者头寸的最新数据,是进行 COT 分析的主要信息来源。