引子
我是一个码农,一个程序员,一个开发者。1998年上大学,计算机科学与技术专业,毕业后一直工作在软件开发行业一线。十几年之后的2016年,阴差阳错开始接触外汇交易行业,尝试使用码农的方法开始追求财务自由。
市场上几乎所有的外汇交易书籍都会强调“交易系统”的重要性。想实现稳定收益,必须要遵守交易纪律,完全按照事先制定好的交易系统执行。在交易中不要思考,不能临时改变主意,越是机械,越是死板越好。但是建立这个“系统”却需要交易者常年累月的经验积累和对系统的不停改进。我是外行,没有经验;也没有那么多时间和精力去操作1年以上的模拟盘来犯那些该犯得错误。而且,我现在还有一份全职的工作,没法每天看盘几个小时。所以,只能走另外的路径。
我用工具,我写程序,我把从别人书里学到的系统写成EA,跑历史数据验证。从验证中,我找到入市信号、途中头寸管理(加减仓)、出场点;学习、分析、理解各个系统原型的原理、优缺点,适应状况。
学习MT4和MQL4时,我参考了两本书。一本是 《MetaTrader4 外汇自动交易圣经》Dave C 王彤 刘斌 编著,中国经济出版社出版。另一本是《Expert Advisor Programming -- Creating Automated Trading Systems in MQL for MetaTrader 4》 Andrew R. Young , EDGEHILL PUBLISHING。
中文的那一本看起来很像是英文版的翻译和改编,目录章节都是基本一模一样。可能这也是作者特意表明 “编著”的原因。
关于MQL4 和 MQL5
从MQL4 build 600 之后,MetaQuest对MQL4做了巨大修改,(也许应该叫MQL4.5?)引入了很多MQL5的内容(Python3在一边看着,不说话),并且把MetaEditor也就是MQL的IDE进行了统一。
我上手晚,用的是MetaEditor version 5.0 build 1351, 04 Jul 2016。在实际中发现build 600之后的与上面书里中一些内容对不上号,比如那四个事件触发函数。本文全文使用的都是build600之后的版本,在2016年以后应该可以算是开箱可用。
MT4基本介绍
外汇散户使用最广泛的是MetaTrader4 软件平台。这个平台上有自己的MQL4语言来于系统交互,执行用户制定的逻辑判断、分析算法和交易动作。
MT4其实和各种“专业”软件很相似,如果你使用过工程软件(比如AutoCAD、IC Designer等等),并且为其写过扩展程序,就更容易掌握这种思想。这些软件都是在系统平台里集成了一门语言、解释器,依赖该语言的能力处理逻辑、计算以及文件读写、网络通信等通用功能,再把自己平台的功能细化并封装成各个函数“命令”供用户调用,给用户二次开发和实现复杂功能的能力。
MetaTrader4 是windows下的软件,在Mac OS X下使用PlayOnMac和Wine来模拟运行环境,已经很成熟,官方网站上有安装说明,可以放心使用。
MT4主要有几个模块:(可以再Menu View里打开和关闭)
- 导航栏 -- Navigator
- 图表窗口 -- Chart
- 数据窗口 -- Data Window
- 控制终端窗口 -- Terminal
- EA测试器 -- Strategy Tester
在开发时,用的最多的是 Navigator和 Tester, 很多时候基本不看其他窗口。
除此之外,还有一个MQL4的IDE -- MetaEditor。
和市场上的大部分IDE相同,MetaEditor也把屏幕切分成上有Menu和Toolbar,左有Navigator,中有Editor,下有Console(Toolbox)四部分。Editor支持语法高亮,自动补全等等功能。
MQL4是一门类似C的语言,或是说是一种扩展了的C语言,有基础的同行很容易上手。但是对于专业码农,这MQL却在空格缩进、变量、函数名大小写这种代码风格上很值得吐槽。我忍,谁叫这是为了赚钱做生意而不是追求码农的自我修养呢。
在Editor里编写完成EA程序后,需要compile成可执行文件,系统后缀为.ex4。屏幕下方Console里的Errors Tab会显示编译结果,有没有error或warning。之后就可以在MT4中使用这个EA了,不论是应用在图表交易上还是进行历史数据测试。
MT4 MQL 文件目录
作为约定MT4会把所有的开发EA相关文件放在它自己安装目录(以下用MT4_HOME代替)下的mql4目录下。如果你的EA有读写文件操作,请在MT4_HOME/tester/files下寻找。
MQL 文件类型
- EA
可以附在图表上进行,监听系统产生的Tick事件,被调用而进行交易。 - Indicator 指标
附在图标上,监听系统Tick,但是不能下单?TODO - Script
一次性执行的脚本程序,由OnStart() 触发。
再看看,还不会 TODO
MQL4 简单文件结构
- 头部变量定义
-- #property
-- #include
-- #input
EA的各个输入参数,在MQL中是全局变量。在之前的版本中使用extern 关键字定义 - 函数
-- int OnInit() 构造函数?
-- void OnTick() 事件循环?
-- void OnDeinit() 析构函数?
-- void OnTimer() 时间触发?
-- double OnTester() Tester最后汇总用?
-- void OnChartEvent() ?
-- void OnStart() 不是给EA用的,而是给一次性运行的script使用。
对于经验丰富的码农来说,这几个函数全是callback,或是说系统里定义好的interface,留给我们自己实现。对应的事件触发时,会执行相应的函数。
void OnStart() 函数
Script启动时调用,如果你的EA里含有这个函数,在新版的MT4中,会提示 “xx” is not expert and cannot be executed.
int OnInit() 函数
在程序初始时执行,可以进行全局变量的初始化等动作。在老版本的MQL4中,函数名为 int init(),现在为了和MQL5兼容,把几个主要函数名都加入了On打头。
下面例子里显示了一些市场信息内部变量,这是我们的交易中都必须使用的,具体会在后面介绍。
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- create timer
EventSetTimer(60);
Print("Balance: ",AccountBalance());
Print("Equity: ",AccountEquity());
Print("TickValue: ",MarketInfo(Symbol(),MODE_TICKVALUE));
Print("Lot Size: ",GetSize());
Print("MINLOT: ",MarketInfo(Symbol(),MODE_MINLOT));
Print("MAXLOT: ",MarketInfo(Symbol(),MODE_MAXLOT));
Print("Symbol name of the current chart=",_Symbol);
Print("Timeframe of the current chart=",_Period);
Print("The latest known seller's price (ask price) for the current symbol=",Ask);
Print("The latest known buyer's price (bid price) of the current symbol=",Bid);
Print("Number of decimal places=",Digits);
Print("Number of decimal places=",_Digits);
Print("Size of the current symbol point in the quote currency=",_Point);
Print("Size of the current symbol point in the quote currency=",Point);
Print("Number of bars in the current chart=",Bars);
Print("Open price of the current bar of the current chart=",Open[0]);
Print("Close price of the current bar of the current chart=",Close[0]);
Print("High price of the current bar of the current chart=",High[0]);
Print("Low price of the current bar of the current chart=",Low[0]);
Print("Time of the current bar of the current chart=",Time[0]);
Print("Tick volume of the current bar of the current chart=",Volume[0]);
Print("Last error code=",_LastError);
Print("Random seed=",_RandomSeed);
Print("Stop flag=",_StopFlag);
Print("Uninitialization reason code=",_UninitReason);
//---
return(INIT_SUCCEEDED);
}
void OnTick()
这是MT4 事件循环触发的最主要函数,市场中的每个Tick事件,都会触发该函数。我们的所有市场交易逻辑都在该函数中执行。
基本上的逻辑是,每个Tick时,相应的市场信息比如Bid,Ask,过去的bar数据,各种指标都会有相应更新。我们的逻辑会按照“交易系统”的逻辑去分析现在市场情况,分析自己持仓情况,做出下列决定:1)买n手;2)卖n手;3)修改头寸的Stop、Limit价;4)不动。
这里需要注意的是,每个Tick的时间很短,一个tick之后马上就是下一个tick。我们的函数运行时间有很严格的限制,如果运行过慢,在下一个tick进来的时候还没有结束,会出现跳过该tick的后果。
对于单张图表,MT4“看起来”是串行工作。处理完OnTick后,才会触发下一个Tick。
这种串行工作方式乍看起来会损失很多精度,但是从模拟人类的角度看起来也没什么问题。只要不是高频交易,这点时间比起人类的判断反应时间上要快的多。只是不能对相邻两个Tick做假设,因为它们的时间间隔是不固定,不保证的。
对于多个图表上同时运行的EA,很遗憾他们也是非并行运行。而是轮换的方法抢夺运行时的权限。
最好把OnTick函数想象成HTTP的stateless 模型。每次只从DB中拿出状态变量,重新判断局势然后做出反应。
void OnDeinit()
这个函数是EA结束时被执行,可以近似成析构函数,进行内存释放、关闭文件句柄等clean up 任务。
识别市场情况
一个系统主要由三部分组成,入市,头寸,出场。在市场的每个Tick到来时,都要首先分析市场的现状。在手工分析和交易时,交易者会把主要精力放到观察图表上,根据图表形状,技术指标来确定交易行为。
对于一个自动化交易的EA,第一步也是要取得市场上的所有信息,MQL4提供了MaketInfo()函数和一组预定义内部变量供我们使用。
MarketInfo()
double MarketInfo(string symbol, int type)
type 包括
MODE_POINT
MODE_DIGITS
MODE_SPREAD
MODE_STOPLEVEL
MODE_BID
MODE_ASK
MODE_LOW
MODE_HIGH
MODE_TIME
MODE_TICKSIZE
MODE_TICKVALUE
MODE_EXPIRATION
MODE_FREEZELEVEL
MODE_STARTING
MODE_STOPLEVEL
MODE_SWAPLONG
MODE_SWAPSHORT
MODE_SWAPTYPE
MODE_LOTSIZE
MODE_LOTSTEP
MODE_MAXLOT
MODE_MINLOT
MODE_CLOSEBY_ALLOWED
写个函数把它们都打出来看看
void report_marketinfo()
{
Print("START");
Print(MarketInfo(_Symbol,MODE_ASK)); // ask
Print(MarketInfo(_Symbol,MODE_BID)); // bid
Print(MarketInfo(_Symbol,MODE_CLOSEBY_ALLOWED)); // 0
Print(MarketInfo(_Symbol,MODE_DIGITS)); // 3 for USDJPY
Print(MarketInfo(_Symbol,MODE_EXPIRATION)); // 0
Print(MarketInfo(_Symbol,MODE_FREEZELEVEL)); // 0
Print(MarketInfo(_Symbol,MODE_HIGH)); // 0
Print(MarketInfo(_Symbol,MODE_LOTSIZE)); // 100000
Print(MarketInfo(_Symbol,MODE_LOTSTEP)); // 0.1
Print(MarketInfo(_Symbol,MODE_LOW)); // 0
Print(MarketInfo(_Symbol,MODE_MARGINCALCMODE)); // 0
Print(MarketInfo(_Symbol,MODE_MARGINHEDGED)); // 10000
Print(MarketInfo(_Symbol,MODE_MARGININIT)); // 0
Print(MarketInfo(_Symbol,MODE_MARGINMAINTENANCE)); //0
Print(MarketInfo(_Symbol,MODE_MARGINREQUIRED)); // 1553.84
Print(MarketInfo(_Symbol,MODE_MAXLOT)); // 50
Print(MarketInfo(_Symbol,MODE_MINLOT)); // 0.1
Print(MarketInfo(_Symbol,MODE_POINT)); // 0.001
Print(MarketInfo(_Symbol,MODE_PROFITCALCMODE)); // 1
Print(MarketInfo(_Symbol,MODE_SPREAD)); // 7
Print(MarketInfo(_Symbol,MODE_STARTING)); // 0
Print(MarketInfo(_Symbol,MODE_SWAPLONG)); // 0
Print(MarketInfo(_Symbol,MODE_SWAPSHORT)); // 0
Print(MarketInfo(_Symbol,MODE_SWAPTYPE)); // 0
Print(MarketInfo(_Symbol,MODE_TICKSIZE)); // 0.001
Print(MarketInfo(_Symbol,MODE_TICKVALUE)); // 0.9980836
Print(MarketInfo(_Symbol,MODE_TIME)); // looks like cpu time, in
Print(MarketInfo(_Symbol,MODE_TIME)); // 1470182399 system epoch time, in second, use http://www.epochconverter.com/ to convert
Print("END PRINT");
//
}
预定义变量
https://docs.mql4.com/cn/predefined
_Digits
_Point
_LastError
_Period
_RandomSeed
_StopFlag
_Symbol
_UninitReason
Ask
Bars
Bid
Close (array)
Digits
High (array)
Low (array)
Open (array)
Point
Time (array)
Volume (array)
其他函数
AccountBalance()
AccountEquity()
如何使用MQL4分析现在的图形
识别单根K线
在交易上我是纯外行,对K线战法只有书本上的认识。下面争取用MQL来识别《外汇交易实战全典》书中提到的几种K线形态。
正常性
大阴线、阳线 (光头光脚)
OCHL
网友评论