Skip to content

Latest commit

 

History

History
564 lines (517 loc) · 18.1 KB

OKCoin期货跨期对冲策略(模板测试使用).md

File metadata and controls

564 lines (517 loc) · 18.1 KB

策略名称

OKCoin期货跨期对冲策略(模板测试使用)

策略作者

小小梦

策略描述

OKCoin期货跨期对冲策略(模板)

策略参数

参数 默认值 描述
OpMode 0 现货-下单方式: 吃单
MaxSpace 0.5 现货-挂单失效距离
SlidePrice 0.1 现货-下单滑动价(元)
MaxAmount 0.8 现货-开仓最大单次下单量
RetryDelay 500 现货-失败重试(毫秒)
MAType 0 现货-均线算法: EMA
Interval 300 期货—失败重试间隔(毫秒)
F_SlidePrice 2 期货—下单滑价(元)
lv 0.5 期货—滑价增长率
max_open_lv true 期货—开仓滑价最大增长率
max_cover_lv true 期货—平仓滑价最大增长率

源码 (javascript)

// 现货部分
function CancelPendingOrders(e, orderType) {
    while (true) {
        var orders = e.GetOrders();
        if (!orders) {
            Sleep(RetryDelay);
            continue;
        }
        var processed = 0;
        for (var j = 0; j < orders.length; j++) {
            if (typeof(orderType) === 'number' && orders[j].Type !== orderType) {
                continue;
            }
            e.CancelOrder(orders[j].Id, orders[j]);
            processed++;
            if (j < (orders.length - 1)) {
                Sleep(RetryDelay);
            }
        }
        if (processed === 0) {
            break;
        }
    }
}

function GetAccount(e, waitFrozen) {
    if (typeof(waitFrozen) == 'undefined') {
        waitFrozen = false;
    }
    var account = null;
    var alreadyAlert = false;
    while (true) {
        account = _C(e.GetAccount);
        if (!waitFrozen || (account.FrozenStocks < e.GetMinStock() && account.FrozenBalance < 0.01)) {
            break;
        }
        if (!alreadyAlert) {
            alreadyAlert = true;
            Log("发现账户有冻结的钱或币", account);
        }
        Sleep(RetryDelay);
    }
    return account;
}


function StripOrders(e, orderId) {
    var order = null;
    if (typeof(orderId) == 'undefined') {
        orderId = null;
    }
    while (true) {
        var dropped = 0;
        var orders = _C(e.GetOrders);
        for (var i = 0; i < orders.length; i++) {
            if (orders[i].Id == orderId) {
                order = orders[i];
            } else {
                var extra = "";
                if (orders[i].DealAmount > 0) {
                    extra = "成交: " + orders[i].DealAmount;
                } else {
                    extra = "未成交";
                }
                e.CancelOrder(orders[i].Id, orders[i].Type == ORDER_TYPE_BUY ? "买单" : "卖单", extra);
                dropped++;
            }
        }
        if (dropped === 0) {
            break;
        }
        Sleep(RetryDelay);
    }
    return order;
}

// mode = 0 : direct buy, 1 : buy as buy1
function Trade(e, tradeType, tradeAmount, mode, slidePrice, maxAmount, maxSpace, retryDelay) {
    var initAccount = GetAccount(e, true);
    var nowAccount = initAccount;
    var orderId = null;
    var prePrice = 0;
    var dealAmount = 0;
    var diffMoney = 0;
    var isFirst = true;
    var tradeFunc = tradeType == ORDER_TYPE_BUY ? e.Buy : e.Sell;
    var isBuy = tradeType == ORDER_TYPE_BUY;
    while (true) {
        var ticker = _C(e.GetTicker);
        var tradePrice = 0;
        if (isBuy) {
            tradePrice = _N((mode === 0 ? ticker.Sell : ticker.Buy) + slidePrice, 4);
        } else {
            tradePrice = _N((mode === 0 ? ticker.Buy : ticker.Sell) - slidePrice, 4);
        }
        if (!orderId) {
            if (isFirst) {
                isFirst = false;
            } else {
                nowAccount = GetAccount(e, true);
            }
            var doAmount = 0;
            if (isBuy) {
                diffMoney = _N(initAccount.Balance - nowAccount.Balance, 4);
                dealAmount = _N(nowAccount.Stocks - initAccount.Stocks, 4);
                doAmount = Math.min(maxAmount, tradeAmount - dealAmount, _N((nowAccount.Balance - 10) / tradePrice, 4));
            } else {
                diffMoney = _N(nowAccount.Balance - initAccount.Balance, 4);
                dealAmount = _N(initAccount.Stocks - nowAccount.Stocks, 4);
                doAmount = Math.min(maxAmount, tradeAmount - dealAmount, nowAccount.Stocks);
            }
            if (doAmount < e.GetMinStock()) {
                break;
            }
            prePrice = tradePrice;
            orderId = tradeFunc(tradePrice, doAmount, ticker);
            if (!orderId) {
                CancelPendingOrders(e, tradeType);
            }
        } else {
            if (mode === 0 || (Math.abs(tradePrice - prePrice) > maxSpace)) {
                orderId = null;
            }
            var order = StripOrders(e, orderId);
            if (!order) {
                orderId = null;
            }
        }
        Sleep(retryDelay);
    }

    if (dealAmount <= 0) {
        return null;
    }

    return {
        price: _N(diffMoney / dealAmount, 4),
        amount: dealAmount
    };
}

