| 发表于 1 小时前 | 显示全部楼层 |复制链接
//+------------------------------------------------------------------+
//|                                       AccountStatsPro.mq4        |
//|                     专业账户统计EA - 按日/月/年统计手数与盈亏         |
//|                     双面板布局 + 分页表格 + 视图切换                 |
//+------------------------------------------------------------------+
#property copyright "Account Statistics Pro"
#property version   "2.00"
#property strict
#property description "专业账户统计面板 - 按日/月/年分组统计手数与盈亏"
#property description "双面板布局 | 分页表格 | 视图切换 | 胜率与利润因子"

//==================================================================
//                          输入参数
//==================================================================
input string  _g1               = "═══════ 面板位置 ═══════";
input int     InpLeftPanelX     = 20;             // 左面板X坐标
input int     InpLeftPanelY     = 30;             // 左面板Y坐标
input int     InpLeftPanelW     = 320;            // 左面板宽度
input int     InpRightPanelX    = 350;            // 右面板X坐标
input int     InpRightPanelW    = 560;            // 右面板宽度
input int     InpRowsPerPage    = 18;             // 每页行数

input string  _g2               = "═══════ 样式 ═══════";
input int     InpFontSize       = 9;              // 字体大小
input string  InpFontName       = "Consolas";     // 字体(建议等宽)
input color   InpBgColor        = C'22,25,35';    // 背景色
input color   InpBorderColor    = C'60,65,85';    // 边框色
input color   InpTitleColor     = C'255,215,0';   // 标题色
input color   InpSectionColor   = C'100,180,255'; // 分节色
input color   InpLabelColor     = C'170,175,190'; // 标签色
input color   InpTextColor      = C'230,230,240'; // 正文色
input color   InpProfitColor    = C'0,220,120';   // 盈利绿
input color   InpLossColor      = C'255,80,90';   // 亏损红
input color   InpHeaderColor    = C'140,200,255'; // 表头色
input color   InpAltRowColor    = C'32,36,48';    // 隔行色

input string  _g3               = "═══════ 统计选项 ═══════";
input bool    InpOnlyThisSymbol = false;          // 仅统计当前品种
input int     InpHistoryDays    = 0;              // 统计天数(0=全部)
input int     InpRefreshSec     = 3;              // 自动刷新(秒)
input bool    InpShowCommission = true;           // 包含佣金和库存费

//==================================================================
//                          常量
//==================================================================
#define OBJ_PREFIX  "ASP2_"
#define MAX_PERIODS 500

//==================================================================
//                          数据结构
//==================================================================
struct SPeriodStat
{
   string   label;      // 周期标签: 2026-09-24 / 2026-09 / 2026
   int      trades;     // 交易笔数
   int      wins;       // 盈利笔数
   double   lots;       // 总手数
   double   grossWin;   // 总盈利
   double   grossLoss;  // 总亏损
   double   netProfit;  // 净利润
};

//==================================================================
//                          全局变量
//==================================================================
SPeriodStat g_days[MAX_PERIODS];
SPeriodStat g_months[MAX_PERIODS];
SPeriodStat g_years[MAX_PERIODS];
int         g_dayCount     = 0;
int         g_monthCount   = 0;
int         g_yearCount    = 0;

int         g_viewMode     = 0;    // 0=日 1=月 2=年
int         g_page         = 0;    // 当前页
datetime    g_lastRefresh  = 0;

// 汇总变量
double g_totalNet   = 0;
double g_totalLots  = 0;
int    g_totalTrades = 0;
int    g_totalWins  = 0;
double g_grossWin   = 0;
double g_grossLoss  = 0;

//==================================================================
//                          辅助函数
//==================================================================
double GetPoint() { return MarketInfo(Symbol(), MODE_POINT); }

color ColorForProfit(double v)
{
   if(v > 0.0001) return InpProfitColor;
   if(v < -0.0001) return InpLossColor;
   return InpTextColor;
}

string FmtMoney(double v, bool withSign = false)
{
   string prefix = "";
   if(withSign && v > 0.0001) prefix = "+";
   return prefix + DoubleToString(v, 2);
}

