双向合约网格交易v1.0.2

Author: wind, Date: 2021-06-24 15:37:17
Tags: Grid

双向合约网格交易v1.0.2

功能

合约网格交易 同时通过做空做多来进行差价套利 由于双向策略爆仓几率很小

  • 买入单超时判断
  • 倍投
  • 自动跟单
  • 趋势开单(待开发,收费版)
  • 涨跌速动态修改开单数量(待开发,收费版)
  • 金叉死叉趋势加入(待开发,收费版)

回测数据

img

img

2000u半年翻2倍 收益很明显,无论大涨还是大跌都能扛得住

维护

持续优化



/*backtest
start: 2021-01-01 00:00:00
end: 2021-06-21 23:59:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT","balance":2000}]
*/

// 首次买入
let FIRST_BUY = true;
// 已存在买涨订单
let MANY_BUYING = false;
// 已存在做空订单
let SHORT_BUYING = false;
// 买涨订单创建时间
let MANY_BUY_TIME = null;
// 做空订单创建时间
let SHORT_BUY_TIME = null;
// 买涨空仓时间
let MANY_EMPTY_STEP_TIME = null;
// 做空空仓时间
let SHORT_EMPTY_STEP_TIME = null;
// 校验空仓时间
let CHECK_TIME = null;

let QUANTITY = [0.001, 0.002, 0.004, 0.008, 0.016, 0.032, 0.064];
// 下次购买价格(多仓)
let MANY_NEXT_BUY_PRICE = 0;
// 下次购买价格(空仓)
let SHORT_NEXT_BUY_PRICE = 0;
// 当前仓位(多仓)
let MANY_STEP = 0;
// 当前仓位(空仓)
let SHORT_STEP = 0;
// 止盈比率
let PROFIT_RATIO = 1;
// 补仓比率
let DOUBLE_THROW_RATIO = 1.5;
// 卖出后下次购买金额下浮比率
let BUY_PRICE_RATIO = 1;
// 交易订单列表(多仓)
let MANY_ORDER_LIST = [];
// 交易订单列表(空仓)
let SHORT_ORDER_LIST = [];

function getManyQuantity() {
    if (MANY_STEP < QUANTITY.length) {
        return QUANTITY[MANY_STEP]
    }
    return QUANTITY[0]
}

function getShortQuantity() {
    if (SHORT_STEP < QUANTITY.length) {
        return QUANTITY[SHORT_STEP]
    }
    return QUANTITY[0]
}

function firstManyBuy(ticker) {
    if (MANY_BUYING) {
        return
    }
    exchange.SetDirection("buy")
    let orderId = exchange.Buy(ticker.Last, getManyQuantity())
    if (!orderId) {
        return
    }
    MANY_BUYING = true
    while (true) {
        exchange.SetDirection("buy") 
        let order = exchange.GetOrder(orderId)
        if (null === order) {
            continue
        }
        if (1 === order.Status || 2 === order.Status) {
            MANY_NEXT_BUY_PRICE = order.Price * ((100 - DOUBLE_THROW_RATIO) / 100)
            MANY_STEP = MANY_STEP + 1
            MANY_BUYING = false
            MANY_EMPTY_STEP_TIME = null
            let sellPrice = order.Price * ((100 + PROFIT_RATIO) / 100)
            MANY_ORDER_LIST.push({
                buyPrice: order.Price,
                sellPrice: sellPrice,
                quantity: order.Amount,
                isSell: false,
            })
            break
        }
    }
}

