在量化交易领域,滚仓(Rolling Position)策略是一个颇具吸引力但也充满挑战的话题。这个策略的核心理念是:在趋势行情中,通过将已实现的盈利再次投入交易,实现复利增长。本文将深入探讨如何将滚仓这个交易想法,一步步转化为可执行的代码逻辑,重点关注思维转换的过程而非技术细节。需要特别说明的是,滚仓策略在放大收益的同时也会放大风险,本文仅供学习交流参考。

滚仓策略的盈利逻辑本质上是一个复利增长模型。让我们用一个简化的例子来理解:
传统单次交易(连续3次各涨10%):
滚仓交易(连续3次各涨10%):
对比结果:
同样市场连续3次各涨10%的情况下:
同样是连续3次各涨10%,单次交易盈利99.3 USDT,滚仓交易盈利119.7 USDT。这个差额就是复利的力量。
用数学公式表达:
javascript
// 传统交易:线性增长
最终资金 = 初始资金 × (1 + 杠杆 × 涨幅)
// 滚仓交易:指数增长
最终资金 = 初始资金 × (1 + 杠杆 × 单次涨幅) ^ 滚仓次数
这揭示了滚仓的本质:将线性增长转化为指数增长。但同时也暴露了风险:一次止损可能回吐之前所有复利成果。
在动手编写任何代码之前,我们需要从策略层面回答三个根本问题:
问题1:什么时候开始?(初次入场)
需要判断趋势的开始信号。
问题2:什么时候继续?(追加滚仓)
这是滚仓的核心:止盈后如何判断趋势是否延续?
问题3:什么时候停止?(退出观望)
这三个问题决定了整个策略的骨架,下面我们将逐一把它们转化为代码逻辑。

让我们先理解滚仓策略最理想的应用场景。
理想场景:
想象一下,如果你能在SHIB从0.000001美元起涨时入场,或者在某个山寨币暴涨前夕建仓,通过连续滚仓,100 USDT可能变成10000 USDT甚至更多。这就是滚仓策略的终极梦想——在币种起爆前入场,抓住十倍甚至百倍的收益。
残酷现实:
但问题在于,如何知道哪个币种会暴涨?什么时候会暴涨?
对于我们大多数人来说,想要精准捕捉这种起爆点,说白了就是撞大运。我们无法预知未来,只能通过历史数据和技术指标,尽可能提高"撞到大运"的概率。
既然无法预知哪个币种会暴涨,我们能做的就是:建立一套可执行的入场规则,用技术指标模拟"趋势开始"的信号。
这就像在茫茫大海中捕鱼,虽然不知道哪里有大鱼,但我们可以:
当多个信号汇聚时,我们认为"可能"有趋势要开始了,于是入场尝试。对了,就跟着趋势滚仓赚钱;错了,及时止损退出。
选择技术工具:
我们使用EMA双均线系统(EMA5和EMA10)作为趋势判断工具。选择它的原因很简单:
核心逻辑:
通过检测均线的"金叉"(EMA5上穿EMA10)和"死叉"(EMA5下穿EMA10)来捕捉趋势转折点:
代码思路:
// 计算EMA指标
var emaFast = TA.EMA(records, FastEMA); // EMA5
var emaSlow = TA.EMA(records, SlowEMA); // EMA10
// 获取当前和前一根K线的EMA值
var ema5_current = emaFast[emaFast.length - 1];
var ema5_prev = emaFast[emaFast.length - 2];
var ema10_current = emaSlow[emaSlow.length - 1];
var ema10_prev = emaSlow[emaSlow.length - 2];
// 检测金叉:前一根K线EMA5<=EMA10,当前K线EMA5>EMA10
var bullCross = ema5_prev <= ema10_prev && ema5_current > ema10_current;
// 检测死叉:前一根K线EMA5>=EMA10,当前K线EMA5<EMA10
var bearCross = ema5_prev >= ema10_prev && ema5_current < ema10_current;
// 空仓时等待信号入场
if (bullCross) {
Log(" 金叉信号 - 做多");
openPosition("LONG", currentPrice);
} else if (bearCross) {
Log(" 死叉信号 - 做空");
openPosition("SHORT", currentPrice);
}
这里不深入展开金叉死叉的细节,这些都是交易中的基础概念。重点是:我们需要一个明确的、可量化的入场信号来触发滚仓的起点。