string FmtLots(double v)
{
   return DoubleToString(v, 2);
}

//--- 日期键
string GetDayKey(datetime t)
{
   MqlDateTime dt;
   TimeToStruct(t, dt);
   return StringFormat("%04d-%02d-%02d", dt.year, dt.mon, dt.day);
}
string GetMonthKey(datetime t)
{
   MqlDateTime dt;
   TimeToStruct(t, dt);
   return StringFormat("%04d-%02d", dt.year, dt.mon);
}
string GetYearKey(datetime t)
{
   MqlDateTime dt;
   TimeToStruct(t, dt);
   return StringFormat("%04d", dt.year);
}

//==================================================================
//                      聚合统计
//==================================================================
int FindOrCreate(SPeriodStat &arr[], int &count, string label)
{
   for(int i = 0; i < count; i++)
      if(arr[i].label == label) return i;

   if(count >= MAX_PERIODS) return -1;

   arr[count].label     = label;
   arr[count].trades    = 0;
   arr[count].wins      = 0;
   arr[count].lots      = 0;
   arr[count].grossWin  = 0;
   arr[count].grossLoss = 0;
   arr[count].netProfit = 0;
   count++;
   return count - 1;
}

void AddTrade(SPeriodStat &arr[], int &count, string label,
              double lots, double profit)
{
   int idx = FindOrCreate(arr, count, label);
   if(idx < 0) return;

   arr[idx].trades++;
   arr[idx].lots += lots;
   arr[idx].netProfit += profit;

   if(profit > 0)
   {
      arr[idx].wins++;
      arr[idx].grossWin += profit;
   }
   else if(profit < 0)
   {
      arr[idx].grossLoss += profit;  // 负值
   }
}

//--- 按日期字符串降序排序(最新在前)
void SortDescending(SPeriodStat &arr[], int count)
{
   for(int i = 0; i < count - 1; i++)
   {
      for(int j = 0; j < count - 1 - i; j++)
      {
         if(arr[j].label < arr[j + 1].label)
         {
            SPeriodStat tmp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = tmp;
         }
      }
   }
}

//--- 聚合所有历史订单
void AggregateStats()
{
   g_dayCount   = 0;
   g_monthCount = 0;
   g_yearCount  = 0;

   g_totalNet    = 0;
   g_totalLots   = 0;
   g_totalTrades = 0;
   g_totalWins   = 0;
   g_grossWin    = 0;
   g_grossLoss   = 0;

   datetime cutoff = (InpHistoryDays > 0) ? TimeCurrent() - InpHistoryDays * 86400 : 0;
   int total = OrdersHistoryTotal();

   for(int i = 0; i < total; i++)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) continue;
      if(OrderType() != OP_BUY && OrderType() != OP_SELL) continue;
      if(InpOnlyThisSymbol && OrderSymbol() != Symbol()) continue;

      datetime ct = OrderCloseTime();
      if(ct == 0) continue;
      if(cutoff > 0 && ct < cutoff) continue;

      double profit = OrderProfit();
      if(InpShowCommission)
         profit += OrderSwap() + OrderCommission();

      double lots = OrderLots();

      //--- 累计
      g_totalTrades++;
      g_totalLots += lots;
      g_totalNet  += profit;
      if(profit > 0) { g_totalWins++; g_grossWin += profit; }
      else if(profit < 0) { g_grossLoss += profit; }

      //--- 分组
      AddTrade(g_days,   g_dayCount,   GetDayKey(ct),   lots, profit);
      AddTrade(g_months, g_monthCount, GetMonthKey(ct), lots, profit);
      AddTrade(g_years,  g_yearCount,  GetYearKey(ct),  lots, profit);
   }

   SortDescending(g_days,   g_dayCount);
   SortDescending(g_months, g_monthCount);
   SortDescending(g_years,  g_yearCount);
}

//==================================================================
//                      对象绘制
//==================================================================
void DeleteObjectSafe(string name)
{
   if(ObjectFind(0, name) >= 0) ObjectDelete(0, name);
}