$.Buy = function(e, amount) {
    if (typeof(e) === 'number') {
        amount = e;
        e = exchange;
    }
    return Trade(e, ORDER_TYPE_BUY, amount, OpMode, SlidePrice, MaxAmount, MaxSpace, RetryDelay);
};

$.Sell = function(e, amount) {
    if (typeof(e) === 'number') {
        amount = e;
        e = exchange;
    }
    return Trade(e, ORDER_TYPE_SELL, amount, OpMode, SlidePrice, MaxAmount, MaxSpace, RetryDelay);
};

$.CancelPendingOrders = function(e, orderType) {
    if (typeof(orderType) === 'undefined') {
        if (typeof(e) === 'number') {
            orderType = e;
            e = exchange;
        } else if (typeof(e) === 'undefined') {
            e = exchange;
        }
    }
    return CancelPendingOrders(e, orderType);
};

$.GetAccount = function(e) {
    if (typeof(e) === 'undefined') {
        e = exchange;
    }
    return _C(e.GetAccount);
};

var _MACalcMethod = [TA.EMA, TA.MA, talib.KAMA][MAType];

// 返回上穿的周期数. 正数为上穿周数, 负数表示下穿的周数, 0指当前价格一样
$.Cross = function(a, b) {
    var crossNum = 0;
    var arr1 = [];
    var arr2 = [];
    if (Array.isArray(a)) {
        arr1 = a;
        arr2 = b;
    } else {
        var records = null;
        while (true) {
            records = exchange.GetRecords();
            if (records && records.length > a && records.length > b) {
                break;
            }
            Sleep(RetryDelay);
        }
        arr1 = _MACalcMethod(records, a);
        arr2 = _MACalcMethod(records, b);
    }
    if (arr1.length !== arr2.length) {
        throw "array length not equal";
    }
    for (var i = arr1.length-1; i >= 0; i--) {
        if (typeof(arr1[i]) !== 'number' || typeof(arr2[i]) !== 'number') {
            break;
        }
        if (arr1[i] < arr2[i]) {
            if (crossNum > 0) {
                break;
            }
            crossNum--;
        } else if (arr1[i] > arr2[i]) {
            if (crossNum < 0) {
                break;
            }
            crossNum++;
        } else {
            break;
        }
    }
    return crossNum;
};

// 期货部分
function GetPosition(e, contractType, direction) {
    var allCost = 0;
    var allAmount = 0;
    var allProfit = 0;
    var allFrozen = 0;
    var posMargin = 0;
    var positions = _C(e.GetPosition);
    for (var i = 0; i < positions.length; i++) {
        if (positions[i].ContractType == contractType &&
            (((positions[i].Type == PD_LONG) && direction == PD_LONG) || ((positions[i].Type == PD_SHORT) && direction == PD_SHORT))
        ) {
            posMargin = positions[i].MarginLevel;
            allCost += (positions[i].Price * positions[i].Amount);
            allAmount += positions[i].Amount;
            allProfit += positions[i].Profit;
            allFrozen += positions[i].FrozenAmount;
        }
    }
    if (allAmount === 0) {
        return null;
    }
    return {
        MarginLevel: posMargin,
        FrozenAmount: allFrozen,
        Price: _N(allCost / allAmount),
        Amount: allAmount,
        Profit: allProfit,
        Type: direction,
        ContractType: contractType
    };
}

