找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 5|回复: 0

[MT5] 金猫眼猎金NOVA源码(全功能解限版)

[复制链接]

269

主题

6

回帖

1001

积分

管理员

积分
1001
发表于 昨天 18:00 | 显示全部楼层 |阅读模式
//+----------------金猫眼猎金NOVA源码(全功能解限版)-----------------------------------+
//+----------------·索罗斯·逆向·源码修正与逻辑重构--------------------------------------+

#property copyright ""
#property version   "2.15"
#include <Trade\Trade.mqh>
CTrade trade;                                    

enum ENUM_PARAM_MODE
{
   PM_CONSERVATIVE = 0,   
   PM_AGGRESSIVE   = 1   
};

input group "一、交易设置"
input bool             InpAcceptTradingRisk        = true;           // 开启自动交易功能
input ENUM_PARAM_MODE  InpParamMode                = PM_CONSERVATIVE;
input double           InpBaseLot                  = 0.03;           
input ENUM_TIMEFRAMES  InpSignalTF                 = PERIOD_M1;      
input double           InpMaxSpreadPrice           = 0.3;            
input double           InpMaxSlippagePrice         = 0.03;           

input group "二、NOVA智能识别系统(自动跟随档位)"
input bool             InpAIRSIMonitor             = false;         
input bool             InpAIADXMonitor             = false;         
input bool             InpAIBollMonitor            = false;         
input bool             InpAIATRMonitor             = false;         

input group "三、手动加单管理"
input bool             InpIncludeManualAddRecovery = false;         
input bool             InpSetManualAddStopLoss     = true;           

double   g_debt        = 0;   
double   g_lotCap      = 0;   
double   g_targetPL    = 0;   
double   g_calcLot     = 0;   
double   g_nextLot     = 0;   
double   g_lastSigPrice= 0;   
double   g_minMoveThresh = 30*_Point;
int      g_cooldown    = 0;   
int      g_state       = 6;    // 默认直接进入 ST_READY (6) 状态
int      g_prevPosCount= 0;   
bool     g_firstScan   = false;
bool     g_useOrigMagicFilter = true;
datetime g_pnlStart = 0;              

int hRSI = INVALID_HANDLE, hADX = INVALID_HANDLE,
    hBAND = INVALID_HANDLE, hATR = INVALID_HANDLE;   
int hEmaTrendF = INVALID_HANDLE;   
int hEmaTrendS = INVALID_HANDLE;   

double IndVal(int h, int buf = 0)
{
   double b[1];
   if(h == INVALID_HANDLE) return EMPTY_VALUE;
   if(CopyBuffer(h, buf, 0, 1, b) < 1) return EMPTY_VALUE;
   return b[0];
}

#define  ORIG_MAGIC_CONST 26063849     
#define ST_INIT              0   
#define ST_RUNTIME_WAIT      1   
#define ST_DATA_WAIT         2   
#define ST_IND_REBUILD       3   
#define ST_IND_REBUILD_FAIL  4   
#define ST_DATA_RESTORED     5   
#define ST_READY             6   
#define ST_CONFIG_BLOCK      7   

int OnInit()
{
   trade.SetExpertMagicNumber(ORIG_MAGIC_CONST);  
   g_nextLot = MathMax(InpBaseLot, SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN));
   if(_Point > 0)
      trade.SetDeviationInPoints((ulong)MathMax(1.0, InpMaxSlippagePrice / _Point));
   if(InpParamMode == PM_AGGRESSIVE) g_minMoveThresh *= 0.5;

   ENUM_TIMEFRAMES sigTf = (ENUM_TIMEFRAMES)InpSignalTF;
   hEmaTrendF = iMA(_Symbol, sigTf, 10, 0, MODE_EMA, PRICE_CLOSE);
   hEmaTrendS = iMA(_Symbol, sigTf, 20, 0, MODE_EMA, PRICE_CLOSE);
   if(InpAIRSIMonitor)  hRSI  = iRSI  (_Symbol, sigTf, 14, PRICE_CLOSE);
   if(InpAIADXMonitor)  hADX  = iADX  (_Symbol, sigTf, 14);
   if(InpAIBollMonitor) hBAND = iBands(_Symbol, sigTf, 20, 0, 2.0, PRICE_CLOSE);
   if(InpAIATRMonitor)  hATR  = iATR  (_Symbol, sigTf, 14);

   string key = "NOVA_GM_0E286BFB_";   
   if(GlobalVariableCheck(key + "DEBT"))   g_debt        = GlobalVariableGet(key+"DEBT");
   if(GlobalVariableCheck(key + "PARSIG")) g_lastSigPrice= GlobalVariableGet(key+"PARSIG");
   if(GlobalVariableCheck(key + "LOT"))    g_calcLot     = GlobalVariableGet(key+"LOT");
   if(GlobalVariableCheck(key + "TGT"))    g_targetPL    = GlobalVariableGet(key+"TGT");
   if(g_targetPL <= 0) g_targetPL = AccountInfoDouble(ACCOUNT_EQUITY) * 0.02;

   g_lotCap = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   string pk = "CATEYE_NOVA_PNL2_" + _Symbol;
   if(GlobalVariableCheck(pk + "_START"))
      g_pnlStart = (datetime)GlobalVariableGet(pk + "_START");   
   else
   {
      g_pnlStart = TimeCurrent();                                
      GlobalVariableSet(pk + "_START", (double)g_pnlStart);
   }

   g_state = ST_READY; // 确保直接就绪

   PrintFormat("[猫眼猎金 NOVA 2.15][INIT] %s 信号模块已就绪 | 状态=正常 | 余额=%.2f | 净值=%.2f | 待追回=%.2f | 下一手=%.2f",
               InpParamMode == PM_CONSERVATIVE ? "保守档" : "激进档",
               AccountInfoDouble(ACCOUNT_BALANCE), AccountInfoDouble(ACCOUNT_EQUITY),
               g_debt, CurrentLot());

   EventSetMillisecondTimer(500);
   PanelRefresh();               
   return(INIT_SUCCEEDED);
}