滚仓策略本质上是一个理智的冒险者游戏。让我们用一个完整的场景来理解:
游戏规则:
1. 你从交易所账户中拿出100 USDT作为冒险资金
2. 这100 USDT独立管理,与账户其他资金隔离
3. 用这100 USDT开始交易:
- 赚了 → 盈利加入资金池,继续用更大的资金交易(滚仓)
- 亏了 → 触发止损,回到空仓状态
4. 重复这个过程,直到:
- 要么把100 USDT亏完(游戏结束)
- 要么滚到一个满意的金额(主动退出)
这个游戏的精妙之处在于:
这是滚仓策略中最核心的设计思想。
传统做法的问题:
假设你的交易所账户有1000 USDT:
资金池解决方案:
// 创建一个虚拟的"策略资金池"
var strategyCapital = InitialCapital; // 初始100 USDT
// 第1次交易
// 开仓金额 = 100 USDT
// 止盈后盈利 = 30 USDT
strategyCapital = strategyCapital + 30; // 资金池变为130 USDT
// 第2次交易(滚仓)
var positionValue = strategyCapital * Leverage; // 130 × 3 = 390
var amount = positionValue / price / ctVal; // 计算开仓数量
// 自动使用了第1次的盈利,这就是复利的关键
// 止盈后盈利 = 39 USDT
strategyCapital = strategyCapital + 39; // 资金池变为169 USDT
// 第3次交易(滚仓)
// 开仓金额 = 169 USDT(继续利滚利)
这个设计的优势:
这是滚仓策略的灵魂环节:当止盈单成交后,我们要做一个关键决策——继续滚还是停手?
决策场景:
假设我们做多BTC:
- 入场价:45000 USDT,用100 USDT开仓
- 止盈价:49500 USDT(涨10%)
- 止盈成交,盈利30 USDT
- 现在资金池:130 USDT
问题来了:
选项A:收手,带着130 USDT退出,回到空仓
选项B:继续,用130 USDT再次开多(滚仓)
如何选择?
这个决策不能靠"感觉",必须有明确标准。我们的判断逻辑是:趋势是否还在延续?
判断方法:
在止盈单成交的瞬间,重新计算最新的技术指标(EMA均线):
// 止盈单成交后,获取最新K线数据
var records = _C(exchange.GetRecords, PERIOD_M1);
var emaFast = TA.EMA(records, FastEMA);
var emaSlow = TA.EMA(records, SlowEMA);
var ema5_current = emaFast[emaFast.length - 1];
var ema10_current = emaSlow[emaSlow.length - 1];
var shouldRoll = false;
if (currentDirection == "LONG") {
// 多头止盈后,如果EMA5仍在EMA10上方,继续做多(滚仓)
if (ema5_current > ema10_current) {
shouldRoll = true;
Log(" EMA5 > EMA10,上升趋势未破坏");
Log(" 决策:继续做多(滚仓)");
} else {
Log(" EMA5 <= EMA10,趋势可能转弱");
Log(" 决策:不滚仓,等待新信号");
}
} else if (currentDirection == "SHORT") {
// 空头止盈后,如果EMA5仍在EMA10下方,继续做空(滚仓)
if (ema5_current < ema10_current) {
shouldRoll = true;
Log(" EMA5 < EMA10,下降趋势未破坏");
Log(" 决策:继续做空(滚仓)");
} else {
Log(" EMA5 >= EMA10,趋势可能转弱");
Log(" 决策:不滚仓,等待新信号");
}
}
如果决策是"继续滚仓":
if (shouldRoll) {
// 1. 增加滚仓计数
currentRoundRolls++;
Log(" 执行滚仓操作... (本轮第", currentRoundRolls, "次滚仓)");
// 2. 获取最新价格
var ticker = _C(exchange.GetTicker);
var newPrice = ticker.Last;
// 3. 基于新资金池重新开仓
if (openPosition(currentDirection, newPrice)) {
Log(" 滚仓成功!");
// 4. 挂新的止盈单(在openPosition函数中完成)
// 5. 设置新的止损价(在checkStopLoss函数中监控)
} else {
Log(" 滚仓失败,等待新信号");
saveRollRecord(false);
resetPositionState();
}
}
如果决策是"停止":
else {
// 1. 保存本轮统计
saveRollRecord(false); // false表示正常结束,非止损
// 2. 保留资金池金额
// strategyCapital 保持当前值,等待下次机会
// 3. 回到空仓状态
resetPositionState();
Log(" 已平仓,等待新信号...");
}
这个流程的关键点:
让我们通过一个完整案例感受复利的威力:
成功案例:
初始资金:100 USDT
止盈比例:10%
杠杆:3倍
第1次:100 USDT → 盈利30 → 资金池130
第2次:130 USDT → 盈利39 → 资金池169
第3次:169 USDT → 盈利50.7 → 资金池219.7
第4次:219.7 USDT → 盈利65.9 → 资金池285.6
第5次:285.6 USDT → 盈利85.7 → 资金池371.3
连续滚5次,100变成371.3,增长271%!
失败案例:
第1次:100 USDT → 盈利30 → 资金池130
第2次:130 USDT → 盈利39 → 资金池169
第3次:169 USDT → 趋势反转 → 触发止损
止损比例5%,亏损:169 × 3 × 5% = 25.35 USDT
剩余资金:169 - 25.35 = 143.65 USDT
原本从100滚到169,一次止损后只剩143.65
这就是滚仓的双刃剑:

