短纤三方套利实盘


创建日期: 2024-07-01 15:14:34 最后修改: 2026-01-23 14:03:57
复制: 0 点击次数: 1009
avatar of ianzeng123 ianzeng123
1
关注
168
关注者
策略源码
/*backtest
start: 2024-01-03 09:00:00
end: 2024-07-01 15:00:00
period: 1d
basePeriod: 1m
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES","depthDeep":20}]
*/

var lastCheckTime = null; // 记录上次检查主力合约的时间

// 判断是否为交易时间
function timeSel() {
    var t = new Date();
    var hour = t.getHours();
    var minute = t.getMinutes();
    var day = t.getDay();

    var curtime = hour * 100 + minute;
    var isTradingTime = (day >= 1 && day <= 5) && // Monday to Friday
        ((curtime >= 900 && curtime <= 1130) || // 9:00 to 11:30 AM
        (curtime >= 1330 && curtime <= 1500) || // 1:30 to 3:00 PM
        (curtime >= 2100 && curtime <= 2300));  // 9:00 to 11:00 PM

    return isTradingTime;
}

// 处理持仓转换
function posTrans(p, mainList) {
    var codeList = mainList.map(item => item.match(/[A-Za-z]+/g)[0]);
    var prePos = exchange.GetPositions();
    
    if (!prePos || prePos.length === 0) {
        return;
    }

    prePos.forEach(function(pos) {
        var mainCode = pos.ContractType.match(/[A-Za-z]+/g)[0];
        if (mainList.indexOf(pos.ContractType) === -1) {
            var index = codeList.indexOf(mainCode);
            var mainID = index !== -1 ? mainList[index] : null;

            if (mainID) {
                Log('旧合约', pos.ContractType, '需要被更换为', mainID);
                p.Cover(pos.ContractType);
                if (pos.Type === PD_LONG || pos.Type === PD_LONG_YD) {
                    p.OpenLong(mainID, pos.Amount);
                } else {
                    p.OpenShort(mainID, pos.Amount);
                }
            }
        }
    });

    // 再次检查所有持仓是否都在主力合约列表中
    var afterPos = exchange.GetPositions();
    if (afterPos && afterPos.length > 0 && afterPos.every(pos => mainList.indexOf(pos.ContractType) !== -1)) {
        Log("所有合约都是主力合约", "#00FF00");
    }
}

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)");
    var p = $.NewPositionManager();
    var initAccount = _C(exchange.GetAccount);
    LogReset(0);
    
    // 主力合约ID缓存
    var ptaId = null;
    var pfId = null;
    var egId = null;

    while (true) {
        if (exchange.IO("status") && timeSel()) {
            
            // 每天仅检查一次主力合约
            var today = new Date();
            var todayStr = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
            if (lastCheckTime !== todayStr) {
                lastCheckTime = todayStr;

                try {
                    // 获取主力合约
                    var ptaInfo = exchange.SetContractType('TA888');
                    if (!ptaInfo) {
                        Log('获取TA主力合约失败,跳过本次循环', '#FF0000');
                        Sleep(1000 * 5);
                        continue;
                    }
                    
                    ptaId = ptaInfo.InstrumentID;
                    var lastThreeDigits = ptaId.slice(-3);
                    pfId = 'PF' + lastThreeDigits;
                    egId = 'eg2' + lastThreeDigits;

                    var mainList = [ptaId, pfId, egId];
                    Log('更新主力合约:', mainList.join(', '), '#00FF00');

                    posTrans(p, mainList); // 处理持仓转换
                } catch (e) {
                    Log('更新主力合约异常:', e, '#FF0000');
                    Sleep(1000 * 5);
                    continue;
                }
            }
            
            // 检查主力合约是否已初始化
            if (!ptaId || !pfId || !egId) {
                Log('主力合约未初始化,跳过本次循环', '#FFAA00');
                Sleep(1000 * 5);
                continue;
            }

            // 获取K线数据
            exchange.SetContractType(ptaId);
            var ptaR = exchange.GetRecords();
            
            if (!ptaR || ptaR.length === 0) {
                Log('获取PTA K线数据失败,跳过本次循环', '#FF0000');
                Sleep(1000 * 5);
                continue;
            }

            exchange.SetContractType(pfId);
            var pfR = exchange.GetRecords();
            
            if (!pfR || pfR.length === 0) {
                Log('获取PF K线数据失败,跳过本次循环', '#FF0000');
                Sleep(1000 * 5);
                continue;
            }

            exchange.SetContractType(egId);
            var egR = exchange.GetRecords();
            
            if (!egR || egR.length === 0) {
                Log('获取EG K线数据失败,跳过本次循环', '#FF0000');
                Sleep(1000 * 5);
                continue;
            }

            // 计算移动平均线
            var ptaMA = TA.MA(ptaR, 5);
            var pfMA = TA.MA(pfR, 5);
            var egMA = TA.MA(egR, 5);
            
            if (!ptaMA || ptaMA.length === 0 || !pfMA || pfMA.length === 0 || !egMA || egMA.length === 0) {
                Log('计算移动平均线失败,跳过本次循环', '#FF0000');
                Sleep(1000 * 5);
                continue;
            }

            // 计算价格差(滑动均值)
            var costPF = (0.855 * ptaMA[ptaR.length - 1] + 0.335 * egMA[egR.length - 1]);
            var diffMA = pfMA[pfR.length - 1] - costPF;

            // 获取持仓信息
            var posInfo = exchange.GetPositions(); 
            if (!posInfo) {
                posInfo = [];
            }

            // 基差画图
            $.PlotMultLine("生产成本", "价格差滑动均值", diffMA, new Date().getTime(), {layout: 'single', height: '600px'});
            $.PlotMultHLine("生产成本", 1030, "价格差上限", "red", "ShortDot");
            $.PlotMultHLine("生产成本", 930, "价格差下限", "green", "ShortDot");

            // 价格差过大,做空短纤,做多PTA和乙二醇
            if (posInfo.length === 0 && diffMA > 1030) {
                Log(diffMA, '价格差过大,做空短纤,做多PTA和乙二醇', '#ff0000');
                p.OpenLong(ptaId, 1 * Amount);
                p.OpenLong(egId, 1 * Amount);
                p.OpenShort(pfId, 2 * Amount);
            }

            // 价格差过小,做多短纤,做空PTA和乙二醇
            if (posInfo.length === 0 && diffMA < 930) {
                Log(diffMA, '价格差过小,做多短纤,做空PTA和乙二醇', '#00ff00');
                p.OpenShort(ptaId, 1 * Amount);
                p.OpenShort(egId, 1 * Amount);
                p.OpenLong(pfId, 2 * Amount);
            }

            // 计算持仓利润
            if (posInfo && posInfo.length === 3) {
                var totalProfit = 0;
                
                try {
                    posInfo.forEach(function(pos) {
                        if (!pos || !pos.ContractType) {
                            return;
                        }
                        
                        var contractTypeClean = pos.ContractType.replace(/[^a-zA-Z]/g, '');
                        var type = pos.Type;
                        var price = pos.Price;
                        var posamount = pos.Amount;

                        if (contractTypeClean === 'eg' && egR && egR.length > 0) {
                            var latestEgClose = egR[egR.length - 1].Close;
                            totalProfit += (latestEgClose - price) * (type % 2 === 0 ? 10 : -10) * posamount;
                        } else if (contractTypeClean === 'TA' && ptaR && ptaR.length > 0) {
                            var latestPtaClose = ptaR[ptaR.length - 1].Close;
                            totalProfit += (latestPtaClose - price) * (type % 2 === 0 ? 5 : -5) * posamount;
                        } else if (contractTypeClean === 'PF' && pfR && pfR.length > 0) {
                            var latestPfClose = pfR[pfR.length - 1].Close;
                            totalProfit += (latestPfClose - price) * (type % 2 === 0 ? 5 : -5) * posamount;
                        }
                    });

                    // 达到止盈点数或者止损点数进行平仓
                    if (totalProfit > 500 * Amount || totalProfit < -3000 * Amount) {
                        Log('触发止盈止损,总盈亏:' + totalProfit, "#FF0000");
                        p.CoverAll();
                        totalProfit = 0;
                    }
                } catch (e) {
                    Log('计算持仓利润异常:', e, '#FF0000');
                }
            }

            // 更新持仓信息
            var tblStatus = {
                type: "table",
                title: "持仓信息",
                cols: ["合约名称", "持仓方向", "持仓均价", "持仓数量", "持仓盈亏"],
                rows: []
            };

            if (posInfo && posInfo.length > 0) {
                posInfo.forEach(function(pos) {
                    if (pos && pos.ContractType) {
                        var pos_dir = pos.Type % 2 === 0 ? '多头' : '空头';
                        tblStatus.rows.push([pos.ContractType, pos_dir, pos.Price, pos.Amount, pos.Profit]);
                    }
                });
            }

            var lastStatus = '`' + JSON.stringify([tblStatus]) + '`';
            LogStatus(lastStatus);

            // 获取和执行用户命令
            var cmd = GetCommand();
            if (cmd) {
                var JavaScript = cmd.split(':', 2)[1];
                Log("执行调试代码:", JavaScript);
                try {
                    eval(JavaScript);
                } catch (e) {
                    Log("Exception", e);
                }
            }

            // 记录账户盈利
            try {
                var accountInfo = _C(exchange.GetAccount);
                if (accountInfo && accountInfo.Equity && initAccount && initAccount.Equity) {
                    LogProfit(accountInfo.Equity - initAccount.Equity, '&');
                }
            } catch (e) {
                Log('记录账户盈利异常:', e, '#FF0000');
            }
        }

        Sleep(1000 * 5);
    }
}