void OnTimer()
{
   PanelRefresh();
}

double CurrentLot()
{
   double lot  = NormalizeLot(g_calcLot);
   double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   if(step > 0) lot = MathFloor(lot/step)*step;
   return MathMax(lot, SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN));
}

void OnDeinit(const int reason)
{
   EventKillTimer();
   ObjectsDeleteAll(0, "CATEYE_NOVA");
   Comment("");
   if(hRSI  != INVALID_HANDLE) IndicatorRelease(hRSI);
   if(hADX  != INVALID_HANDLE) IndicatorRelease(hADX);
   if(hBAND != INVALID_HANDLE) IndicatorRelease(hBAND);
   if(hATR  != INVALID_HANDLE) IndicatorRelease(hATR);
   if(hEmaTrendF != INVALID_HANDLE) IndicatorRelease(hEmaTrendF);
   if(hEmaTrendS != INVALID_HANDLE) IndicatorRelease(hEmaTrendS);
}

#define PNL_X    12
#define PNL_Y    22
#define C_BG   C'18,23,30'      
#define C_HDR  C'22,29,38'      
#define C_CARD C'26,34,45'      
#define C_BRD  C'42,56,72'      
#define C_LBL  C'142,157,174'   
#define C_VAL  C'220,228,236'   
#define C_OK   C'38,208,124'   
#define C_GOLD C'229,169,60'   
#define C_GOLD2 C'168,124,40'   
#define C_WARN C'240,164,60'   
#define C_BAD  C'240,80,80'

string PanelFont(int idx)   
{
   switch(idx)
   {
      case 0: return "Microsoft YaHei UI";         
      case 1: return "Microsoft YaHei UI Semibold";
      case 2: return "Consolas";                    
   }
   return "Microsoft YaHei UI";
}

void PnlObj(const string id, ENUM_OBJECT type, int x, int y, int dx, int dy,
            color bg, color tx, int fs, const string font)
{
   string name = "CATEYE_NOVA_" + id;
   if(ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, type, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_BACK, false);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
   }
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, name, OBJPROP_XSIZE, dx);
   ObjectSetInteger(0, name, OBJPROP_YSIZE, dy);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg);
   ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, bg);
   ObjectSetInteger(0, name, OBJPROP_COLOR, tx);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fs);
   ObjectSetString (0, name, OBJPROP_FONT, font);
}

void PnlLbl(const string id, int x, int y, const string txt, color c, int fs,
            const string font)
{
   PnlObj(id, OBJ_LABEL, x, y, 0, 0, clrNONE, c, fs, font);
   ObjectSetString(0, "CATEYE_NOVA_" + id, OBJPROP_TEXT, txt);
}

string HeroText()
{
   if(!InpAcceptTradingRisk) return "交易功能已安全暂停";              
   int nEA = CountManagedPositions();
   if(nEA <= 0 && g_debt > 0)
      return "债务账本已接管,等待下一次有效行情";                     
   if(nEA > 0)
      return "当前持仓 " + IntegerToString(nEA) + " 笔 · ";           
   if(g_cooldown > 0)        return "等待行情";                        
   return "EA已就绪,等待下一次有效信号";                              
}

