概览
本人在多年交易经历中,总结出人性中一些难以克服的弱点,编写出了此风控EA。
目的是帮助交易员规避重仓、频繁交易、亏损加仓等坏习惯,控制单笔及单日亏损比例。
从而避免账户大亏,早日实现稳定盈利!
应用步骤
第一步:复制代码
创建一个命名为E_Auto_Risk_Monitor_Zsg.mq4的mq4文件(名称可改),复制代码后将其放入脚本目录,比如我本地的目录如下:
C:\Users\zhong.shuigen\AppData\Roaming\MetaTrader 4 IC Markets\MQL4\Experts
第二步:编译mq4文件,生成执行文件
第三步:启用EA
回到MT4界面,导航 - EA交易,双击后运行到某一个(也可多个)货币对上。并保证自动交易按钮已点开(绿色)。
这样后面的交易就能被EA监控了。
代码
//+------------------------------------------------------------------+
//| E_Auto_Risk_Monitor_Zsg.mq4 |
//| wouter.zhong |
//| 自动交易EA |
//+------------------------------------------------------------------+
#property copyright "wouter.zhong"
#property link "自动风控EA-福汇"
#property version "v2"
//配置参数
//1 - 亏损限制
double maxLossRatio = 0.15; //单笔亏损,占账户余额的最大比例
double maxLossRatioOneDay = 0.3; //总亏损,当日总亏损占账户期初余额的最大比例
//2 - 重仓限制
double perMaxMarginRatio = 0.3; //单笔仓位,最大可用保证金比例
double maxMarginRatio = 0.5; //总仓位,最大可用保证金比例
//3 - 单量限制
int limitCurOrderNums = 3; //当前单量限制(含挂单)
int limitOrderNums = 10; //单日总单量限制
//4 - 黄金限制
int riskFactor = 1; //风险因子(黄金)
int riskPoint = 150; //风险止损(黄金)
int riskOrderNums = 10; //黄金单日下单限制
double conceitMarginLimit = 0.2; //骄傲自负或赌博心态,仓位限制
string limitMargeNoSym = ""; //重仓限制,不受限币种
string limitOrderNoSym = ""; //单量限制,不受限币种
int limitOrderDays = 1; //单量限制,期间,单位:day
int slipPoint = 30; //滑点数
/** =========== 辅助函数模块 =========== */
/**
* 1)获取最近一个非挂单的订单号
*/
string getLastCuTicket(){
int i;
int num = 0;
string orderTicket = "";
for(i=OrdersTotal()-1; i>=0; i--){
if(OrderSelect(i,SELECT_BY_POS)){
if(OrderType()==OP_BUY || OrderType()==OP_SELL){
orderTicket = OrderTicket();
return orderTicket;
}
}
}
return orderTicket;
}
/**
* 2)获取MTDMarketInfo的mode
*/
string getModeByType(string orderType){
string mode;
if(orderType == OP_SELL){
mode = MODE_ASK;
}else if(orderType == OP_BUY){
mode = MODE_BID;
}else {
mode = MODE_BID;
}
return mode;
}
/**
* 3)获取某期间内的总盈亏
* @(不含被删除/未触发的挂单,不含未平仓订单)
* @PS:系统时间加6小时,即北京时间
*/
double getPeriodOrderProfit(int days=1){
string year = TimeYear(TimeLocal()); //北京时间
string month = TimeMonth(TimeLocal());
string day = TimeDay(TimeLocal());
string localDayEnd = year +"."+ month +"."+ day +" 23:59:59"; //期间截至日的时间
int i;
int profit = 0;
int startCount = OrdersHistoryTotal(); //历史总订单数(只返回'账户历史'里的订单)
//Print("天数:"+days+",历史订单数:"+startCount);
for(i=0; i<startCount; i++) //倒序
{
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)){
//Print("编号:"+ i +", 订单号:"+ OrderTicket() + ", 币种:"+ OrderSymbol() +", 订单开仓时间:" + (OrderOpenTime()+6*3600) +", 类型: "+ OrderType() +", 盈亏: "+ OrderProfit());
//统计
if(OrderOpenTime() < StrToTime(localDayEnd) && OrderOpenTime() >= (StrToTime(localDayEnd) - 86400*days) ){
if(OrderType()==OP_BUY || OrderType()==OP_SELL){
profit = profit + OrderProfit();
}
}
}
}
return profit;
}
/**
* 4)获取某期间内的出入金
* @PS:系统时间加6小时,即北京时间
*/
double getPeriodBalance(int days=5, bool getOutBlance=false, bool getInBlance=false){
string year = TimeYear(TimeLocal()); //北京时间
string month = TimeMonth(TimeLocal());
string day = TimeDay(TimeLocal());
string localDayEnd = year +"."+ month +"."+ day +" 23:59:59"; //期间截至日的时间
double outBalance = 0;
double inBalance = 0;
int i;
int startCount = OrdersHistoryTotal(); //历史总订单数
for(i=0; i<startCount; i++) //倒序
{
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)){
//统计
if(OrderOpenTime() < StrToTime(localDayEnd) && OrderOpenTime() >= (StrToTime(localDayEnd) - 86400*days) && OrderType()==6){
double profit = OrderProfit();
string date = TimeYear(OrderOpenTime()) +"-"+ TimeMonth(OrderOpenTime()) +"-"+ TimeDay(OrderOpenTime());
if(getOutBlance==true && profit<0){
//Print("出金--------订单号:" + OrderTicket() + ", 订单类型 "+OrderType()+", 出金金额:"+profit+ ", 日期:"+ date);
outBalance = outBalance + profit;
}
if(getInBlance==true && profit>0){
//Print("入金--------订单号:" + OrderTicket() + ", 订单类型 "+OrderType()+", 入金金额:"+profit+ ", 日期:"+ date);
inBalance = inBalance + profit;
}
}
}
}
if(getOutBlance==true){
return outBalance;
}else if(getInBlance==true){
return inBalance;
}
}
/**
* 5)获取某期间内 下单总数
* @(不含被删除/未触发的挂单,不含未平仓订单)
* @param smybol 筛选的币种
* @param noSmybol 排除的币种
* @PS:系统时间加6小时,即北京时间
*/
int getPeriodOrderNums(int days=1, string smybol="", string noSmybol=""){
string year = TimeYear(TimeLocal()); //北京时间
string month = TimeMonth(TimeLocal());
string day = TimeDay(TimeLocal());
string localDayEnd = year +"."+ month +"."+ day +" 23:59:59"; //期间截至日的时间
int i;
int num = 0;
int startCount = OrdersHistoryTotal(); //历史总订单数
for(i=0; i<startCount; i++) //倒序
{
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)){
//Print("编号:"+ i +", 币种:"+ OrderSymbol() +", 订单开仓时间:" + (OrderOpenTime()+6*3600) +", 类型: "+ OrderType() +", 盈亏: "+ OrderProfit());
//筛选币种判断
if(smybol != "" && OrderSymbol()!=smybol){
continue;
}
//排除币种判断
if(noSmybol != "" && OrderSymbol()==noSmybol){
continue;
}
//统计
if(OrderOpenTime() < StrToTime(localDayEnd)
&& OrderOpenTime() >= (StrToTime(localDayEnd) - 86400*days)
&& (OrderType()==OP_BUY || OrderType()==OP_SELL))
{
num++;
}
}
}
//Print("开始时间:" + TimeToStr((StrToTime(localDayEnd) - 86400*days)) + ";截至时间:" + localDayEnd + "。期间下单总数: "+ num);
return num;
}
/**
* 6)判断某币种期间亏损总数
* @(不含被删除/未触发的挂单,不含未平仓订单)
* @PS:系统时间加6小时,即北京时间
*/
int getPeriodOrderLossNums(int days=1, string symbol=""){
string year = TimeYear(TimeLocal()); //北京时间
string month = TimeMonth(TimeLocal());
string day = TimeDay(TimeLocal());
string localDayEnd = year +"."+ month +"."+ day +" 23:59:59"; //期间截至日的时间
int i;
int nums = 0;
int startCount = OrdersHistoryTotal(); //历史总订单数
for(i=0; i<startCount; i++) //倒序
{
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)){
if(symbol != OrderSymbol() || OrderProfit()>=0){
continue;
}
//统计
if(OrderOpenTime() < StrToTime(localDayEnd) && OrderOpenTime() >= (StrToTime(localDayEnd) - 86400*days) ){
if(OrderType()==OP_BUY || OrderType()==OP_SELL){
nums++;
}
}
}
}
return nums;
}
/**
订单类型对应值
0 OP_BUY 买入订单(市价单)
1 OP_SELL 卖出订单(市价单)
2 OP_BUYLIMIT 买入限价单(挂单)
3 OP_SELLLIMIT 卖出限价单(挂单)
4 OP_BUYSTOP 买入止损单(挂单)
5 OP_SELLSTOP 卖出止损单(挂单)
6 OP_CLOSE 平仓订单(用于EA内部逻辑,实际交易中不直接使用)
**/
/** =========== 风控函数模块 =========== */
/**
* 1. 限制重仓!
* @return null
*/
void limitOrderMagin(){
double accBlance = AccountBalance(); //账户余额
double accMargin = AccountMargin(); //已用保证金
//单笔订单仓位控制
int j;
for(j=OrdersTotal()-1; j>=0; j--){
if(OrderSelect(j, SELECT_BY_POS)){
if(OrderSymbol() == "XAUUSD"){
continue;
}
//挂单不能关闭
if(OrderType()!=OP_SELL && OrderType()!=OP_BUY){
continue;
}
//保证金金额 = 手数乘以1000 (100倍杠杆情况下)
double OrderOpeBond = OrderLots()*1000;
//平仓
if(OrderOpeBond > perMaxMarginRatio*accBlance){
Print("单笔订单-重仓限制_V2,关闭订单:"+ OrderTicket() + ", 已用保证金:" + OrderOpeBond + ", 账户余额:" + accBlance
+ " -- 币种:" + OrderSymbol() +", 订单盈利:"+ OrderProfit());
OrderClose(
OrderTicket(),
OrderLots(),
MarketInfo(NULL, getModeByType(OrderType())),
slipPoint
);
}
}
}
if(accMargin > maxMarginRatio*accBlance && accBlance>100){
//最后一个非挂单
string lastCuOrderTicket = getLastCuTicket();
int i;
for(i=OrdersTotal()-1; i>=0; i--){
if(OrderSelect(i, SELECT_BY_POS)){
//不受限币种判断
if(limitMargeNoSym != "" && limitMargeNoSym==OrderSymbol()){
continue;
}
//挂单不能关闭
if(OrderType()!=OP_SELL && OrderType()!=OP_BUY){
continue;
}
//平仓
if(OrderTicket() == lastCuOrderTicket){
Print("重仓限制_V2,关闭订单:"+ lastCuOrderTicket + ", 已用保证金:" + accMargin + ", 账户余额:" + accBlance
+ " -- 币种:" + OrderSymbol() +", 订单盈利:"+ OrderProfit());
OrderClose(
lastCuOrderTicket,
OrderLots(),
MarketInfo(NULL, getModeByType(OrderType())),
slipPoint
);
}
}
}
}
}
/**
* 2.限制亏损!
* @return null
*/
void limitOrderLoss(){
double totalProfit = getPeriodOrderProfit(1);
//Print("当日总盈亏: " + totalProfit);
int i;
for(i=OrdersTotal()-1; i>=0; i--){
if(OrderSelect(i,SELECT_BY_POS)){
double profit = OrderProfit();
double balance = AccountBalance(); //账户余额
//Print("订单货币对: " + OrderSymbol() + ",下单时间:"+TimeHour(OrderOpenTime()));
//1:限制单笔订单亏损(亏损超过账户余额的1-ratio时,强平止损)
if((profit + balance) < (1-maxLossRatio)*balance) {
//打印日志
Print("亏损限制_V2,关闭订单: " + OrderTicket()
+ ", 币种:"+ OrderSymbol()
+ ", 手数:"+ OrderLots()
+ ", 类型:"+ OrderType()
+ ", 止盈价"+ OrderTakeProfit()
+ ", 止损价"+ OrderStopLoss()
+ ", 订单盈亏:"+ DoubleToStr(NormalizeDouble(profit, 4),4)
);
OrderClose(
OrderTicket(),
OrderLots(),
MarketInfo(OrderSymbol(), getModeByType(OrderType())),
slipPoint
);
}
//2:限制当日总亏损
if(totalProfit<0 && MathAbs(totalProfit) > maxLossRatioOneDay*(balance + MathAbs(totalProfit))){
//挂单不提示
if(OrderType()!=OP_SELL && OrderType()!=OP_BUY){
continue;
}
//打印日志
Print("总亏损限制_V2,关闭订单: " + OrderTicket()
+ ", 币种:"+ OrderSymbol()
+ ", 手数:"+ OrderLots()
+ ", 类型:"+ OrderType()
+ ", 止盈价"+ OrderTakeProfit()
+ ", 止损价"+ OrderStopLoss()
+ ", 订单盈亏:"+ DoubleToStr(NormalizeDouble(profit, 4),4)
+ "。 当日总亏损:"+ totalProfit
);
OrderClose(
OrderTicket(),
OrderLots(),
MarketInfo(OrderSymbol(), getModeByType(OrderType())),
slipPoint
);
}
//3:限制币种当日亏损订单数
int lossNums = getPeriodOrderLossNums(1, OrderSymbol());
//Print(OrderSymbol() + " 当日亏损订单数:" + lossNums);
/**if(lossNums>=4){
//打印日志
Print(OrderSymbol()+" 亏损订单数量限制_V2,关闭订单: " + OrderTicket()
+ ", 币种:"+ OrderSymbol()
+ ", 手数:"+ OrderLots()
+ ", 类型:"+ OrderType()
+ ", 建仓时间:"+ OrderOpenTime()
+ ", 止盈价"+ OrderTakeProfit()
+ ", 止损价"+ OrderStopLoss()
+ ", 订单盈亏:"+ DoubleToStr(NormalizeDouble(profit, 4),4)
+ "。 当日总亏损:"+ totalProfit
);
OrderClose(
OrderTicket(),
OrderLots(),
MarketInfo(OrderSymbol(), getModeByType(OrderType())),
slipPoint
);
}**/
}
}
}
/**
* 3. 限制下单数
* @ 超出限制后,删除挂单及最新开仓的订单
* @return null
* @PS:字符串用双引号,单引号会报错
*/
void limitPeriodOrderNums(){
//期间历史下单总数(不含被删除的挂单,不含未平仓订单)
int pOrderAll = getPeriodOrderNums(limitOrderDays, "", limitOrderNoSym);
int curOrderNums = OrdersTotal();
//Print("V2_期间已平仓订单总数: "+ pOrderAll + ",现有订单总数:"+ curOrderNums + ",限制订单数:"+limitOrderNums);
//历史总订单数
int startCount = OrdersHistoryTotal();
//最近一个关闭的订单
int i=0;
int lastClostOrderTicket = 0;
for(i=startCount-1; i>=startCount-1; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)){
if(i==startCount-1){
lastClostOrderTicket = OrderTicket();
}
}
}
//Print("V2_最近一个关闭的订单: "+ lastClostOrderTicket);
//限制当前订单数
if(curOrderNums > limitCurOrderNums){
Print("现有订单总数:"+ curOrderNums);
i=0;
for(i=curOrderNums-1; i>=curOrderNums-1; i++) //正序(订单号从小到大排序)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
if(OrderTicket() > lastClostOrderTicket){
Print("限制当前订单数_V2, 币种:"+ OrderSymbol() +", 订单号:"+OrderTicket() +", 订单盈利:"+ OrderProfit());
//挂单删除
if(OrderType() == 2 || OrderType() == 3){
Print("限制当前订单数_V2,挂单.序号:"+i+ "币种:" + OrderSymbol() + ", 订单号:"+ OrderTicket());
OrderDelete(OrderTicket());
}else{
//交易单关闭
OrderClose(
OrderTicket(),
OrderLots(),
MarketInfo(OrderSymbol(), getModeByType(OrderType())),
slipPoint
);
}
}
}
}
}
//如果期间历史加现单超过了限制,平掉最近一单
if(curOrderNums + pOrderAll > limitOrderNums){
//Print("期间已平仓订单总数:"+ pOrderAll + ",现有订单总数:"+ curOrderNums);
i=0;
for(i=curOrderNums-1; i>=curOrderNums-1; i++) //正序(订单号从小到大排序)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
//Print("limitPeriodOrderNums - 序号: " + i + ", 订单号:"+OrderTicket()+ "币种: "+ OrderSymbol());
if(OrderTicket() > lastClostOrderTicket){
Print("限制订单数_V2, 币种:"+OrderSymbol()+", 订单盈利:"+OrderProfit()+", 订单号:"+OrderTicket()+", 订单类型:"+OrderType());
//挂单删除
if(OrderType() == 2 || OrderType() == 3){
Print("限制订单数_V2,挂单:"+OrderSymbol() + ", 订单号:"+OrderTicket());
OrderDelete(OrderTicket());
}else{
//交易单关闭
OrderClose(
OrderTicket(),
OrderLots(),
MarketInfo(OrderSymbol(), getModeByType(OrderType())),
slipPoint
);
}
}
}
}
}
}
/**
* 4. 限制下单时间
*/
void limitOrderOpenTime(){
//Print("订单数: " + OrdersTotal());
int i;
for(i=OrdersTotal()-1; i>=0; i--){
if(OrderSelect(i, SELECT_BY_POS)){
double profit = OrderProfit();
//Print(i +"限制下单时间_test,关闭订单: " + OrderTicket() + ",开仓分:"+TimeMinute(OrderOpenTime()));
if(TimeHour(OrderOpenTime())>10 && TimeHour(OrderOpenTime())<50){
//Print(i +"限制下单分钟_V2,关闭订单: " + OrderTicket() + ",开仓分:"+TimeMinute(OrderOpenTime()));
}
if(TimeHour(OrderOpenTime())>=21){
OrderClose(
OrderTicket(),
OrderLots(),
MarketInfo(OrderSymbol(), getModeByType(OrderType())),
slipPoint
);
//打印日志
Print(i +"限制下单时间_V2,关闭订单: " + OrderTicket()
+ ", 币种:"+ OrderSymbol()
+ ", 建仓小时:"+ TimeHour(OrderOpenTime())
+ ", 订单盈亏:"+ DoubleToStr(NormalizeDouble(profit, 4),4)
);
}
}
}
}
/**
* 5. 限制亏损加仓
* 应有把握第一个订单不会亏损,
* 否则当其由盈转亏时,后下的这个订单将被平仓,且一定是亏损的(入场点位不如第一单)
*/
void limitAddOrderWhenLoss(){
datetime lastOrderTime = 0; // 最后一个订单的时间戳
int lastOrderTicket = 0; // 最后一个订单的编号
bool hasLoss = false; // 标记是否存在亏损订单
double balance = AccountBalance(); //账户余额
// 遍历所有订单
int total = OrdersTotal();
int curSymbolLossOrderNums = 0; //当前货币对下的亏损订单数(以为点差,新单刚开始的利润肯定是负数)
for(int i=0; i<total; i++){
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
// 检查订单是否属于当前货币对
if(OrderSymbol() == Symbol()){
double profit = OrderProfit();
// 检查订单 亏损是否超过 账户余额的比例
if(profit<0){
curSymbolLossOrderNums++;
hasLoss = true;
// 记录下亏损订单的时间戳和编号
if(OrderOpenTime() > lastOrderTime){
lastOrderTime = OrderOpenTime();
lastOrderTicket = OrderTicket();
}
}
}
}
}
// 如果存在亏损订单,并且找到了最近下的亏损订单,则关闭它
if(hasLoss && lastOrderTicket > 0 && curSymbolLossOrderNums>2){
Print("当前货币对:"+Symbol()+" 存在亏损订单,即将关闭最近下的亏损订单:", lastOrderTicket);
OrderClose(
lastOrderTicket,
OrderLots(),
MarketInfo(OrderSymbol(), getModeByType(OrderType())),
slipPoint
);
Print("限制亏损加仓_V2。关闭订单:" + lastOrderTicket
+ ", 币种:"+ OrderSymbol()
+ ", 手数:"+ OrderLots()
+ ", 盈亏:" + DoubleToStr(NormalizeDouble(OrderProfit(), 4),4)
);
/**if(OrderClose(lastOrderTicket, OrderLots(), Bid, 0, clrNONE) > 0){
Print("限制亏损加仓_V2。关闭订单:" + lastOrderTicket
+ ", 币种:"+ OrderSymbol()
+ ", 手数:"+ OrderLots()
+ ", 盈亏:" + DoubleToStr(NormalizeDouble(OrderProfit(), 4),4)
+ ", 建仓时间:" + OrderOpenTime());
}else{
Print("订单关闭失败,错误信息:"+lastOrderTicket, GetLastError());
}*/
}
}
/**
* 6. 限制订单亏损时修改止损
* 先定义一个订单结构体,必须写在limitEditStopLoss函数外面,否则结构体数组中都是订单的最新信息,无法判断有无修改止损价
* 再初始化一个固定大小为20的结构体数组
*/
struct OrderStruct{
int order_ticket; //订单号
string symbol; //币种
double stop_loss; //止损价
};
OrderStruct orderStructArray[30];
// 主体代码
void limitEditStopLoss(){
int arraySize = 0; //数组中结构体实例的数量
int total = OrdersTotal(); //当前订单总数,包括休市中的订单
//Print("当前订单总数:"+total);
//时间对象
MqlDateTime timeStruct;
for(int i=0; i<total; i++){
//if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
if(OrderSelect(i, SELECT_BY_POS)){
//订单建仓时间
TimeToStruct(OrderOpenTime(), timeStruct);
string orderTime = IntegerToString(timeStruct.year) + "." +
IntegerToString(timeStruct.mon) + "." +
IntegerToString(timeStruct.day) + " " +
IntegerToString(timeStruct.hour) + ":" +
IntegerToString(timeStruct.min) + ":" +
IntegerToString(timeStruct.sec);
//Print("当前订单号:"+OrderTicket()+",币种:"+OrderSymbol()+ ", 建仓时间:"+orderTime+",止损价:"+OrderStopLoss()+", 盈亏:"+OrderProfit()+ ", 类型:"+ OrderType());
//当前订单信息存入结构体 声明结构体变量
OrderStruct curOrderStruct;
curOrderStruct.order_ticket = OrderTicket();
curOrderStruct.symbol = OrderSymbol();
curOrderStruct.stop_loss = OrderStopLoss();
//Print("当前订单号1:"+curOrderStruct.symbol);
//判断是否移动了止损(亏损时)
if(orderStructArray[i].order_ticket > 0){
//该订单已在结构体中存在
OrderStruct existOrderStruct = orderStructArray[i];
//Print("当前订单号2:"+existOrderStruct.order_ticket);
//结构体中的止损价不为0,修改了止损价,订单处于亏损状态
if(existOrderStruct.order_ticket==OrderTicket() && existOrderStruct.stop_loss>0 && OrderStopLoss()>0
&& existOrderStruct.stop_loss!=OrderStopLoss() && OrderProfit()<0)
{
Print("亏损中修改了止损,订单号:"+ existOrderStruct.order_ticket+ ",币种:"+ existOrderStruct.symbol
+",止损价:"+existOrderStruct.stop_loss + "。 修改后的止损价:"+ OrderStopLoss()
);
OrderClose(
OrderTicket(),
OrderLots(),
MarketInfo(OrderSymbol(), getModeByType(OrderType())),
slipPoint
);
Print("限制亏损中修改止损_V2,关闭订单:" + OrderTicket()
+ ", 币种:"+ OrderSymbol()
+ ", 盈亏:" + DoubleToStr(NormalizeDouble(OrderProfit(), 4),4)
);
/**if(OrderClose(OrderTicket(), OrderLots(), Bid, 0, clrNONE) > 0){
Print("限制亏损中修改止损_V2,关闭订单:" + OrderTicket()
+ ", 币种:"+ OrderSymbol()
+ ", 盈亏:" + DoubleToStr(NormalizeDouble(OrderProfit(), 4),4)
);
}else{
Print("订单关闭失败,错误信息:"+ OrderTicket(), GetLastError());
}*/
}
}else{
//当前订单结构体进入数组,若订单关闭,会从数组中剔除
if(arraySize < 20){
if(OrderType()==0 || OrderType()==1){
//Print("当前订单进入结构体,订单号:"+OrderTicket() + ",订单类型:" +OrderType());
orderStructArray[arraySize] = curOrderStruct;
arraySize++;
}
}else{
Print("订单结构体数组已满!");
}
}
}
}
}
/**
* 7. 黄金交易限制!
* @return null
*/
void limitXauTrement(){
double balance = AccountBalance(); //账户余额
double accMargin = AccountMargin(); //已用保证金
int pOrderAll = getPeriodOrderNums(limitOrderDays, "XAUUSD", limitOrderNoSym); //黄金当日下单量
//Print("黄金当日交易量: " + pOrderAll);
int i;
for(i=OrdersTotal()-1; i>=0; i--){
//黄金单仓位
if(OrderSelect(i,SELECT_BY_POS)){
double profit = OrderProfit();
//保证金金额 = 手数乘以开仓价 (100倍杠杆情况下)
double OrderOpeBond = 0.01*OrderLots()*OrderOpenPrice();
if(OrderSymbol() == "XAUUSD"){
if(pOrderAll >= riskOrderNums){
//打印日志
Print("黄金交易单日下单量控制-关闭订单: " + OrderTicket()+ ",今日已下单数:" + pOrderAll);
OrderClose(
OrderTicket(),
OrderLots(),
MarketInfo(OrderSymbol(), getModeByType(OrderType())),
slipPoint
);
}
//保证金乘以倍数,是因为黄金风险比其他品质波动更大,而加的风险因子
if(OrderOpeBond*riskFactor >= maxMarginRatio*balance){
//打印日志
Print("黄金交易仓位限制_V2-关闭订单: " + OrderTicket()
+ ", 订单盈亏:"+ DoubleToStr(NormalizeDouble(profit, 4),4)
+ "。保证金*" + riskFactor + ":" + OrderOpeBond*riskFactor
+ ",仓位限制:" + maxMarginRatio*balance
);
OrderClose(
OrderTicket(),
OrderLots(),
MarketInfo(OrderSymbol(), getModeByType(OrderType())),
slipPoint
);
}
double pointGap = MathAbs(OrderClosePrice() - OrderOpenPrice()) * 10;
//Print("黄金交易-盈/亏点数: " + pointGap + ",利润:" + profit);
//控制XAUUSD亏损不超过设置的点数
if(profit<0 && pointGap > riskPoint){
//打印日志
Print("黄金交易--亏损点数限制-关闭订单: " + OrderTicket()
+ ", 订单盈亏:"+ DoubleToStr(NormalizeDouble(profit, 4),4)
+ "。保证金*" + riskFactor + ":" + OrderOpeBond*riskFactor
);
OrderClose(
OrderTicket(),
OrderLots(),
MarketInfo(OrderSymbol(), getModeByType(OrderType())),
slipPoint
);
}
}
}
}
}
/**
* 8.限制-骄傲自负和赌徒心态
* ps: 翻倍后5天内限制仓位10%,亏损50%后5天内限制仓位10%
* @return null
*/
void limitConceitAndGamble(){
int days = 5;
double totalProfit = getPeriodOrderProfit(days); //期间总盈亏
double totalInBalance = getPeriodBalance(days,false,true); //期间总入金
double totalOutBalance = MathAbs(getPeriodBalance(days,true,false)); //期间总出金
//Print("期间总盈亏:" + totalProfit + ",期间总入金:" +totalInBalance+ ",期间总出金:" +totalOutBalance);
double accBlance = AccountBalance(); //账户余额
double accMargin = AccountMargin(); //已用保证金
if(totalProfit>0){
double profitRatio = ceil(100*totalProfit/(accBlance + totalOutBalance - totalProfit));
}else{
double lossRatio = ceil(100*totalProfit/(accBlance - totalProfit));
}
bool needClose = false;
//限制仓位
// 骄傲自负:5天内总盈利占账户余额5成以上(即翻1.2倍),限制仓位20%
// 赌徒心态:5天内总亏损占超过当前账户余额(即亏损超40%),限制仓位20%
if(accMargin > conceitMarginLimit*accBlance && accBlance>300){
if(totalProfit>0 && profitRatio>120){
needClose = true;
Print("(" +days+ "日)期间总入金:" +totalInBalance+ ", 总出金:" +totalOutBalance+
". -------- 总盈亏: " + totalProfit + ",账户余额:" +accBlance+ ",盈利比例: " +profitRatio+ "%");
}else if(totalProfit<0 && lossRatio>40){
needClose = true;
Print("(" +days+ "日)期间总入金:" +totalInBalance+ ", 总出金:" +totalOutBalance+
". -------- 总盈亏: " + totalProfit + ",账户余额:" +accBlance+ ",亏损比例: " +lossRatio+ "%");
}
}
//限制亏损
if(accBlance>300 && (profitRatio>120 || lossRatio>40)){
//当日总亏损(限制15%)
double todayProfit = getPeriodOrderProfit(1);
if(todayProfit<0 && MathAbs(todayProfit) > 0.15*(accBlance - todayProfit)){
needClose = true;
}
//每单亏损(限制10%)
if(OrderProfit()<0 && MathAbs(OrderProfit()) > 0.1*accBlance){
needClose = true;
}
}
if(needClose){
string lastCuOrderTicket = getLastCuTicket(); //最后一个非挂单
int i;
for(i=OrdersTotal()-1; i>=0; i--){
if(OrderSelect(i, SELECT_BY_POS)){
//挂单不能关闭
if(OrderType()!=OP_SELL && OrderType()!=OP_BUY){
continue;
}
//平仓
if(OrderTicket() == lastCuOrderTicket){
Print("限制-骄傲自负-控制仓位,关闭订单:"+ lastCuOrderTicket +
", 已用保证金:" + accMargin +
", 账户余额:" + accBlance +
", 币种:" + OrderSymbol() +
", 订单盈利:"+ OrderProfit()+
", 订单序号:" +i);
OrderClose(
lastCuOrderTicket,
OrderLots(),
MarketInfo(NULL, getModeByType(OrderType())),
slipPoint
);
}
}
}
}
}
/**
* 报价变动事件
*/
void OnTick(){
//风控 -- 生命线!!!
if(AccountEquity()> 0){
limitOrderMagin(); //1.限制-重仓
limitOrderLoss(); //2.限制-亏损(单笔/单日/单币种亏损数)
limitPeriodOrderNums(); //3.限制-下单量
limitOrderOpenTime(); //4.限制-下单时间
limitAddOrderWhenLoss(); //5.限制-亏损加仓
limitEditStopLoss(); //6.限制-修改止损
limitXauTrement(); //7.限制-黄金交易
limitConceitAndGamble(); //8.限制-骄傲自负&赌徒心态
}
}
//+------------------------------------------------------------------+