function Open(e, contractType, direction, opAmount, price) {
    var initPosition = GetPosition(e, contractType, direction);
    var isFirst = true;
    var initAmount = initPosition ? initPosition.Amount : 0;
    var positionNow = initPosition;
    var step = 0;
    while (true) {
        var needOpen = opAmount;
        if (isFirst) {
            isFirst = false;
        } else {
            positionNow = GetPosition(e, contractType, direction);
            if (positionNow) {
                needOpen = opAmount - (positionNow.Amount - initAmount);
            }
        }
        if (needOpen < e.GetMinStock()) {
            break;
        }
        if (step > max_open_lv) {
            break;
        }
        var amount = needOpen;
        e.SetDirection(direction == PD_LONG ? "buy" : "sell");
        var orderId;
        if (direction == PD_LONG) {
            orderId = e.Buy(price + F_SlidePrice * (1 + step), amount, "开多仓", contractType, price);
        } else {
            orderId = e.Sell(price - F_SlidePrice * (1 + step), amount, "开空仓", contractType, price);
        }
        
        Sleep(Interval)      //  增加延迟 避免 调用间隔过小导致 API 反应不过来,调换一下位置,写在合适的位置上, GetOrders 之前,避免刚下单就获取 未完成订单 导致问题。
        
        while (true) {
            var orders = _C(e.GetOrders);
            if (orders.length === 0) {
                break;
            }
            Sleep(Interval);
            for (var j = 0; j < orders.length; j++) {
                e.CancelOrder(orders[j].Id);
                if (j < (orders.length - 1)) {
                    Sleep(Interval);
                }
            }
        }
        step += lv;
        //  Sleep(Interval)      //  增加延迟 避免 调用间隔过小导致 API 反应不过来,调换一下位置,写在合适的位置上, GetOrders 之前,避免刚下单就获取 未完成订单 导致问题。
    }
    var ret = {
        price: 0,
        amount: 0,
        position: positionNow
    };
    if (!positionNow) {
        return ret;
    }
    if (!initPosition) {
        ret.price = positionNow.Price;
        ret.amount = positionNow.Amount;
    } else {
        ret.amount = positionNow.Amount - initPosition.Amount;
        ret.price = _N(((positionNow.Price * positionNow.Amount) - (initPosition.Price * initPosition.Amount)) / ret.amount);
    }
    return ret;
}

function Cover(e, contractType, price, OP_amount, direction) {
    var initP = null;
    var positions = null;
    var isFirst = true;
    var ID = null;
    var step = 0;
    var index = 0;
    var initP_contractTypeAmount = 0
    while (true) {
        var n = 0;
        positions = _C(e.GetPosition);
        if (isFirst === true) {
            if (typeof(direction) === 'undefined' && positions.length > 1 || (direction !== PD_LONG && direction !== PD_SHORT && typeof(direction) !== 'undefined')) {
                throw "有多,空双向持仓,并且参数direction未明确方向!或 direction 参数异常:" + direction;
            }
            initP = positions;
            isFirst = false;
            // 修改BUG 
            for(var m = 0 ; m < initP.length; m++){
                if(initP[m].ContractType == contractType){
                    initP_contractTypeAmount = initP[m].Amount
                }
            }
        }
        for (var i = 0; i < positions.length; i++) {
            if (positions[i].ContractType != contractType || (positions[i].Type !== direction && typeof(direction) !== 'undefined')) {
                continue;
            }
            var amount = 0;
            if (typeof(OP_amount) === 'undefined') {
                amount = positions[i].Amount;
            } else {
                amount = OP_amount - (/*initP[i].Amount*/ initP_contractTypeAmount - positions[i].Amount);
            }

            if (amount <= 0) {     //  修改为  小于 等于  0 
                continue;
            }
            if (positions[i].Type == PD_LONG) {
                e.SetDirection("closebuy");
                ID = e.Sell(price - F_SlidePrice * (1 + step), amount, "平多仓", contractType, price);
                n++;
            } else if (positions[i].Type == PD_SHORT) {
                e.SetDirection("closesell");
                ID = e.Buy(price + F_SlidePrice * (1 + step), amount, "平空仓", contractType, price);
                n++;
            }
            index = i;
        }
        if (n === 0) {
            break;
        }
        Sleep(Interval);
        if (typeof(ID) !== 'number') {
            CancelPendingOrders(e);  // 测试
            Log("ID:", ID);
            continue;
        }

        //_C(e.CancelOrder, ID);  // 测试修改 e.CancelOrder(ID);
        CancelPendingOrders(e);  // 测试
        step += lv;
        if (step > max_cover_lv) {
            break;
        }
    }

    var nowP = _C(e.GetPosition);
    if (!nowP[index] || nowP[index].Type !== initP[index].Type) {
        return initP.length === 0 ? 0 : initP[index].Amount;
    } else {
        var diff = initP[index].Amount - nowP[index].Amount;
        return diff;
    }
}