function firstShortBuy(ticker) {
    if (SHORT_BUYING) {
        return
    }
    exchange.SetDirection("sell")
    let orderId = exchange.Sell(ticker.Last, getShortQuantity())
    if (!orderId) {
        return
    }
    SHORT_BUYING = true
    while (true) {
        let order = exchange.GetOrder(orderId)
        if (null === order) {
            continue
        }
        if (1 === order.Status || 2 === order.Status) {
            SHORT_NEXT_BUY_PRICE = order.Price * ((100 + DOUBLE_THROW_RATIO) / 100)
            SHORT_STEP = SHORT_STEP + 1
            SHORT_BUYING = false
            SHORT_EMPTY_STEP_TIME = null
            let sellPrice = order.Price * ((100 - PROFIT_RATIO) / 100)
            SHORT_ORDER_LIST.push({
                buyPrice: order.Price,
                sellPrice: sellPrice,
                quantity: order.Amount,
                isSell: false,
            })
            break
        }
    }
}

function manyBuy(ticker) {
    if (MANY_BUYING) {
        return
    }
    Log('ticker: ' + ticker.Last + ' MANY_NEXT_BUY_PRICE: ' + MANY_NEXT_BUY_PRICE)
    if (ticker.Last > MANY_NEXT_BUY_PRICE) {
        return
    }
    exchange.SetDirection("buy")
    let orderId = exchange.Buy(ticker.Last, getManyQuantity())
    if (!orderId) {
        return
    }
    MANY_BUYING = true
    MANY_BUY_TIME = Unix()
    while (true) {
        let now = Unix()
        let order = exchange.GetOrder(orderId)
        let expire = MANY_BUY_TIME + (60 * 30)
        if (null === order) {
            continue
        }
        // 买入成功处理
        if (1 === order.Status || 2 === order.Status) {
            MANY_NEXT_BUY_PRICE = order.Price * ((100 - DOUBLE_THROW_RATIO) / 100)
            MANY_STEP = MANY_STEP + 1
            MANY_BUYING = false
            MANY_EMPTY_STEP_TIME = null
            let sellPrice = order.Price * ((100 + PROFIT_RATIO) / 100)
            MANY_ORDER_LIST.push({
                buyPrice: order.Price,
                sellPrice: sellPrice,
                quantity: order.Amount,
                isSell: false,
            })
            break
        }
        // 买入超时处理
        if (now >= expire) {
            exchange.CancelOrder(orderId)
            MANY_BUYING = false
            MANY_BUY_TIME = null
            MANY_NEXT_BUY_PRICE = ticker.Last * ((100 - DOUBLE_THROW_RATIO) / 100)
            return
        }
    }
}

function shortBuy(ticker) {
    if (SHORT_BUYING) {
        return
    }
    Log('ticker: ' + ticker.Last + ' SHORT_NEXT_BUY_PRICE: ' + SHORT_NEXT_BUY_PRICE)
    if (ticker.Last < SHORT_NEXT_BUY_PRICE) {
        return
    }
    exchange.SetDirection("sell")
    let orderId = exchange.Sell(ticker.Last, getShortQuantity())
    if (!orderId) {
        return
    }
    SHORT_BUYING = true
    SHORT_BUY_TIME = Unix()
    while (true) {
        let now = Unix()
        let expire = SHORT_BUY_TIME + (60 * 30)
        let order = exchange.GetOrder(orderId)
        if (null === order) {
            continue
        }
        // 买入成功处理
        if (1 === order.Status || 2 === order.Status) {
            SHORT_NEXT_BUY_PRICE = order.Price * ((100 + DOUBLE_THROW_RATIO) / 100)
            SHORT_STEP = SHORT_STEP + 1
            SHORT_BUYING = false
            SHORT_EMPTY_STEP_TIME = null
            let sellPrice = order.Price * ((100 - PROFIT_RATIO) / 100)
            SHORT_ORDER_LIST.push({
                buyPrice: order.Price,
                sellPrice: sellPrice,
                quantity: order.Amount,
                isSell: false,
            })
            break
        }
        // 买入超时处理
        if (now >= expire) {
            exchange.CancelOrder(orderId)
            SHORT_BUYING = false
            SHORT_BUY_TIME = null
            SHORT_NEXT_BUY_PRICE = ticker.Last * ((100 + DOUBLE_THROW_RATIO) / 100)
            return
        }
    }
}


