avatar of 发明者量化-小小梦 发明者量化-小小梦
Suivre Messages privés
4
Suivre
1271
Abonnés

Stratégie de point tournant à double moyenne mobile sur les contrats à terme sur devises numériques (enseignement)

Créé le: 2021-12-03 16:50:24, Mis à jour le: 2023-09-20 10:24:03
comments   3
hits   2445

Stratégie de point tournant à double moyenne mobile sur les contrats à terme sur devises numériques (enseignement)

Stratégie de point tournant à double moyenne mobile sur les contrats à terme sur devises numériques (enseignement)

Cet article explique la conception d’une stratégie de tendance simple, uniquement du point de vue de la conception de la stratégie, pour aider les débutants à apprendre à concevoir une stratégie simple et à comprendre le processus d’exécution du programme de stratégie. Quant à la performance de la stratégie, elle est en grande partie liée aux paramètres de la stratégie (c’est le cas de presque la plupart des stratégies de tendance).

Conception de la stratégie

Utilisez deux indicateurs de moyenne mobile EMA lorsque les deux moyennes mobiles EMA ont des points de retournement. Le point de retournement est utilisé comme signal pour ouvrir une position longue ou courte (ou inversement), et une différence de profit cible fixe est conçue pour fermer la position. Les commentaires sont écrits directement dans le code de la politique pour une lecture plus facile. Le code de stratégie est généralement très court et convient aux débutants.

Code de stratégie

/*backtest
start: 2021-09-01 00:00:00
end: 2021-12-02 00:00:00
period: 1h
basePeriod: 5m
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/

// 以上 /**/ 内为回测默认设置,在回测页面可以用回测页面上的相关控件重新设置

var LONG = 1       // 持多头仓位的标记,枚举常量
var SHORT = -1     // 持空头仓位的标记,枚举常量
var IDLE = 0       // 不持仓时的标记,枚举常量

// 获取指定方向的持仓,positions为持仓数据,direction为要获取的持仓方向
function getPosition(positions, direction) {
    var ret = {Price : 0, Amount : 0, Type : ""}    // 定义一个不持仓时的结构
    // 遍历positions,从中找到符合direction方向的持仓
    _.each(positions, function(pos) {
        if (pos.Type == direction) {
            ret = pos
        }
    })
    // 返回找到的持仓
    return ret 
}

// 取消当前交易对、合约的所有挂单
function cancellAll() {
    // 死循环,不停检测,直到触发break跳出
    while (true) {
        // 获取当前交易对、合约的挂单数据,即orders
        var orders = _C(exchange.GetOrders)
        if (orders.length == 0) {
            // 当orders为空数组时,即 orders.length == 0 时,执行break跳出while循环
            break
        } else {
            // 遍历当前所有挂单,逐个取消挂单
            for (var i = 0 ; i < orders.length ; i++) {
                // 具体撤销某个订单的函数,撤销ID为:orders[i].Id的订单
                exchange.CancelOrder(orders[i].Id, orders[i])
                Sleep(500)
            }
        }
        Sleep(500)
    }
}

// 平仓函数,根据传入的交易函数tradeFunc,方向direction,去执行平仓
function cover(tradeFunc, direction) {
    var mapDirection = {"closebuy": PD_LONG, "closesell": PD_SHORT}
    var positions = _C(exchange.GetPosition)  // 获取当前交易对、合约的持仓数据
    var pos = getPosition(positions, mapDirection[direction])  // 找到指定的平仓方向的持仓信息
    // 当持仓量大于0(有仓位才能平仓)
    if (pos.Amount > 0) {
        // 撤销所有可能存在的挂单
        cancellAll()
        // 设置交易方向
        exchange.SetDirection(direction)
        // 执行平仓交易函数
        if (tradeFunc(-1, pos.Amount)) {
            // 下单成功返回 true
            return true 
        } else {
            // 下单失败返回 false 
            return false 
        }
    }
    // 没有仓位返回 true
    return true 
}

