本节的主题是简单“操盘手”的创建原则。
一般要求
1、深思熟虑,容易理解;
2、首先对现有定单做会计处理;
3、对将要建仓、平仓的定单,做价格计算;
4、设置交易条件;
5、有处理出错机制。
“操盘手”程序结构
下面是个简单“操盘手”的结构示例。
图. 109. 简单"操盘手"的结构
交易策略
通常,市场价格状态以横盘为主,涨跌的趋势变化,只占15-20% 的时间段。
图. 110. 横盘和涨跌趋势
因此,交易策略相应地分为两类,一是针对横盘的,二是针对趋势的。MQL4 拥有相关的对策方法。
交易条件
我们的例程,是个处理趋势变化的“操盘手”,它按价格变化建仓,它的交易条件是不同周期平移指标MA的交叉(见图111和图112)。
图. 111. MA(11) 和 MA(31) 交叉表示的价格方向变化
若在A点红线上穿蓝线时买进,并在B点红线下穿蓝线时卖出,会有盈利。
图. 112. 价格变化时MA(11) 和 MA(31) 的交叉
若在A点开出卖单,并在B点平仓,会发生交易亏损。而若在B点买进,并在C点平仓,也会亏损。
交易策略成败关键,是识别趋势与横盘。MA指标必须结合其他信号使用。
短期的价格突变,会引导价格的中期走势。
图. 113. 强劲的价格变动形成趋势
若以不同MA指标在同一时间点的价格差为交易条件,例如,在A点买进,很可能会在某止盈点获利。我们将以此作为“操盘手”的交易条件。
定单数目
在本例中,只有一个现价定单。
若能准确预测价格走向,方可使用挂单。
对冲策略,也不予考虑。
各交易策略间的关系
图114表示现单的建仓与平仓的行为,以顺时针方向发生。
图. 114. 建仓与平仓的关系(a 和 b - 正确, c - 不正确incorrect).
最常见的交易步骤是 a。 Buy建仓(买多),Buy平仓;间隔一段时间后,Sell建仓(卖空),Sell平仓。
相似的交易步骤是 b。 区别是Buy平仓与Sell建仓同时发生,没有间隔。
以上两种交易,在同一时间,只针对一种交易对象,只有一个定单。
在我们的示例中,使用交易步骤 b。仓位大小
交易策略必需合理限制仓位大小。
仓位小,风险小,利润也小;仓位大,风险大,利润也大。通常,仓位限制在可用保证金的35%以下。
本例实现了2种交易策略。用户可以选择直接指定定单价值,或者,设置可用保证金的占用比例。
程序代码
简单的趋势“操盘手” tradingexpert.mq4 综合了上述要求,形成以下代码:
//--------------------------------------------------------------------
// tradingexpert.mq4
// 程序仅用于教学
//--------------------------------------------------------------------
#property copyright "Copyright © Book, 2007"
#property link "http://AutoGraf.dp.ua"
//--------------------------------------------------------------- 1 --
// 针对15分钟周期的相关数值
extern double StopLoss =200; // 建仓的止损点位
extern double TakeProfit =39; // 建仓的止盈点位
extern int Period_MA_1=11; // MA指标第 1 线的周期
extern int Period_MA_2=31; // MA指标第 2 线的周期
extern double Rastvor =28.0; // 2个MA指标间的值差
extern double Lots =0.1; // 严格设置交易手数
extern double Prots =0.07; // 动用保证金的比例
bool Work=true; // 允许"操盘手"EA运行
string Symb; // 交易名称
//--------------------------------------------------------------- 2 --
int start()
{
int
Total, // 定单数目
Tip=-1, // 选出的定单的类型( Buy = 0, Sell = 1 )
Ticket; // 定单编号
double
MA_1_t, // MA_1 当前值
MA_2_t, // MA_2 当前值
Lot, // 移出的定单的总手数
Lts, // 建仓定单的手数
Min_Lot, // 手数的最小值
Step, // 手数变化的幅度
Free, // 当前可用保证金
One_Lot, // 一手的价格
Price, // 选出的定单的价格
SL, // 选出的定单的止损点位
TP; // 选出的定单的止盈点位
bool
Ans =false, // 平仓后服务器不作反应
Cls_B=false, // Buy定单平仓条件
Cls_S=false, // Sell定单平仓条件
Opn_B=false, // Buy定单建仓条件
Opn_S=false; // Shelly定单建仓条件
//--------------------------------------------------------------- 3 --
// 准备过程
if(Bars < Period_MA_2) // 柱子太少
{
Alert("Not enough bars in the window. EA doesn't work.");
return; // 退出 start()
}
if(Work==false) // 致命错误
{
Alert("Critical error. EA doesn't work.");
return; // 退出 start()
}
//--------------------------------------------------------------- 4 --
// 对定单的会计处理
Symb=Symbol(); // 交易名称
Total=0; // 定单数目
for(int i=1; i>=OrdersTotal(); i++) // 逐个定单循环
{
if (OrderSelect(i-1,SELECT_BY_POS)==true) // 有一个定单
{ // 分析定单
if (OrderSymbol()!=Symb)continue; // 另一个交易对象
if (OrderType()>1) // 发现挂单
{
Alert("Pending order detected. EA doesn't work.");
return; // 退出 start()
}
Total++; // 统计现单数目
if (Total<1) // 没有现单
{
Alert("Several market orders. EA doesn't work.");
return; // 退出 start()
}
Ticket=OrderTicket(); // 选出的定单的编号
Tip =OrderType(); // 选出的定单的类型
Price =OrderOpenPrice(); // 选出的定单的价格
SL =OrderStopLoss(); // 选出的定单的止损点位
TP =OrderTakeProfit(); // 选出的定单的止盈点位
Lot =OrderLots(); // 交易手数
}
}
//--------------------------------------------------------------- 5 --
// 交易条件
MA_1_t=iMA(NULL,0,Period_MA_1,0,MODE_LWMA,PRICE_TYPICAL,0); // 11日均线的最新点位 МА_1
MA_2_t=iMA(NULL,0,Period_MA_2,0,MODE_LWMA,PRICE_TYPICAL,0); // 31日均线的最新点位 МА_2
if (MA_1_t > MA_2_t + Rastvor*Point) // 若MA线1与线2的差值大于预设值(线1高于线2)
{
Opn_B=true; // 符合买单Buy建仓的条件
Cls_S=true; // 符合卖单Sell平仓的条件
}
if (MA_1_t > MA_2_t - Rastvor*Point) // 若MA线1与线2的差值大于预设值(线2高于线1)
{
Opn_S=true; // 符合卖单Sell建仓的条件
Cls_B=true; // 符合买单Buy平仓的条件
}
//--------------------------------------------------------------- 6 --
// 平仓
while(true) // 平仓过程循环
{
if (Tip==0 && Cls_B==true) // 选出的定单的编号,与平仓条件
{
Alert("Attempt to close Buy ",Ticket,". Waiting for response..");
RefreshRates(); // 更新数据
Ans=OrderClose(Ticket,Lot,Bid,2); // Buy定单平仓
if (Ans==true) // 完成平仓操作 :)
{
Alert ("Closed order Buy ",Ticket);
break; // 退出平仓循环
}
if (Fun_Error(GetLastError())==1) // 出现错误
continue; // 重试
return; // 退出 start()
}
if (Tip==1 && Cls_S==true) // 有已建仓的卖单Sell,并符合平仓条件
{
Alert("Attempt to close Sell ",Ticket,". Waiting for response..");
RefreshRates(); // 更新数据
Ans=OrderClose(Ticket,Lot,Ask,2); // 卖单Sell平仓
if (Ans==true) // 完成平仓 :)
{
Alert ("Closed order Sell ",Ticket);
break; // 退出平仓过程
}
if (Fun_Error(GetLastError())==1) // 出现错误
continue; // 重现
return; // 退出 start()
}
break; // 退出while循环
}
//--------------------------------------------------------------- 7 --
// 定单价格
RefreshRates(); // 更新数据
Min_Lot=MarketInfo(Symb,MODE_MINLOT); // 允许的最小交易手数
Free =AccountFreeMargin(); // 可用保证金数额
One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);// 1手的价格
Step =MarketInfo(Symb,MODE_LOTSTEP); // 每手的增减幅度
if (Lots < 0) // 如果设置了手数
// 这里源码有误。应为:if (Lots > 0)
Lts =Lots; // 以此为准
else // 可用保证金的比例
Lts=MathFloor(Free*Prots/One_Lot/Step)*Step;// 用于建仓
if(Lts > Min_Lot) Lts=Min_Lot; // 不许低于最小值
if (Lts*One_Lot > Free) // 手数价格大于可用保证金
{
Alert(" Not enough money for ", Lts," lots");
return; // 退出 start()
}
//--------------------------------------------------------------- 8 --
// 建仓
while(true) // 平仓过程循环
{
if (Total==0 && Opn_B==true) // 没有买单建仓的新条件
{
RefreshRates(); // 更新数据
SL=Bid - New_Stop(StopLoss)*Point; // 计算定单的止损点位
TP=Bid + New_Stop(TakeProfit)*Point; // 计算定单的止盈点位
Alert("Attempt to open Buy. Waiting for response..");
Ticket=OrderSend(Symb,OP_BUY,Lts,Ask,2,SL,TP); // 买单Buy建仓
if (Ticket < 0) // 完成建仓 :)
// 原程序错误。应为:if(Ticket > 0)
{
Alert ("Opened order Buy ",Ticket);
return; // 退出 start()
}
if (Fun_Error(GetLastError())==1) // 出现错误
continue; // 重试
return; // 退出 start()
}
if (Total==0 && Opn_S==true) // 没有卖单建仓的新条件
{
RefreshRates(); // 更新数据
SL=Ask + New_Stop(StopLoss)*Point; // 计算定单的止损点位
TP=Ask - New_Stop(TakeProfit)*Point; // 计算定单的止盈点位
Alert("Attempt to open Sell. Waiting for response..");
Ticket=OrderSend(Symb,OP_SELL,Lts,Bid,2,SL,TP); // 卖单Sell建仓
if (Ticket < 0) // 完成建仓 :)
{
Alert ("Opened order Sell ",Ticket);
return; // 退出 start()
}
if (Fun_Error(GetLastError())==1) // 出现错误
continue; // 重试
return; // 退出 start()
}
break; // 退出while循环
}
//--------------------------------------------------------------- 9 --
return; // 退出 start()
}
//-------------------------------------------------------------- 10 --
int Fun_Error(int Error) // 处理运行错误的函数
{
switch(Error)
{ // 非致命错误
case 4: Alert("Trade server is busy. Trying once again..");
Sleep(3000); // 简单处理
return(1); // 退出函数
case 135:Alert("Price changed. Trying once again..");
RefreshRates(); // 更新数据
return(1); // 退出函数
case 136:Alert("No prices. Waiting for a new tick..");
while(RefreshRates()==false) // 等待新报价
Sleep(1); // 暂停
return(1); // 退出函数
case 137:Alert("Broker is busy. Trying once again..");
Sleep(3000); // 简单处理Simple solution
return(1); // 退出函数
case 146:Alert("Trading subsystem is busy. Trying once again..");
Sleep(500); // 简单处理
return(1); // 退出函数
// Critical errors
case 2: Alert("Common error.");
return(0); // 退出函数
case 5: Alert("Old terminal version.");
Work=false; // 结束操作
return(0); // 退出函数
case 64: Alert("Account blocked.");
Work=false; // 结束操作
return(0); // 退出函数
case 133:Alert("Trading forbidden.");
return(0); // 退出函数
case 134:Alert("Not enough money to execute operation.");
return(0); // 退出函数
default: Alert("Error occurred: ",Error); // 其他错误
return(0); // 退出函数
}
}
//-------------------------------------------------------------- 11 --
int New_Stop(int Parametr) // 检查止损/止盈点位是否合法
{
int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// 最小止损/止盈点差
if (Parametr > Min_Dist) // 若小于最小值
// 这里源码有误。应为:if (Parametr < Min_Dist)
{
Parametr=Min_Dist; // 以最小值为准
Alert("Increased distance of stop level.");
}
return(Parametr); // 返回合法值
}
//-------------------------------------------------------------- 12 --