double RealizedSinceStart(int &settledCount)
{
   double s = 0;
   settledCount = 0;
   if(!HistorySelect(g_pnlStart, TimeCurrent())) return 0;
   for(int i = HistoryDealsTotal()-1; i >= 0; i--)
   {
      ulong dk = HistoryDealGetTicket(i);
      if(HistoryDealGetString(dk, DEAL_SYMBOL) != _Symbol) continue;
      if(HistoryDealGetInteger(dk, DEAL_MAGIC) != ORIG_MAGIC_CONST) continue;
      if(HistoryDealGetInteger(dk, DEAL_ENTRY) != DEAL_ENTRY_OUT) continue;
      s += HistoryDealGetDouble(dk, DEAL_PROFIT)
         + HistoryDealGetDouble(dk, DEAL_SWAP)
         + HistoryDealGetDouble(dk, DEAL_COMMISSION)
         + HistoryDealGetDouble(dk, DEAL_FEE);
      settledCount++;
   }
   return s;
}

string PositionMainLine()
{
   int    nBuy = 0, nSell = 0;
   double volBuy = 0, volSell = 0, wsumBuy = 0, wsumSell = 0;
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      long mg = PositionGetInteger(POSITION_MAGIC);
      if(mg != ORIG_MAGIC_CONST && !InpIncludeManualAddRecovery) continue;
      long t  = PositionGetInteger(POSITION_TYPE);
      double v = PositionGetDouble(POSITION_VOLUME);
      double p = PositionGetDouble(POSITION_PRICE_OPEN);
      if(t == POSITION_TYPE_BUY) { nBuy++;  volBuy += v;  wsumBuy  += v * p; }
      else                       { nSell++; volSell += v; wsumSell += v * p; }
   }
   int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   if(nBuy + nSell == 0)
      return "当前无持仓 · 预计开仓 " + DoubleToString(CurrentLot(), 2) + " 手";
   string dir  = (volBuy >= volSell) ? "多单" : "空单";                  
   double vol  = (volBuy >= volSell) ? volBuy : volSell;
   double avg  = (volBuy >= volSell)
               ? (volBuy  > 0 ? wsumBuy  / volBuy  : 0)
               : (volSell > 0 ? wsumSell / volSell : 0);
   double cur  = (volBuy >= volSell)
               ? SymbolInfoDouble(_Symbol, SYMBOL_BID)
               : SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   string manual = ((InpIncludeManualAddRecovery && CountManualPositions() > 0) ? " · 含手动" : "");     
   return dir                                                            
        + " " + DoubleToString(vol, 2) + " 手 · "                        
        + "均价 " + DoubleToString(avg, digits)                           
        + " · 现价 " + DoubleToString(cur, digits) + manual;              
}

string AiFactorsLine(double &rsi, double &adx, double &bollPct, double &atrV)
{
   bool any = InpAIRSIMonitor || InpAIADXMonitor || InpAIBollMonitor || InpAIATRMonitor;
   if(!any) return "未开启AI因子监测";                                   
   rsi = IndVal(hRSI);
   adx = IndVal(hADX);
   double mid = IndVal(hBAND, 0), up = IndVal(hBAND, 1), lo_ = IndVal(hBAND, 2);
   atrV    = IndVal(hATR);
   double cls = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   bollPct    = (up != EMPTY_VALUE && lo_ != EMPTY_VALUE && up > lo_)
              ? (cls - lo_) / (up - lo_) * 100.0 : 50.0;

   string s = "";
   if(InpAIRSIMonitor && rsi != EMPTY_VALUE)
      s += StringFormat("RSI %.1f", rsi);
   if(InpAIADXMonitor && adx != EMPTY_VALUE)
      s += (s == "" ? "" : " · ") + StringFormat("ADX %.1f", adx);
   if(InpAIBollMonitor)
      s += (s == "" ? "" : " · ") + StringFormat("BOLL %.0f%%", bollPct);
   if(InpAIATRMonitor && atrV != EMPTY_VALUE)
      s += (s == "" ? "" : " · ") + StringFormat("ATR %.2f", atrV);
   return (s == "") ? "观测数据准备中" : s;                              
}

string AiStateLine(double rsi)
{
   if(!InpAIRSIMonitor) return "市场状态 · 正常运行";
   string w = "动能均衡";                                                
   if(rsi > 55) w = "动能偏强";                                          
   if(rsi < 45) w = "动能偏弱";                                          
   return "市场状态 · " + w;                                             
}

