资源加载中... loading...

量化揭秘纯碱2023年操盘手法:157倍的快乐你想象不到

Author: ianzeng123, Created: 2024-11-12 11:39:09, Updated: 2024-11-14 10:28:50

img

对于熟悉期货市场的朋友来说,2023年的纯碱行情无疑是令人难忘的。年初,市场从3000点的高位开始,由于远兴增产的传闻,价格急剧下降至年中的1500点,上演了一波经典的逼多行情,导致某私募基金传出大幅亏损的消息。随后,市场经历了为期五个月的大幅震荡,直到年末,又出现了从1600点拉升至2700点的逼空行情,再次成为传奇。如果你认为这就是2023年的终点,那么妖碱永不会让人失望,因为在12月份移仓到05合约时,市场又从2300点跌至2000点。这一系列的操作据说是由某家传奇期货操盘手所为,其操盘资金也实现了百倍增长的传说。这位操盘手的具体策略并不复杂,在消息推动的情况下,他采用了滚动盈利加仓的手法,实现了利滚利的效果。

img

许多人好奇,如果跟随这位操盘手的步伐,2023年能实现多少利润。今天我们尝试使用量化方法进行模拟。这个策略理念源于数字货币领域的“币钱平衡”,其实它的最初起源是金融市场的“股债平衡”。由于两者天生的对立性,股票和债券通常处于此消彼长的关系,对冲交易可以降低持仓的风险。除非像英国传奇老哥尼克李森那样,遇到日本大地震这样的股债双杀行情。在期货市场中,我们尝试将原始资金分为两半,一半用来开仓,另一半用来平衡仓位。期货合约的涨跌会影响仓位资金的涨跌,我们根据其与可供资金的比例,来进行仓位的平衡,实现盈利加仓和亏损减仓的操作。

经过在优宽量化平台使用分钟级别数据进行模拟运行,结果显示,从年初的100万资金增长到了约1.57亿资金,整整增长了157倍,这种上帝视角的快乐是难以言喻的。

img

img

作为一个开放性平台,我们当然要分享策略源码,并附上注释,感兴趣的朋友可以针对自己喜欢的品种在特定的时间段进行尝试。

该策略基于双移动平均线的交叉信号进行交易决策,主要逻辑如下:

  1. 策略初始化

    • 设定错误过滤器以忽略特定网络和服务器错误。
    • 初始化 K 线图和初始账户信息,用于绘制数据和计算盈亏。
  2. 计算多头和空头信号

    • 使用 talib 库计算短期(shortPeriod)和长期(longPeriod)的移动平均线。
    • 当短期均线向上穿越长期均线时产生多头信号,反之则为空头信号。
  3. 开平仓逻辑

    • 若当前无持仓且出现多头信号,则开多仓。若出现空头信号,则开空仓。
    • 若已有持仓且出现相反信号,则清仓对应的仓位。
  4. 动态仓位管理

    • 若持有多仓且未出现空头信号,则根据可用资金和持仓保证金来平衡仓位:
      • 若资金充足且盈利超过阈值,则加仓。
      • 若仓位过多,减仓以控制风险。
    • 空仓的平衡逻辑类似,平衡持仓以适应市场波动和资金情况。
  5. 绘图

    • 在 K 线图上绘制长短周期的移动平均线,方便观测行情走势和均线交叉情况。

策略目的

该策略通过移动平均线交叉信号判断市场趋势,结合动态仓位调整以提高在趋势行情中的持仓效率,并在震荡行情中降低损失。


/*backtest
start: 2023-01-01 00:00:00
end: 2023-12-31 00:00:00
period: 1d
basePeriod: 1m
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
args: [["longPeriod",7]]
*/

var symbol = 'SA888'
var shortPeriod = 5
var longPeriod = 7