function manySell(ticker) {
    // 遍历卖出订单
    for (let item of MANY_ORDER_LIST) {
        if (item.isSell) {
            continue
        }
        if (ticker.Last >= item.sellPrice) {
            item.isSell = true;
            exchange.SetDirection("closebuy")
            let orderId = exchange.Sell(ticker.Last, item.quantity)
            if (!orderId) {
                return
            }
            while (true) {
                let order = exchange.GetOrder(orderId)
                if (null === order) {
                    continue
                }
                if (1 === order.Status || 2 === order.Status) {
                    MANY_NEXT_BUY_PRICE = ticker.Last * ((100 - BUY_PRICE_RATIO) / 100)
                    MANY_STEP = MANY_STEP - 1
                    if (0 === MANY_STEP) {
                        MANY_EMPTY_STEP_TIME = Unix()
                    }
                    break
                }
            }
        }
    }
}

function shortSell(ticker) {
    // 遍历卖出订单
    for (let item of SHORT_ORDER_LIST) {
        if (item.isSell) {
            continue
        }
        if (ticker.Last <= item.sellPrice) {
            item.isSell = true;
            exchange.SetDirection("closesell")
            let orderId = exchange.Buy(ticker.Last, item.quantity)
            if (!orderId) {
                return
            }
            while (true) {
                let order = exchange.GetOrder(orderId)
                if (null === order) {
                    continue
                }
                if (1 === order.Status || 2 === order.Status) {
                    SHORT_NEXT_BUY_PRICE = ticker.Last * ((100 + BUY_PRICE_RATIO) / 100)
                    SHORT_STEP = SHORT_STEP - 1
                    if (0 === SHORT_STEP) {
                        SHORT_EMPTY_STEP_TIME = Unix()
                    }
                    break
                }
            }
        }
    }
}

function check(ticker) {
    let now = Unix()
    if (null !== CHECK_TIME) {
        let expire = CHECK_TIME + (60 * 10)
        if (now < expire) {
            return
        }
    }
    CHECK_TIME = now

    if (null !== MANY_EMPTY_STEP_TIME) {
        let expire = MANY_EMPTY_STEP_TIME + (60 * 30)
        if (now >= expire) {
            MANY_NEXT_BUY_PRICE = ticker.Last * ((100 - DOUBLE_THROW_RATIO) / 100)
            Log('没有买涨持仓, 调整买入价: ' + MANY_NEXT_BUY_PRICE)
        }
    }
    
    if (null !== SHORT_EMPTY_STEP_TIME) {
        let expire = SHORT_EMPTY_STEP_TIME + (60 * 30)
        if (now >= expire) {
            SHORT_NEXT_BUY_PRICE = ticker.Last * ((100 + DOUBLE_THROW_RATIO) / 100)
            Log('没有做空持仓, 调整买入价: ' + SHORT_NEXT_BUY_PRICE)
        }
    }
}

function onTick() {
    // 在这里写策略逻辑,将会不断调用,例如打印行情信息
    let ticker = exchange.GetTicker()
    if (!ticker) {
        return
    }
    if (FIRST_BUY) {
        // 首次做多购买
        firstManyBuy(ticker)
        // 首次做空购买
        firstShortBuy(ticker)
        FIRST_BUY = false
        return
    }
    
    // 做多买入
    manyBuy(ticker)
    // 做空买入
    shortBuy(ticker)
    // 做多卖出
    manySell(ticker)
    // 做空卖出
    shortSell(ticker)
    // 空仓检测
    check(ticker)
}

function main() {
    // 开合约
    exchange.SetContractType("swap")

    while(true){
        onTick()
        // Sleep函数主要用于数字货币策略的轮询频率控制,防止访问交易所API接口过于频繁
        Sleep(60000)
    }
}