void PanelRefresh()
{
   static uint last = 0;
   if(GetTickCount() - last < 500) return;
   last = GetTickCount();

   int x = PNL_X, y = PNL_Y;
   int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   PnlObj("SHADOW", OBJ_RECTANGLE_LABEL, x+2, y+2, 300, 430, C_BG, C_BG, 0, "");
   PnlObj("BG",     OBJ_RECTANGLE_LABEL, x, y, 300, 430, C_BG, C_BRD, 0, "");

   bool runok = InpAcceptTradingRisk;
   string pill = runok ? "运行正常" : "暂停新开";

   PnlLbl("TITLE", x+10, y+6, "猫眼猎金 • NOVA 2.1 极简版", runok ? C_GOLD : C_LBL, 10, PanelFont(1));
   PnlLbl("ENGINE_DOT", x+234, y+7, runok ? "■" : "□", runok ? C_OK : C_WARN, 9, PanelFont(0));
   PnlLbl("ENGINE_TEXT", x+248, y+7, pill, runok ? C_OK : C_WARN, 9, PanelFont(0));

   PnlLbl("STATUS", x+10, y+24,
          _Symbol + " · "
          + StringSubstr(EnumToString(InpSignalTF == PERIOD_CURRENT ? _Period : InpSignalTF), 7)
          + " · " + (InpParamMode == PM_CONSERVATIVE ? "保守档" : "激进档"),
          C_VAL, 8, PanelFont(1));

   // 移除到期授权显示
   PnlLbl("AUTH_TEXT", x+150, y+24, "无限制完整版", C_GOLD, 8, PanelFont(1));

   int cy = y + 44;
   PnlObj("POS_CARD", OBJ_RECTANGLE_LABEL, x+8, cy, 284, 62, C_CARD, C_BRD, 0, "");
   PnlObj("POS_ACCENT", OBJ_RECTANGLE_LABEL, x+8, cy, 3, 62, CountManagedPositions() > 0 ? C_OK : C_LBL, C_CARD, 0, "");

   int nPos = CountManagedPositions();
   double volTot2 = 0;
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != ORIG_MAGIC_CONST && !InpIncludeManualAddRecovery) continue;
      volTot2 += PositionGetDouble(POSITION_VOLUME);
   }

   PnlLbl("POSITION_MAIN", x+16, cy+5, nPos <= 0 ? HeroText() : PositionMainLine(), nPos > 0 ? C_OK : C_LBL, 9, PanelFont(1));
   PnlLbl("POSITION_TRAIL", x+16, cy+22, TrailLine(digits), C_GOLD, 9, PanelFont(1));
   PnlLbl("POSITION_CNT", x+16, cy+40, nPos > 0 ? StringFormat("当前持仓 %d 笔 · %.2f 手", nPos, volTot2) : "当前空仓", C_VAL, 8, PanelFont(1));

   double floatPL = TotalFloatingPL(true);
   PnlLbl("FLOAT_LABEL", x+196, cy+8, "当前浮盈 / USD", C_LBL, 8, PanelFont(0));
   PnlLbl("FLOAT_VALUE", x+196, cy+26, (floatPL >= 0 ? "+" : "") + DoubleToString(floatPL, 2), floatPL >= 0 ? C_OK : C_BAD, 12, PanelFont(2));

   cy = y + 116;
   int sc = 0;
   double realized = RealizedSinceStart(sc);
   PnlObj("SESSION_CARD", OBJ_RECTANGLE_LABEL, x+8, cy, 138, 66, C_CARD, C_BRD, 0, "");
   PnlLbl("SESSION_TITLE", x+14, cy+5, "EA累计盈亏", C_WARN, 9, PanelFont(1));
   PnlLbl("SESSION_VALUE", x+14, cy+21, (realized + floatPL >= 0 ? "+" : "") + DoubleToString(realized + floatPL, 2), realized + floatPL >= 0 ? C_OK : C_BAD, 13, PanelFont(2));
   PnlLbl("SESSION_DETAIL", x+14, cy+41, "已结算 +" + DoubleToString(realized, 2) + " · 浮动 " + (floatPL >= 0 ? "+" : "") + DoubleToString(floatPL, 2), C_LBL, 7, PanelFont(0));
   PnlLbl("SESSION_START", x+14, cy+53, "起始 " + TimeToString(g_pnlStart, TIME_DATE) + " · 结算 " + IntegerToString(sc) + " 笔", C_LBL, 7, PanelFont(0));

   long sprPt = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
   double avgPx = 0, vv = 0;
   {
      double wv = 0;
      for(int i = PositionsTotal()-1; i >= 0; i--)
      {
         ulong tk = PositionGetTicket(i);
         if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
         if(PositionGetInteger(POSITION_MAGIC) != ORIG_MAGIC_CONST && !InpIncludeManualAddRecovery) continue;
         double v = PositionGetDouble(POSITION_VOLUME);
         wv += v * PositionGetDouble(POSITION_PRICE_OPEN);
         vv += v;
      }
      if(vv > 0) avgPx = wv / vv;
   }
   double curPx = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   PnlObj("OVER_CARD", OBJ_RECTANGLE_LABEL, x+154, cy, 138, 66, C_CARD, C_BRD, 0, "");
   PnlLbl("POSITION_TITLE", x+160, cy+5, "持仓概览", C_WARN, 9, PanelFont(1));
   PnlLbl("POSITION_SPREAD", x+160, cy+22, "点差 " + DoubleToString(sprPt * _Point, digits), C_VAL, 8, PanelFont(2));
   PnlLbl("POSITION_DESC", x+160, cy+38, PositionMainLine(), C_VAL, 8, PanelFont(1));
   PnlLbl("POSITION_PX", x+160, cy+53, vv > 0 ? "均价 " + DoubleToString(avgPx, digits) + " 现价 " + DoubleToString(curPx, digits) : "—", C_LBL, 7, PanelFont(0));

   cy = y + 190;
   PnlLbl("REC_TITLE", x+12, cy, "追回与下一步", C_LBL, 9, PanelFont(1));
   PnlLbl("REC_VALUE", x+12, cy+17, g_debt > 0 ? "待追回 " + DoubleToString(g_debt, 2) : "当前无追回", g_debt > 0 ? C_VAL : C_OK, 10, PanelFont(2));
   PnlLbl("NEXT_TEXT", x+120, cy+18, nPos > 0 ? "下一步 管理现有持仓" : "下一步 等待有效信号", C_LBL, 8, PanelFont(0));

   PnlObj("ACTION_BTN", OBJ_BUTTON, x+8, y+222, 284, 26, C'90,30,37', C'255,165,165', 9, PanelFont(1));
   ObjectSetString(0, "CATEYE_NOVA_ACTION_BTN", OBJPROP_TEXT, "平仓并回归初始");

   cy = y + 258;
   PnlObj("AI_CARD", OBJ_RECTANGLE_LABEL, x+8, cy, 284, 54, C_CARD, C_GOLD2, 0, "");
   double rsi = 50, adx = 0, bp = 50, av = 0;
   PnlLbl("AI_TITLE", x+16, cy+5, "NOVA智能观测", C_GOLD, 9, PanelFont(1));
   PnlLbl("AI_FACTORS", x+16, cy+22, AiFactorsLine(rsi, adx, bp, av), C_VAL, 8, PanelFont(2));
   PnlLbl("AI_STATE", x+16, cy+38, "市场状态·" + (InpParamMode == PM_CONSERVATIVE ? "保守" : "激进") + "档位联动 · " + AiStateLine(rsi), C_LBL, 8, PanelFont(0));

   PnlLbl("AD_LINE", x+12, y+324, "EA策略自动化量化运行中", C_GOLD, 9, PanelFont(0));
   PnlLbl("AD_SUB", x+12, y+342, "智能止盈 · 动态追回 · 风险风控系统", C_LBL, 8, PanelFont(0));

   PnlLbl("FOOT_OK", x+12, y+364, runok ? "系统正常运行" : "交易功能暂停", runok ? C_OK : C_WARN, 8, PanelFont(0));
   PnlLbl("FOOT_RISK", x+150, y+364, "市场有风险,交易需谨慎", C_LBL, 8, PanelFont(0));

   ChartRedraw();
}