void SetRect(string name, int x, int y, int w, int h, color bg, color border)
{
   if(ObjectFind(0, name) < 0)
      ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, name, OBJPROP_XSIZE, w);
   ObjectSetInteger(0, name, OBJPROP_YSIZE, h);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg);
   ObjectSetInteger(0, name, OBJPROP_COLOR, border);
   ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
   ObjectSetInteger(0, name, OBJPROP_BACK, false);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
}

void SetLabel(string name, int x, int y, string text, color clr,
              int anchor = ANCHOR_LEFT_UPPER, int fontSize = 0)
{
   if(ObjectFind(0, name) < 0)
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
   ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize > 0 ? fontSize : InpFontSize);
   ObjectSetString(0, name, OBJPROP_FONT, InpFontName);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, name, OBJPROP_BACK, false);
}

void SetButton(string name, int x, int y, int w, int h, string text,
               color bg, color textClr, bool pressed)
{
   if(ObjectFind(0, name) < 0)
      ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, name, OBJPROP_XSIZE, w);
   ObjectSetInteger(0, name, OBJPROP_YSIZE, h);
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg);
   ObjectSetInteger(0, name, OBJPROP_COLOR, textClr);
   ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, InpBorderColor);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, InpFontSize);
   ObjectSetString(0, name, OBJPROP_FONT, InpFontName);
   ObjectSetInteger(0, name, OBJPROP_STATE, pressed);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
}

//==================================================================
//                      左面板:汇总
//==================================================================
void DrawLeftPanel()
{
   int x = InpLeftPanelX;
   int y = InpLeftPanelY;
   int w = InpLeftPanelW;

   //--- 背景
   SetRect(OBJ_PREFIX + "left_bg", x, y, w, 400, InpBgColor, InpBorderColor);

   int cy = y + 10;
   int lx = x + 15;
   int rx = x + w - 15;

   //--- 标题
   SetLabel(OBJ_PREFIX + "left_title", x + w / 2, cy,
            "◈ 账户概览 ◈", InpTitleColor, ANCHOR_UPPER);
   cy += 24;

   //--- 账户信息
   double balance   = AccountBalance();
   double equity    = AccountEquity();
   double floating  = equity - balance;
   double margin    = AccountMargin();
   double freeM     = AccountFreeMargin();
   string currency  = AccountCurrency();

   SetLabel(OBJ_PREFIX + "l1a", lx, cy, "账户货币", InpLabelColor);
   SetLabel(OBJ_PREFIX + "l1b", rx, cy, currency, InpTextColor, ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "l2a", lx, cy, "账户余额", InpLabelColor);
   SetLabel(OBJ_PREFIX + "l2b", rx, cy, FmtMoney(balance), InpTextColor, ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "l3a", lx, cy, "账户净值", InpLabelColor);
   SetLabel(OBJ_PREFIX + "l3b", rx, cy, FmtMoney(equity), ColorForProfit(equity - balance), ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "l4a", lx, cy, "浮动盈亏", InpLabelColor);
   SetLabel(OBJ_PREFIX + "l4b", rx, cy, FmtMoney(floating, true), ColorForProfit(floating), ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "l5a", lx, cy, "已用保证金", InpLabelColor);
   SetLabel(OBJ_PREFIX + "l5b", rx, cy, FmtMoney(margin), InpTextColor, ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "l6a", lx, cy, "可用保证金", InpLabelColor);
   SetLabel(OBJ_PREFIX + "l6b", rx, cy, FmtMoney(freeM), InpTextColor, ANCHOR_RIGHT_UPPER);
   cy += 16;

   if(margin > 0.0001)
   {
      double level = equity / margin * 100.0;
      SetLabel(OBJ_PREFIX + "l7a", lx, cy, "保证金水平", InpLabelColor);
      SetLabel(OBJ_PREFIX + "l7b", rx, cy,
               DoubleToString(level, 1) + "%",
               (level > 200 ? InpProfitColor : InpLossColor),
               ANCHOR_RIGHT_UPPER);
      cy += 16;
   }

   cy += 8;

   //--- 分节:统计概览
   SetLabel(OBJ_PREFIX + "left_sec1", lx, cy, "【 历史统计汇总 】", InpSectionColor);
   cy += 20;

   SetLabel(OBJ_PREFIX + "s1a", lx, cy, "统计交易数", InpLabelColor);
   SetLabel(OBJ_PREFIX + "s1b", rx, cy, (string)g_totalTrades + " 笔", InpTextColor, ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "s2a", lx, cy, "统计总手数", InpLabelColor);
   SetLabel(OBJ_PREFIX + "s2b", rx, cy, FmtLots(g_totalLots) + " 手", InpTextColor, ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "s3a", lx, cy, "盈利笔数", InpLabelColor);
   double winRate = (g_totalTrades > 0) ? (double)g_totalWins / g_totalTrades * 100.0 : 0;
   SetLabel(OBJ_PREFIX + "s3b", rx, cy,
            (string)g_totalWins + "  (" + DoubleToString(winRate, 1) + "%)",
            InpProfitColor, ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "s4a", lx, cy, "亏损笔数", InpLabelColor);
   SetLabel(OBJ_PREFIX + "s4b", rx, cy,
            (string)(g_totalTrades - g_totalWins) + "  (" +
            DoubleToString(100.0 - winRate, 1) + "%)",
            InpLossColor, ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "s5a", lx, cy, "总盈利", InpLabelColor);
   SetLabel(OBJ_PREFIX + "s5b", rx, cy, FmtMoney(g_grossWin), InpProfitColor, ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "s6a", lx, cy, "总亏损", InpLabelColor);
   SetLabel(OBJ_PREFIX + "s6b", rx, cy, FmtMoney(g_grossLoss), InpLossColor, ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "s7a", lx, cy, "净利润", InpLabelColor);
   SetLabel(OBJ_PREFIX + "s7b", rx, cy, FmtMoney(g_totalNet, true),
            ColorForProfit(g_totalNet), ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "s8a", lx, cy, "盈利因子", InpLabelColor);
   double pf = (g_grossLoss != 0) ? MathAbs(g_grossWin / g_grossLoss) : 0;
   SetLabel(OBJ_PREFIX + "s8b", rx, cy, DoubleToString(pf, 2),
            (pf >= 1.5 ? InpProfitColor : (pf >= 1.0 ? InpTextColor : InpLossColor)),
            ANCHOR_RIGHT_UPPER);
   cy += 16;

   SetLabel(OBJ_PREFIX + "s9a", lx, cy, "平均每笔", InpLabelColor);
   double avg = (g_totalTrades > 0) ? g_totalNet / g_totalTrades : 0;
   SetLabel(OBJ_PREFIX + "s9b", rx, cy, FmtMoney(avg, true),
            ColorForProfit(avg), ANCHOR_RIGHT_UPPER);
   cy += 20;

   //--- 更新左面板高度
   SetRect(OBJ_PREFIX + "left_bg", x, y, w, cy - y + 8, InpBgColor, InpBorderColor);
}

//==================================================================
//                      右面板:表格
//==================================================================
void DrawRightPanel()
{
   int x = InpRightPanelX;
   int y = InpLeftPanelY;
   int w = InpRightPanelW;

   //--- 列布局
   int colDate    = x + 12;
   int colTrades  = x + 130;
   int colLots    = x + 200;
   int colWin     = x + 270;
   int colLoss    = x + 350;
   int colNet     = x + 430;
   int colRate    = x + 505;

   //--- 选择数据源
   SPeriodStat arr[];
   int count = 0;
   string title = "";
   string dateHeader = "";

   if(g_viewMode == 0)
   {
      ArrayResize(arr, g_dayCount);
      for(int i = 0; i < g_dayCount; i++) arr[i] = g_days[i];
      count = g_dayCount;
      title = "
最近访问 头像模式
举报

评论 使用道具

发新帖
EA交易
您需要登录后才可以评论 登录 | 立即注册