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

加仓策略(原版)

Author: ianzeng123, Date: 2024-07-15 17:48:35
Tags:


/*backtest
start: 2024-01-03 09:00:00
end: 2024-07-16 14:01:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
args: [["MaxLots",3],["ATRPeriod",30]]
*/


var _q = $.NewTaskQueue();
var openNumber = 0 //开仓次数
var cumaddNumber = 0 //累计加仓次数
var curaddNumber = 0 //本轮加仓次数
var winNumber = 0 //成功次数
var lossNumber = 0 //失败次数
var cumProfit = 0 //累计利润
var cumLoss = 0 //累计损失
var holdSymbol = '' //持仓品种
var holdAmount = 0 //持仓数量
var holdPrice = 0 //持仓价格
var holdProfit = 0 //持仓利润
var holdDir = '' //持仓方向
var openPrice = 0 //最新开仓价格,判断加仓
var winRate = 0 //胜率
var plRatio = 0 //盈亏比
var InitBalance = 0 //未开仓金额


let openContracts = {};  // 追踪所有未平仓的合约
let profitLossHistory = {};  // 追踪每个合约的盈亏历史
let processedOrderIds = new Set();  // 记录已处理的订单 ID
let winLossCount = { win: 0, loss: 0, total: 0 };  // 统计胜败和总交易数量

function updateContracts(newOrders, commissionFee) {
    let totalProfit = 0;
    let totalLoss = 0;

    for (let order of newOrders) {
        let symbol = order.Symbol;
        let orderId = order.Id;

        // 如果订单已处理,跳过
        if (processedOrderIds.has(orderId)) {
            continue;
        }

        // 初始化合约追踪信息
        if (!openContracts[symbol]) {
            openContracts[symbol] = { 
                longPositions: [],  // 保存多单开仓的价格和数量
                shortPositions: []  // 保存空单开仓的价格和数量
            };
        }

        // 处理开仓和加仓订单
        if (order.Type === 0 && order.Offset === 0) {  // 买入开仓(多单)
            openContracts[symbol].longPositions.push({ amount: order.Amount, price: order.AvgPrice });
        } else if (order.Type === 1 && order.Offset === 0) {  // 卖出开仓(空单)
            openContracts[symbol].shortPositions.push({ amount: order.Amount, price: order.AvgPrice });
        }

        // 处理平仓订单
        if (order.Offset === 1) {
            let remainingAmount = order.Amount;
            let profitOrLoss = 0;
            let isError = false;

            // 平多单
            if (order.Type === 1) {
                if (openContracts[symbol].longPositions.length === 0) {
                    Log(`错误: 没有多单开仓记录,无法平仓,订单ID ${orderId}`);
                    isError = true;
                } else {
                    while (remainingAmount > 0 && openContracts[symbol].longPositions.length > 0) {
                        let position = openContracts[symbol].longPositions[0];
                        let amountToClose = Math.min(position.amount, remainingAmount);

                        // 计算盈亏
                        profitOrLoss += (order.AvgPrice - position.price) * amountToClose;

                        remainingAmount -= amountToClose;
                        openContracts[symbol].longPositions[0].amount -= amountToClose;

                        if (openContracts[symbol].longPositions[0].amount === 0) {
                            openContracts[symbol].longPositions.shift();
                        }
                    }

                    // 如果仍然有未平仓的数量,记录错误
                    if (remainingAmount > 0) {
                        Log(`错误: 平仓数量大于开仓数量,订单ID ${orderId}`);
                        isError = true;
                    }
                }
            }

            // 平空单
            if (order.Type === 0) {
                if (openContracts[symbol].shortPositions.length === 0) {
                    Log(`错误: 没有空单开仓记录,无法平仓,订单ID ${orderId}`);
                    isError = true;
                } else {
                    while (remainingAmount > 0 && openContracts[symbol].shortPositions.length > 0) {
                        let position = openContracts[symbol].shortPositions[0];
                        let amountToClose = Math.min(position.amount, remainingAmount);

                        // 计算盈亏
                        profitOrLoss += (position.price - order.AvgPrice) * amountToClose;

                        remainingAmount -= amountToClose;
                        openContracts[symbol].shortPositions[0].amount -= amountToClose;

                        if (openContracts[symbol].shortPositions[0].amount === 0) {
                            openContracts[symbol].shortPositions.shift();
                        }
                    }

                    // 如果仍然有未平仓的数量,记录错误
                    if (remainingAmount > 0) {
                        Log(`错误: 平仓数量大于开仓数量,订单ID ${orderId}`);
                        isError = true;
                    }
                }
            }

            if (!isError) {
                // 扣除手续费并更新盈亏
                let volumeMultiple = exchange.SetContractType(symbol).VolumeMultiple // 合约乘数
                let totalCommissionFee = commissionFee * order.Amount * 2 / volumeMultiple;  // 买入和卖出的手续费
                profitOrLoss -= totalCommissionFee;

                // 更新盈亏历史
                if (profitOrLoss > 0) {
                    totalProfit += profitOrLoss;
                    winLossCount.win++;
                } else {
                    totalLoss += Math.abs(profitOrLoss);
                    winLossCount.loss++;
                }
                winLossCount.total++;
            }
        }

        // 记录订单 ID 为已处理
        processedOrderIds.add(orderId);
    }

    // 记录盈亏历史
    profitLossHistory.totalProfit = (profitLossHistory.totalProfit || 0) + totalProfit;
    profitLossHistory.totalLoss = (profitLossHistory.totalLoss || 0) + totalLoss;

    // 计算盈亏比
    let winLossRatio = profitLossHistory.totalLoss > 0
        ? profitLossHistory.totalProfit > 0 ? profitLossHistory.totalProfit / profitLossHistory.totalLoss : -1 
        : profitLossHistory.totalProfit > 0 ? 1 : 0;  // 避免除以0

    // 计算胜率
    let winRate = winLossCount.total > 0
        ? winLossCount.win / winLossCount.total
        : 0;

    return [winLossRatio, winRate];
}