bool g_modalUp = false;
void ShowResetModal()
{
   int x = PNL_X + 40, y = PNL_Y + 40;
   PnlObj("MODAL_BG",   OBJ_RECTANGLE_LABEL, x-2, y-2, 300, 110, C_HDR, C_HDR, 0, "");
   PnlObj("MODAL",      OBJ_RECTANGLE_LABEL, x, y, 296, 106, C_BG, C_BG, 0, "");
   PnlLbl("M_TTL",  x+10, y+8,  "确认平仓归零", C_VAL, 11, PanelFont(1));
   PnlLbl("M_L1",   x+10, y+34, "关闭本EA当前持仓,全部成功后清除债务并恢复基础手数", C_LBL, 9, PanelFont(0));
   PnlObj("M_OK", OBJ_BUTTON, x+10, y+66, 132, 26, C'190,60,60', C'255,245,245', 9, PanelFont(1));
   ObjectSetString(0, "CATEYE_NOVA_M_OK", OBJPROP_TEXT, "确认平仓并归零");
   PnlObj("M_NO", OBJ_BUTTON, x+154, y+66, 132, 26, C'60,70,90', C'235,238,245', 9, PanelFont(1));
   ObjectSetString(0, "CATEYE_NOVA_M_NO", OBJPROP_TEXT, "取消");
   g_modalUp = true;
}

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   if(id != CHARTEVENT_OBJECT_CLICK) return;
   if(sparam == "CATEYE_NOVA_ACTION_BTN")
      ShowResetModal();
   else if(sparam == "CATEYE_NOVA_M_OK")
   {
      ObjectsDeleteAll(0, "CATEYE_NOVA_M_");
      g_modalUp = false;
      OnResetButton();
      PanelRefresh();
   }
   else if(sparam == "CATEYE_NOVA_M_NO")
   {
      ObjectsDeleteAll(0, "CATEYE_NOVA_M_");
      g_modalUp = false;
      ChartRedraw();
   }
}

