Created
March 31, 2026 15:08
-
-
Save C-L-STARK/6e6304fd61f63e0776e7f5455e172001 to your computer and use it in GitHub Desktop.
MT5 Amazing EA demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //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