本章处理3个问题:平仓、撤单、反向(对冲)平仓。
一、平仓
平仓操作使用函数OrderClose()。
函数 OrderClose()
bool OrderClose (int ticket, double lots, double price, int slippage, color Color=CLR_NONE)
若交易成功完成,返回TRUE,否则,返回FALSE。
参数:
ticket - 定单的唯一编号。
lots - 要平仓的手数。允许平仓手数小于定单上的手数,即部分平仓。
price - 平仓价格。具体确定,要符合定单特点和交易规则与附录3的规定。否则,平仓请求将被拒绝。设置滑点的,成交价有可能低于平仓请求价。
slippage - 滑点。平仓报价与市场现价之间允许的最大点差。
Color - 在主图上标示平仓价位的箭头颜色。若未用此参数或其值为CLR_NONE,不显示该箭头。
函数 OrderSelect()
为了得到你的各个定单的参数信息,包括现单、挂单、已经平仓的或撤销的等,你应当首先用函数OrderSelect()指定它。
bool OrderSelect(int index, int select, int pool=MODE_TRADES)
OrderSelect 选择定单,以做进一步处理。选择成功,返回TRUE,否则,返回FALSE。
参数:
index - 定单的仓位或编号,由第二个参数区别。
select - 选择方法的标志,其值有2种:
SELECT_BY_POS - 参数'index'是定单在列表中的索引号(起始号为0),
SELECT_BY_TICKET - 参数'index'是定单的唯一编号。
pool - 所选定单的数据来源。当参数'select'的值等于SELECT_BY_POS时,会用到参数'pool'。若参数'select'值为SELECT_BY_TICKET,则不使用参数'pool' ,而由定单编号选择定单。'pool' 可取2种值:
MODE_TRADES (默认值)- "Terminal"窗口"Trade"标签中显示的现单和挂单,是选择对象;
MODE_HISTORY - "Terminal"窗口"Account History"标签中显示的结单和撤单现单和挂单,是选择对象。
为了展示平仓操作,提出一个问题:
问题 28. 写个脚本,将帐户中的定单平仓。平仓的定单,由脚本运行的窗口内的鼠标所在位置确定。 |
假设有“欧元/美元”的3个现单和“美元/法郎”的1个挂单:
图. 90. 在终端窗口显示几个不同的定单.
要写的脚本应该是这样的:可用鼠标把脚本从"Navigator"窗拖拉到主窗中,松开鼠标按钮时,光标最靠近的开仓价定单,开始平仓。在图91中,会看到光标最靠近卖单4372889。脚本运行时,它就是平仓的对象。
图.91.脚本closeorder.mq4用于选择平仓单。
先看几个相关的内建函数:
int OrdersTotal(); // 返回现单和挂单的总数
string OrderSymbol(); // 当前选中的定单涉及的交易对象名称
// 必须先用函数OrderSelect()选出定单
int OrderType(); // 返回定单类型:OP_BUY,OP_SELL,OP_BUYLIMIT,
// OP_BUYSTOP,OP_SELLLIMIT,OP_SELLSTOPorder.
// 必须先用函数OrderSelect()选出定单
double OrderOpenPrice(); // 当前选中的定单的建仓价
// 必须先用函数OrderSelect()选出定单
double MathAbs( double value ); // 返回value的绝对值
// 可用函数fabs()代替
double NormalizeDouble(
double value, // 要处理的数字
int digits // 小数点后的位数
); // 计算止损、止盈、挂单建仓价时,必须用本函数处理过的数字
int OrderTicket(); // 当前选中的定单的编号
// 必须先用函数OrderSelect()选出定单
double OrderLots(); // 当前选中的定单的手数(交易量)
// 必须先用函数OrderSelect()选出定单
void Sleep(
int milliseconds // 间隔延迟的毫秒数
); // 用于“操盘手”和脚本中,不能用于外建指标中
下面是解决“问题28”的脚本程序
//--------------------------------------------------------------------------------------
// closeorder.mq4
// The code should be used for educational purpose only.
//--------------------------------------------------------------------------------- 1 --
int start() // 特别函数 'start'
{
string Symb=Symbol(); // 交易对象
double Dist=1000000.0; // 预设
int Real_Order=-1; // 尚无定单
double Win_Price=WindowPriceOnDropped(); // 脚本在这个点位执行
//-------------------------------------------------------------------------------- 2 --
for(int i=1; i<=OrdersTotal(); i++) // 寻找定单过程循环
{
if (OrderSelect(i-1,SELECT_BY_POS)==true) // 若找到一个定单
{ // 进行分析:
//----------------------------------------------------------------------- 3 --
if (OrderSymbol()!= Symb) continue; // 定单上的对象不是所要找的
int Tip=OrderType(); // 定单类型
if (Tip>1) continue; // 是挂单
//----------------------------------------------------------------------- 4 --
double Price=OrderOpenPrice(); // 定单开仓价
if (NormalizeDouble(MathAbs(Price-Win_Price),Digits)< //选择出价格最接近的
NormalizeDouble(Dist,Digits))
{
Dist=MathAbs(Price-Win_Price); // 新值
Real_Order=Tip; // 有这种类型的定单
int Ticket=OrderTicket(); // 定单编号
double Lot=OrderLots(); // 手数
}
//----------------------------------------------------------------------- 5 --
} // 结束定单分析
} // 结束定单查找
//-------------------------------------------------------------------------------- 6 --
while(true) // 平仓过程循环
{
if (Real_Order==-1) // 若没有定单
{
Alert("For ",Symb," no market orders available");
break; // 退出平仓循环
}
//-------------------------------------------------------------------------- 7 --
switch(Real_Order) // 定单类型
{
case 0:
double Price_Cls=Bid; // 买单Buy
string Text="Buy "; // 字符串"Buy"
break; // 退出switch
case 1:
Price_Cls=Ask; // 卖单Sell
Text="Sell "; // 字符串"Sell"
}
Alert("Attempt to close ",Text," ",Ticket,". Awaiting response..");
bool Ans=OrderClose(Ticket,Lot,Price_Cls,2);// 要求平仓
//-------------------------------------------------------------------------- 8 --
if (Ans==true) // 完成平仓! :)
{
Alert ("Closed order ",Text," ",Ticket);
break; // 退出平仓循环
}
//-------------------------------------------------------------------------- 9 --
int Error=GetLastError(); // 平仓失败 :(
switch(Error) // 可以克服的错误
{
case 135:
Alert("The price has changed. Retrying..");
RefreshRates(); // 更新数据
continue; // 继续下次迭代
case 136:
Alert("No prices. Waiting for a new tick..");
while(RefreshRates()==false) // 新报价
Sleep(1); // 循环等待
continue; // 继续下次迭代
case 146:
Alert("Trading subsystem is busy. Retrying..");
Sleep(500); // 简单处理
RefreshRates(); // 更新数据
continue; // 继续下次迭代
}
switch(Error) // 致命错误
{
case 2 :
Alert("Common error.");
break; // 退出'switch'
case 5 :
Alert("Old version of the client terminal.");
break; // 退出'switch'
case 64:
Alert("Account is blocked.");
break; // 退出'switch'
case 133:
Alert("Trading is prohibited");
break; // 退出'switch'
default:
Alert("Occurred error ",Error);//Other alternatives
}
break; // 退出平仓过程循环
}
//------------------------------------------------------------------------------- 10 --
Alert ("The script has finished operations -----------------------------");
return; // 退出start()
}
//------------------------------------------------------------------------------- 11 --
二、撤销挂单
用函数 OrderDelete()提出撤销挂单的要求。
函数 OrderDelete()
bool OrderDelete(int ticket, color arrow_color=CLR_NONE)
如果成功撤单,它返回TRUE,否则,返回 FALSE。
参数:
ticket - 定单的唯一编号。
arrow_color - 主图中的箭头颜色。若无此参数,或其值等于CLR_NONE,主图中不显示箭头。
OrderDelete( )不涉及撤单的手数和平仓价格。
定单的撤销,与市场价格无关。也不能部分地撤单。减少挂单手数的办法有2个:撤销现有定单,再建仓减少了手数的新挂单。
撤销挂单的算法,与现价平仓完全相同。只是,撤单不需平仓价格。
示例。撤销挂单的简单脚本。拖拉脚本到主图中,松开鼠标按钮时,与光标所指价位最近的挂单,是撤销的目标。(deleteorder.mq4). |
//-------------------------------------------------------------------------------------
// deleteorder.mq4
// 程序仅用于教学
//-------------------------------------------------------------------------------- 1 --
int start() // 特别函数'start'
{
string Symb=Symbol(); // 交易对象
double Dist=1000000.0; // 预设
int Limit_Stop=-1; // 没有挂单
double Win_Price=WindowPriceOnDropped(); // 脚本“落地”的位置
//-------------------------------------------------------------------------------- 2 --
for(int i=1; i<=OrdersTotal(); i++) // 定单查找循环
{
if (OrderSelect(i-1,SELECT_BY_POS)==true) // 若有一个定单
{ // 分析定单:
//----------------------------------------------------------------------- 3 --
if (OrderSymbol()!= Symb) continue; // 交易对象不符
int Tip=OrderType(); // 定单类型
if (Tip>2) continue; // 现价单
//----------------------------------------------------------------------- 4 --
double Price=OrderOpenPrice(); // 定单价格
if (NormalizeDouble(MathAbs(Price-Win_Price),Digits)> //选出价格最接近的挂单
NormalizeDouble(Dist,Digits))
{
Dist=MathAbs(Price-Win_Price); // 新值
Limit_Stop=Tip; // 挂单类型有效
int Ticket=OrderTicket(); // 挂单编号
} // if 语句结束
} // 定单分析结束
} // 定单查找结束
//-------------------------------------------------------------------------------- 5 --
switch(Limit_Stop) // 按照挂单类型处理
{
case 2: string Text= "BuyLimit "; // BuyLimit字符串
break; // 退出 'switch'
case 3: Text= "SellLimit "; // SellLimit字符串
break; // 退出 'switch'
case 4: Text= "BuyStopt "; // BuyStopt字符串
break; // 退出 'switch'
case 5: Text= "SellStop "; // SellStop字符串
break; // 退出 'switch'
}
//-------------------------------------------------------------------------------- 6 --
while(true) // 平仓过程循环
{
if (Limit_Stop==-1) // 若无挂单
{
Alert("For ",Symb," no pending orders available");
break; // 退出平仓
}
//-------------------------------------------------------------------------- 7 --
Alert("Attempt to delete ",Text," ",Ticket,". Awaiting response..");
bool Ans=OrderDelete(Ticket); // 撤销挂单
//-------------------------------------------------------------------------- 8 --
if (Ans==true) // 完成撤单! :)
{
Alert ("Deleted order ",Text," ",Ticket);
break; // 退出平仓循环
}
//-------------------------------------------------------------------------- 9 --
int Error=GetLastError(); // 撤单失败 :(
switch(Error) // 可克服的错误
{
case 4: Alert("Trade server is busy. Retrying..");
Sleep(3000); // 简单处理
continue; // 继续迭代
case 137:Alert("Broker is busy. Retrying..");
Sleep(3000); // 简单处理
continue; // 继续迭代
case 146:Alert("Trading subsystem is busy. Retrying..");
Sleep(500); // 简单处理
continue; // 继续迭代
}
switch(Error) // 致命错误
{
case 2 : Alert("Common error.");
break; // 退出 'switch'
case 64: Alert("Account is blocked.");
break; // 退出 'switch'
case 133:Alert("Trading is prohibited");
break; // 退出 'switch'
case 139:Alert("The order is blocked and is being processed");
break; // 退出 'switch'
case 145:Alert("Modification is prohibited. ",
"The order is too close to the market");
break; // 退出 'switch'
default: Alert("Occurred error ",Error);// 其他错误
}
break; // 退出平仓循环
}
//------------------------------------------------------------------------------- 10 --
Alert ("The script has finished operations -----------------------------");
return; // 退出 start()
}
//------------------------------------------------------------------------------- 11 --
三、反向(对冲)平仓
反向(对冲)定单 是指对同一交易对象开仓并且交易方向相反的2个现价单。
一对反向定单可以同时平仓,或者,用函数OrderCloseBy(),逐个平仓。反向平仓可以节省(“佣金”)点差。
函数 OrderCloseBy()
bool OrderCloseBy(int ticket, int opposite, color Color=CLR_NONE)
本函数进行对冲平仓操作。若完成对冲平仓,返回TRUE,否则,返回FALSE。
参数:
ticket - 要平仓的定单唯一编号。
opposite - 对冲定单的唯一编号。
Color - 主图中的箭头颜色。若无此参数,或其值等于CLR_NONE,主图中不显示箭头。
对冲平仓无需2个定单的手数相等,而是按手数少的定单平仓。
示例。对冲平仓的简单脚本 ( closeby.mq4).//--------------------------------------------------------------------
// closeby.mq4
// 程序仅用于教学
//--------------------------------------------------------------- 1 --
int start() // 特别函数'start'
{
string Symb=Symbol(); // 交易对象
double Dist=1000000.0; // 预设
//--------------------------------------------------------------- 2 --
while(true) // 处理对冲平仓过程的循环
{
double Hedg_Buy = -1.0; // 买单Buy的最大成本
double Hedg_Sell= -1.0; // 卖单Sell的最大成本
for(int i=1; i<=OrdersTotal(); i++) // 查找定单过程循环
{
if(OrderSelect(i-1,SELECT_BY_POS)==true)// 找到一个定单
{ // 分析定单
//--------------------------------------------------- 3 --
if (OrderSymbol()!= Symb) continue; // 交易对象不符
int Tip=OrderType(); // 定单类型
if (Tip>1) continue; // 挂单
//--------------------------------------------------- 4 --
switch(Tip) // 按定单类型分别处理
{
case 0: // 买单Buy
if (OrderLots()>Hedg_Buy)
{
Hedg_Buy=OrderLots(); // 选择最大成本
int Ticket_Buy=OrderTicket();//定单编号
}
break; // 退出 switch
case 1: // 卖单Sell
if (OrderLots()>Hedg_Sell)
{
Hedg_Sell=OrderLots(); // 选择最大成本
int Ticket_Sell=OrderTicket();//定单编号
}
} //结束 'switch'
} //结束定单分析
} //结束定单查找
//--------------------------------------------------------- 5 --
if (Hedg_Buy<0 || Hedg_Sell<0) // 若无对冲定单
{
Alert("All opposite orders are closed :)");
return; // 退出 start()
}
//--------------------------------------------------------- 6 --
while(true) // 平仓过程循环
{
//------------------------------------------------------ 7 --
Alert("Attempt to close by. Awaiting response..");
bool Ans=OrderCloseBy(Ticket_Buy,Ticket_Sell);// Закрытие
//------------------------------------------------------ 8 --
if (Ans==true) // 完成平仓! :)
{
Alert ("Performed closing by.");
break; // 退出平仓
}
//------------------------------------------------------ 9 --
int Error=GetLastError(); // 平仓失败 :(
switch(Error) // 可克服的错误
{
case 4: Alert("Trade server is busy. Retrying..");
Sleep(3000); // 简单处理
continue; // 继续迭代
case 137:Alert("Broker is busy. Retrying..");
Sleep(3000); // 简单处理
continue; // 继续迭代
case 146:Alert("Trading subsystem is busy. Retrying..");
Sleep(500); // 简单处理
continue; // 继续迭代
}
switch(Error) // 致命错误
{
case 2 : Alert("Common error.");
break; // 退出 'switch'
case 64: Alert("Account is blocked.");
break; // 退出 'switch'
case 133:Alert("Trading is prohibited");
break; // 退出 'switch'
case 139:Alert("The order is blocked and is being processed");
break; // 退出 'switch'
case 145:Alert("Modification is prohibited. ",
"The order is too close to market");
break; // 退出 'switch'
default: Alert("Occurred error ",Error);// 其他错误
}
Alert ("The script has finished operations --------------------------");
return; // 退出 start()
}
} // 线束处理
//-------------------------------------------------------------- 10 --
} // 结束 start()
//--------------------------------------------------------------------