int  g_fzState     = 0;      
int  g_fzChecks    = 0;      
int  g_fzOk        = 0;      
int  g_fzStallSec  = 0;      
long g_fzLastMsc   = 0;      
bool g_fzProtectMsg= false;   

#define FZ_STALL_LIMIT   15   
#define FZ_CHECKS_NEED   3   
#define FZ_CONFIRM_NEED  2   

void FreezeGuardCheck()
{
   long   msc = SymbolInfoInteger(_Symbol, SYMBOL_TIME_MSC);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   if(msc != g_fzLastMsc) { g_fzLastMsc = msc; g_fzStallSec = 0; }
   else                   { g_fzStallSec++;                    }

   switch(g_fzState)
   {
      case 0:                                    
         g_fzChecks = 0;
         if(g_fzStallSec >= FZ_STALL_LIMIT)
         {
            g_fzState = 1; g_fzChecks = 1;
         }
         break;
      case 1:                                    
         if(g_fzStallSec == 0) { g_fzState = 0; break; }   
         g_fzChecks++;
         if(g_fzChecks >= FZ_CHECKS_NEED)
         {
            g_fzState = 2; g_fzOk = 0; g_fzProtectMsg = false;
         }
         break;
      case 2:                                    
         break;
      case 3:                                    
         if(g_fzStallSec >= FZ_STALL_LIMIT)      
         {
            g_fzState = 2; g_fzOk = 0;
            break;
         }
         g_fzOk++;
         if(g_fzOk >= FZ_CONFIRM_NEED)
         {
            g_fzState = 0;
         }
         break;
   }
}

bool FrozenBlock() { return (g_fzState == 2 || g_fzState == 3); }

bool   g_trailOn      = false;
double g_trailExtreme = 0;     
double g_trailOpenRef = 0;     
datetime g_slLastPush = 0;     

#define TRAIL_TRIGGER_BASE   700*_Point  
#define TRAIL_INITIAL_SL     800*_Point  
#define TRAIL_RETRACE_STEP   100*_Point  

string TrailLine(int digits)
{
   if(CountManagedPositions() <= 0) return "移动止盈  开仓后计算";   
   if(!g_trailOn)                   return "移动止盈触发距离 计算中";
   return "移动止盈触发距离 " + DoubleToString(TRAIL_TRIGGER_BASE, digits);
}

void TrailManage()
{
   int n = CountManagedPositions();
   if(n <= 0) { g_trailOn = false; g_trailExtreme = 0; return; }

   double volBuy=0, volSell=0, wsumBuy=0, wsumSell=0;
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      long mg = PositionGetInteger(POSITION_MAGIC);
      if(mg != ORIG_MAGIC_CONST && !InpIncludeManualAddRecovery) continue;
      double v = PositionGetDouble(POSITION_VOLUME);
      double p = PositionGetDouble(POSITION_PRICE_OPEN);
      if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
           { volBuy += v;  wsumBuy += v*p; }
      else  { volSell += v; wsumSell += v*p; }
   }
   if(volBuy+volSell <= 0) { g_trailOn = false; return; }

   bool   isLong = volBuy >= volSell;
   double avg    = isLong ? (volBuy>0 ? wsumBuy/volBuy : 0)
                          : (volSell>0 ? wsumSell/volSell : 0);
   double bid    = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask    = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double volTot = volBuy + volSell;
   double trig   = TRAIL_TRIGGER_BASE + g_debt / (100.0 * volTot);
   double dist   = isLong ? (bid - avg) : (avg - ask);

   if(g_trailExtreme == 0)                   g_trailExtreme = isLong ? bid : ask;
   else if(isLong && bid > g_trailExtreme)   g_trailExtreme = bid;
   else if(!isLong && ask < g_trailExtreme)  g_trailExtreme = ask;

   if(!g_trailOn)
   {
      if(dist >= trig)
      {
         g_trailOn      = true;
         g_trailOpenRef = avg;
      }
      return;
   }

   bool hit = isLong ? (bid <= g_trailExtreme - TRAIL_RETRACE_STEP)
                     : (ask >= g_trailExtreme + TRAIL_RETRACE_STEP);
   if(hit)
   {
      Print("[猫眼猎金 NOVA 2.15][移动止盈] 回撤保护触发 全部平仓");
      CloseOwnPositions();            
      g_trailOn = false;
      return;
   }

   datetime now = TimeTradeServer();   
   if(now - g_slLastPush < 1) return;

   int dg = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      long mg = PositionGetInteger(POSITION_MAGIC);
      if(mg != ORIG_MAGIC_CONST && !InpIncludeManualAddRecovery) continue;
      bool lng = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
      double want = lng ? MathMin(g_trailExtreme, SymbolInfoDouble(_Symbol, SYMBOL_BID)) - TRAIL_RETRACE_STEP
                        : MathMax(g_trailExtreme, SymbolInfoDouble(_Symbol, SYMBOL_ASK)) + TRAIL_RETRACE_STEP;
      want = NormalizeDouble(want, dg);
      double have = PositionGetDouble(POSITION_SL);
      if(MathAbs(want - have) <= 0.5 * _Point) continue;
      if(trade.PositionModify(tk, want, PositionGetDouble(POSITION_TP)))
         g_slLastPush = now;         
   }
}

