MQL5 CSV 交易日誌:自動記錄每筆交易到檔案
📌 本文重點
學習用 MQL5 的檔案操作函數建立完整的交易日誌系統,自動將每筆交易記錄為 CSV 格式,方便用 Excel 分析績效。
學習用 MQL5 的檔案操作函數建立完整的交易日誌系統,自動將每筆交易記錄為 CSV 格式,方便用 Excel 分析績效。
MQL5 檔案操作基礎
// 檔案路徑:MT5 的 MQL5/Files/ 目錄下
// 可在 MT5「文件」→「開啟數據資料夾」→「MQL5/Files」找到
void WriteTestFile()
{
// 開啟(或建立)CSV 檔案,附加模式(不覆蓋舊數據)
int fileHandle = FileOpen("trade_log.csv",
FILE_WRITE | FILE_READ | FILE_CSV | FILE_ANSI,
','); // 分隔符
if (fileHandle == INVALID_HANDLE)
{
Print("無法開啟檔案!錯誤: ", GetLastError());
return;
}
// 移動到檔案末尾(附加模式)
FileSeek(fileHandle, 0, SEEK_END);
// 寫入一行(逗號分隔)
FileWrite(fileHandle,
TimeToString(TimeCurrent()), // 時間
_Symbol, // 品種
"BUY", // 方向
"0.10", // 手數
"1.08500", // 進場價
"1.08000", // 止損
"1.09500", // 止盈
"55.20" // 盈虧
);
FileClose(fileHandle);
Print("數據已寫入 trade_log.csv");
}
完整交易日誌系統
class CTradeLogger
{
private:
string m_filename;
bool m_headerWritten;
void EnsureHeader()
{
if (m_headerWritten) return;
// 檢查檔案是否存在
bool fileExists = FileIsExist(m_filename, FILE_COMMON);
m_headerWritten = fileExists; // 已存在則假設標頭已寫
if (!fileExists)
{
int fh = FileOpen(m_filename, FILE_WRITE|FILE_CSV|FILE_COMMON|FILE_ANSI, ',');
if (fh == INVALID_HANDLE) return;
// 寫入標頭
FileWrite(fh, "Time","Symbol","Direction","Lots","OpenPrice",
"ClosePrice","SL","TP","Profit","Duration","MagicNo","Comment");
FileClose(fh);
m_headerWritten = true;
}
}
public:
CTradeLogger(string filename = "TradeLog.csv")
: m_filename(filename), m_headerWritten(false) {}
void LogTrade(string symbol, string dir, double lots,
double openPx, double closePx, double sl, double tp,
double profit, int durationMin, long magic, string comment)
{
EnsureHeader();
int fh = FileOpen(m_filename, FILE_READ|FILE_WRITE|FILE_CSV|FILE_COMMON|FILE_ANSI, ',');
if (fh == INVALID_HANDLE) { Print("日誌寫入失敗"); return; }
FileSeek(fh, 0, SEEK_END);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
FileWrite(fh,
TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS),
symbol, dir,
DoubleToString(lots, 2),
DoubleToString(openPx, digits),
DoubleToString(closePx, digits),
DoubleToString(sl, digits),
DoubleToString(tp, digits),
DoubleToString(profit, 2),
IntegerToString(durationMin),
IntegerToString(magic),
comment
);
FileClose(fh);
}
};
// 全域日誌物件
CTradeLogger g_logger;
// 在 OnTradeTransaction 中自動記錄平倉
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &req,
const MqlTradeResult &res)
{
// 只記錄持倉關閉事件
if (trans.type != TRADE_TRANSACTION_DEAL_ADD) return;
if (trans.deal_type != DEAL_TYPE_BUY && trans.deal_type != DEAL_TYPE_SELL) return;
// 從歷史中取得交易詳情
if (!HistoryDealSelect(trans.deal)) return;
long entry = HistoryDealGetInteger(trans.deal, DEAL_ENTRY);
if (entry != DEAL_ENTRY_OUT && entry != DEAL_ENTRY_INOUT) return; // 只記錄平倉
string symbol = HistoryDealGetString(trans.deal, DEAL_SYMBOL);
double profit = HistoryDealGetDouble(trans.deal, DEAL_PROFIT);
double lots = HistoryDealGetDouble(trans.deal, DEAL_VOLUME);
double price = HistoryDealGetDouble(trans.deal, DEAL_PRICE);
long magic = HistoryDealGetInteger(trans.deal, DEAL_MAGIC);
string comment = HistoryDealGetString(trans.deal, DEAL_COMMENT);
int type = (int)HistoryDealGetInteger(trans.deal, DEAL_TYPE);
g_logger.LogTrade(
symbol,
(type == DEAL_TYPE_SELL) ? "BUY_CLOSE" : "SELL_CLOSE",
lots, 0, price, 0, 0, profit, 0, magic, comment
);
Print("交易已記錄:", symbol, " 盈虧=$", profit);
}
讀取 CSV 進行統計
void PrintTradeSummary()
{
int fh = FileOpen("TradeLog.csv", FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ',');
if (fh == INVALID_HANDLE) return;
FileReadString(fh); // 跳過標頭行
int totalTrades = 0, winners = 0;
double totalProfit = 0, totalWin = 0, totalLoss = 0;
while (!FileIsEnding(fh))
{
for (int col = 0; col < 8; col++) FileReadString(fh); // 跳過前幾列
double profit = StringToDouble(FileReadString(fh)); // 第9列 = 盈虧
for (int col = 0; col < 3; col++) FileReadString(fh); // 跳過剩餘列
totalTrades++;
totalProfit += profit;
if (profit > 0) { winners++; totalWin += profit; }
else { totalLoss += MathAbs(profit); }
}
FileClose(fh);
double winRate = totalTrades > 0 ? (double)winners/totalTrades*100 : 0;
double pf = totalLoss > 0 ? totalWin/totalLoss : 0;
Print("=== 交易統計 ===");
Print("總交易數: ", totalTrades, " | 勝率: ", DoubleToString(winRate,1), "%");
Print("總獲利: $", DoubleToString(totalProfit,2));
Print("利潤因子: ", DoubleToString(pf,2));
}
本文由 James Lee 撰寫。內容僅供教育目的,不構成投資建議。