// 策略主函数
function main() {
    // 用于切换到OKEX V5模拟盘
    if (okexSimulate) {
        exchange.IO("simulate", true) // 切换到OKEX V5模拟盘测试 
        Log("切换到OKEX V5模拟盘")
    }    
    
    // 设置合约代码,ct为swap 即设置当前操作的合约是永续合约
    exchange.SetContractType(ct)
    // 初始化状态为未持仓
    var state = IDLE
    // 初始化持仓价格为0
    var holdPrice = 0
    // 初始化对比用的时间戳,用于对比当前K线BAR是否变化
    var preTime = 0
    
    // 策略主循环
    while (true) {
        // 获取当前交易对、合约的K线数据
        var r = _C(exchange.GetRecords)
        // 获取K线数据长度,即l
        var l = r.length
        // 判断K线长度 l 必须大于指标周期(不大于指标周期,指标函数无法计算出有效的指标数据),否则重新循环
        if (l < Math.max(ema1Period, ema2Period)) {
            // 等待1000毫秒,即1秒,避免轮转过快
            Sleep(1000)
            // 忽略当前if以后的代码, 重新while循环
            continue
        }
        
        // 计算ema指标数据
        var ema1 = TA.EMA(r, ema1Period)
        var ema2 = TA.EMA(r, ema2Period)
        
        // 画图
        $.PlotRecords(r, 'K线')    // 画K线图
        // 当最后一个BAR时间戳发生变化时,即有新K线BAR产生时
        if(preTime !== r[l - 1].Time){
            // 新BAR出现之前的倒数第一根BAR最后一次更新
            $.PlotLine('ema1', ema1[l - 2], r[l - 2].Time)
            $.PlotLine('ema2', ema2[l - 2], r[l - 2].Time)
            
            // 画新BAR的指标线,即当前倒数第一根BAR上的指标数据
            $.PlotLine('ema1', ema1[l - 1], r[l - 1].Time)
            $.PlotLine('ema2', ema2[l - 1], r[l - 1].Time)
            
            // 更新用于对比的时间戳
            preTime = r[l - 1].Time
        } else {
            // 当没有新BAR产生时,仅仅更新图表上倒数第一根BAR的指标数据
            $.PlotLine('ema1', ema1[l - 1], r[l - 1].Time)
            $.PlotLine('ema2', ema2[l - 1], r[l - 1].Time)
        }
        
        // 开多仓的条件,拐点
        var up = (ema1[l - 2] > ema1[l - 3] && ema1[l - 4] > ema1[l - 3]) && (ema2[l - 2] > ema2[l - 3] && ema2[l - 4] > ema2[l - 3])
        // 开空仓的条件,拐点
        var down = (ema1[l - 2] < ema1[l - 3] && ema1[l - 4] < ema1[l - 3]) && (ema2[l - 2] < ema2[l - 3] && ema2[l - 4] < ema2[l - 3])
        
        // 开多仓的条件触发并且当前持有空头仓位,或者开多仓的条件触发并且没持仓
        if (up && (state == SHORT || state == IDLE)) {
            // 如果持有空头仓位,先平仓
            if (state == SHORT && cover(exchange.Buy, "closesell")) {
                // 平仓后标记未持仓状态
                state = IDLE
                // 重置持仓价格为0
                holdPrice = 0
                // 在图表上标记
                $.PlotFlag(r[l - 1].Time, 'coverShort', 'CS')
            }
            // 平仓后反手开多仓
            exchange.SetDirection("buy")
            if (exchange.Buy(-1, amount)) {
                // 标记当前状态
                state = LONG
                // 记录当前价格
                holdPrice = r[l - 1].Close
                $.PlotFlag(r[l - 1].Time, 'openLong', 'L')
            }
        } else if (down && (state == LONG || state == IDLE)) {
            // 和 up 条件的判断同理
            if (state == LONG && cover(exchange.Sell, "closebuy")) {
                state = IDLE
                holdPrice = 0
                $.PlotFlag(r[l - 1].Time, 'coverLong', 'CL')
            }
            exchange.SetDirection("sell")
            if (exchange.Sell(-1, amount)) {
                state = SHORT
                holdPrice = r[l - 1].Close
                $.PlotFlag(r[l - 1].Time, 'openShort', 'S')
            }
        }
        
        // 止盈
        if (state == LONG && r[l - 1].Close - holdPrice > profitTarget && cover(exchange.Sell, "closebuy")) {            
            state = IDLE
            holdPrice = 0
            $.PlotFlag(r[l - 1].Time, 'coverLong', 'CL')
        } else if (state == SHORT && holdPrice - r[l - 1].Close > profitTarget && cover(exchange.Buy, "closesell")) {            
            state = IDLE
            holdPrice = 0
            $.PlotFlag(r[l - 1].Time, 'coverShort', 'CS')
        }
        // 在状态栏上显示时间
        LogStatus(_D())
        Sleep(500)        
    }
}

Stratégie de point tournant à double moyenne mobile sur les contrats à terme sur devises numériques (enseignement)

Stratégie de point tournant à double moyenne mobile sur les contrats à terme sur devises numériques (enseignement)

Code source de la stratégie : https://www.fmz.com/strategy/333269

La stratégie est purement destinée à l’enseignement de la programmation, veuillez ne pas la pratiquer dans le trading réel.