function main() {
    // 设置错误过滤器,忽略特定的网络和服务器错误
    SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused|(CTP_T@10010)|(CTP_T@7090)");

    // 初始化K线图,用于后续的数据绘制
    var c = KLineChart();

    // 获取初始账户信息,用于后续计算盈亏
    var initAccount = _C(exchange.GetAccount);

    // 定义多头和空头检查信号,初始值为false
    var checklong = false;
    var checkshort = false;

    // 调用CTA策略函数,并传入交易品种symbol
    $.CTA(symbol, function(st) {

        // 获取K线数据
        var r = st.records;

        // 如果K线数据不足需要的条数时,则退出函数
        if (r.length < longPeriod) {
            return;
        }

        // 使用talib库计算长短周期的移动平均线
        var long_line = talib.MA(r, longPeriod);
        var short_line = talib.MA(r, shortPeriod);

        // 判断多头和空头信号
        var long_signal = short_line[r.length - 2] > long_line[r.length - 2]; // 多头信号
        var short_signal = short_line[r.length - 2] < long_line[r.length - 2]; // 空头信号

        // 获取合约信息并计算多头和空头的保证金比例
        var symbolInfo = exchange.SetContractType(st.symbol);
        var longratio = symbolInfo.LongMarginRatio + 0.08;
        var shortratio = symbolInfo.ShortMarginRatio + 0.08;

        // 获取当前最新价格
        var lastprice = r[r.length - 1].Close;

        // 获取当前账户信息
        var accountInfo = _C(exchange.GetAccount);

        // 计算总盈亏
        var totalprofit = accountInfo.Equity - initAccount.Equity;

        // 记录总盈亏信息
        LogProfit(totalprofit, "权益", '&');

        // 如果当前持有空仓并且出现多头信号,平掉空仓
        if (st.position.amount < 0 && long_signal) {
            Log('清仓空仓: ', exchange.GetPositions(st.symbol)[0]['Price'] - lastprice);
            checkshort = false;
            return -st.position.amount;
        }

        // 如果没有持仓并且出现多头信号,开多仓
        if (st.position.amount == 0 && long_signal) {
            Log('多仓建仓');
            opennum = Math.floor(st.account.Balance / (2 * lastprice * symbolInfo.VolumeMultiple * longratio));
            checklong = true;
            return opennum;
        }

        // 如果已经持有多仓,且未出现空头信号,尝试平衡仓位
        if (checklong && !short_signal && st.position.amount > 0) {
            let curmargin = exchange.GetPositions(st.symbol)[0]['Margin']; // 当前持仓保证金
            let availmoney = st.account.Equity - curmargin; // 可用资金
            let adjustpos = (availmoney - curmargin) / (2 * lastprice * symbolInfo.VolumeMultiple * longratio); // 调整仓位数量
            let curprofit = lastprice - exchange.GetPositions(st.symbol)[0]['Price']; // 当前盈利

            // 当仓位不足且盈利超过5时,加仓
            if (adjustpos > 0 && Math.floor(adjustpos) >= 1 && curprofit > 5) {
                Log('钱多仓少,平衡仓位,加仓:', Math.floor(adjustpos));
                return Math.floor(adjustpos);
            }

            // 当仓位过多时,减仓
            if (adjustpos < 0 && Math.ceil(adjustpos) <= -1) {
                Log('钱少仓多,平衡仓位,减仓:', Math.ceil(adjustpos));
                return Math.ceil(adjustpos);
            }
        }

        // 如果当前持有多仓且出现空头信号,平掉多仓
        if (st.position.amount > 0 && short_signal) {
            Log('清仓多仓: ', lastprice - exchange.GetPositions(st.symbol)[0]['Price']);
            checklong = false;
            return -st.position.amount;
        }

        // 如果没有持仓并且出现空头信号,开空仓
        if (st.position.amount == 0 && short_signal) {
            Log('空仓建仓');
            opennum = Math.floor(st.account.Balance / (2 * lastprice * symbolInfo.VolumeMultiple * shortratio));
            checkshort = true;
            return -opennum;
        }

        // 如果已经持有空仓,且未出现多头信号,尝试平衡仓位
        if (checkshort && !long_signal && st.position.amount < 0) {
            let curmargin = exchange.GetPositions(st.symbol)[0]['Margin'];
            let availmoney = st.account.Equity - curmargin;
            let adjustpos = (availmoney - curmargin) / (2 * lastprice * symbolInfo.VolumeMultiple * shortratio);
            let curprofit = exchange.GetPositions(st.symbol)[0]['Price'] - lastprice;

            // 当仓位不足且盈利超过5时,加仓
            if (adjustpos > 0 && Math.floor(adjustpos) >= 1 && curprofit > 5) {
                Log('钱多仓少,平衡仓位,加仓:', Math.floor(adjustpos), curprofit);
                return -Math.floor(adjustpos);
            }

            // 当仓位过多时,减仓
            if (adjustpos < 0 && Math.ceil(adjustpos) <= -1) {
                Log('钱少仓多,平衡仓位,减仓:', Math.ceil(adjustpos));
                return -Math.ceil(adjustpos);
            }
        }

        // 绘制K线图和移动平均线
        for (var i = 0; i < r.length; i++) {
            var bar = r[i];
            c.begin(bar);
            c.plot(long_line[i], "短线", {overlay: true}); // 绘制短期移动平均线
            c.plot(short_line[i], "长线", {overlay: true}); // 绘制长期移动平均线
            c.close();
        }
    });
}

最后,大家可能会好奇这个策略是否稳定。结果很明确,它极不稳定。在2024年的行情中,这个策略实现了大亏损。看来,一个策略的成功需要天时地利人和,缺一不可。但我们可以使用量化手段针对自己喜欢的品种进行更多探索性的工作,也许下一个百倍的行情,就在命运轮转的此刻等待着我们。

img

注: 本文仅做探索性尝试,不构成任何投资性建议。


更多内容