var PositionManager = (function() {
    function PositionManager(e) {
        if (typeof(e) === 'undefined') {
            e = exchange;
        }
        if (e.GetName() !== 'Futures_OKCoin' && e.GetName() !== 'Futures_BitVC') {
            throw 'Only support Futures_OKCoin & Futures_BitVC';
        }
        this.e = e;
        this.account = null;
    }
    PositionManager.prototype.GetAccount = function() {
        return _C(this.e.GetAccount);
    };

    PositionManager.prototype.OpenLong = function(contractType, shares, price) {
        if (!this.account) {
            this.account = _C(exchange.GetAccount);
        }
        return Open(this.e, contractType, PD_LONG, shares, price);
    };

    PositionManager.prototype.OpenShort = function(contractType, shares, price) {
        if (!this.account) {
            this.account = _C(exchange.GetAccount);
        }
        return Open(this.e, contractType, PD_SHORT, shares, price);
    };

    PositionManager.prototype.Cover = function(contractType, price, OP_amount, direction) {
        if (!this.account) {
            this.account = _C(exchange.GetAccount);
        }
        return Cover(this.e, contractType, price, OP_amount, direction);
    };

    PositionManager.prototype.Profit = function(contractType) {
        var accountNow = _C(this.e.GetAccount);
        Log("NOW:", accountNow, "--account:", this.account);
        return _N(accountNow.Balance - this.account.Balance);
    };

    return PositionManager;
})();

$.NewPositionManager = function(e) {
    return new PositionManager(e);
};


// 测试代码
function main() {
    if (exchange.GetName() === 'Futures_OKCoin') {
        var info = exchange.SetContractType("this_week");
        Log("info 返回值:", info);
        Log("当前持仓信息", exchange.GetPosition(), _C(exchange.GetTicker));
        var depth = exchange.GetDepth();
        var p = $.NewPositionManager();
        p.OpenShort("this_week", 10, depth.Bids[0].Price - 2);
        Log(exchange.GetPosition());
        Sleep(500 * 1000);
        depth = exchange.GetDepth();
        var ret = p.Cover("this_week", depth.Bids[0].Price + 2, 5);
        Log("cover ret:", ret);
        //LogProfit(p.Profit());
        Log(exchange.GetPosition());
        Log("-----------------------------测试分割线----------------------------------------");
        var depth = exchange.GetDepth();
        p.OpenLong("this_week", 20, depth.Bids[0].Price + 2);
        Log(exchange.GetPosition());
        Sleep(500 * 1000);
        depth = exchange.GetDepth();
        var ret = p.Cover("this_week", depth.Bids[0].Price - 2, 10, PD_LONG);
        Log("cover ret:", ret);
        Log(exchange.GetPosition());
        Log("-----------------------------测试分割线----------------------------------------");
        var ret = p.Cover("this_week", depth.Bids[0].Price - 3, 10, PD_LONG);
        Log("cover ret:", ret);
        var ret = p.Cover("this_week", depth.Bids[0].Price + 3, 5, PD_SHORT);
        Log("cover ret:", ret);
        Log(exchange.GetPosition());
    } else if (exchange.GetName() === 'Futures_BitVC') {
        var info = exchange.SetContractType("week");
        Log("info 返回值:", info);
        Log("当前持仓信息", exchange.GetPosition(), _C(exchange.GetTicker));
        var depth = exchange.GetDepth();
        var p = $.NewPositionManager();
        p.OpenLong("week", 500, depth.Bids[0].Price + 2);
        Log(exchange.GetPosition());
        Sleep(500 * 1000);
        depth = exchange.GetDepth();
        var ret = p.Cover("week", depth.Bids[0].Price - 2, 500);
        Log("cover ret:", ret);
        Log(exchange.GetPosition());
        Log("-----------------------------测试分割线----------------------------------------");
        var info = exchange.SetContractType("week");
        Log("info 返回值:", info);
        Log("当前持仓信息", exchange.GetPosition(), _C(exchange.GetTicker));
        var depth = exchange.GetDepth();
        p.OpenShort("week", 600, depth.Bids[0].Price - 2);
        Log(exchange.GetPosition());
        Sleep(500 * 1000);
        depth = exchange.GetDepth();
        var ret = p.Cover("week", depth.Bids[0].Price + 2, 500, PD_SHORT);
        Log("cover ret:", ret);
        Log(exchange.GetPosition());
        Log("-----------------------------测试分割线----------------------------------------");
        var ret = p.Cover("week", depth.Bids[0].Price + 3, 100, PD_SHORT);
        Log("cover ret:", ret);
        //p.Cover("week", depth.Asks[0].Price - 3, 300, PD_LONG);
        Log(exchange.GetPosition());
    } else if(exchange.GetName() === 'huobi' || exchange.GetName() === 'OKCoin'){
        Log($.GetAccount());
        Log($.Buy(0.5));
        Log($.Sell(0.5));
        exchange.Buy(1000, 3);
        $.CancelPendingOrders(exchanges[0]);
        Log($.Cross(30, 7));
        Log($.Cross([1,2,3,2.8,3.5], [3,1.9,2,5,0.6]));
    }
}

策略出处

https://www.fmz.com/strategy/36762

更新时间

2017-10-22 23:14:09