Skip to content

资金管理的4种方式

1.固定金额:以损定量,每次开仓固定100美金止损

  • 止损距离大,开仓手数就会小
  • 止损距离小,开仓手数就会大
  • 回测的时候采用这种方式可以公平的看待每一笔交易,避免策略因为早期盈利弱,近期盈利强导致回测结果盈利因子的问题

2.固定账户百分比止损: 每次开仓固定1%账户止损

  • 使用账户余额的1%作为止损
  • 和第一种区别是随着账户的净值增长,止损金额也会相应增长
  • 具有赢冲输缩的特性

3.固定手数: 每次开仓固定0.1

4.子账户百分比止损: 每次开仓止损固定子账户1%

  • 多个策略的时候,其中一个表现很好,另一个表现很差,如果都用总账户的1%止损,差的哪个非但没有表现差降低仓位,反而仓位越来越大,从而影响到总账户的净值
  • 每个策略都有自己的子账户,分离开来

代码实现

cpp
/*
symbol 品种名称 ,entryPrice 入场价格, sl 止损价格, slType 止损类型, slParam 止损参数, tp 止盈价格, tpType 止盈类型, tpParam 止盈参数
*/
double calcLots(string symbol, double entryPrice, double slPrice, double slType,
                double slParam) {
  double slMoney = 0;
  if (slType == 1) // 1:固定金额{100}美金
    slMoney = slParam;
  else if (slType == 2) { // 2:账户{1}%
    slMoney = AccountInfoDouble(ACCOUNT_BALANCE) * slParam / 100;
  } else if (slType == 3) {
    return slParam; // 3:固定{0.1}手
  } else if (slType == 4) {
    slMoney = slParam; // 4:子账户总账户{1}%
  }
  // 当前品种小数位数
  int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
  // 当前品种的最小价格变动单位(即一个点的大小)
  double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
  // 计算止损距离
  double slDistance = NormalizeDouble(MathAbs(entryPrice - slPrice), digits) / point;
  if (slDistance <= 0)
    return 0;
  // IC平台获取 tickval有时会是0 获取点值 每次尝试间隔1秒
  // 如果尝试3次后仍然无法获取到交易点值,打印异常 ,返回0
  // 价格跳动一个点价值多少
  double tickVal = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
  if (tickVal == 0) {
    Sleep(1000);
    tickVal = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
    Print("SYMBOL_TRADE_TICK_VALUE 异常第一次尝试:" + (string)tickVal);
    if (tickVal == 0) {
      Sleep(1000);
      tickVal = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
      Print("SYMBOL_TRADE_TICK_VALUE 异常第二次尝试:" + (string)tickVal);
    }
    if (tickVal == 0) {
      Sleep(1000);
      tickVal = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
      Print("SYMBOL_TRADE_TICK_VALUE 异常第三次尝试:" + (string)tickVal);
    }
    if (tickVal == 0) {
      Print("SYMBOL_TRADE_TICK_VALUE 异常:" + symbol, "-", entryPrice, "-", slPrice, "-",
            slType, "-", slParam, "-", slMoney, "-", tickVal);
      Print("SYMBOL_TRADE_TICK_VALUE 异常2:" + (string)point, "-", slDistance,
            "-", tickVal);
      return 0;
    }
    return 0;
  }
  // 最小0.01手 没有0.001所以 用2够了
  double lot =
      NormalizeDouble(slMoney / slDistance / tickVal, 2); // 风控/点值/点值
  double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
  // 规范手数, 确保手数符合品种的步长要求
  lot = MathRound(lot / lotStep) * lotStep;
  if (lot < SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN)) {
    lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
  } else if (lot >= SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX)) {
    Print("手数异常:", symbol, "-", entryPrice, "-", slPrice, "-", slType, "-", slParam);
    Print("手数异常2:", point, "-", slDistance, "-", tickVal, "-", lot, "-",
          lotStep);
    lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
    return 0;
  }
  return lot;
}

选择子账户余额1%止损的情况

cpp

if (SlType == 4) {
      slp = calculateEachEaBalance(MagicNum, initBalance); // 子账户百分之一
    }
double lotSize = calcLots(_Symbol, ask, sl, SlType, slp);

计算子账户余额

cpp
/*
计算子账户余额
magicNum 子账户魔术号, initBa 初始资金
*/ 
double calculateEachEaBalance(long magicNum, double initBa) {
  // 统计子账户的盈亏,手续费,库存费,余额
  double countProfit = 0, countCommission = 0, countSwap = 0, countMoney = 0;
  // 选择历史交易记录
  HistorySelect(0, TimeCurrent());
  // 获取历史交易总数
  uint deals = HistoryDealsTotal();
  for (uint i = 0; i < deals; i++) {
    // 根据索引获取交易的订单号
    ulong ticket = HistoryDealGetTicket(i);
    if (ticket <= 0)
      continue;
    long magic = HistoryDealGetInteger(ticket, DEAL_MAGIC);
    string symbol = HistoryDealGetString(ticket, DEAL_SYMBOL);
    double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
    double commission = HistoryDealGetDouble(ticket, DEAL_COMMISSION);
    double swap = HistoryDealGetDouble(ticket, DEAL_SWAP);
    // 累加全部的利润 手续费 库存费 的到该ea的绩效
    if (magic == magicNum) {
      countProfit += profit;
      countCommission += commission;
      countSwap += swap;
      countMoney = countProfit + countCommission + countSwap;
    }
  }
  // 目前子账户余额的1%
  double currentBalance = countMoney + initBa;
  return currentBalance / 100;
}