主动退出:趋势转弱
这种情况在"问题二"中已经处理了——止盈后判断趋势不支持继续,主动选择停止。这是理想的退出方式,带着盈利离场。
被动退出:触发止损
这是我们现在要重点讨论的——当行情走反,价格触及止损线,被迫平仓。
很多人不喜欢止损,因为:
但在滚仓策略中,止损是保命的底线。想想看:
如果没有止损:
第1次:100 → 滚到 169
第2次:169 → 趋势反转,不止损
价格持续下跌:169 → 150 → 120 → 80 → 50...
最终可能全亏,甚至爆仓
如果有止损:
第1次:100 → 滚到 169
第2次:169 → 趋势反转,触发止损
止损5%:亏损 25.35
剩余:143.65
虽然亏了,但保留了大部分资金
可以等待下一个机会
止损的本质: 用小的确定性亏损,避免大的不确定性风险。
// 检查止损
function checkStopLoss(currentPrice, position) {
var totalDrawdown = 0;
// 计算当前回撤
if (currentDirection == "LONG") {
totalDrawdown = (currentPrice - entryPrice) / entryPrice;
} else {
totalDrawdown = (entryPrice - currentPrice) / entryPrice;
}
// 判断是否触发止损
if (totalDrawdown < -StopLossPercent) {
Log(" 触发止损!回撤:", (totalDrawdown * 100).toFixed(2), "%");
// 1. 取消止盈单
if (takeProfitOrderId) {
Log("取消止盈单:", takeProfitOrderId);
exchange.CancelOrder(takeProfitOrderId);
takeProfitOrderId = null;
Sleep(500);
}
// 2. 市价平仓(循环重试直到成功)
var profit = closePositionMarketWithRetry(currentPrice, position);
// 3. 更新策略资金池
strategyCapital += profit; // profit是负数
totalProfitRealized += profit;
Log("止损亏损:", profit.toFixed(2), "U");
Log("策略剩余资金:", strategyCapital.toFixed(2), "U");
// 4. 记录本轮止损亏损
currentRoundLoss = Math.abs(profit);
Log("本轮止损亏损:", currentRoundLoss.toFixed(2), "U");
// 5. 保存本轮滚仓记录(被止损中断)
saveRollRecord(true); // true表示止损结束
// 6. 重置状态
resetPositionState();
// 7. 检查资金是否充足
if (strategyCapital < 10) {
Log(" 策略资金不足10U,停止运行");
throw "资金不足";
}
Log(" 已止损,等待新信号...");
}
}
还记得我们说的"理智的冒险者游戏"吗?这个游戏有明确的结束条件:
条件1:资金池归零
if (strategyCapital <= 0) {
Log(" 游戏结束:资金池已归零");
Log("本次冒险失败,100 USDT全部亏光");
throw "资金耗尽";
}
条件2:主动退出
if (strategyCapital >= 目标金额) {
Log(" 达到目标金额,可以选择主动退出");
Log("锁定利润,开始新一轮100 USDT的游戏");
}
条件3:达到最大滚仓次数
if (连续滚仓次数 >= 10次) {
Log(" 达到最大滚仓次数,主动退出");
Log("持续时间太长,风险累积,见好就收");
saveRollRecord(false);
resetPositionState();
}
整个滚仓策略的设计,核心就是在风险与收益之间找平衡:
收益端:
风险端:
TRUMP_USDT 币安期货上市首日(2025年1月20日至2025年1月21日)回测参考分析:


从回测结果可以看到:
亮点表现:
风险暴露:
关键数据:
通过上述分析,我们可以清楚地看到,这个策略本质上是在模拟:
一个理智的冒险者的交易行为:
它的核心逻辑是:
局限1:依赖趋势市场
这个策略在震荡市中表现很差,因为:
局限2:参数敏感
止盈10%、止损5%这些参数并非最优:
局限3:无法预测起爆点
正如前面所说,我们用技术指标入场,本质上是"撞大运":
方向1:结合工作流筛选币种
方向2:动态调整参数
方向3:多资金池并行
通过三个核心问题的推演,我们完整地展示了如何将滚仓这个交易想法转化为代码逻辑。这个过程的本质是:把一个理智的冒险者的交易思维,用精确的规则和数据结构表达出来。
重要提示:
这只是一个滚仓思路的模拟实现,实际上滚仓是一个非常需要市场经验的交易策略。本策略只是一个工具,后期可以结合工作流判断热门或者极具爆发力的币种,使用该工具,会为我们带来更多惊喜。
记住:
没有稳赚不赔的策略,滚仓只是一个工具。真正决定成败的,是你能否:
祝各位在量化交易的道路上,找到属于自己的"大运"!
完整策略地址:**策略源码 -> ** https://www.fmz.com/strategy/521864