void OnTick()
{
   PanelRefresh();                    
   FreezeGuardCheck();               
   TrailManage();                     

   TakeoverCheck();      
   SltpGuardCheck();     
   DealSettleScan();     
   UpdateRecoveryLot();  

   if(InpAcceptTradingRisk && !FrozenBlock())
      BreakoutEngine();
}

void UpdateRecoveryLot()
{
   if(g_cooldown > 0) { g_cooldown--; }            
   g_calcLot = g_nextLot;
}

double CeilStepLot(double lot)
{
   double st = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   if(st <= 0) return NormalizeLot(lot);
   return MathMax(MathCeil(lot / st - 1e-9) * st,
                  SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN));
}

datetime g_settleScanFrom = 0;
ulong    g_lastDealTicket = 0;

void DealSettleScan()
{
   datetime now = TimeCurrent();
   if(g_settleScanFrom == 0) g_settleScanFrom = now - 86400;
   if(!HistorySelect(g_settleScanFrom - 3600, now + 60)) return;

   int total = HistoryDealsTotal();
   for(int i = 0; i < total; i++)
   {
      ulong dt = HistoryDealGetTicket(i);
      if(dt == 0 || dt <= g_lastDealTicket) continue;
      if(HistoryDealGetString(dt, DEAL_SYMBOL) != _Symbol) continue;
      if(HistoryDealGetInteger(dt, DEAL_MAGIC) != ORIG_MAGIC_CONST) continue;
      long e = HistoryDealGetInteger(dt, DEAL_ENTRY);
      if(e != DEAL_ENTRY_OUT && e != DEAL_ENTRY_INOUT && e != DEAL_ENTRY_OUT_BY) continue;

      double res = HistoryDealGetDouble(dt, DEAL_PROFIT)
                 + HistoryDealGetDouble(dt, DEAL_SWAP)
                 + HistoryDealGetDouble(dt, DEAL_COMMISSION)
                 + HistoryDealGetDouble(dt, DEAL_FEE);

      if(res < -0.005 && g_debt >= 0)
      {
         g_debt += -res;
         g_nextLot = CeilStepLot(g_nextLot * 1.4);
      }
      else if(res > 0.005 && g_debt > 0.005)
      {
         if(res >= g_debt - 0.005)
         {
            g_debt = 0;
            g_nextLot = MathMax(InpBaseLot, SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN));
         }
         else
         {
            g_debt -= res;
         }
      }
      DebtPersist();
   }
   if(total > 0)
      g_lastDealTicket = HistoryDealGetTicket(total - 1);
   g_settleScanFrom = now;
}

void DebtPersist()
{
   string key = "NOVA_GM_0E286BFB_";   
   GlobalVariableSet(key + "DEBT", g_debt);
   GlobalVariableSet(key + "TGT",  g_targetPL);
}

void TakeoverCheck()
{
   int now = PositionsTotal();                     
   if(!g_firstScan) { g_firstScan = true; }
   if(now > g_prevPosCount && InpSetManualAddStopLoss)
      ManualAddStopLoss();      
   g_prevPosCount = now;
}

void ManualAddStopLoss()
{
   double spr  = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double dist = MathMax(spr + InpMaxSlippagePrice, 10 * _Point) * 10.0;
   int digits  = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) == ORIG_MAGIC_CONST) continue;
      if(PositionGetDouble(POSITION_SL) > 0) continue;   

      long type = PositionGetInteger(POSITION_TYPE);
      double op = PositionGetDouble(POSITION_PRICE_OPEN);
      double sl = (type == POSITION_TYPE_BUY) ? op - dist : op + dist;
      sl = NormalizeDouble(sl, digits);
      trade.PositionModify(tk, sl, PositionGetDouble(POSITION_TP));
   }
}

void OnResetButton()   
{
   CloseOwnPositions();
   g_debt = 0;   
   g_nextLot = MathMax(InpBaseLot, SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN));
   DebtPersist();
}

void SltpGuardCheck() {}

double g_tEmaF = 0, g_tEmaS = 0;   
bool   g_boTrendInit = false;      
datetime g_boEvalBar = 0;           

int ManagedPosCount()
{
   int n = 0;
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != ORIG_MAGIC_CONST) continue;
      n++;
   }
   return n;
}

void BreakoutEngine()
{
   ENUM_TIMEFRAMES tf = (ENUM_TIMEFRAMES)InpSignalTF;
   datetime bt = iTime(_Symbol, tf, 0);
   if(bt == 0 || bt == g_boEvalBar) return;      
   g_boEvalBar = bt;

   if(Bars(_Symbol, tf) < 15) return;

   double close1  = iClose(_Symbol, tf, 1);
   double high12  = iHigh(_Symbol, tf, iHighest(_Symbol, tf, MODE_HIGH, 12, 2));
   double low12   = iLow (_Symbol, tf, iLowest (_Symbol, tf, MODE_LOW , 12, 2));

   if(g_boTrendInit)
   {
      double c1 = iClose(_Symbol, tf, 1);
      g_tEmaF = (2.0 / 11) * c1 + (9.0 / 11) * g_tEmaF;
      g_tEmaS = (2.0 / 21) * c1 + (19.0 / 21) * g_tEmaS;
   }
   else
   {
      g_tEmaF = iClose(_Symbol, tf, 1);
      g_tEmaS = g_tEmaF;
      g_boTrendInit = true;
   }

   double eFv = g_tEmaF, eSv = g_tEmaS;
   string g_boTrend = (eFv > eSv) ? "多头" : "空头";

   bool bbRaw = close1 > high12;
   bool sbRaw = close1 < low12;
   bool buyBreak  = bbRaw && g_boTrend == "多头";
   bool sellBreak = sbRaw && g_boTrend == "空头";

   if(!(buyBreak || sellBreak)) return;
   OpenOnSignal(buyBreak ? ORDER_TYPE_BUY : ORDER_TYPE_SELL);
}

void OpenOnSignal(ENUM_ORDER_TYPE side)
{
   bool isBuy = (side == ORDER_TYPE_BUY);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double spr = ask - bid;

   if(spr > InpMaxSpreadPrice) return;
   if(ManagedPosCount() > 0) return;

   double lot = CurrentLot();
   if(lot <= 0) return;

   int dg = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   double px  = isBuy ? ask : bid;
   double slv = NormalizeDouble(isBuy ? px - TRAIL_INITIAL_SL : px + TRAIL_INITIAL_SL, dg);

   bool ok = isBuy ? trade.Buy(lot, _Symbol, 0, slv, 0, "NOVA")
                   : trade.Sell(lot, _Symbol, 0, slv, 0, "NOVA");

   uint rc = trade.ResultRetcode();
   if(ok && (rc == TRADE_RETCODE_DONE || rc == TRADE_RETCODE_PLACED))
   {
      DebtPersist();
   }
}

double NormalizeLot(double lot)
{
   double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   double mn   = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   if(step > 0) lot = MathFloor(lot/step)*step;
   return MathMax(lot, mn);
}

void CloseOwnPositions()
{
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != ORIG_MAGIC_CONST) continue;  
      trade.PositionClose(tk);
   }
}

double TotalFloatingPL(bool includeManual = false)
{
   double s = 0;
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      long mg = PositionGetInteger(POSITION_MAGIC);
      if(mg != ORIG_MAGIC_CONST)
      {
         if(!(includeManual && InpIncludeManualAddRecovery)) continue;
      }
      s += PositionGetDouble(POSITION_PROFIT);
   }
   return s;
}

// 修正:正确的统计手动持仓逻辑 (Magic != ORIG_MAGIC_CONST)
int CountManualPositions()
{
   int n = 0;
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != ORIG_MAGIC_CONST) n++;
   }
   return n;
}

int CountManagedPositions()
{
   int nEA = 0, nMan = 0;
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) == ORIG_MAGIC_CONST) nEA++;
      else nMan++;
   }
   return nEA + (InpIncludeManualAddRecovery ? nMan : 0);
}


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|量化魔方 ( 陕ICP备2025062059号-3 )|网站地图

GMT+8, 2026-9-8 03:05 , Processed in 0.283222 second(s), 5 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表