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. 🔜 進階交易策略:多時間框架分析實作

---

*資金管理是交易中最個人化的部分,沒有一體適用的解決方案。最重要的是找到適合你風險承受度和交易風格的策略。如果你在實作過程中遇到問題,歡迎在下方留言討論!*

Similar Posts

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *