Skip to content

Instantly share code, notes, and snippets.

@C-L-STARK
Created March 31, 2026 15:08
Show Gist options
  • Select an option

  • Save C-L-STARK/6e6304fd61f63e0776e7f5455e172001 to your computer and use it in GitHub Desktop.

Select an option

Save C-L-STARK/6e6304fd61f63e0776e7f5455e172001 to your computer and use it in GitHub Desktop.
MT5 Amazing EA demo
//EA交易 => ...\MT5\MQL5\Experts
#property copyright "Copyright 2011,YQL"
#property link "https://hisanhe.com"
#property version "2.10"
#include <Trade/Trade.mqh>
//--- Enums
enum opentime
{
A = 1, // only place orders when a new bar appears on the selected timeframe
B = 2, // enforce a fixed delay between pending-order placements
C = 3 // place orders immediately when conditions are met
};
//--- CTrade instance
CTrade trade;
//--- Inputs
input group "01. 价格过滤"
input double InpFirstBuyBlockAbovePrice = 0.0; // 首单做多时,仅作提示线;价格高于此值不建议开多
input double InpFirstSellBlockBelowPrice = 0.0; // 首单做空时,仅作提示线;价格低于此值不建议开空
input double InpAddBuyBlockAbovePrice = 0.0; // 补多单限制:挂单价格高于此值时不再补多
input double InpAddSellBlockBelowPrice = 0.0; // 补空单限制:挂单价格低于此值时不再补空
input string InpPriceFilterStartTime = "00:00";
input string InpPriceFilterStopTime = "24:00";
input group "02. 交易时段"
input opentime InpOpenMode = B;
input ENUM_TIMEFRAMES InpOpenSignalTimeframe = PERIOD_M1; // A 模式使用的判定周期
input int InpOpenSpacingSeconds = 180; // B 模式下的最小开单间隔
input string InpEaStartTime = "01:00";
input string InpEaStopTime = "23:30";
input bool InpStopTradingAfterBasketClose = false;
input int InpRestartDelaySeconds = 0;
input group "03. 网格距离"
input int InpFirstEntryDistancePoints = 120;
input int InpPrimaryMinDistancePoints = 250;
input int InpSecondaryMinDistancePoints = 400;
input int InpPendingOrderTrailPoints = 30;
input int InpPrimaryGridStepPoints = 450;
input int InpSecondaryGridStepPoints = 700;
input double InpSecondaryModeTriggerLoss = 25.0; // 浮亏达到该值后切换到第二套距离参数,0 表示不切换
input group "04. 风控与平仓"
input bool InpEnableCounterTrendProtection = true;
input bool InpEnableTrendProtectionClose = true;
input bool InpEnableTrendOrdersWhenHedged = false;
input double InpMaxSideFloatingLossForAdds = 250.0; // 单边浮亏超过该值时停止继续加仓
input double InpSideCloseLossLimit = 80.0; // 单边亏损大于该容忍值时,不执行单边止盈
input double InpBasketTakeProfit = 10.0;
input bool InpScalePerSideTakeProfitByOrderCount = true;
input double InpPerSideTakeProfit = 4.0;
input double InpBasketStopLoss = 0.0;
input group "05. 手数管理"
input double InpInitialLot = 0.01;
input double InpMaxLot = 1.0;
input double InpLotIncrementPerOrder = 0.0;
input double InpLotMultiplier = 1.15;
input int InpLotPrecisionDigits = 2;
input group "06. 环境限制"
input int InpMagicNumber = 9527;
input int InpMaxOpenPositions = 12;
input int InpMaxSpreadPoints = 120;
input int InpMinAccountLeverage = 100;
input group "07. 每日控制"
input double InpDailyTargetProfit = 80.0; // 北京时间当日累计利润达到该值后,平仓并停止当日工作;0 表示不限制
input string InpDailyStartTimeBJ = "09:00"; // 北京时间:从该时刻起开始检查上面的日目标
input string InpDailyServerStartTime = "01:00"; // 服务器时间:每日到该时刻后才允许 EA 自动开始工作
input double InpDailyFloatingProfitStop = 35.0; // 服务器日内:当前 EA 浮盈达到该值后停止当日工作;0 表示不限制
input group "08. 订单备注"
input string InpNormalOrderComment = "备注1";
input string InpTrendOrderComment = "备注2";
input group "09. 图表显示"
input color InpBuyAverageLineColor = MediumSeaGreen;
input color InpSellAverageLineColor = Crimson;
//--- Global state
string gAuthVerifyUrlPrefix = "";
string gAuthVerifyUrl = "";
string gAuthMessage = "";
string gProductName = "GoldKylin";
int gLastDailyTargetHitBjDay = -1;
int gLastDailyFloatingStopServerDay = -1;
int gLastDailyServerStartCleanupDay = -1;
datetime gLastAuthCheckTime = 0;
int gAuthStatusCode = 0;
bool gStatsPanelVisible = true;
bool gButtonPanelVisible = false;
string gStatsPanelPrefix = "StatisticsPanel";
string gButtonPanelPrefix = "ButtonPanel";
int gPanelFontSize = 10;
uint gPanelAccentColor = Lime;
string gPanelFontName = "Microsoft YaHei";
int gPanelCorner = 1;
double gInitialDeposit = 0.0;
int gButtonPanelTopY = 0;
bool gAllowManualBuy = true;
bool gAllowManualSell = true;
int gDefaultPendingDistancePoints = 30;
double gRiskScaleFactor = 1.0;
bool gCanPlaceBuyPending = true;
bool gCanPlaceSellPending = true;
int gCandleRangeTimeframe = 1;
int gMaxCandleRangePoints = 0;
int gDisplayTextSize = 10;
uint gUiProfitColor = Lime;
uint gUiInfoColor = Blue;
uint gUiLossColor = Red;
datetime gTradingResumeTime = 0;
bool gIgnoreMarginCheck = true;
bool gBuyTrendSignalActive = false;
bool gSellTrendSignalActive = false;
string gReservedStatusText = "";
int gReservedTriggerMode = 3;
int gReservedThresholdOne = 20;
int gReservedThresholdTwo = 25;
int gReservedOffset = 0;
int gLegacySignalTimeframeMinutes = 15;
int gBrokerMinDistancePoints = 0;
int gLegacyMagicSeed = 346856;
int gTradeDeviationPoints = 0;
double gBuyProtectionPeakDiff = 0.0;
double gSellProtectionPeakDiff = 0.0;
string gSpreadLabelText = "Spread";
int gSpreadLabelOffset = 0;
bool gSpreadLabelVisible = true;
string gBuyProfitButtonName = "Button1";
string gSellProfitButtonName = "Button2";
string gTotalProfitButtonName = "Button5";
int gOpenUpColor = 55295;
int gOpenDownColor = 16777215;
int gCurrentCandleColor = 65280;
int gPositiveCandleColor = 65280;
int gCloseUpColor = 65535;
int gCloseDownColor = 12632256;
string gLeverageLabelText = "Lever";
string gSpreadSummaryText = "Spreads";
int gStatusCorner = 3;
int gStatusOffsetX = 25;
int gStatusOffsetY = 30;
bool gAdvancedUiEnabled = false;
string gProgramNameSnapshot = "Amazing不爆仓调教版";
double gReservedValueA = 0.0;
double gReservedValueB = 0.0;
int gReservedCounterA = 0;
int gReservedCounterB = 0;
int gReservedCounterC = 0;
string gColorStringBlack = "000,000,000";
string gColorStringBlue = "000,000,255";
int gUiPadding = 40;
int gReservedChartOffsetA = 0;
int gReservedChartOffsetB = 0;
int gReservedChartOffsetC = 0;
datetime gLastOpenSignalBarTime = 0;
datetime gEaWindowStartTime = 0;
datetime gEaWindowStopTime = 0;
datetime gPriceFilterWindowStart = 0;
datetime gPriceFilterWindowStop = 0;
long gReservedLongA = 0;
long gReservedLongB = 0;
int gLastOrderTicket = 0;
int gReservedFlagA = 0;
int gReservedFlagB = 0;
// Internal input-modified copies
double gSideCloseLossLimit;
double gMaxSideFloatingLossForAdds;
double gBasketStopLoss;
double gSecondaryModeTriggerLoss;
string gEaStartTime;
string gEaStopTime;
string gPriceFilterStartTime;
string gPriceFilterStopTime;
int gFirstEntryDistancePoints;
int gPrimaryMinDistancePoints;
int gSecondaryMinDistancePoints;
int gPrimaryGridStepPoints;
int gSecondaryGridStepPoints;
string gDailyServerStartTime;
double gDailyFloatingProfitStop;
#define On_top_of_this_price_not_Buy_first_order InpFirstBuyBlockAbovePrice
#define On_under_of_this_price_not_Sell_first_order InpFirstSellBlockBelowPrice
#define On_top_of_this_price_not_Buy_order InpAddBuyBlockAbovePrice
#define On_under_of_this_price_not_Sell_order InpAddSellBlockBelowPrice
#define Limit_StartTime InpPriceFilterStartTime
#define Limit_StopTime InpPriceFilterStopTime
#define CloseBuySell InpEnableCounterTrendProtection
#define HomeopathyCloseAll InpEnableTrendProtectionClose
#define Homeopathy InpEnableTrendOrdersWhenHedged
#define Over InpStopTradingAfterBasketClose
#define NextTime InpRestartDelaySeconds
#define Money InpSecondaryModeTriggerLoss
#define FirstStep gFirstEntryDistancePoints
#define MinDistance gPrimaryMinDistancePoints
#define TwoMinDistance gSecondaryMinDistancePoints
#define StepTrallOrders InpPendingOrderTrailPoints
#define Step gPrimaryGridStepPoints
#define TwoStep gSecondaryGridStepPoints
#define OpenMode InpOpenMode
#define TimeZone InpOpenSignalTimeframe
#define sleep InpOpenSpacingSeconds
#define MaxLoss InpMaxSideFloatingLossForAdds
#define MaxLossCloseAll InpSideCloseLossLimit
#define lot InpInitialLot
#define Maxlot InpMaxLot
#define PlusLot InpLotIncrementPerOrder
#define K_Lot InpLotMultiplier
#define DigitsLot InpLotPrecisionDigits
#define CloseAll InpBasketTakeProfit
#define Profit InpScalePerSideTakeProfitByOrderCount
#define StopProfit InpPerSideTakeProfit
#define StopLoss InpBasketStopLoss
#define Magic InpMagicNumber
#define Totals InpMaxOpenPositions
#define MaxSpread InpMaxSpreadPoints
#define Leverage InpMinAccountLeverage
#define EA_StartTime InpEaStartTime
#define EA_StopTime InpEaStopTime
#define clr1 InpBuyAverageLineColor
#define clr2 InpSellAverageLineColor
#define _MaxLossCloseAll gSideCloseLossLimit
#define _MaxLoss gMaxSideFloatingLossForAdds
#define _StopLoss gBasketStopLoss
#define _Money gSecondaryModeTriggerLoss
#define _EA_StartTime gEaStartTime
#define _EA_StopTime gEaStopTime
#define _Limit_StartTime gPriceFilterStartTime
#define _Limit_StopTime gPriceFilterStopTime
//--- MT4 compatibility wrappers
int TimeDay(datetime t) { MqlDateTime d; TimeToStruct(t,d); return d.day; }
int TimeMonth(datetime t) { MqlDateTime d; TimeToStruct(t,d); return d.mon; }
int TimeYear(datetime t) { MqlDateTime d; TimeToStruct(t,d); return d.year; }
int TimeHour(datetime t) { MqlDateTime d; TimeToStruct(t,d); return d.hour; }
int TimeMinute(datetime t) { MqlDateTime d; TimeToStruct(t,d); return d.min; }
bool IsDemo() { return AccountInfoInteger(ACCOUNT_TRADE_MODE)==ACCOUNT_TRADE_MODE_DEMO; }
bool IsTesting() { return (bool)MQLInfoInteger(MQL_TESTER); }
bool IsTradeAllowed() { return (bool)TerminalInfoInteger(TERMINAL_TRADE_ALLOWED); }
bool IsExpertEnabled(){ return (bool)MQLInfoInteger(MQL_TRADE_ALLOWED); }
int PositionTotal() { return PositionsTotal(); }
int AccountNumber() { return (int)AccountInfoInteger(ACCOUNT_LOGIN); }
double AccountBalance() { return AccountInfoDouble(ACCOUNT_BALANCE); }
double AccountEquity() { return AccountInfoDouble(ACCOUNT_EQUITY); }
double AccountMargin() { return AccountInfoDouble(ACCOUNT_MARGIN); }
double AccountProfit() { return AccountInfoDouble(ACCOUNT_PROFIT); }
double AccountFreeMargin(){ return AccountInfoDouble(ACCOUNT_MARGIN_FREE); }
int AccountLeverage(){ return (int)AccountInfoInteger(ACCOUNT_LEVERAGE); }
string AccountCurrency(){ return AccountInfoString(ACCOUNT_CURRENCY); }
string WindowExpertName(){ return MQLInfoString(MQL_PROGRAM_NAME); }
double MarketInfo(string sym, int mode) {
switch(mode) {
case 9: return SymbolInfoDouble(sym, SYMBOL_BID);
case 10: return SymbolInfoDouble(sym, SYMBOL_ASK);
case 13: return (double)SymbolInfoInteger(sym, SYMBOL_SPREAD);
case 14: return (double)SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL);
case 33: return (double)SymbolInfoInteger(sym, SYMBOL_TRADE_FREEZE_LEVEL);
case 28: return SymbolInfoDouble(sym, SYMBOL_MARGIN_INITIAL);
}
return 0.0;
}
bool ObjectSet(string name, int prop, double val) {
return ObjectSetInteger(0, name, (ENUM_OBJECT_PROPERTY_INTEGER)prop, (long)val);
}
bool ObjectSetText(string name, string text, int fontSize, string fontName, color textColor) {
if(ObjectFind(0,name) < 0)
return false;
ObjectSetString(0,name,OBJPROP_TEXT,text);
ObjectSetInteger(0,name,OBJPROP_FONTSIZE,fontSize);
ObjectSetString(0,name,OBJPROP_FONT,fontName);
return ObjectSetInteger(0,name,OBJPROP_COLOR,textColor);
}
int ObjFind(string name) { return (int)ObjectFind(0, name); }
bool ObjDelete(string name){ return ObjectDelete(0, name); }
bool ObjMove(string name, int pt, datetime t, double p){ return ObjectMove(0, name, pt, t, p); }
int GetDateKey(datetime timeValue) {
MqlDateTime dt;
TimeToStruct(timeValue,dt);
return dt.year*10000 + dt.mon*100 + dt.day;
}
bool HasReachedIntradayTime(datetime currentTime, const string timeText, datetime &targetTime) {
targetTime=StringToTime(StringFormat("%d.%d.%d %s",TimeYear(currentTime),TimeMonth(currentTime),TimeDay(currentTime),timeText));
return currentTime>=targetTime;
}
void GetDayBounds(datetime baseTime, int offsetDays, datetime &rangeStart, datetime &rangeStop) {
MqlDateTime dt;
TimeToStruct(baseTime,dt);
dt.hour=0;
dt.min=0;
dt.sec=0;
rangeStart=StructToTime(dt) + offsetDays*86400;
rangeStop=rangeStart + 86399;
}
double GetCurrentEaFloatingProfit() {
double profit=0.0;
for(int i=PositionTotal()-1; i>=0; i--) {
ulong ticket=PositionGetTicket(i);
if(!PositionSelectByTicket(ticket)) continue;
if(PositionGetString(POSITION_SYMBOL)!=Symbol()) continue;
if(PositionGetInteger(POSITION_MAGIC)!=Magic) continue;
profit+=PositionGetDouble(POSITION_PROFIT)+PositionGetDouble(POSITION_SWAP);
}
return profit;
}
void DeleteEaPendingOrders() {
for(int i=OrdersTotal()-1; i>=0; i--) {
ulong ticket=OrderGetTicket(i);
if(!OrderSelect(ticket)) continue;
if(OrderGetString(ORDER_SYMBOL)!=Symbol()) continue;
if(OrderGetInteger(ORDER_MAGIC)!=Magic) continue;
trade.OrderDelete(ticket);
}
}
void ShowStopStatus(const string text) {
if(ObjectFind(0,"Stop")<0) {
ObjectCreate(0,"Stop",OBJ_LABEL,0,0,0.0);
ObjectSetInteger(0,"Stop",OBJPROP_CORNER,gStatusCorner);
ObjectSetInteger(0,"Stop",OBJPROP_XDISTANCE,gStatusOffsetX);
ObjectSetInteger(0,"Stop",OBJPROP_YDISTANCE,gStatusOffsetY);
}
ObjectSetText("Stop",text,gDisplayTextSize,"Arial",gStatusTextColor);
}
void EnsureStatsToggleObjects() {
if(ObjectFind(0,"tubiao")<0)
ObjectCreate(0,"tubiao",OBJ_LABEL,0,0,0);
ObjectSetInteger(0,"tubiao",OBJPROP_CORNER,0);
ObjectSetInteger(0,"tubiao",OBJPROP_XDISTANCE,10);
ObjectSetInteger(0,"tubiao",OBJPROP_YDISTANCE,30);
ObjectSetInteger(0,"tubiao",OBJPROP_HIDDEN,1);
ObjectSetText("tubiao",MQLInfoString(MQL_PROGRAM_NAME),gPanelFontSize+2,gPanelFontName,DarkSlateGray);
string toggleName=gStatsPanelVisible?"tubiao1":"tubiao2";
string otherToggleName=gStatsPanelVisible?"tubiao2":"tubiao1";
string toggleText=gStatsPanelVisible?"收起":"展开";
ObjectDelete(0,otherToggleName);
if(ObjectFind(0,toggleName)<0)
ObjectCreate(0,toggleName,OBJ_BUTTON,0,0,0);
ObjectSetInteger(0,toggleName,OBJPROP_CORNER,1);
ObjectSetInteger(0,toggleName,OBJPROP_XDISTANCE,190);
ObjectSetInteger(0,toggleName,OBJPROP_YDISTANCE,50);
ObjectSetInteger(0,toggleName,OBJPROP_XSIZE,80);
ObjectSetInteger(0,toggleName,OBJPROP_YSIZE,24);
ObjectSetInteger(0,toggleName,OBJPROP_HIDDEN,1);
ObjectSetInteger(0,toggleName,OBJPROP_BACK,0);
ObjectSetInteger(0,toggleName,OBJPROP_SELECTABLE,0);
ObjectSetInteger(0,toggleName,OBJPROP_BGCOLOR,gStatsPanelVisible?LightSteelBlue:Lavender);
ObjectSetInteger(0,toggleName,OBJPROP_COLOR,DarkSlateGray);
ObjectSetString(0,toggleName,OBJPROP_FONT,gPanelFontName);
ObjectSetInteger(0,toggleName,OBJPROP_FONTSIZE,gPanelFontSize);
ObjectSetString(0,toggleName,OBJPROP_TEXT,toggleText);
}
//+------------------------------------------------------------------+
//| Daily profit functions |
//+------------------------------------------------------------------+
double GetDailyProfitBJ() {
double profit = 0;
datetime bjTime = TimeGMT() + 8*3600;
int curDay = TimeDay(bjTime), curMon = TimeMonth(bjTime), curYr = TimeYear(bjTime);
int brokerOff = (int)(TimeCurrent() - TimeGMT());
HistorySelect(0, TimeCurrent());
for(int i = (int)HistoryDealsTotal()-1; i >= 0; i--) {
ulong ticket = HistoryDealGetTicket(i);
if(HistoryDealGetString(ticket, DEAL_SYMBOL) != Symbol()) continue;
if(HistoryDealGetInteger(ticket, DEAL_MAGIC) != Magic) continue;
ENUM_DEAL_ENTRY de = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket, DEAL_ENTRY);
if(de == DEAL_ENTRY_IN) continue;
datetime ct = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME);
datetime bjCt = ct - brokerOff + 8*3600;
if(TimeDay(bjCt)==curDay && TimeMonth(bjCt)==curMon && TimeYear(bjCt)==curYr)
profit += HistoryDealGetDouble(ticket, DEAL_PROFIT)
+ HistoryDealGetDouble(ticket, DEAL_COMMISSION)
+ HistoryDealGetDouble(ticket, DEAL_SWAP);
}
for(int i = PositionTotal()-1; i >= 0; i--) {
ulong t = PositionGetTicket(i);
if(!PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL)!=Symbol() || PositionGetInteger(POSITION_MAGIC)!=Magic) continue;
profit += PositionGetDouble(POSITION_PROFIT)
+ PositionGetDouble(POSITION_SWAP);
}
return profit;
}
void CloseAllPositionsDailyCustom() {
for(int i = PositionTotal()-1; i >= 0; i--) {
ulong t = PositionGetTicket(i);
if(!PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL)!=Symbol() || PositionGetInteger(POSITION_MAGIC)!=Magic) continue;
trade.PositionClose(t);
}
for(int i = OrdersTotal()-1; i >= 0; i--) {
ulong t = OrderGetTicket(i);
if(!OrderSelect(t)) continue;
if(OrderGetString(ORDER_SYMBOL)!=Symbol() || OrderGetInteger(ORDER_MAGIC)!=Magic) continue;
trade.OrderDelete(t);
}
}
bool IsWithinTimeWindow(datetime currentTime, const string startTime, const string stopTime,
datetime &windowStart, datetime &windowStop) {
windowStart=StringToTime(StringFormat("%d.%d.%d %s",TimeYear(currentTime),TimeMonth(currentTime),TimeDay(currentTime),startTime));
windowStop =StringToTime(StringFormat("%d.%d.%d %s",TimeYear(currentTime),TimeMonth(currentTime),TimeDay(currentTime),stopTime));
if(windowStart<windowStop)
return !(currentTime<windowStart || currentTime>windowStop);
if(windowStart>windowStop)
return !(currentTime<windowStart && currentTime>windowStop);
return true;
}
void EnsurePriceLine(const string name, const double price, const ENUM_LINE_STYLE style, const color lineColor) {
if(price<=0.0) {
ObjectDelete(0,name);
return;
}
if(ObjectFind(0,name)<0)
ObjectCreate(0,name,OBJ_HLINE,0,0,price);
ObjectSetDouble(0,name,OBJPROP_PRICE,price);
ObjectSetInteger(0,name,OBJPROP_STYLE,style);
ObjectSetInteger(0,name,OBJPROP_COLOR,lineColor);
}
void UpdatePriceFilterLines(const bool visible) {
if(!visible) {
ObjectDelete(0,"HLINE_LONG");
ObjectDelete(0,"HLINE_SHORT");
ObjectDelete(0,"HLINE_LONGII");
ObjectDelete(0,"HLINE_SHORTII");
return;
}
EnsurePriceLine("HLINE_LONG", On_top_of_this_price_not_Buy_first_order, STYLE_SOLID, (color)10025880);
EnsurePriceLine("HLINE_SHORT", On_under_of_this_price_not_Sell_first_order, STYLE_SOLID, Magenta);
EnsurePriceLine("HLINE_LONGII", On_top_of_this_price_not_Buy_order, STYLE_DOT, (color)10025880);
EnsurePriceLine("HLINE_SHORTII",On_under_of_this_price_not_Sell_order, STYLE_DOT, Magenta);
}
//+------------------------------------------------------------------+
//| Helper: timeframe integer normalizer |
//+------------------------------------------------------------------+
int NormalizeLegacyTimeframeValue(int v) {
if(v>43200) return 0;
if(v>10080) return 43200;
if(v>1440) return 10080;
if(v>240) return 1440;
if(v>60) return 240;
if(v>30) return 60;
if(v>15) return 30;
if(v>5) return 15;
if(v>1) return 5;
if(v==1) return 1;
if(v==0) return (int)Period();
return 0;
}
//+------------------------------------------------------------------+
//| Helper: close most/least profitable orders (batch) |
//+------------------------------------------------------------------+
void CloseBestOrWorstPositions(int posType, int magicNum, int count, int mode) {
while(count > 0) {
ulong bestTicket = 0; double bestProfit = 0.0; bool found = false;
for(int i = PositionTotal()-1; i >= 0; i--) {
ulong t = PositionGetTicket(i);
if(!PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL)!=Symbol()) continue;
if(magicNum!=-1 && PositionGetInteger(POSITION_MAGIC)!=magicNum) continue;
int pt = (int)PositionGetInteger(POSITION_TYPE);
if(posType!=-100 && pt!=posType) continue;
double prf = PositionGetDouble(POSITION_PROFIT);
if(!found || (mode==1 && prf>bestProfit) || (mode==2 && prf<bestProfit))
{ bestProfit=prf; bestTicket=t; found=true; }
}
if(found) {
if(mode==1 && bestProfit>=0.0) trade.PositionClose(bestTicket);
if(mode==2 && bestProfit<0.0) trade.PositionClose(bestTicket);
}
count--;
}
}
//+------------------------------------------------------------------+
//| Helper: sum top-N profits by type |
//+------------------------------------------------------------------+
double SumTopPositionProfits(int posType, int magicNum, int profitMode, int topN) {
double arr[100]; ArrayInitialize(arr,0.0); int cnt=0;
for(int i = PositionTotal()-1; i >= 0; i--) {
ulong t = PositionGetTicket(i);
if(!PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL)!=Symbol()) continue;
if(magicNum!=-1 && PositionGetInteger(POSITION_MAGIC)!=magicNum) continue;
int pt = (int)PositionGetInteger(POSITION_TYPE);
if(posType!=-100 && pt!=posType) continue;
double prf = PositionGetDouble(POSITION_PROFIT);
if(profitMode==1 && prf>=0.0 && cnt<100) { arr[cnt]=prf; cnt++; }
if(profitMode==2 && prf<0.0 && cnt<100) { arr[cnt]=-prf; cnt++; }
}
double sum=0.0;
for(int pick=0; pick<topN && pick<cnt; pick++) {
int bestIndex=-1;
double bestValue=-1.0;
for(int i=0; i<cnt; i++) {
if(arr[i]>bestValue) {
bestValue=arr[i];
bestIndex=i;
}
}
if(bestIndex==-1 || bestValue<=0.0)
break;
sum+=bestValue;
arr[bestIndex]=-1.0;
}
return sum;
}
//+------------------------------------------------------------------+
//| Helper: Close-By loop — close all opposing pairs |
//+------------------------------------------------------------------+
void CloseByLoop() {
for(int tries=0; tries<50; tries++) {
ulong buyTkt=0, sellTkt=0;
for(int i=PositionTotal()-1; i>=0; i--) {
ulong t=PositionGetTicket(i);
if(!PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL)!=Symbol()||PositionGetInteger(POSITION_MAGIC)!=Magic) continue;
ENUM_POSITION_TYPE pt=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(pt==POSITION_TYPE_BUY && t>buyTkt) buyTkt=t;
if(pt==POSITION_TYPE_SELL && t>sellTkt) sellTkt=t;
}
if(buyTkt==0 || sellTkt==0) break;
if(!trade.PositionCloseBy(buyTkt, sellTkt)) break;
}
}
//+------------------------------------------------------------------+
//| Helper: auth always returns true (no online check) |
//+------------------------------------------------------------------+
bool PassAuthorizationCheck() { return true; }
//+------------------------------------------------------------------+
//| CloseAllTradesWithRetry: Close all with retry (manual close, big slippage) |
//+------------------------------------------------------------------+
int CloseAllTradesWithRetry(int dir) {
int retries=0;
for(;;) {
bool anyLeft=false;
for(int i=PositionTotal()-1; i>=0; i--) {
ulong t=PositionGetTicket(i);
if(!PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL)!=Symbol()||PositionGetInteger(POSITION_MAGIC)!=Magic) continue;
ENUM_POSITION_TYPE pt=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(pt==POSITION_TYPE_BUY && (dir==1||dir==0)) { if(!trade.PositionClose(t)) anyLeft=true; }
if(pt==POSITION_TYPE_SELL && (dir==-1||dir==0)){ if(!trade.PositionClose(t)) anyLeft=true; }
}
for(int i=OrdersTotal()-1; i>=0; i--) {
ulong t=OrderGetTicket(i);
if(!OrderSelect(t)) continue;
if(OrderGetString(ORDER_SYMBOL)!=Symbol()||OrderGetInteger(ORDER_MAGIC)!=Magic) continue;
ENUM_ORDER_TYPE ot=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
bool isBuy = (ot==ORDER_TYPE_BUY_STOP||ot==ORDER_TYPE_BUY_LIMIT);
bool isSell= (ot==ORDER_TYPE_SELL_STOP||ot==ORDER_TYPE_SELL_LIMIT);
if(isBuy && (dir==1||dir==0)) { if(!trade.OrderDelete(t)) anyLeft=true; }
if(isSell && (dir==-1||dir==0)) { if(!trade.OrderDelete(t)) anyLeft=true; }
}
if(!anyLeft) break;
if(++retries>10) { Print(Symbol(),"平仓超过10次,剩余未平仓单"); return 0; }
Sleep(1000);
}
return 1;
}
//+------------------------------------------------------------------+
//| CloseEaTradesWithRetry: Close all with retry (EA close, normal slippage) |
//+------------------------------------------------------------------+
int CloseEaTradesWithRetry(int dir) { return CloseAllTradesWithRetry(dir); }
//+------------------------------------------------------------------+
//| OnInit |
//+------------------------------------------------------------------+
int OnInit() {
_EA_StartTime = EA_StartTime;
_EA_StopTime = EA_StopTime;
_Limit_StartTime = Limit_StartTime;
_Limit_StopTime = Limit_StopTime;
_MaxLossCloseAll = -(MaxLossCloseAll);
_MaxLoss = -(MaxLoss);
_StopLoss = -(StopLoss);
_Money = -(Money);
gDailyServerStartTime = InpDailyServerStartTime;
gDailyFloatingProfitStop = InpDailyFloatingProfitStop;
gProgramNameSnapshot = WindowExpertName();
gLegacySignalTimeframeMinutes = NormalizeLegacyTimeframeValue(gLegacySignalTimeframeMinutes);
if((_Digits==5||_Digits==3)) gTradeDeviationPoints=30;
// CTrade one-time setup
trade.SetExpertMagicNumber(Magic);
trade.SetDeviationInPoints(gTradeDeviationPoints);
trade.SetTypeFilling(ORDER_FILLING_IOC);
StringReplace(_EA_StartTime," ",""); StringTrimLeft(_EA_StartTime); StringTrimRight(_EA_StartTime);
StringReplace(_EA_StopTime," ",""); StringTrimLeft(_EA_StopTime); StringTrimRight(_EA_StopTime);
if(_EA_StopTime=="24:00") _EA_StopTime="23:59:59";
StringReplace(_Limit_StartTime," ",""); StringTrimLeft(_Limit_StartTime); StringTrimRight(_Limit_StartTime);
StringReplace(_Limit_StopTime," ",""); StringTrimLeft(_Limit_StopTime); StringTrimRight(_Limit_StopTime);
if(_Limit_StopTime=="24:00") _Limit_StopTime="23:59:59";
StringReplace(gDailyServerStartTime," ",""); StringTrimLeft(gDailyServerStartTime); StringTrimRight(gDailyServerStartTime);
if(gDailyServerStartTime=="24:00") gDailyServerStartTime="23:59:59";
// Adjust price-distance parameters so pending orders respect broker limits.
gBrokerMinDistancePoints=(int)MathMax((double)SymbolInfoInteger(Symbol(),SYMBOL_TRADE_STOPS_LEVEL),
(double)SymbolInfoInteger(Symbol(),SYMBOL_TRADE_FREEZE_LEVEL))+1;
gPrimaryGridStepPoints = (int)MathMax((double)InpPrimaryGridStepPoints, (double)gBrokerMinDistancePoints);
gSecondaryGridStepPoints = (int)MathMax((double)InpSecondaryGridStepPoints, (double)gBrokerMinDistancePoints);
gFirstEntryDistancePoints = (int)MathMax((double)InpFirstEntryDistancePoints, (double)gBrokerMinDistancePoints);
gPrimaryMinDistancePoints = (int)MathMax((double)InpPrimaryMinDistancePoints, (double)gBrokerMinDistancePoints);
gSecondaryMinDistancePoints = (int)MathMax((double)InpSecondaryMinDistancePoints, (double)gBrokerMinDistancePoints);
// Calc initial deposit from history
double Lin_do_1=0.0;
HistorySelect(0, TimeCurrent());
for(int i=0; i<(int)HistoryDealsTotal(); i++) {
ulong t=HistoryDealGetTicket(i);
if((ENUM_DEAL_TYPE)HistoryDealGetInteger(t,DEAL_TYPE)!=DEAL_TYPE_BALANCE) continue;
double p=HistoryDealGetDouble(t,DEAL_PROFIT);
if(p>0.0) Lin_do_1+=p;
}
if(Lin_do_1==0.0) Lin_do_1=100.0;
gInitialDeposit=Lin_do_1;
Print("TotalDeposits=",gInitialDeposit);
EventSetTimer(1);
RenderStatisticsPanel();
PlaySound("Starting.wav");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| OnDeinit |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
ObjectDelete(0,"HLINE_LONGII"); ObjectDelete(0,"HLINE_SHORTII");
ObjectDelete(0,"HLINE_LONG"); ObjectDelete(0,"HLINE_SHORT");
ObjectsDeleteAll(0,-1);
}
//+------------------------------------------------------------------+
//| OnTimer |
//+------------------------------------------------------------------+
void OnTimer() {
RenderStatisticsPanel();
if(gButtonPanelVisible) RenderButtonPanel();
}
//+------------------------------------------------------------------+
//| OnTick (was start()) |
//+------------------------------------------------------------------+
void OnTick() {
if(!PassAuthorizationCheck()) { ExpertRemove(); return; }
//--- Daily schedule & broker-session controls
datetime serverNow=TimeCurrent();
datetime bjNow=TimeGMT() + 8*3600;
int currentServerDayKey=GetDateKey(serverNow);
int currentBjDayKey=GetDateKey(bjNow);
datetime dailyServerStartTime=0;
datetime dailyBjTargetStartTime=0;
bool hasReachedDailyServerStart=HasReachedIntradayTime(serverNow,gDailyServerStartTime,dailyServerStartTime);
bool hasReachedDailyTargetCheckStart=HasReachedIntradayTime(bjNow,InpDailyStartTimeBJ,dailyBjTargetStartTime);
bool dailyTargetReached=(currentBjDayKey==gLastDailyTargetHitBjDay);
if(!dailyTargetReached && InpDailyTargetProfit>0.0 && hasReachedDailyTargetCheckStart) {
double dailyProfit=GetDailyProfitBJ();
if(dailyProfit>=InpDailyTargetProfit) {
CloseAllPositionsDailyCustom();
gLastDailyTargetHitBjDay=currentBjDayKey;
Print("BJ daily target hit. Daily profit: ",dailyProfit);
RenderStatisticsPanel();
return;
}
}
dailyTargetReached=(currentBjDayKey==gLastDailyTargetHitBjDay);
double Bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
double Ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
int digits=_Digits;
double pt=_Point;
//--- Collect position/order statistics
double currentOpenPrice,sellFloatingProfit=0,buyFloatingProfit=0,buyLots=0,sellLots=0;
double positionLots,highestBuyReferencePrice=0,lowestBuyEntryPrice=0,highestSellEntryPrice=0,lowestSellReferencePrice=0;
double activeBuyStopPrice=0,activeSellStopPrice=0,sellWeightedPriceSum=0,buyWeightedPriceSum=0;
double buyAveragePrice=0,sellAveragePrice=0,pendingOrderPrice=0,pendingOrderLots=0;
int buyPositionCount=0,sellPositionCount=0,buyStopPendingCount=0,sellStopPendingCount=0;
ulong buyStopPendingTicket=0,sellStopPendingTicket=0;
bool usePrimaryGridSettings=false;
// Positions (BUY / SELL)
for(int i=PositionTotal()-1; i>=0; i--) {
ulong t=PositionGetTicket(i);
if(!PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL)!=Symbol()||PositionGetInteger(POSITION_MAGIC)!=Magic) continue;
ENUM_POSITION_TYPE pt2=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
positionLots=PositionGetDouble(POSITION_VOLUME);
currentOpenPrice=NormalizeDouble(PositionGetDouble(POSITION_PRICE_OPEN),digits);
double prf=PositionGetDouble(POSITION_PROFIT)+PositionGetDouble(POSITION_SWAP);
if(pt2==POSITION_TYPE_BUY) {
buyPositionCount++; buyLots+=positionLots; buyWeightedPriceSum+=currentOpenPrice*positionLots;
if(highestBuyReferencePrice<currentOpenPrice||highestBuyReferencePrice==0) highestBuyReferencePrice=currentOpenPrice;
if(lowestBuyEntryPrice>currentOpenPrice||lowestBuyEntryPrice==0) lowestBuyEntryPrice=currentOpenPrice;
buyFloatingProfit+=prf;
} else {
sellPositionCount++; sellLots+=positionLots; sellWeightedPriceSum+=currentOpenPrice*positionLots;
if(lowestSellReferencePrice>currentOpenPrice||lowestSellReferencePrice==0) lowestSellReferencePrice=currentOpenPrice;
if(highestSellEntryPrice<currentOpenPrice||highestSellEntryPrice==0) highestSellEntryPrice=currentOpenPrice;
sellFloatingProfit+=prf;
}
}
// Pending orders (BUY STOP / SELL STOP)
for(int i=OrdersTotal()-1; i>=0; i--) {
ulong t=OrderGetTicket(i);
if(!OrderSelect(t)) continue;
if(OrderGetString(ORDER_SYMBOL)!=Symbol()||OrderGetInteger(ORDER_MAGIC)!=Magic) continue;
ENUM_ORDER_TYPE ot=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
currentOpenPrice=NormalizeDouble(OrderGetDouble(ORDER_PRICE_OPEN),digits);
if(ot==ORDER_TYPE_BUY_STOP) {
buyStopPendingCount++;
if(highestBuyReferencePrice<currentOpenPrice||highestBuyReferencePrice==0) highestBuyReferencePrice=currentOpenPrice;
buyStopPendingTicket=t; activeBuyStopPrice=currentOpenPrice;
}
if(ot==ORDER_TYPE_SELL_STOP) {
sellStopPendingCount++;
if(lowestSellReferencePrice>currentOpenPrice||lowestSellReferencePrice==0) lowestSellReferencePrice=currentOpenPrice;
sellStopPendingTicket=t; activeSellStopPrice=currentOpenPrice;
}
}
//--- Button state colors
ObjectSetInteger(0,gBuyProfitButtonName,OBJPROP_BGCOLOR,buyFloatingProfit>0?17919:6908265);
ObjectSetInteger(0,gSellProfitButtonName,OBJPROP_BGCOLOR,sellFloatingProfit>0?17919:6908265);
ObjectSetInteger(0,gTotalProfitButtonName,OBJPROP_BGCOLOR,(buyFloatingProfit+sellFloatingProfit)>0?17919:6908265);
//--- Trend-pressure flags
gBuyTrendSignalActive=(buyLots>0&&sellLots/buyLots>3.0&&sellLots-buyLots>0.2);
gSellTrendSignalActive=(sellLots>0&&buyLots/sellLots>3.0&&buyLots-sellLots>0.2);
//--- Candle move indicator
double hi0=iHigh(Symbol(),(ENUM_TIMEFRAMES)gCandleRangeTimeframe,0);
double lo0=iLow (Symbol(),(ENUM_TIMEFRAMES)gCandleRangeTimeframe,0);
double hi5=iHigh(Symbol(),(ENUM_TIMEFRAMES)gCandleRangeTimeframe,5);
double lo5=iLow (Symbol(),(ENUM_TIMEFRAMES)gCandleRangeTimeframe,5);
double candleRiseDistance=hi0-lo5, candleDropDistance=lo0-hi5;
double candleRisePoints=(int)(candleRiseDistance/pt);
double candleDropPoints=MathAbs(candleDropDistance/pt);
//--- Trade-allowed check
int spread=(int)SymbolInfoInteger(Symbol(),SYMBOL_SPREAD);
if(AccountLeverage()<Leverage||!IsTradeAllowed()||!IsExpertEnabled()||IsStopped()||
buyPositionCount+sellPositionCount>=Totals||spread>MaxSpread||
(gMaxCandleRangePoints!=0&&candleRisePoints>=gMaxCandleRangePoints)||(gMaxCandleRangePoints!=0&&candleDropPoints>=gMaxCandleRangePoints)) {
gCanPlaceBuyPending=false; gCanPlaceSellPending=false;
if(ObjectFind(0,"Stop")<0) {
ObjectCreate(0,"Stop",OBJ_LABEL,0,0,0.0);
ObjectSetInteger(0,"Stop",OBJPROP_CORNER,gStatusCorner);
ObjectSetInteger(0,"Stop",OBJPROP_XDISTANCE,gStatusOffsetX);
ObjectSetInteger(0,"Stop",OBJPROP_YDISTANCE,gStatusOffsetY);
}
ObjectSetText("Stop","不符合设定环境,EA停止运行!",gDisplayTextSize,"Arial",gStatusTextColor);
} else {
gCanPlaceBuyPending=true; gCanPlaceSellPending=true;
if(ObjectFind(0,"Stop")<0) {
ObjectCreate(0,"Stop",OBJ_LABEL,0,0,0.0);
ObjectSetInteger(0,"Stop",OBJPROP_CORNER,gStatusCorner);
ObjectSetInteger(0,"Stop",OBJPROP_XDISTANCE,gStatusOffsetX);
ObjectSetInteger(0,"Stop",OBJPROP_YDISTANCE,gStatusOffsetY);
}
ObjectSetText("Stop","",gDisplayTextSize,"Arial",gStatusTextColor);
}
//--- EA time window check
datetime locNow = IsTesting()?TimeCurrent():TimeLocal();
gEaWindowStartTime=StringToTime(StringFormat("%d.%d.%d %s",TimeYear(locNow),TimeMonth(locNow),TimeDay(locNow),_EA_StartTime));
gEaWindowStopTime=StringToTime(StringFormat("%d.%d.%d %s",TimeYear(locNow),TimeMonth(locNow),TimeDay(locNow),_EA_StopTime));
bool inEATime=true;
if(gEaWindowStartTime<gEaWindowStopTime&&(locNow<gEaWindowStartTime||locNow>gEaWindowStopTime)) inEATime=false;
else if(gEaWindowStartTime>gEaWindowStopTime&&locNow<gEaWindowStartTime&&locNow>gEaWindowStopTime) inEATime=false;
bool inPriceFilterWindow=IsWithinTimeWindow(locNow,_Limit_StartTime,_Limit_StopTime,gPriceFilterWindowStart,gPriceFilterWindowStop);
UpdatePriceFilterLines(inPriceFilterWindow);
if(!inEATime) {
gCanPlaceBuyPending=false;
gCanPlaceSellPending=false;
ShowStopStatus("非开仓时间区间,停止开仓!");
}
if(WindowExpertName()!=gProgramNameSnapshot) {
gCanPlaceBuyPending=false; gCanPlaceSellPending=false;
}
if(TimeCurrent()<gTradingResumeTime && inEATime) {
gCanPlaceBuyPending=false; gCanPlaceSellPending=false;
ShowStopStatus("EA停止运行 "+string(NextTime)+"秒!");
}
//--- Over flag
if(Over&&buyPositionCount==0) gCanPlaceBuyPending=false;
if(Over&&sellPositionCount==0) gCanPlaceSellPending=false;
//--- Average price lines
ObjectDelete(0,"SLb"); ObjectDelete(0,"SLs");
if(buyPositionCount>0) {
buyAveragePrice=NormalizeDouble(buyWeightedPriceSum/buyLots,digits);
if(ObjectFind(0,"SLb")!=-1) {
ObjectMove(0,"SLb",0,iTime(Symbol(),PERIOD_M1,4),buyAveragePrice);
ObjectMove(0,"SLb",1,iTime(Symbol(),PERIOD_M1,0),buyAveragePrice);
} else {
ObjectCreate(0,"SLb",OBJ_TREND,0,iTime(Symbol(),PERIOD_M1,4),buyAveragePrice,iTime(Symbol(),PERIOD_M1,0),buyAveragePrice);
ObjectSetInteger(0,"SLb",OBJPROP_COLOR,clr1);
ObjectSetInteger(0,"SLb",OBJPROP_STYLE,0);
ObjectSetInteger(0,"SLb",OBJPROP_WIDTH,2);
ObjectSetInteger(0,"SLb",OBJPROP_BACK,0);
ObjectSetInteger(0,"SLb",OBJPROP_RAY_RIGHT,0);
}
}
if(sellPositionCount>0) {
sellAveragePrice=NormalizeDouble(sellWeightedPriceSum/sellLots,digits);
if(ObjectFind(0,"SLs")!=-1) {
ObjectMove(0,"SLs",0,iTime(Symbol(),PERIOD_M1,4),sellAveragePrice);
ObjectMove(0,"SLs",1,iTime(Symbol(),PERIOD_M1,0),sellAveragePrice);
} else {
ObjectCreate(0,"SLs",OBJ_TREND,0,iTime(Symbol(),PERIOD_M1,4),sellAveragePrice,iTime(Symbol(),PERIOD_M1,0),sellAveragePrice);
ObjectSetInteger(0,"SLs",OBJPROP_COLOR,clr2);
ObjectSetInteger(0,"SLs",OBJPROP_STYLE,0);
ObjectSetInteger(0,"SLs",OBJPROP_WIDTH,2);
ObjectSetInteger(0,"SLs",OBJPROP_BACK,0);
ObjectSetInteger(0,"SLs",OBJPROP_RAY_RIGHT,0);
}
}
ObjectSetText("Char.op",CharToString(74),gDisplayTextSize+2,"Wingdings",Red);
double basketFloatingProfit=buyFloatingProfit+sellFloatingProfit;
bool dailyFloatingStopReached=(currentServerDayKey==gLastDailyFloatingStopServerDay);
if(!hasReachedDailyServerStart && gLastDailyServerStartCleanupDay!=currentServerDayKey) {
DeleteEaPendingOrders();
gLastDailyServerStartCleanupDay=currentServerDayKey;
Print("Daily server start gate active. Pending orders cleared until ",gDailyServerStartTime);
}
if(!dailyFloatingStopReached && gDailyFloatingProfitStop>0.0 && basketFloatingProfit>=gDailyFloatingProfitStop) {
gLastDailyFloatingStopServerDay=currentServerDayKey;
DeleteEaPendingOrders();
dailyFloatingStopReached=true;
Print("Daily floating profit stop hit. Floating profit: ",basketFloatingProfit);
}
if(!hasReachedDailyServerStart) {
gCanPlaceBuyPending=false;
gCanPlaceSellPending=false;
ShowStopStatus("未到服务器日内开工时间,停止开仓!");
}
if(dailyTargetReached) {
gCanPlaceBuyPending=false;
gCanPlaceSellPending=false;
ShowStopStatus("北京时间日目标已达成,今日停止工作!");
}
if(dailyFloatingStopReached) {
gCanPlaceBuyPending=false;
gCanPlaceSellPending=false;
ShowStopStatus("今日浮动盈利达到阈值,停止工作!");
}
//--- Over: close-by all if profit reached
if(Over&&basketFloatingProfit>=CloseAll) {
gCanPlaceBuyPending=false; gCanPlaceSellPending=false;
CloseByLoop();
CloseEaTradesWithRetry(0);
if(NextTime>0) gTradingResumeTime=TimeCurrent()+NextTime;
RenderStatisticsPanel();
return;
}
if(!Over) {
// HomeopathyCloseAll: check for SS-comment trend orders
int ssBuy=0, ssSell=0;
if(HomeopathyCloseAll) {
for(int i=PositionTotal()-1;i>=0;i--) {
ulong t=PositionGetTicket(i);
if(!PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL)!=Symbol()||PositionGetInteger(POSITION_MAGIC)!=Magic) continue;
if(PositionGetString(POSITION_COMMENT)!=InpTrendOrderComment) continue;
ENUM_POSITION_TYPE ptp=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(ptp==POSITION_TYPE_BUY) ssBuy++;
if(ptp==POSITION_TYPE_SELL) ssSell++;
}
}
// SingleSide profit check
if((ssSell<1||!HomeopathyCloseAll)&&buyFloatingProfit>_MaxLossCloseAll&&sellFloatingProfit>_MaxLossCloseAll) {
ObjectSetText("Char.op",CharToString(251),gDisplayTextSize+2,"Wingdings",Silver);
if((Profit&&buyFloatingProfit>StopProfit*buyPositionCount)||((!Profit)&&buyFloatingProfit>StopProfit)) {
Print("买单盈利 ",buyFloatingProfit);
CloseEaTradesWithRetry(1); RenderStatisticsPanel(); return;
}
if((Profit&&sellFloatingProfit>StopProfit*sellPositionCount)||((!Profit)&&sellFloatingProfit>StopProfit)) {
Print("卖单盈利 ",sellFloatingProfit);
CloseEaTradesWithRetry(-1); RenderStatisticsPanel(); return;
}
}
// HomeopathyCloseAll: trend plus whole closeAll
if(HomeopathyCloseAll&&(ssBuy>0||ssSell>0)&&basketFloatingProfit>=CloseAll) {
CloseByLoop();
CloseEaTradesWithRetry(0);
if(NextTime>0) gTradingResumeTime=TimeCurrent()+NextTime;
RenderStatisticsPanel(); return;
}
// Global close-all by balance
if(basketFloatingProfit>=CloseAll&&(buyFloatingProfit<=_MaxLossCloseAll||sellFloatingProfit<=_MaxLossCloseAll)) {
CloseByLoop();
CloseEaTradesWithRetry(0);
if(NextTime>0) gTradingResumeTime=TimeCurrent()+NextTime;
RenderStatisticsPanel(); return;
}
}
//--- Stop-loss check
if(_StopLoss!=0&&basketFloatingProfit<=_StopLoss) {
Print("Buy Loss ",buyFloatingProfit," Sell Loss ",sellFloatingProfit);
CloseEaTradesWithRetry(0);
if(NextTime>0) gTradingResumeTime=TimeCurrent()+NextTime;
RenderStatisticsPanel(); return;
}
//--- MaxLoss indicator
if(buyFloatingProfit<=_MaxLoss) ObjectSetText("Char.b",CharToString(225)+CharToString(251),gDisplayTextSize,"Wingdings",Red);
else ObjectSetText("Char.b",CharToString(233),gDisplayTextSize,"Wingdings",Lime);
if(sellFloatingProfit<=_MaxLoss) ObjectSetText("Char.s",CharToString(226)+CharToString(251),gDisplayTextSize,"Wingdings",Red);
else ObjectSetText("Char.s",CharToString(234),gDisplayTextSize,"Wingdings",Lime);
//--- Candle direction color
if(iOpen(Symbol(),PERIOD_M1,0)>iOpen(Symbol(),PERIOD_M1,1)) gCurrentCandleColor=gOpenUpColor;
if(iOpen(Symbol(),PERIOD_M1,0)<iOpen(Symbol(),PERIOD_M1,1)) gCurrentCandleColor=gOpenDownColor;
if(iClose(Symbol(),PERIOD_M1,0)>iClose(Symbol(),PERIOD_M1,1)) gCurrentCandleColor=gCloseUpColor;
if(iClose(Symbol(),PERIOD_M1,0)<iClose(Symbol(),PERIOD_M1,1)) gCurrentCandleColor=gCloseDownColor;
//--- Reverse protection (CloseBuySell)
if(CloseBuySell) {
double diffB=SumTopPositionProfits(0,Magic,1,(int)gProtectionCloseProfitCount)-SumTopPositionProfits(0,Magic,2,(int)gProtectionCloseLossCount);
if(gBuyProtectionPeakDiff<diffB) gBuyProtectionPeakDiff=diffB;
if(gBuyProtectionPeakDiff>0&&diffB>0) {
// find max-profit buy lot
double maxBuyLot=0; double minBuyPrf=0; bool foundB=false;
for(int i=PositionTotal()-1;i>=0;i--) {
ulong t=PositionGetTicket(i);
if(!PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL)!=Symbol()||PositionGetInteger(POSITION_MAGIC)!=Magic) continue;
if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE)!=POSITION_TYPE_BUY) continue;
double prf=PositionGetDouble(POSITION_PROFIT);
if(!foundB||prf>minBuyPrf) { minBuyPrf=prf; maxBuyLot=PositionGetDouble(POSITION_VOLUME); foundB=true; }
}
if(buyLots>maxBuyLot*3+sellLots&&buyPositionCount>3) {
CloseBestOrWorstPositions(0,Magic,(int)gProtectionCloseProfitCount,1);
CloseBestOrWorstPositions(0,Magic,(int)gProtectionCloseLossCount,2);
gBuyProtectionPeakDiff=0; gSellProtectionPeakDiff=0;
}
}
double diffS=SumTopPositionProfits(1,Magic,1,(int)gProtectionCloseProfitCount)-SumTopPositionProfits(1,Magic,2,(int)gProtectionCloseLossCount);
if(gSellProtectionPeakDiff<diffS) gSellProtectionPeakDiff=diffS;
if(gSellProtectionPeakDiff>0&&diffS>0) {
double maxSellLot=0; double minSellPrf=0; bool foundS=false;
for(int i=PositionTotal()-1;i>=0;i--) {
ulong t=PositionGetTicket(i);
if(!PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL)!=Symbol()||PositionGetInteger(POSITION_MAGIC)!=Magic) continue;
if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE)!=POSITION_TYPE_SELL) continue;
double prf=PositionGetDouble(POSITION_PROFIT);
if(!foundS||prf>minSellPrf) { minSellPrf=prf; maxSellLot=PositionGetDouble(POSITION_VOLUME); foundS=true; }
}
if(sellLots>maxSellLot*3+buyLots&&sellPositionCount>3) {
CloseBestOrWorstPositions(1,Magic,(int)gProtectionCloseProfitCount,1);
CloseBestOrWorstPositions(1,Magic,(int)gProtectionCloseLossCount,2);
gBuyProtectionPeakDiff=0; gSellProtectionPeakDiff=0;
}
}
}
//--- Second-param flag
if((_Money!=0&&basketFloatingProfit>_Money)||_Money==0) usePrimaryGridSettings=true;
if(_Money!=0&&basketFloatingProfit<=_Money) usePrimaryGridSettings=false;
//--- Open mode gate
if((OpenMode==A&&gLastOpenSignalBarTime!=iTime(NULL,TimeZone,0))||OpenMode==B||OpenMode==C) {
//============ BUY STOP placement ============
if(buyStopPendingCount==0&&buyFloatingProfit>_MaxLoss&&gCanPlaceBuyPending&&gAllowManualBuy) {
if(buyPositionCount==0) pendingOrderPrice=NormalizeDouble(FirstStep*pt+Ask,digits);
else {
pendingOrderPrice=usePrimaryGridSettings?NormalizeDouble(MinDistance*pt+Ask,digits):NormalizeDouble(TwoMinDistance*pt+Ask,digits);
if(pendingOrderPrice<NormalizeDouble(lowestBuyEntryPrice-Step*pt,digits)&&usePrimaryGridSettings) pendingOrderPrice=NormalizeDouble(Step*pt+Ask,digits);
if(pendingOrderPrice<NormalizeDouble(lowestBuyEntryPrice-TwoStep*pt,digits)&&!usePrimaryGridSettings&&_Money!=0) pendingOrderPrice=NormalizeDouble(TwoStep*pt+Ask,digits);
}
bool canOpenB=(buyPositionCount==0)||
(highestBuyReferencePrice!=0&&pendingOrderPrice>=NormalizeDouble(Step*pt+highestBuyReferencePrice,digits)&&gBuyTrendSignalActive&&usePrimaryGridSettings)||
(highestBuyReferencePrice!=0&&pendingOrderPrice>=NormalizeDouble(TwoStep*pt+highestBuyReferencePrice,digits)&&gBuyTrendSignalActive&&!usePrimaryGridSettings&&_Money!=0)||
(lowestBuyEntryPrice!=0&&pendingOrderPrice<=NormalizeDouble(lowestBuyEntryPrice-Step*pt,digits)&&usePrimaryGridSettings)||
(lowestBuyEntryPrice!=0&&pendingOrderPrice<=NormalizeDouble(lowestBuyEntryPrice-TwoStep*pt,digits)&&!usePrimaryGridSettings&&_Money!=0)||
(Homeopathy&&highestBuyReferencePrice!=0&&pendingOrderPrice>=NormalizeDouble(Step*pt+highestBuyReferencePrice,digits)&&buyLots==sellLots);
if(canOpenB) {
pendingOrderLots=(buyPositionCount==0)?lot:NormalizeDouble(buyPositionCount*PlusLot+lot*MathPow(K_Lot,buyPositionCount),DigitsLot);
if(pendingOrderLots>Maxlot) pendingOrderLots=Maxlot;
bool hasMargin=(pendingOrderLots*2<AccountFreeMargin()/SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_INITIAL)&&buyPositionCount>0)||gIgnoreMarginCheck;
if(hasMargin) {
if(On_top_of_this_price_not_Buy_order==0||(inPriceFilterWindow&&buyPositionCount>=1&&pendingOrderPrice<On_top_of_this_price_not_Buy_order)||buyPositionCount==0||!inPriceFilterWindow) {
// Timing check
ulong lastBuyTkt=0; datetime lastBuyTime=0;
for(int i=OrdersTotal()-1;i>=0;i--) {
ulong t=OrderGetTicket(i); if(!OrderSelect(t)) continue;
if(OrderGetString(ORDER_SYMBOL)!=Symbol()||OrderGetInteger(ORDER_MAGIC)!=Magic) continue;
if((ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE)!=ORDER_TYPE_BUY_STOP) continue;
if(t>lastBuyTkt){ lastBuyTkt=t; lastBuyTime=(datetime)OrderGetInteger(ORDER_TIME_SETUP); }
}
long buySpacingSeconds=(long)(TimeCurrent()-lastBuyTime);
bool timingOK=((buySpacingSeconds>=(long)sleep&&OpenMode==B)||OpenMode==C||OpenMode==A);
if(timingOK) {
bool isSSBuy=(highestBuyReferencePrice!=0&&pendingOrderPrice>=NormalizeDouble(Step*pt+highestBuyReferencePrice,digits)&&gBuyTrendSignalActive&&usePrimaryGridSettings)||
(Homeopathy&&highestBuyReferencePrice!=0&&pendingOrderPrice>=NormalizeDouble(Step*pt+highestBuyReferencePrice,digits)&&buyLots==sellLots);
string comment=isSSBuy?InpTrendOrderComment:InpNormalOrderComment;
if(trade.BuyStop(pendingOrderLots,pendingOrderPrice,Symbol(),0,0,ORDER_TIME_GTC,0,comment))
Print(Symbol()+"开单成功,单号:",trade.ResultOrder());
else
Print(Symbol()+"开单失败!错误码:",trade.ResultRetcode()," ",trade.ResultRetcodeDescription());
}
}
} else Comment("Lot ",DoubleToString(pendingOrderLots,2));
}
}
//============ SELL STOP placement ============
if(sellStopPendingCount==0&&sellFloatingProfit>_MaxLoss&&gCanPlaceSellPending&&gAllowManualSell) {
if(sellPositionCount==0) pendingOrderPrice=NormalizeDouble(Bid-FirstStep*pt,digits);
else {
pendingOrderPrice=usePrimaryGridSettings?NormalizeDouble(Bid-MinDistance*pt,digits):NormalizeDouble(Bid-TwoMinDistance*pt,digits);
if(pendingOrderPrice<NormalizeDouble(Step*pt+highestSellEntryPrice,digits)&&usePrimaryGridSettings) pendingOrderPrice=NormalizeDouble(Bid-Step*pt,digits);
if(pendingOrderPrice<NormalizeDouble(TwoStep*pt+highestSellEntryPrice,digits)&&!usePrimaryGridSettings&&_Money!=0) pendingOrderPrice=NormalizeDouble(Bid-TwoStep*pt,digits);
}
bool canOpenS=(sellPositionCount==0)||
(lowestSellReferencePrice!=0&&pendingOrderPrice<=NormalizeDouble(lowestSellReferencePrice-Step*pt,digits)&&gSellTrendSignalActive&&usePrimaryGridSettings)||
(lowestSellReferencePrice!=0&&pendingOrderPrice<=NormalizeDouble(lowestSellReferencePrice-TwoStep*pt,digits)&&gSellTrendSignalActive&&!usePrimaryGridSettings&&_Money!=0)||
(highestSellEntryPrice!=0&&pendingOrderPrice>=NormalizeDouble(Step*pt+highestSellEntryPrice,digits)&&usePrimaryGridSettings)||
(highestSellEntryPrice!=0&&pendingOrderPrice>=NormalizeDouble(TwoStep*pt+highestSellEntryPrice,digits)&&!usePrimaryGridSettings&&_Money!=0)||
(Homeopathy&&lowestSellReferencePrice!=0&&pendingOrderPrice<=NormalizeDouble(lowestSellReferencePrice-Step*pt,digits)&&buyLots==sellLots);
if(canOpenS) {
pendingOrderLots=(sellPositionCount==0)?lot:NormalizeDouble(sellPositionCount*PlusLot+lot*MathPow(K_Lot,sellPositionCount),DigitsLot);
if(pendingOrderLots>Maxlot) pendingOrderLots=Maxlot;
bool hasMargin=(pendingOrderLots*2<AccountFreeMargin()/SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_INITIAL)&&sellPositionCount>0)||gIgnoreMarginCheck;
if(hasMargin) {
if(On_under_of_this_price_not_Sell_order==0||(inPriceFilterWindow&&sellPositionCount>=1&&pendingOrderPrice>On_under_of_this_price_not_Sell_order)||sellPositionCount==0||!inPriceFilterWindow) {
ulong lastSellTkt=0; datetime lastSellTime=0;
for(int i=OrdersTotal()-1;i>=0;i--) {
ulong t=OrderGetTicket(i); if(!OrderSelect(t)) continue;
if(OrderGetString(ORDER_SYMBOL)!=Symbol()||OrderGetInteger(ORDER_MAGIC)!=Magic) continue;
if((ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE)!=ORDER_TYPE_SELL_STOP) continue;
if(t>lastSellTkt){ lastSellTkt=t; lastSellTime=(datetime)OrderGetInteger(ORDER_TIME_SETUP); }
}
long sellSpacingSeconds=(long)(TimeCurrent()-lastSellTime);
bool timingOK=((sellSpacingSeconds>=(long)sleep&&OpenMode==B)||OpenMode==C||OpenMode==A);
if(timingOK) {
bool isSSSell=(lowestSellReferencePrice!=0&&pendingOrderPrice<=NormalizeDouble(lowestSellReferencePrice-Step*pt,digits)&&gSellTrendSignalActive&&usePrimaryGridSettings)||
(Homeopathy&&lowestSellReferencePrice!=0&&pendingOrderPrice<=NormalizeDouble(lowestSellReferencePrice-Step*pt,digits)&&buyLots==sellLots);
string comment=isSSSell?InpTrendOrderComment:InpNormalOrderComment;
if(trade.SellStop(pendingOrderLots,pendingOrderPrice,Symbol(),0,0,ORDER_TIME_GTC,0,comment))
Print(Symbol()+"开单成功,单号:",trade.ResultOrder());
else
Print(Symbol()+"开单失败!错误码:",trade.ResultRetcode()," ",trade.ResultRetcodeDescription());
}
}
} else Comment("Lot ",DoubleToString(pendingOrderLots,2));
}
}
gLastOpenSignalBarTime=iTime(NULL,TimeZone,0);
}
//--- Profit display
double totalFloatingProfitText=buyFloatingProfit+sellFloatingProfit;
if(buyLots>0) {
color buyProfitTextColor=(buyFloatingProfit>0)?255:65280;
ObjectSetText("ProfitB",StringFormat("Buy %d单 , %s手, 盈亏= %s",buyPositionCount,DoubleToString(buyLots,2),DoubleToString(buyFloatingProfit,2)),gDisplayTextSize,"Arial",buyProfitTextColor);
} else ObjectSetText("ProfitB","",gDisplayTextSize,"Arial",Gray);
if(sellLots>0) {
color sellProfitTextColor=(sellFloatingProfit>0)?255:65280;
ObjectSetText("ProfitS",StringFormat("Sell %d单 , %s手, 盈亏= %s",sellPositionCount,DoubleToString(sellLots,2),DoubleToString(sellFloatingProfit,2)),gDisplayTextSize,"Arial",sellProfitTextColor);
} else ObjectSetText("ProfitS","",gDisplayTextSize,"Arial",Gray);
if(sellLots+buyLots>0) {
color totalProfitTextColor=(totalFloatingProfitText>0)?255:65280;
ObjectSetText("Profit",StringFormat("总盈亏= %s",DoubleToString(totalFloatingProfitText,2)),gDisplayTextSize,"Arial",totalProfitTextColor);
} else ObjectSetText("Profit","",gDisplayTextSize,"Arial",White);
//--- Trailing pending BUY STOP
if(activeBuyStopPrice!=0&&gCanPlaceBuyPending&&gAllowManualBuy) {
if(buyPositionCount==0) pendingOrderPrice=NormalizeDouble(FirstStep*pt+Ask,digits);
else if(usePrimaryGridSettings) pendingOrderPrice=NormalizeDouble(MinDistance*pt+Ask,digits);
else if(_Money!=0) pendingOrderPrice=NormalizeDouble(TwoMinDistance*pt+Ask,digits);
if(NormalizeDouble(activeBuyStopPrice-StepTrallOrders*pt,digits)>pendingOrderPrice) {
if(!trade.OrderModify(buyStopPendingTicket,pendingOrderPrice,0,0,ORDER_TIME_GTC,0))
Print("Error OrderModify BuyStop → ",trade.ResultRetcodeDescription());
else Print("Order Buy Modify OOP ",activeBuyStopPrice,"->",pendingOrderPrice);
}
}
//--- Trailing pending SELL STOP
if(activeSellStopPrice!=0&&gCanPlaceSellPending&&gAllowManualSell) {
if(sellPositionCount==0) pendingOrderPrice=NormalizeDouble(Bid-FirstStep*pt,digits);
else if(usePrimaryGridSettings) pendingOrderPrice=NormalizeDouble(Bid-MinDistance*pt,digits);
else if(_Money!=0) pendingOrderPrice=NormalizeDouble(Bid-TwoMinDistance*pt,digits);
if(NormalizeDouble(StepTrallOrders*pt+activeSellStopPrice,digits)<pendingOrderPrice) {
if(!trade.OrderModify(sellStopPendingTicket,pendingOrderPrice,0,0,ORDER_TIME_GTC,0))
Print("Error OrderModify SellStop → ",trade.ResultRetcodeDescription());
else Print("Order Sell Modify OOP ",activeSellStopPrice,"->",pendingOrderPrice);
}
}
RenderStatisticsPanel();
}
// Forward declarations for UI functions
void RenderButtonPanel();
void RenderStatisticsPanel();
void UpdateButtonObject(string,string,string,int,int,int,int,int,uint,uint,uint,uint,string,int);
color gStatusTextColor=DimGray;
int gProtectionCloseProfitCount=1;
int gProtectionCloseLossCount=2;
//+------------------------------------------------------------------+
//| OnChartEvent |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) {
if(id==CHARTEVENT_OBJECT_CLICK) {
if(sparam=="tubiao2"||sparam=="tubiao1") {
if(gStatsPanelVisible) { gStatsPanelVisible=false; ObjectDelete(0,"tubiao1"); }
else { gStatsPanelVisible=true; ObjectDelete(0,"tubiao2"); }
// Delete panel objects
for(int i=ObjectsTotal(0,-1,-1)-1;i>=0;i--) {
string n=ObjectName(0,i,-1,-1);
if(StringFind(n,gStatsPanelPrefix,0)>=0) ObjectDelete(0,n);
if(StringFind(n,gButtonPanelPrefix,0)>=0) ObjectDelete(0,n);
}
RenderStatisticsPanel();
if(gButtonPanelVisible) RenderButtonPanel();
return;
}
if(sparam==gStatsPanelPrefix+"OpenBoard") {
ObjectSetInteger(0,gStatsPanelPrefix+"OpenBoard",OBJPROP_STATE,0);
if(gButtonPanelVisible) {
for(int i=ObjectsTotal(0,-1,-1)-1;i>=0;i--) {
string n=ObjectName(0,i,-1,-1);
if(StringFind(n,gButtonPanelPrefix,0)>=0) ObjectDelete(0,n);
}
} else RenderButtonPanel();
gButtonPanelVisible=!(gButtonPanelVisible);
ChartRedraw(0);
return;
}
}
// Button panel actions
if(ObjectGetInteger(0,gButtonPanelPrefix+"StopAll",OBJPROP_STATE)==1) {
gAllowManualBuy=false; gAllowManualSell=false;
RenderStatisticsPanel(); if(gButtonPanelVisible) RenderButtonPanel(); return;
}
if(ObjectGetInteger(0,gButtonPanelPrefix+"StopBuy",OBJPROP_STATE)==1)
{ gAllowManualBuy=false; RenderStatisticsPanel(); if(gButtonPanelVisible) RenderButtonPanel(); }
else { gAllowManualBuy=true; RenderStatisticsPanel(); if(gButtonPanelVisible) RenderButtonPanel(); }
if(ObjectGetInteger(0,gButtonPanelPrefix+"StopSell",OBJPROP_STATE)==1)
{ gAllowManualSell=false; RenderStatisticsPanel(); if(gButtonPanelVisible) RenderButtonPanel(); }
else { gAllowManualSell=true; RenderStatisticsPanel(); if(gButtonPanelVisible) RenderButtonPanel(); }
// Close buttons
if(ObjectGetInteger(0,gButtonPanelPrefix+"CloseAll",OBJPROP_STATE)==1) {
CloseAllTradesWithRetry(0); ObjectSetInteger(0,gButtonPanelPrefix+"CloseAll",OBJPROP_STATE,0); }
if(ObjectGetInteger(0,gButtonPanelPrefix+"CloseBuy",OBJPROP_STATE)==1) {
CloseAllTradesWithRetry(1); ObjectSetInteger(0,gButtonPanelPrefix+"CloseBuy",OBJPROP_STATE,0); }
if(ObjectGetInteger(0,gButtonPanelPrefix+"CloseSell",OBJPROP_STATE)==1) {
CloseAllTradesWithRetry(-1); ObjectSetInteger(0,gButtonPanelPrefix+"CloseSell",OBJPROP_STATE,0); }
if(ObjectGetInteger(0,gButtonPanelPrefix+"CloseProfit",OBJPROP_STATE)==1) {
for(int i=PositionTotal()-1;i>=0;i--) {
ulong t=PositionGetTicket(i); if(!PositionSelectByTicket(t)) continue;
if(PositionGetInteger(POSITION_MAGIC)!=Magic) continue;
if(PositionGetDouble(POSITION_PROFIT)+PositionGetDouble(POSITION_SWAP)>=0)
trade.PositionClose(t);
}
ObjectSetInteger(0,gButtonPanelPrefix+"CloseProfit",OBJPROP_STATE,0);
}
if(ObjectGetInteger(0,gButtonPanelPrefix+"CloseLoss",OBJPROP_STATE)==1) {
for(int i=PositionTotal()-1;i>=0;i--) {
ulong t=PositionGetTicket(i); if(!PositionSelectByTicket(t)) continue;
if(PositionGetInteger(POSITION_MAGIC)!=Magic) continue;
if(PositionGetDouble(POSITION_PROFIT)+PositionGetDouble(POSITION_SWAP)<0)
trade.PositionClose(t);
}
ObjectSetInteger(0,gButtonPanelPrefix+"CloseLoss",OBJPROP_STATE,0);
}
if(ObjectGetInteger(0,gButtonPanelPrefix+"CloseSymbol",OBJPROP_STATE)==1) {
for(int i=PositionTotal()-1;i>=0;i--) {
ulong t=PositionGetTicket(i); if(!PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL)==Symbol()) trade.PositionClose(t);
}
ObjectSetInteger(0,gButtonPanelPrefix+"CloseSymbol",OBJPROP_STATE,0);
}
if(ObjectGetInteger(0,gButtonPanelPrefix+"CloseAccountAll",OBJPROP_STATE)==1) {
for(int i=PositionTotal()-1;i>=0;i--) {
ulong t=PositionGetTicket(i); if(!PositionSelectByTicket(t)) continue;
trade.PositionClose(t);
}
ObjectSetInteger(0,gButtonPanelPrefix+"CloseAccountAll",OBJPROP_STATE,0);
}
}
//+------------------------------------------------------------------+
//| RenderButtonPanel: Button panel UI |
//+------------------------------------------------------------------+
void RenderButtonPanel() {
int currentY=gButtonPanelTopY, rowGap=3, buttonHeight=30;
if(ObjectFind(0,gButtonPanelPrefix+"Panel")<0) {
ObjectCreate(0,gButtonPanelPrefix+"Panel",OBJ_RECTANGLE_LABEL,0,0,0);
ObjectSetInteger(0,gButtonPanelPrefix+"Panel",OBJPROP_XDISTANCE,308);
ObjectSetInteger(0,gButtonPanelPrefix+"Panel",OBJPROP_YDISTANCE,gButtonPanelTopY);
ObjectSetInteger(0,gButtonPanelPrefix+"Panel",OBJPROP_XSIZE,300);
ObjectSetInteger(0,gButtonPanelPrefix+"Panel",OBJPROP_YSIZE,205);
ObjectSetInteger(0,gButtonPanelPrefix+"Panel",OBJPROP_BORDER_TYPE,0);
ObjectSetInteger(0,gButtonPanelPrefix+"Panel",OBJPROP_CORNER,1);
ObjectSetInteger(0,gButtonPanelPrefix+"Panel",OBJPROP_COLOR,15453831);
ObjectSetInteger(0,gButtonPanelPrefix+"Panel",OBJPROP_BACK,0);
ObjectSetInteger(0,gButtonPanelPrefix+"Panel",OBJPROP_HIDDEN,1);
}
ObjectSetInteger(0,gButtonPanelPrefix+"Panel",OBJPROP_BGCOLOR,PowderBlue);
currentY+=rowGap;
UpdateButtonObject(gButtonPanelPrefix+"StopAll","停止交易","开启交易",298,currentY,280,buttonHeight,gPanelCorner,White,White,Brown,SteelBlue,gPanelFontName,gPanelFontSize);
currentY+=rowGap+buttonHeight;
UpdateButtonObject(gButtonPanelPrefix+"StopBuy","停止做多","开启做多",298,currentY,135,buttonHeight,gPanelCorner,White,White,Brown,SteelBlue,gPanelFontName,gPanelFontSize);
UpdateButtonObject(gButtonPanelPrefix+"StopSell","停止做空","开启做空",154,currentY,135,buttonHeight,gPanelCorner,White,White,Brown,SteelBlue,gPanelFontName,gPanelFontSize);
currentY+=rowGap+buttonHeight;
UpdateButtonObject(gButtonPanelPrefix+"CloseProfit","获利单平仓","获利单平仓中",298,currentY,135,buttonHeight,gPanelCorner,White,White,Teal,Teal,gPanelFontName,gPanelFontSize);
UpdateButtonObject(gButtonPanelPrefix+"CloseLoss","亏损单平仓","亏损单平仓中",154,currentY,135,buttonHeight,gPanelCorner,White,White,Teal,Teal,gPanelFontName,gPanelFontSize);
currentY+=rowGap+buttonHeight;
UpdateButtonObject(gButtonPanelPrefix+"CloseBuy","多单平仓","多单平仓中",298,currentY,135,buttonHeight,gPanelCorner,White,White,Teal,Teal,gPanelFontName,gPanelFontSize);
UpdateButtonObject(gButtonPanelPrefix+"CloseSell","空单平仓","空单平仓中",154,currentY,135,buttonHeight,gPanelCorner,White,White,Teal,Teal,gPanelFontName,gPanelFontSize);
currentY+=rowGap+buttonHeight;
UpdateButtonObject(gButtonPanelPrefix+"CloseSymbol",Symbol()+"全部平仓",Symbol()+"全部平仓中",298,currentY,135,buttonHeight,gPanelCorner,White,White,Teal,Teal,gPanelFontName,gPanelFontSize);
UpdateButtonObject(gButtonPanelPrefix+"CloseAll","EA全部平仓","EA全部平仓中",154,currentY,135,buttonHeight,gPanelCorner,White,White,Teal,Teal,gPanelFontName,gPanelFontSize);
currentY+=rowGap+buttonHeight;
UpdateButtonObject(gButtonPanelPrefix+"CloseAccountAll","账号全部平仓","账号全部平仓中",298,currentY,280,buttonHeight,gPanelCorner,White,White,SeaGreen,DodgerBlue,gPanelFontName,gPanelFontSize);
}
//+------------------------------------------------------------------+
//| Helper: create/update label |
//+------------------------------------------------------------------+
void MakeLabel(string name, string text, int sz, string font, color clr, int corner, int xd, int yd) {
if(ObjectFind(0,name)<0) {
ObjectCreate(0,name,OBJ_LABEL,0,0,0);
ObjectSetInteger(0,name,OBJPROP_CORNER,corner);
ObjectSetInteger(0,name,OBJPROP_XDISTANCE,xd);
ObjectSetInteger(0,name,OBJPROP_YDISTANCE,yd);
ObjectSetInteger(0,name,OBJPROP_BACK,0);
ObjectSetString(0,name,OBJPROP_FONT,font);
}
ObjectSetText(name,text,sz,font,clr);
}
//+------------------------------------------------------------------+
//| Helper: calc history profit for day range |
//+------------------------------------------------------------------+
double CalcHistProfit(datetime fromTime, datetime toTime, long mag) {
HistorySelect(fromTime,toTime);
double sum=0;
for(int i=(int)HistoryDealsTotal()-1;i>=0;i--) {
ulong t=HistoryDealGetTicket(i);
if((ENUM_DEAL_ENTRY)HistoryDealGetInteger(t,DEAL_ENTRY)==DEAL_ENTRY_IN) continue;
if(mag!=-1&&HistoryDealGetInteger(t,DEAL_MAGIC)!=mag) continue;
datetime dealTime=(datetime)HistoryDealGetInteger(t,DEAL_TIME);
if(dealTime<fromTime||dealTime>toTime) continue;
sum+=HistoryDealGetDouble(t,DEAL_PROFIT)+HistoryDealGetDouble(t,DEAL_COMMISSION)+HistoryDealGetDouble(t,DEAL_SWAP);
}
return NormalizeDouble(sum,2);
}
//+------------------------------------------------------------------+
//| RenderStatisticsPanel: Statistics display panel |
//+------------------------------------------------------------------+
void RenderStatisticsPanel() {
string cur=" "+AccountCurrency();
int statRowIndex=1, panelY=110;
EnsureStatsToggleObjects();
if(!gStatsPanelVisible) return;
// Background panel
if(ObjectFind(0,gStatsPanelPrefix+"background")<0) {
ObjectCreate(0,gStatsPanelPrefix+"background",OBJ_RECTANGLE_LABEL,0,0,0);
ObjectSetInteger(0,gStatsPanelPrefix+"background",OBJPROP_XDISTANCE,308);
ObjectSetInteger(0,gStatsPanelPrefix+"background",OBJPROP_YDISTANCE,50);
ObjectSetInteger(0,gStatsPanelPrefix+"background",OBJPROP_XSIZE,300);
ObjectSetInteger(0,gStatsPanelPrefix+"background",OBJPROP_YSIZE,440);
ObjectSetInteger(0,gStatsPanelPrefix+"background",OBJPROP_CORNER,1);
ObjectSetInteger(0,gStatsPanelPrefix+"background",OBJPROP_COLOR,8421376);
ObjectSetInteger(0,gStatsPanelPrefix+"background",OBJPROP_WIDTH,10);
ObjectSetInteger(0,gStatsPanelPrefix+"background",OBJPROP_BACK,0);
ObjectSetInteger(0,gStatsPanelPrefix+"background",OBJPROP_HIDDEN,1);
}
ObjectSetInteger(0,gStatsPanelPrefix+"background",OBJPROP_BGCOLOR,Snow);
// Status: can buy/sell
MakeLabel(gStatsPanelPrefix+"1B",gAllowManualBuy?"可以多":"禁止多",gPanelFontSize,gPanelFontName,gAllowManualBuy?Green:Tomato,gPanelCorner,230,65);
MakeLabel(gStatsPanelPrefix+"1S",gAllowManualSell?"可以空":"禁止空",gPanelFontSize,gPanelFontName,gAllowManualSell?Green:Tomato,gPanelCorner,40,65);
MakeLabel(gStatsPanelPrefix+"1N","获利统计",gPanelFontSize,gPanelFontName,0,gPanelCorner,122,80);
// Today profit
statRowIndex++;
datetime todayStart=0, todayStop=0, yesterdayStart=0, yesterdayStop=0;
GetDayBounds(TimeCurrent(),0,todayStart,todayStop);
GetDayBounds(TimeCurrent(),-1,yesterdayStart,yesterdayStop);
double todayPrf=CalcHistProfit(todayStart,todayStop,(long)Magic);
MakeLabel(gStatsPanelPrefix+string(statRowIndex)+"N","今日获利:",gPanelFontSize,gPanelFontName,0,gPanelCorner,230,panelY);
color c1=(todayPrf>=0)?(color)25600:(color)3937500;
MakeLabel(gStatsPanelPrefix+string(statRowIndex)+"V",DoubleToString(todayPrf,2)+cur,gPanelFontSize,gPanelFontName,c1,gPanelCorner,100,panelY);
MakeLabel(gStatsPanelPrefix+string(statRowIndex)+"P",DoubleToString(gInitialDeposit>0?todayPrf/gInitialDeposit*100:0,2)+"%",gPanelFontSize,gPanelFontName,c1,gPanelCorner,25,panelY);
statRowIndex++; panelY+=gPanelFontSize*2;
// Yesterday profit
double yestPrf=CalcHistProfit(yesterdayStart,yesterdayStop,(long)Magic);
MakeLabel(gStatsPanelPrefix+string(statRowIndex)+"N","昨日获利:",gPanelFontSize,gPanelFontName,0,gPanelCorner,230,panelY);
color c2=(yestPrf>=0)?(color)25600:(color)3937500;
MakeLabel(gStatsPanelPrefix+string(statRowIndex)+"V",DoubleToString(yestPrf,2)+cur,gPanelFontSize,gPanelFontName,c2,gPanelCorner,100,panelY);
MakeLabel(gStatsPanelPrefix+string(statRowIndex)+"P",DoubleToString(gInitialDeposit>0?yestPrf/gInitialDeposit*100:0,2)+"%",gPanelFontSize,gPanelFontName,c2,gPanelCorner,25,panelY);
statRowIndex++; panelY+=gPanelFontSize*2;
// Total profit
double totPrf=CalcHistProfit(0,TimeCurrent(),(long)Magic);
MakeLabel(gStatsPanelPrefix+string(statRowIndex)+"N","总计获利:",gPanelFontSize,gPanelFontName,0,gPanelCorner,230,panelY);
color c3=(totPrf>=0)?(color)25600:(color)3937500;
MakeLabel(gStatsPanelPrefix+string(statRowIndex)+"V",DoubleToString(totPrf,2)+cur,gPanelFontSize,gPanelFontName,c3,gPanelCorner,100,panelY);
MakeLabel(gStatsPanelPrefix+string(statRowIndex)+"P",DoubleToString(gInitialDeposit>0?totPrf/gInitialDeposit*100:0,2)+"%",gPanelFontSize,gPanelFontName,c3,gPanelCorner,25,panelY);
panelY+=gPanelFontSize*3;
// Balance / Equity / Margin
MakeLabel(gStatsPanelPrefix+"BalanceN","账户余额:",gPanelFontSize,gPanelFontName,Teal,gPanelCorner,230,panelY);
MakeLabel(gStatsPanelPrefix+"BalanceV",DoubleToString(AccountBalance(),2)+cur,gPanelFontSize,gPanelFontName,Teal,gPanelCorner,80,panelY);
panelY+=gPanelFontSize*2;
MakeLabel(gStatsPanelPrefix+"EquityN","账户净值:",gPanelFontSize,gPanelFontName,Teal,gPanelCorner,230,panelY);
MakeLabel(gStatsPanelPrefix+"EquityV",DoubleToString(AccountEquity(),2)+cur,gPanelFontSize,gPanelFontName,Green,gPanelCorner,80,panelY);
panelY+=gPanelFontSize*2;
double marg=AccountMargin();
string mlStr=marg==0?"0.00%":DoubleToString(AccountEquity()/marg*100,2)+"%";
MakeLabel(gStatsPanelPrefix+"MarginLevelN","预付款比例:",gPanelFontSize,gPanelFontName,Teal,gPanelCorner,218,panelY);
MakeLabel(gStatsPanelPrefix+"MarginLevelV",mlStr,gPanelFontSize,gPanelFontName,DarkBlue,gPanelCorner,80,panelY);
panelY+=gPanelFontSize*2;
// EA floating profit by side
double buyLots=0,sellLots=0,buyPrf=0,sellPrf=0;
for(int i=PositionTotal()-1;i>=0;i--) {
ulong t=PositionGetTicket(i); if(!PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL)!=Symbol()||PositionGetInteger(POSITION_MAGIC)!=Magic) continue;
double vol=PositionGetDouble(POSITION_VOLUME);
double prf=PositionGetDouble(POSITION_PROFIT);
if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) { buyLots+=vol; buyPrf+=prf; }
else { sellLots+=vol; sellPrf+=prf; }
}
double acPrf=AccountProfit();
color acC=(acPrf>0)?DarkGreen:(acPrf<0)?Brown:Teal;
string acStr=(acPrf>0)?"账号盈利:":(acPrf<0)?"账号亏损:":" 账号空仓:";
if(ObjectFind(0,gStatsPanelPrefix+"showprofit")<0) {
ObjectCreate(0,gStatsPanelPrefix+"showprofit",OBJ_RECTANGLE_LABEL,0,0,0);
ObjectSetInteger(0,gStatsPanelPrefix+"showprofit",OBJPROP_XDISTANCE,298);
ObjectSetInteger(0,gStatsPanelPrefix+"showprofit",OBJPROP_YDISTANCE,90);
ObjectSetInteger(0,gStatsPanelPrefix+"showprofit",OBJPROP_XSIZE,280);
ObjectSetInteger(0,gStatsPanelPrefix+"showprofit",OBJPROP_YSIZE,85);
ObjectSetInteger(0,gStatsPanelPrefix+"showprofit",OBJPROP_CORNER,1);
ObjectSetInteger(0,gStatsPanelPrefix+"showprofit",OBJPROP_BACK,0);
ObjectSetInteger(0,gStatsPanelPrefix+"showprofit",OBJPROP_HIDDEN,1);
}
ObjectSetInteger(0,gStatsPanelPrefix+"showprofit",OBJPROP_BGCOLOR,LightCyan);
MakeLabel(gStatsPanelPrefix+"TotalProfitN",acStr,int(gPanelFontSize*1.5),gPanelFontName,acC,gPanelCorner,188,panelY);
MakeLabel(gStatsPanelPrefix+"TotalProfitV",DoubleToString(acPrf,2)+"$",int(gPanelFontSize*1.5),gPanelFontName,acC,gPanelCorner,35,panelY-gPanelFontSize);
MakeLabel(gStatsPanelPrefix+"TotalProfitP",DoubleToString(gInitialDeposit>0?acPrf/gInitialDeposit*100:0,2)+"%",gPanelFontSize,gPanelFontName,acC,gPanelCorner,50,panelY+gPanelFontSize*2);
panelY+=gPanelFontSize*4;
// EA side stats
MakeLabel(gStatsPanelPrefix+"BuyOrdersN","买单:",gPanelFontSize,gPanelFontName,CadetBlue,gPanelCorner,255,panelY);
MakeLabel(gStatsPanelPrefix+"BuyOrdersL",DoubleToString(buyLots,2)+"手",gPanelFontSize,gPanelFontName,DarkBlue,gPanelCorner,120,panelY);
color bc=(buyPrf>=0)?(color)9109504:(color)3937500;
MakeLabel(gStatsPanelPrefix+"BuyOrdersP",DoubleToString(buyPrf,2)+cur,gPanelFontSize,gPanelFontName,bc,gPanelCorner,25,panelY);
panelY+=gPanelFontSize*2;
MakeLabel(gStatsPanelPrefix+"SellOrdersN","卖单:",gPanelFontSize,gPanelFontName,Tomato,gPanelCorner,255,panelY);
MakeLabel(gStatsPanelPrefix+"SellOrdersL",DoubleToString(sellLots,2)+"手",gPanelFontSize,gPanelFontName,Tomato,gPanelCorner,120,panelY);
color sc=(sellPrf>=0)?(color)9109504:(color)3937500;
MakeLabel(gStatsPanelPrefix+"SellOrdersP",DoubleToString(sellPrf,2)+cur,gPanelFontSize,gPanelFontName,sc,gPanelCorner,25,panelY);
panelY+=gPanelFontSize*2;
double allLots=buyLots+sellLots, allPrf=buyPrf+sellPrf;
MakeLabel(gStatsPanelPrefix+"AllOrdersN","总共:",gPanelFontSize,gPanelFontName,Green,gPanelCorner,255,panelY);
MakeLabel(gStatsPanelPrefix+"AllOrdersL",DoubleToString(allLots,2)+"手",gPanelFontSize,gPanelFontName,DarkBlue,gPanelCorner,120,panelY);
color ac2=(allPrf>=0)?(color)9109504:(color)3937500;
MakeLabel(gStatsPanelPrefix+"AllOrdersP",DoubleToString(allPrf,2)+cur,gPanelFontSize,gPanelFontName,ac2,gPanelCorner,25,panelY);
panelY=int(panelY+gPanelFontSize*2.5);
// EA name label + toggle button
MakeLabel(gStatsPanelPrefix+"copyrightN",MQLInfoString(MQL_PROGRAM_NAME),gPanelFontSize,gPanelFontName,Green,gPanelCorner,125,panelY);
panelY+=gPanelFontSize*2;
string arw=gButtonPanelVisible?"▲":"▼";
UpdateButtonObject(gStatsPanelPrefix+"OpenBoard",arw,arw,80,panelY,40,20,gPanelCorner,0,0,Snow,Snow,gPanelFontName,gPanelFontSize);
gButtonPanelTopY=panelY+gPanelFontSize+25;
}
//+------------------------------------------------------------------+
//| UpdateButtonObject: Create/update button |
//+------------------------------------------------------------------+
void UpdateButtonObject(string name,string txt,string txtp,int xd,int yd,int xs,int ys,int corner,
uint clrOn,uint clrOff,uint bgOn,uint bgOff,string font,int fsize) {
if(ObjectFind(0,name)<0) ObjectCreate(0,name,OBJ_BUTTON,0,0,0);
ObjectSetInteger(0,name,OBJPROP_XDISTANCE,xd);
ObjectSetInteger(0,name,OBJPROP_YDISTANCE,yd);
ObjectSetInteger(0,name,OBJPROP_XSIZE,xs);
ObjectSetInteger(0,name,OBJPROP_YSIZE,ys);
ObjectSetString(0,name,OBJPROP_FONT,font);
ObjectSetInteger(0,name,OBJPROP_FONTSIZE,fsize);
ObjectSetInteger(0,name,OBJPROP_CORNER,corner);
ObjectSetInteger(0,name,OBJPROP_HIDDEN,1);
ObjectSetInteger(0,name,OBJPROP_BACK,0);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,0);
ObjectSetInteger(0,name,OBJPROP_ZORDER,1);
if(ObjectGetInteger(0,name,OBJPROP_STATE)==1) {
ObjectSetInteger(0,name,OBJPROP_COLOR,clrOn);
ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bgOn);
ObjectSetString(0,name,OBJPROP_TEXT,txtp);
} else {
ObjectSetInteger(0,name,OBJPROP_COLOR,clrOff);
ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bgOff);
ObjectSetString(0,name,OBJPROP_TEXT,txt);
}
}
//+------------------------------------------------------------------+
//| StringSplit wrapper |
//+------------------------------------------------------------------+
int StringSplit(string sv, string &result[], string sep, string SEP=NULL) {
StringTrimLeft(sv); StringTrimRight(sv);
if(SEP!=NULL) StringReplace(sv,SEP,sep);
ArrayFree(result);
ushort u=StringGetCharacter(sep,0);
return StringSplit(sv,u,result);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment