MQL5 資金管理演算法完整指南:從凱利公式到動態手數調整
本文完整介紹 MQL5 資金管理的核心演算法,從最基本的固定手數到進階的凱利公式、動態風險調整。包含所有方法的完整 MQL5 程式碼、優缺點分析,以及適合不同交易策略的選擇建議。
MQL5 資金管理演算法完整指南:從凱利公式到動態調整
> 「好的交易者知道如何賺錢,偉大的交易者知道如何不虧錢。」 — 資金管理的核心哲學
前言:為什麼90%的交易者失敗?
我曾經問過一個在華爾街工作了20年的交易員:「新手交易者最常犯的錯誤是什麼?」
他的回答很簡單:「他們專注於尋找『聖杯』策略,卻忽略了資金管理。就像一個廚師只關心食譜,卻忘了控制火候。」
今天,我想分享的不只是資金管理的「食譜」,更重要的是如何根據市場狀況「控制火候」。
第一部分:資金管理的基本原則
1.1 兩個不可違反的規則
在我多年的交易經驗中,我總結出兩個鐵律:
規則一:永遠不要在一次交易中冒超過2%的風險
– 這意味著即使連續虧損10次,你只會損失20%的資金
– 你還有80%的資金可以東山再起
規則二:永遠不要在一次交易中冒超過總風險的20%
– 這包括所有持倉的總風險
– 避免「把所有雞蛋放在一個籃子裡」
//+------------------------------------------------------------------+
//| 資金管理基礎檢查類別 |
//+------------------------------------------------------------------+
class MoneyManagementBase
{
protected:
double accountBalance;
double accountEquity;
double freeMargin;
public:
MoneyManagementBase()
{
UpdateAccountInfo();
}
void UpdateAccountInfo()
{
accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
accountEquity = AccountInfoDouble(ACCOUNT_EQUITY);
freeMargin = AccountInfoDouble(ACCOUNT_FREEMARGIN);
}
// 檢查單筆交易風險
bool CheckSingleTradeRisk(double riskAmount, double maxRiskPercent = 2.0)
{
double maxRisk = accountBalance * (maxRiskPercent / 100.0);
if(riskAmount > maxRisk)
{
Print("❌ 風險檢查失敗: 單筆風險超過 ", maxRiskPercent, "%");
Print("風險金額: $", DoubleToString(riskAmount, 2));
Print("允許上限: $", DoubleToString(maxRisk, 2));
return false;
}
Print("✅ 單筆風險檢查通過");
return true;
}
// 檢查總風險
bool CheckTotalRisk(double newTradeRisk, double maxTotalRiskPercent = 20.0)
{
// 計算現有持倉的總風險
double existingRisk = CalculateExistingPositionsRisk();
double totalRisk = existingRisk + newTradeRisk;
double maxTotalRisk = accountBalance * (maxTotalRiskPercent / 100.0);
if(totalRisk > maxTotalRisk)
{
Print("❌ 總風險檢查失敗: 超過 ", maxTotalRiskPercent, "%");
Print("現有風險: $", DoubleToString(existingRisk, 2));
Print("新增風險: $", DoubleToString(newTradeRisk, 2));
Print("總風險: $", DoubleToString(totalRisk, 2));
Print("允許上限: $", DoubleToString(maxTotalRisk, 2));
return false;
}
Print("✅ 總風險檢查通過");
return true;
}
private:
double CalculateExistingPositionsRisk()
{
double totalRisk = 0;
int totalPositions = PositionsTotal();
for(int i = 0; i < totalPositions; i++)
{
if(PositionGetSymbol(i) == _Symbol)
{
double volume = PositionGetDouble(POSITION_VOLUME);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double stopLoss = PositionGetDouble(POSITION_SL);
if(stopLoss > 0)
{
double riskPerLot = MathAbs(openPrice - stopLoss) *
SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE) *
volume;
totalRisk += riskPerLot;
}
}
}
return totalRisk;
}
};
第二部分:六種實用的資金管理策略
2.1 固定手數策略(最簡單,但不推薦)
//+------------------------------------------------------------------+
//| 固定手數策略 |
//+------------------------------------------------------------------+
class FixedLotStrategy : public MoneyManagementBase
{
private:
double fixedLotSize;
public:
FixedLotStrategy(double lotSize = 0.1) : fixedLotSize(lotSize) {}
double CalculateLotSize()
{
Print("使用固定手數策略: ", DoubleToString(fixedLotSize, 2));
// 檢查手數是否有效
if(!ValidateLotSize(fixedLotSize))
{
Print("警告: 固定手數無效,使用最小手數");
return SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
}
return fixedLotSize;
}
// 優點:簡單易用
// 缺點:無法根據帳戶規模調整風險
// 適用:測試階段、小資金帳戶
};
2.2 固定百分比風險策略(最常用)
//+------------------------------------------------------------------+
//| 固定百分比風險策略 |
//+------------------------------------------------------------------+
class FixedPercentRiskStrategy : public MoneyManagementBase
{
private:
double riskPercent; // 每筆交易風險百分比
double stopLossPips; // 止損點數
public:
FixedPercentRiskStrategy(double percent = 1.0, double slPips = 50.0) :
riskPercent(percent), stopLossPips(slPips) {}
double CalculateLotSize()
{
Print("使用固定百分比風險策略");
Print("風險百分比: ", DoubleToString(riskPercent, 1), "%");
Print("止損點數: ", DoubleToString(stopLossPips, 0));
// 計算風險金額
double riskAmount = accountBalance * (riskPercent / 100.0);
// 計算每點價值
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double pointValue = tickValue * (tickSize / SymbolInfoDouble(_Symbol, SYMBOL_POINT));
if(pointValue <= 0)
{
Print("錯誤: 無法計算點值");
return 0;
}
// 計算手數 = 風險金額 / (止損點數 × 每點價值)
double calculatedLots = riskAmount / (stopLossPips * pointValue);
// 調整到符合交易規則
calculatedLots = AdjustLotSize(calculatedLots);
Print("計算手數: ", DoubleToString(calculatedLots, 2));
Print("風險金額: $", DoubleToString(riskAmount, 2));
return calculatedLots;
}
// 優點:風險固定,適合多數交易者
// 缺點:無法根據市場波動調整
// 適用:穩健型交易者
};
2.3 凱利公式策略(數學最優,但風險高)
//+------------------------------------------------------------------+
//| 凱利公式策略 |
//+------------------------------------------------------------------+
class KellyCriterionStrategy : public MoneyManagementBase
{
private:
double winRate; // 勝率 (0-1)
double winLossRatio; // 盈虧比
double kellyFraction; // 凱利分數 (建議用0.5或0.25)
public:
KellyCriterionStrategy(double wr = 0.5, double wlr = 2.0, double fraction = 0.5) :
winRate(wr), winLossRatio(wlr), kellyFraction(fraction) {}
double CalculateLotSize(double stopLossPips)
{
Print("使用凱利公式策略");
Print("勝率: ", DoubleToString(winRate * 100, 1), "%");
Print("盈虧比: ", DoubleToString(winLossRatio, 2));
Print("凱利分數: ", DoubleToString(kellyFraction, 2));
// 凱利公式: f* = (bp - q) / b
// 其中: b = 盈虧比, p = 勝率, q = 敗率
double b = winLossRatio;
double p = winRate;
double q = 1 - p;
double kellyPercent = (b * p - q) / b;
// 使用分數凱利(更安全)
kellyPercent *= kellyFraction;
// 限制在合理範圍內
kellyPercent = MathMin(kellyPercent, 0.25); // 最大25%
kellyPercent = MathMax(kellyPercent, 0.01); // 最小1%
Print("凱利百分比: ", DoubleToString(kellyPercent * 100, 2), "%");
// 轉換為手數
double riskAmount = accountBalance * kellyPercent;
double pointValue = CalculatePointValue();
if(pointValue <= 0)
return 0;
double calculatedLots = riskAmount / (stopLossPips * pointValue);
calculatedLots = AdjustLotSize(calculatedLots);
Print("凱利手數: ", DoubleToString(calculatedLots, 2));
return calculatedLots;
}
// 動態更新勝率和盈虧比
void UpdateStatistics(int totalTrades, int winningTrades, double totalProfit, double totalLoss)
{
if(totalTrades > 0)
{
winRate = (double)winningTrades / totalTrades;
if(winningTrades > 0 && totalLoss < 0)
{
double avgWin = totalProfit / winningTrades;
double avgLoss = MathAbs(totalLoss) / (totalTrades - winningTrades);
winLossRatio = avgWin / avgLoss;
}
Print("統計更新:");
Print("總交易: ", totalTrades, ", 勝率: ", DoubleToString(winRate * 100, 1), "%");
Print("盈虧比: ", DoubleToString(winLossRatio, 2));
}
}
// 優點:數學上最優
// 缺點:需要準確的統計數據,風險較高
// 適用:有長期統計數據的系統化交易者
};
2.4 ATR動態調整策略(根據市場波動)
//+------------------------------------------------------------------+
//| ATR動態調整策略 |
//+------------------------------------------------------------------+
class ATRDynamicStrategy : public MoneyManagementBase
{
private:
int atrPeriod;
double atrMultiplier;
double baseRiskPercent;
int atrHandle;
public:
ATRDynamicStrategy(int period = 14, double multiplier = 2.0, double baseRisk = 1.0) :
atrPeriod(period), atrMultiplier(multiplier), baseRiskPercent(baseRisk),
atrHandle(INVALID_HANDLE) {}
bool Initialize()
{
atrHandle = iATR(_Symbol, _Period, atrPeriod);
return (atrHandle != INVALID_HANDLE);
}
double CalculateLotSize(double stopLossATR = 2.0)
{
if(atrHandle == INVALID_HANDLE)
{
Print("錯誤: ATR指標未初始化");
return 0;
}
// 取得當前ATR值
double atrValues[1];
if(CopyBuffer(atrHandle, 0, 0, 1, atrValues) <= 0)
{
Print("錯誤: 無法取得ATR值");
return 0;
}
double currentATR = atrValues[0];
Print("使用ATR動態調整策略");
Print("當前ATR: ", DoubleToString(currentATR, 5));
Print("ATR週期: ", atrPeriod);
Print("乘數: ", DoubleToString(atrMultiplier, 1));
// 根據ATR調整風險
// ATR較大時(波動大)降低風險,ATR較小時(波動小)增加風險
double atrRatio = CalculateATRRatio(currentATR);
double adjustedRiskPercent = baseRiskPercent * atrRatio;
// 限制風險範圍
adjustedRiskPercent = MathMin(adjustedRiskPercent, 2.0); // 最大2%
adjustedRiskPercent = MathMax(adjustedRiskPercent, 0.5); // 最小0.5%
// 計算止損(基於ATR)
double stopLossPips = stopLossATR * currentATR / SymbolInfoDouble(_Symbol, SYMBOL_POINT);
Print("ATR比率: ", DoubleToString(atrRatio, 2));
Print("調整後風險: ", DoubleToString(adjustedRiskPercent, 2), "%");
Print("動態止損: ", DoubleToString(stopLossPips, 0), "點");
// 計算手數
double riskAmount = accountBalance * (adjustedRiskPercent / 100.0);
double pointValue = CalculatePointValue();
if(pointValue <= 0)
return 0;
double calculatedLots = riskAmount / (stopLossPips * pointValue);
calculatedLots = AdjustLotSize(calculatedLots);
Print("ATR動態手數: ", DoubleToString(calculatedLots, 2));
return calculatedLots;
}
private:
double CalculateATRRatio(double currentATR)
{
// 取得歷史ATR數據計算比率
double atrHistory[100];
if(CopyBuffer(atrHandle, 0, 0, 100, atrHistory) > 0)
{
// 計算平均ATR
double sum = 0;
int count = 0;
for(int i = 0; i < 100; i++)
{
if(atrHistory[i] > 0)
{
sum += atrHistory[i];
count++;
}
}
if(count > 0)
{
double avgATR = sum / count;
double ratio = avgATR / currentATR;
// 限制比率在0.5-2.0之間
return MathMin(MathMax(ratio, 0.5), 2.0);
}
}
return 1.0; // 默認比率
}
// 優點:根據市場波動動態調整
// 缺點:需要ATR指標數據
// 適用:波動性市場、趨勢交易
};
2.5 資金曲線管理策略(根據績效調整)
//+------------------------------------------------------------------+
//| 資金曲線管理策略 |
//+------------------------------------------------------------------+
class EquityCurveStrategy : public MoneyManagementBase
{
private:
double initialBalance;
double highWaterMark; // 資金曲線高點
double currentDrawdown; // 當前回撤
double maxAllowedDrawdown; // 最大允許回撤
double riskMultiplier; // 風險乘數
public:
EquityCurveStrategy(double maxDrawdownPercent = 20.0) :
initialBalance(0), highWaterMark(0), currentDrawdown(0),
maxAllowedDrawdown(maxDrawdownPercent), riskMultiplier(1.0)
{
initialBalance = accountBalance;
highWaterMark = accountBalance;
}
void UpdateEquityCurve()
{
double currentEquity = accountEquity;
// 更新高水位線
if(currentEquity > highWaterMark)
{
highWaterMark = currentEquity;
currentDrawdown = 0;
// 創新高時可以稍微增加風險
riskMultiplier = MathMin(riskMultiplier * 1.1, 2.0);
Print("🎉 創新高!風險乘數調整為: ", DoubleToString(riskMultiplier, 2));
}
else
{
// 計算當前回撤
currentDrawdown = (highWaterMark - currentEquity) / highWaterMark * 100;
// 根據回撤調整風險
AdjustRiskByDrawdown();
}
Print("資金曲線狀態:");
Print("初始資金: $", DoubleToString(initialBalance, 2));
Print("高水位線: $", DoubleToString(highWaterMark, 2));
Print("當前淨值: $", DoubleToString(currentEquity, 2));
Print("當前回撤: ", DoubleToString(currentDrawdown, 2), "%");
Print("風險乘數: ", DoubleToString(riskMultiplier, 2));
}
double CalculateLotSize(double baseLotSize)
{
UpdateEquityCurve();
// 根據資金曲線狀態調整手數
double adjustedLotSize = baseLotSize * riskMultiplier;
// 如果回撤過大,強制降低風險
if(currentDrawdown > maxAllowedDrawdown * 0.5) // 超過50%最大允許回撤
{
double reductionFactor = 1.0 - (currentDrawdown / maxAllowedDrawdown);
adjustedLotSize *= MathMax(reductionFactor, 0.1); // 至少保留10%
Print("⚠️ 回撤警告: 降低風險至 ", DoubleToString(reductionFactor * 100, 0), "%");
}
// 如果接近最大回撤,停止交易
if(currentDrawdown >= maxAllowedDrawdown)
{
Print("❌ 達到最大回撤限制: ", DoubleToString(currentDrawdown, 2), "%");
Print("建議: 停止交易,重新評估策略");
return 0;
}
adjustedLotSize = AdjustLotSize(adjustedLotSize);
Print("資金曲線調整手數: ", DoubleToString(adjustedLotSize, 2));
return adjustedLotSize;
}
private:
void AdjustRiskByDrawdown()
{
if(currentDrawdown <= maxAllowedDrawdown * 0.2) // 回撤小於20%
{
// 正常狀態,使用標準風險
riskMultiplier = 1.0;
}
else if(currentDrawdown <= maxAllowedDrawdown * 0.5) // 回撤20-50%
{
// 中等回撤,降低風險
riskMultiplier = 0.7;
Print("中等回撤,風險乘數調整為0.7");
}
else if(currentDrawdown <= maxAllowedDrawdown * 0.8) // 回撤50-80%
{
// 較大回撤,大幅降低風險
riskMultiplier = 0.3;
Print("較大回撤,風險乘數調整為0.3");
}
else // 回撤80-100%
{
// 接近最大回撤,極度保守
riskMultiplier = 0.1;
Print("接近最大回撤,風險乘數調整為0.1");
}
}
// 優點:根據實際績效動態調整
// 缺點:需要追蹤資金曲線
// 適用:所有類型的交易者,特別是風險厭惡者
};
2.6 馬丁格爾與反馬丁格爾策略(高風險,謹慎使用)
//+------------------------------------------------------------------+
//| 馬丁格爾策略(謹慎使用!) |
//+------------------------------------------------------------------+
class MartingaleStrategy : public MoneyManagementBase
{
private:
double baseLotSize;
double multiplier;
int maxLevels;
int currentLevel;
double lastLoss;
public:
MartingaleStrategy(double base = 0.01, double mult = 2.0, int maxLvl = 5) :
baseLotSize(base), multiplier(mult), maxLevels(maxLvl),
currentLevel(0), lastLoss(0) {}
double CalculateLotSize(bool lastTradeWon, double lastTradeProfit)
{
if(lastTradeWon)
{
// 贏了,重置到基礎層級
currentLevel = 0;
lastLoss = 0;
Print("贏利交易,重置馬丁格爾層級");
return baseLotSize;
}
else
{
// 輸了,增加層級
currentLevel++;
lastLoss = MathAbs(lastTradeProfit);
if(currentLevel > maxLevels)
{
Print("❌ 達到最大馬丁格爾層級: ", currentLevel);
Print("建議: 停止交易,重新評估");
return 0;
}
// 計算新手數 = 基礎手數 × 乘數^層級
double newLotSize = baseLotSize * MathPow(multiplier, currentLevel);
// 檢查風險
double riskAmount = newLotSize * lastLoss / baseLotSize;
if(!CheckSingleTradeRisk(riskAmount, 5.0)) // 馬丁格爾允許稍高風險
{
Print("馬丁格爾風險過高,使用最大允許手數");
newLotSize = CalculateMaxAllowedLotSize(riskAmount);
}
newLotSize = AdjustLotSize(newLotSize);
Print("馬丁格爾層級: ", currentLevel, "/", maxLevels);
Print("新手數: ", DoubleToString(newLotSize, 2));
Print("乘數: ", DoubleToString(multiplier, 1));
return newLotSize;
}
}
// 反馬丁格爾(贏時加碼,輸時減碼)
double CalculateAntiMartingale(bool lastTradeWon, double currentProfit)
{
if(lastTradeWon && currentProfit > 0)
{
// 贏了且總體盈利,增加手數
double profitRatio = currentProfit / accountBalance;
double increaseFactor = 1.0 + (profitRatio * 2.0); // 根據盈利比例增加
double newLotSize = baseLotSize * increaseFactor;
newLotSize = MathMin(newLotSize, baseLotSize * 3.0); // 最多3倍
Print("反馬丁格爾: 贏利加碼");
Print("盈利比例: ", DoubleToString(profitRatio * 100, 2), "%");
Print("加碼係數: ", DoubleToString(increaseFactor, 2));
return AdjustLotSize(newLotSize);
}
else
{
// 輸了或總體虧損,減少手數
Print("反馬丁格爾: 虧損減碼");
return baseLotSize * 0.5; // 減半
}
}
// ⚠️ 警告:馬丁格爾策略風險極高!
// 優點:理論上可以快速回本
// 缺點:需要無限資金,可能導致爆倉
// 適用:極度謹慎,只用於模擬帳戶測試
};
第三部分:實戰整合 - 智能資金管理系統
//+------------------------------------------------------------------+
//| 智能資金管理系統 |
//+------------------------------------------------------------------+
class SmartMoneyManagementSystem
{
private:
enum STRATEGY_TYPE
{
STRATEGY_FIXED_PERCENT, // 固定百分比
STRATEGY_KELLY, // 凱利公式
STRATEGY_ATR_DYNAMIC, // ATR動態
STRATEGY_EQUITY_CURVE, // 資金曲線
STRATEGY_ADAPTIVE // 自適應混合
};
STRATEGY_TYPE currentStrategy;
// 各種策略實例
FixedPercentRiskStrategy* fixedPercent;
KellyCriterionStrategy* kelly;
ATRDynamicStrategy* atrDynamic;
EquityCurveStrategy* equityCurve;
// 交易統計
struct TradeStatistics
{
int totalTrades;
int winningTrades;
double totalProfit;
double totalLoss;
double maxProfit;
double maxLoss;
double currentStreak; // 當前連勝/連負
};
TradeStatistics stats;
public:
SmartMoneyManagementSystem()
{
fixedPercent = new FixedPercentRiskStrategy(1.0, 50.0);
kelly = new KellyCriterionStrategy(0.5, 2.0, 0.5);
atrDynamic = new ATRDynamicStrategy(14, 2.0, 1.0);
equityCurve = new EquityCurveStrategy(20.0);
currentStrategy = STRATEGY_FIXED_PERCENT;
// 初始化統計
stats.totalTrades = 0;
stats.winningTrades = 0;
stats.totalProfit = 0;
stats.totalLoss = 0;
stats.maxProfit = 0;
stats.maxLoss = 0;
stats.currentStreak = 0;
// 初始化ATR策略
atrDynamic.Initialize();
}
~SmartMoneyManagementSystem()
{
delete fixedPercent;
delete kelly;
delete atrDynamic;
delete equityCurve;
}
// 主要接口:計算手數
double CalculateLotSize(double stopLossPips = 50.0)
{
Print("=== 智能資金管理系統 ===");
// 根據市場狀況選擇策略
SelectOptimalStrategy();
double lotSize = 0;
switch(currentStrategy)
{
case STRATEGY_FIXED_PERCENT:
lotSize = fixedPercent->CalculateLotSize();
Print("使用策略: 固定百分比");
break;
case STRATEGY_KELLY:
// 更新凱利公式的統計數據
kelly->UpdateStatistics(stats.totalTrades, stats.winningTrades,
stats.totalProfit, stats.totalLoss);
lotSize = kelly->CalculateLotSize(stopLossPips);
Print("使用策略: 凱利公式");
break;
case STRATEGY_ATR_DYNAMIC:
lotSize = atrDynamic->CalculateLotSize(stopLossPips);
Print("使用策略: ATR動態調整");
break;
case STRATEGY_EQUITY_CURVE:
double baseLot = fixedPercent->CalculateLotSize();
lotSize = equityCurve->CalculateLotSize(baseLot);
Print("使用策略: 資金曲線管理");
break;
case STRATEGY_ADAPTIVE:
lotSize = CalculateAdaptiveLotSize(stopLossPips);
Print("使用策略: 自適應混合");
break;
}
// 最終風險檢查
if(!PerformFinalRiskCheck(lotSize, stopLossPips))
{
Print("最終風險檢查失敗,調整手數");
lotSize = AdjustToSafeLevel(lotSize, stopLossPips);
}
Print("最終手數: ", DoubleToString(lotSize, 2));
return lotSize;
}
// 更新交易統計
void UpdateTradeStatistics(bool tradeWon, double profit)
{
stats.totalTrades++;
if(tradeWon)
{
stats.winningTrades++;
stats.totalProfit += profit;
if(profit > stats.maxProfit)
stats.maxProfit = profit;
if(stats.currentStreak >= 0)
stats.currentStreak++;
else
stats.currentStreak = 1;
}
else
{
stats.totalLoss += profit;
if(profit < stats.maxLoss)
stats.maxLoss = profit;
if(stats.currentStreak <= 0)
stats.currentStreak--;
else
stats.currentStreak = -1;
}
PrintStatistics();
}
private:
void SelectOptimalStrategy()
{
// 根據多種因素選擇最佳策略
// 因素1:交易經驗(交易次數)
if(stats.totalTrades < 30)
{
// 新手:使用固定百分比
currentStrategy = STRATEGY_FIXED_PERCENT;
Print("選擇理由: 交易經驗不足 (<30筆)");
return;
}
// 因素2:市場波動性
double atrValue = GetCurrentATR();
double avgATR = GetAverageATR(100);
if(atrValue > avgATR * 1.5)
{
// 高波動市場:使用ATR動態調整
currentStrategy = STRATEGY_ATR_DYNAMIC;
Print("選擇理由: 高波動市場 (ATR偏高)");
return;
}
// 因素3:當前回撤
double currentDrawdown = equityCurve.GetCurrentDrawdown();
if(currentDrawdown > 10.0)
{
// 較大回撤:使用資金曲線管理
currentStrategy = STRATEGY_EQUITY_CURVE;
Print("選擇理由: 當前回撤較大 (", DoubleToString(currentDrawdown, 1), "%)");
return;
}
// 因素4:統計數據可靠性
if(stats.totalTrades >= 100 && stats.winningTrades >= 30)
{
// 有足夠統計數據:使用凱利公式
currentStrategy = STRATEGY_KELLY;
Print("選擇理由: 有足夠統計數據 (", stats.totalTrades, "筆交易)");
return;
}
// 默認:自適應混合策略
currentStrategy = STRATEGY_ADAPTIVE;
Print("選擇理由: 自適應混合策略");
}
double CalculateAdaptiveLotSize(double stopLossPips)
{
// 計算多種策略的手數,取加權平均
double weights[4] = {0.3, 0.25, 0.25, 0.2}; // 權重
double lot1 = fixedPercent->CalculateLotSize();
double lot2 = kelly->CalculateLotSize(stopLossPips);
double lot3 = atrDynamic->CalculateLotSize(stopLossPips);
double baseLot = fixedPercent->CalculateLotSize();
double lot4 = equityCurve->CalculateLotSize(baseLot);
// 加權平均
double weightedLot = (lot1 * weights[0] +
lot2 * weights[1] +
lot3 * weights[2] +
lot4 * weights[3]);
Print("自適應計算:");
Print("固定百分比: ", DoubleToString(lot1, 2), " (權重: ", DoubleToString(weights[0]*100, 0), "%)");
Print("凱利公式: ", DoubleToString(lot2, 2), " (權重: ", DoubleToString(weights[1]*100, 0), "%)");
Print("ATR動態: ", DoubleToString(lot3, 2), " (權重: ", DoubleToString(weights[2]*100, 0), "%)");
Print("資金曲線: ", DoubleToString(lot4, 2), " (權重: ", DoubleToString(weights[3]*100, 0), "%)");
Print("加權平均: ", DoubleToString(weightedLot, 2));
return weightedLot;
}
bool PerformFinalRiskCheck(double lotSize, double stopLossPips)
{
// 計算實際風險金額
double pointValue = CalculatePointValue();
double riskAmount = lotSize * stopLossPips * pointValue;
// 檢查單筆風險
if(!fixedPercent->CheckSingleTradeRisk(riskAmount, 2.0))
return false;
// 檢查總風險
if(!fixedPercent->CheckTotalRisk(riskAmount, 20.0))
return false;
// 檢查保證金
double marginRequired;
if(OrderCalcMargin(ORDER_TYPE_BUY, _Symbol, lotSize,
SymbolInfoDouble(_Symbol, SYMBOL_ASK), marginRequired))
{
double freeMargin = AccountInfoDouble(ACCOUNT_FREEMARGIN);
if(marginRequired > freeMargin * 0.8) // 使用不超過80%的可用保證金
{
Print("保證金檢查失敗: 需要 $", DoubleToString(marginRequired, 2),
", 可用 $", DoubleToString(freeMargin, 2));
return false;
}
}
return true;
}
double AdjustToSafeLevel(double lotSize, double stopLossPips)
{
// 逐步降低手數直到通過風險檢查
double safeLotSize = lotSize;
for(int i = 0; i < 10; i++) // 最多嘗試10次
{
if(PerformFinalRiskCheck(safeLotSize, stopLossPips))
{
Print("找到安全手數: ", DoubleToString(safeLotSize, 2));
return safeLotSize;
}
// 每次降低20%
safeLotSize *= 0.8;
// 不能低於最小手數
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
if(safeLotSize < minLot)
{
Print("已達最小手數: ", DoubleToString(minLot, 2));
return minLot;
}
}
Print("無法找到安全手數,使用最小手數");
return SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
}
void PrintStatistics()
{
Print("=== 交易統計 ===");
Print("總交易次數: ", stats.totalTrades);
Print("勝率: ", DoubleToString((double)stats.winningTrades / stats.totalTrades * 100, 1), "%");
Print("總盈利: $", DoubleToString(stats.totalProfit, 2));
Print("總虧損: $", DoubleToString(stats.totalLoss, 2));
Print("淨利潤: $", DoubleToString(stats.totalProfit + stats.totalLoss, 2));
Print("最大單筆盈利: $", DoubleToString(stats.maxProfit, 2));
Print("最大單筆虧損: $", DoubleToString(stats.maxLoss, 2));
Print("當前連勝/連負: ", DoubleToString(stats.currentStreak, 0));
if(stats.totalTrades >= 10)
{
double profitFactor = MathAbs(stats.totalProfit) / MathAbs(stats.totalLoss);
Print("獲利因子: ", DoubleToString(profitFactor, 2));
double avgWin = stats.winningTrades > 0 ? stats.totalProfit / stats.winningTrades : 0;
double avgLoss = (stats.totalTrades - stats.winningTrades) > 0 ?
MathAbs(stats.totalLoss) / (stats.totalTrades - stats.winningTrades) : 0;
double winLossRatio = avgLoss > 0 ? avgWin / avgLoss : 0;
Print("盈虧比: ", DoubleToString(winLossRatio, 2));
}
}
double GetCurrentATR()
{
// 取得當前ATR值
return 0; // 簡化實現
}
double GetAverageATR(int periods)
{
// 取得平均ATR值
return 0; // 簡化實現
}
double CalculatePointValue()
{
// 計算每點價值
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
return tickValue * (tickSize / point);
}
double AdjustLotSize(double lotSize)
{
// 調整手數符合交易規則
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
// 確保在範圍內
lotSize = MathMax(lotSize, minLot);
lotSize = MathMin(lotSize, maxLot);
// 調整到符合步進
lotSize = MathRound(lotSize / lotStep) * lotStep;
return NormalizeDouble(lotSize, 2);
}
};
// 全域資金管理實例
SmartMoneyManagementSystem g_moneyManager;
// 使用範例
double GetOptimalLotSize(double stopLossPips = 50.0)
{
return g_moneyManager.CalculateLotSize(stopLossPips);
}
void RecordTradeResult(bool won, double profit)
{
g_moneyManager.UpdateTradeStatistics(won, profit);
}
第四部分:實戰案例與常見錯誤
4.1 案例研究:三種不同風格的交易者
// 案例1:保守型交易者(退休人士)
class ConservativeTrader
{
public:
void Configure()
{
Print("=== 保守型交易者設定 ===");
Print("1. 使用固定百分比策略 (0.5-1%)");
Print("2. 最大總風險: 10%");
Print("3. 使用資金曲線管理");
Print("4. 避開馬丁格爾等高風險策略");
Print("目標: 穩定增長,低回撤");
}
};
// 案例2:穩健型交易者(專業交易員)
class ModerateTrader
{
public:
void Configure()
{
Print("=== 穩健型交易者設定 ===");
Print("1. 使用ATR動態調整策略");
Print("2. 最大單筆風險: 2%");
Print("3. 最大總風險: 20%");
Print("4. 根據市場波動調整");
Print("目標: 平衡風險與回報");
}
};
// 案例3:激進型交易者(年輕投資者)
class AggressiveTrader
{
public:
void Configure()
{
Print("=== 激進型交易者設定 ===");
Print("1. 使用凱利公式 (分數凱利)");
Print("2. 最大單筆風險: 5%");
Print("3. 使用反馬丁格爾加碼");
Print("4. 接受較高回撤");
Print("⚠️ 警告: 高風險策略!");
Print("目標: 最大化長期回報");
}
};
4.2 常見資金管理錯誤
// 錯誤1:沒有資金管理
void Mistake1_NoMoneyManagement()
{
Print("❌ 錯誤1: 沒有資金管理");
Print("表現: 隨意決定手數,情緒化交易");
Print("結果: 可能一次虧損就爆倉");
Print("解決: 實作至少一種資金管理策略");
}
// 錯誤2:過度交易
void Mistake2_OverTrading()
{
Print("❌ 錯誤2: 過度交易");
Print("表現: 頻繁交易,手數過大");
Print("結果: 高額手續費,情緒疲勞");
Print("解決: 設定每日/每週交易限制");
}
// 錯誤3:報復性交易
void Mistake3_RevengeTrading()
{
Print("❌ 錯誤3: 報復性交易");
Print("表現: 虧損後加大手數想快速回本");
Print("結果: 通常導致更大虧損");
Print("解決: 虧損後暫停交易,冷靜後再繼續");
}
// 錯誤4:忽略相關性風險
void Mistake4_IgnoreCorrelation()
{
Print("❌ 錯誤4: 忽略相關性風險");
Print("表現: 同時交易高度相關的商品");
Print("結果: 看似分散風險,實則集中風險");
Print("解決: 分析商品相關性,真正分散投資");
}
// 錯誤5:沒有定期重新平衡
void Mistake5_NoRebalancing()
{
Print("❌ 錯誤5: 沒有定期重新平衡");
Print("表現: 獲利部位越來越大,風險集中");
Print("結果: 一次回撤可能損失大量利潤");
Print("解決: 定期提取利潤,重新平衡倉位");
}
第五部分:實戰檢查清單與最佳實踐
5.1 資金管理檢查清單
bool RunMoneyManagementChecklist()
{
Print("=== 資金管理檢查清單 ===");
bool checklist[10] = {false};
int passed = 0;
// 1. 是否有明確的資金管理策略?
checklist[0] = HasMoneyManagementStrategy();
Print(checklist[0] ? "✅ 1. 有明確資金管理策略" : "❌ 1. 無明確資金管理策略");
// 2. 單筆風險是否控制在2%以內?
checklist[1] = CheckSingleTradeRisk(2.0);
Print(checklist[1] ? "✅ 2. 單筆風險 ≤ 2%" : "❌ 2. 單筆風險 > 2%");
// 3. 總風險是否控制在20%以內?
checklist[2] = CheckTotalRisk(20.0);
Print(checklist[2] ? "✅ 3. 總風險 ≤ 20%" : "❌ 3. 總風險 > 20%");
// 4. 是否有最大回撤限制?
checklist[3] = HasMaxDrawdownLimit();
Print(checklist[3] ? "✅ 4. 有最大回撤限制" : "❌ 4. 無最大回撤限制");
// 5. 是否有定期提取利潤計劃?
checklist[4] = HasProfitWithdrawalPlan();
Print(checklist[4] ? "✅ 5. 有定期提取利潤計劃" : "❌ 5. 無利潤提取計劃");
// 6. 是否有災難恢復計劃?
checklist[5] = HasDisasterRecoveryPlan();
Print(checklist[5] ? "✅ 6. 有災難恢復計劃" : "❌ 6. 無災難恢復計劃");
// 7. 是否有情緒控制機制?
checklist[6] = HasEmotionalControlMechanism();
Print(checklist[6] ? "✅ 7. 有情緒控制機制" : "❌ 7. 無情緒控制機制");
// 8. 是否有交易日誌記錄?
checklist[7] = HasTradeJournal();
Print(checklist[7] ? "✅ 8. 有交易日誌記錄" : "❌ 8. 無交易日誌記錄");
// 9. 是否有定期評估機制?
checklist[8] = HasRegularReviewMechanism();
Print(checklist[8] ? "✅ 9. 有定期評估機制" : "❌ 9. 無定期評估機制");
// 10. 是否有停止交易條件?
checklist[9] = HasStopTradingConditions();
Print(checklist[9] ? "✅ 10. 有停止交易條件" : "❌ 10. 無停止交易條件");
// 計算通過率
for(int i = 0; i < 10; i++)
{
if(checklist[i]) passed++;
}
double passRate = (double)passed / 10 * 100;
Print("\n檢查結果: ", passed, "/10 通過 (", DoubleToString(passRate, 1), "%)");
if(passRate >= 80.0)
{
Print("🎉 資金管理良好!");
return true;
}
else if(passRate >= 60.0)
{
Print("⚠️ 資金管理需要改進");
return false;
}
else
{
Print("❌ 資金管理存在嚴重問題");
return false;
}
}
5.2 最佳實踐總結
1. 從保守開始:先用1%風險測試,證明有效後再考慮增加
2. 分散風險:不要把所有資金投入單一策略或商品
3. 定期提取利潤:把部分利潤轉出,保護已實現收益
4. 保持紀錄:詳細記錄每筆交易和資金變化
5. 定期評估:每月評估資金管理效果,必要時調整
6. 情緒隔離:用規則代替情緒決策
7. 準備備用計劃:預先設定各種情況的應對策略
結語:資金管理是藝術也是科學
資金管理就像開車時的安全帶——平時可能覺得麻煩,但關鍵時刻能救你的命。
我學到的最重要一課是:成功的交易不是關於你賺了多少,而是關於你沒有虧多少。
三個關鍵收穫:
1. 風險控制優先:永遠把風險控制放在第一位
2. 一致性最重要:堅持執行你的資金管理規則
3. 持續學習改進:根據經驗不斷優化你的策略
記住,市場永遠在那裡,但你的資金可能不會。保護好你的資金,你就有無限的機會。
---
系列文章進度:
1. ✅ 從零開始建立你的第一個 MQL5 專家顧問
2. ✅ MQL5 程式設計常見錯誤與最佳實踐
3. ✅ 回測與實盤測試完全指南
4. ✅ MQL5 資金管理演算法完整指南(本篇)
5. 🔜 MQL5 技術指標深度解析與實戰應用
6. 🔜 進階交易策略:多時間框架分析實作
---
*資金管理是交易中最個人化的部分,沒有一體適用的解決方案。最重要的是找到適合你風險承受度和交易風格的策略。如果你在實作過程中遇到問題,歡迎在下方留言討論!*