Related

More

hexie8 可以多币种吗

rich_rory check是什么意思。。。。定期增加/减少 买入门槛? 有什么意义

artron 这个怎么没有策略参数设置?

Exodus[策略代写] 又发现问题了呢,在回测2020-1-1到2020-6月的时候,到3-30号整个机器人就停止了,此外,测试ETH的时候在测试2021-1-1到201-6-21的时候在1-2号就会停止呢,不知道是fmz的问题还是机器人的问题呢。作者可以自己测试一下

evan 不能算是真正的 多空对冲啊 像合约网格啊

Exodus[策略代写] 加作者微信没有回应,只能来这里说几句了,运行了7天,200刀本金赚了70刀。挺不错的,但是在运行期间我发现了几个问题。 1.双向开单功能是正常的嘛?看策略里说双向看单,爆仓几率很小,可是七天内除了机器人刚启动时开了双向单,几乎没有双向开单,经过我的研究发现是10分钟更新一次开单位置,这导致变化较慢时毫无意义,也开不了双向单,比如今天btc今天5%的跌幅竟然没有开单?这里是否需要优化呢?比如延长检测间隔?此外,在大行情中就算假突破开了双向单,假突破一方也是最低档,不太理解有什么意义。还是说双向是指能吃两边行情,而对阻止爆仓没有什么意义吗? 2.爆就默认的加仓资金来说,想不暴仓需要确保1000刀到2000刀以上的资金,我两百刀今天要是没有莫名其妙醒过来我已经爆仓了,临时加的保证金。如果资金不够也可以自己手动调整加仓的数量,这里提醒一下,免得不会看代码的爆仓了。 3.请问一下,因为永不止损嘛,所以在本金足够的情况下是否永远不会爆仓呢,可是回测中为何会出现亏损呢?按理来说亏损就是爆仓,就是资金归零才对呀,我哪里理解错了吗,求指点。 这个策略我挺喜欢的,可以无视方向就能获利,调整加仓量和本金的话爆仓风险也不大,感觉调整开空和开多的价格可以优化,根据本金加入自适应加仓量等等

YesX 老哥微信

Exodus[策略代写] 解决了,换成币安就行了

Exodus[策略代写] 看回测感觉好叼,但是好像不能用在实盘吗?我火币开bch的会报错,Sell(490.14, 0.001): map[err_code:1067 err_msg:The volume field is illegal. Please re-enter. status:error ts:1.625132342294e+12]。合约后面不是张数吗,为什么是小数呢

wbsy 没有实盘么

Exodus[策略代写] 这收益曲线太可怕了吧,亏了120%多

醉里挑灯看剑 笑死我了

Exodus[策略代写] 难受,今天亏爆了,很期待2.0,请问怎样才能体验新策略呢

wind 在开发2.0版本中发现了这些问题 做空买不进的原因是,开单的百分比是固定的,也就是涨百分之一,做空 ,跌百分之一,买涨 由于涨跌都是百分之一,涨跌买入百分比一致,从1000涨到1500,1000的百分之一和1500的百分之一是不一样的,所以做空比较难买进,而且改价太过于频繁更导致做空可能买不进 止损问题,在2.0版本中做了一定的处理,很简单的一个止损处理,但是从回测数据来看效果还可以,当你买涨或者做空仓位达到一定阀值时开始全仓卖出,1.0没有处理所以会出现部分订单永远都无法成交,比如在btc 65000买涨数量0.001,那么btc跌了这个单子永远出不去了 /upload/asset/2034c4ec56c423120b9c6.png /upload/asset/203032c94e60a3915cc9f.png /upload/asset/2030b880b030476977f4b.png /upload/asset/2030d89e9fd59f528be4c.png

wind 我用币安回测 跑的实盘 其他平台可能会报一些错误

wind 实盘还在跑,需要时间

wind 看看最新的曲线,之前的刚写出来有bug