function main(){
    SetErrorFilter("login|ready|流控|连接失败|初始|Timeout");
    Log("风险系数:", RiskRatio, "N值周期:", ATRPeriod, "加仓系数:", IncSpace, "单品种最多开仓:", MaxLots, "次");
    var pretime = 0
    var checklock = true
    var prewinlossratio = 0
    var prewinrate = 0
    const commissionFee = 5

    while (true) {
        if(exchange.IO("status")) {

            var currentOrder = exchange.GetHistoryOrders()
            var result = updateContracts(currentOrder, commissionFee);

            if(result[0] != prewinlossratio || result[1] != prewinrate){
                prewinlossratio = result[0]
                prewinrate = result[1]

                Log('盈亏比:', result[0], '#ff0000')
                Log('胜率:', result[1], '#00ff00')
            }

            var symbolDetail = _C(exchange.SetContractType, Instruments)
            var record = exchange.GetRecords()

            var lastPrice = record[record.length - 1].Close
            var shortMean = TA.MA(record, shortPeriod)
            var longMean = TA.MA(record, longPeriod)
            var longSignal = shortMean[shortMean.length - 2] > longMean[longMean.length - 2]
            var shortSignal = shortMean[shortMean.length - 2] < longMean[longMean.length - 2]

            // 检查主力合约
            var DRecords = exchange.GetRecords(PERIOD_D1)
            var curTime = DRecords[DRecords.length - 1].Time
            var mainSymbol = symbolDetail.InstrumentID;

            var curPos = exchange.GetPosition()
            var atr = TA.ATR(DRecords, ATRPeriod)
            var N = atr[atr.length - 1]

            // 开仓/加仓手数计算
            var account = _q.GetAccount(exchange);
            var availmoney = account.Balance * (1 - KeepRatio/100)
            var unit = parseInt((availmoney) * (RiskRatio / 100) / N / symbolDetail.VolumeMultiple); //风险制约手数
            var canOpen = parseInt((availmoney) / (longSignal ? symbolDetail.LongMarginRatio : symbolDetail.ShortMarginRatio) / (lastPrice * 1.2) / symbolDetail.VolumeMultiple); //金额制约手数
            unit = Math.min(unit, canOpen);

            if (unit < symbolDetail.MinLimitOrderVolume) {
                Log("可开 " + unit + " 手 无法开仓, " + (canOpen >= symbolDetail.MinLimitOrderVolume ? "风控触发" : "资金限制") + "可用: " + account.Balance);
                
            }

            if(checklock){
                var initAccount = _q.GetAccount(exchange);
                var initMargin = JSON.parse(exchange.GetRawJSON()).CurrMargin;
                InitBalance = initAccount.Balance + initMargin;
            }

            // 开仓
            if(curPos && curPos.length == 0 && (longSignal || shortSignal)){
                
                _q.pushTask(exchange, mainSymbol, (longSignal ? "buy" : "sell"), unit, function(task, ret) {
                    Log('现有仓位:',curPos.length, "做多信号:", longSignal, "做空信号:", shortSignal)
                    if(ret){
                        checklock = false
                        openNumber += 1
                        openPrice = ret.price
                    }
                    
                });
            }

            // 移仓+加仓+平仓
            if(curPos && curPos.length > 0){
                holdPrice = curPos[0].Price
                holdAmount = curPos[0].Amount
                holdProfit = (curPos[0].Type % 2) == 0 ? (lastPrice - holdPrice) * holdAmount * symbolDetail.VolumeMultiple : (holdPrice - lastPrice) * holdAmount * symbolDetail.VolumeMultiple
                holdDir = curPos[0].Type % 2 == 0 ? "多" : "空"
                holdSymbol = curPos[0].ContractType

                

                // 移仓
                var moveSignal = pretime != curTime && holdSymbol != mainSymbol
                
                if (moveSignal) {
                    _q.pushTask(exchange, holdSymbol, "coverall", holdAmount, function(task, ret) {
                        if (ret) {
                            Log(holdSymbol, "移仓平仓成功 #ff0000");
                            pretime = curTime
                            curaddNumber = 0 //本轮加仓次数归零
                            var afterAccount = _q.GetAccount(exchange);
                            var afterMargin = JSON.parse(exchange.GetRawJSON()).CurrMargin;
                            var afterBalance = afterAccount.Balance + afterMargin;

                            var curprofit = afterBalance - InitBalance

                            if(curprofit > 0){
                                cumProfit += curprofit
                                winNumber += 1
                            }else{
                                cumLoss += curprofit
                                lossNumber += 1
                            }

                            curaddNumber = 0
                            holdSymbol = '' 
                            holdAmount = 0 
                            holdPrice = 0 
                            holdProfit = 0 
                            holdDir = '' 
                            openPrice = 0 
                            checklock = true
                        }
                    })
                }

                //加仓
                var spread = holdDir == '多' ? (openPrice - lastPrice) : (lastPrice - openPrice)
                var addInterval = IncSpace * N

                var addLongSignal = -spread > addInterval && holdProfit > 0 && longSignal && curaddNumber < MaxLots && holdDir == '多'
                var addShortSignal = -spread > addInterval && holdProfit > 0 && shortSignal && cumaddNumber < MaxLots && holdDir == '空'

                if(addLongSignal){
                    _q.pushTask(exchange, mainSymbol, "buy", unit, function(task, ret) {
                        Log('满足加仓条件,进行多头方向加仓#ff0000')
                        if(ret){
                            openPrice = ret.price 
                            curaddNumber += 1
                            cumaddNumber += 1
                        }
                        
                    });
                }

                if(addShortSignal){
                    _q.pushTask(exchange, mainSymbol, "sell", unit, function(task, ret) {
                        Log('满足加仓条件,进行空头方向加仓#ff0000')
                        if(ret){
                            openPrice = ret.price 
                            curaddNumber += 1
                            cumaddNumber += 1
                        }
                    });
                }

                //平仓
                var coverLongSignal = shortSignal && holdDir == '多'
                var coverShortSignal = longSignal && holdDir == '空'

                if(coverLongSignal || coverShortSignal){
                    _q.pushTask(exchange, mainSymbol, "coverall", 0, function(task, ret) {
                        if(ret){
                            var afterAccount = _q.GetAccount(exchange);
                            var afterMargin = JSON.parse(exchange.GetRawJSON()).CurrMargin;
                            var afterBalance = afterAccount.Balance + afterMargin;

                            var curprofit = afterBalance - InitBalance

                            Log('平仓后资金:', afterAccount.Balance, afterMargin, afterBalance)
                            Log('平仓后利润:', afterBalance - InitBalance)

                            if(curprofit > 0){
                                cumProfit += curprofit
                                winNumber += 1
                            }else{
                                cumLoss += curprofit
                                lossNumber += 1
                            }
                            curaddNumber = 0
                            holdSymbol = '' 
                            holdAmount = 0 
                            holdPrice = 0 
                            holdProfit = 0 
                            holdDir = '' 
                            openPrice = 0 
                            checklock = true
                        }
                    })
                }
            }

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

            var tblStrategy = {
                type: "table",
                title: "策略评价",
                cols: ["开仓次数", "成功次数", "累计利润", "失败次数", "累计亏损", "胜率", "盈亏比"],
                rows: []
            };

            if(openNumber){
                winRate = winNumber / openNumber
            }

            winRate = openNumber ? winNumber / openNumber : 0

            if(cumLoss == 0 && cumProfit != 0 ) plRatio = 1
            if(cumLoss != 0 && cumProfit == 0) plRatio = -1
            if(cumProfit && cumLoss) plRatio = cumProfit / cumLoss

            tblStatus.rows.push([holdSymbol, holdDir, holdPrice, holdAmount, holdProfit, curaddNumber]);
            tblStrategy.rows.push([openNumber, winNumber, cumProfit, lossNumber, cumLoss, winRate, Math.abs(plRatio)]);
            var now = new Date();

            lastStatus = '`' + JSON.stringify([tblStatus, tblStrategy]) + '`\n' + '`' + ' 当前时间: ' + _D() + ', 星期' + ['日', '一', '二', '三', '四', '五', '六'][now.getDay()] + ", 交易任务: " + _q.size();
            LogStatus(lastStatus);
            _q.poll();

            Sleep(LoopInterval * 1000);
        }
    }
}





    



template: strategy.tpl:40:21: executing "strategy.tpl" at <.api.GetStrategyListByName>: wrong number of args for GetStrategyListByName: want 7 got 6