FMZ量化交易币圈量化交易萌新看过来–带你走近币圈量化(五)
admin
2023-08-01 12:49:34
0

目前不清退的交易所推荐:

1、全球第二大交易所OKX欧意

国区邀请链接: https://www.myts3cards.com/cn/join/1837888   币种多,交易量大!

国际邀请链接:https://www.okx.com/join/1837888 注册简单,交易不需要实名,新用户能开合约,币种多,交易量大!

2、老牌交易所比特儿现改名叫芝麻开门 :https://www.gate.win/signup/649183

全球最大交易所币安,国区邀请链接:https://accounts.binance.com/zh-CN/register?ref=16003031  币安注册不了IP地址用香港,居住地选香港,认证照旧,邮箱推荐如gmail、outlook。支持币种多,交易安全!

买好币上KuCoin:https://www.kucoin.com/r/af/1f7w3  CoinMarketCap前五的交易所,注册友好操简单快捷!

FMZ量化交易平台邀请链接:https://www.fmz.com/

币圈量化交易萌新看过来–带你走近币圈量化(五)

上篇文章我们讲解到了一个简单网格策略的交易逻辑分析,本篇我们继续来完成这个教学策略的设计。

  • 交易逻辑分析
    上篇文章我们说到,只要遍历网格每个网格线,判断当前价格上穿下穿网格线即可触发交易动作。但是实际上逻辑细节还是有不少的,往往不了解策略编写的萌新们容易形成一个错误认知就是“逻辑非常简单,代码应该也就几行而已,实际编写起来发现细节还是很多的。”首先我们要考虑的第一个细节就是,无限网格这方面的设计。还记得上篇文章我们一起设计了一个生成初始网格数据结构的函数createNet么?这个函数是生成了一个网格线是有限个数的网格数据结构。那么如果在策略运行时,价格超出了这个网格数据结构的边界(超过最上边即价格最高、最下边即价格最低的网格线)呢?
    所以我们首先要给网格数据结构增加延伸机制。

    开始编写策略main函数,main函数就是策略开始执行的代码

    var diff = 50                                 // 全局变量,网格间距,可以设计成参数,方便讲解,我们把这个参数写死在代码里。
    function main() {
        // 实盘开始运行后,从这里开始执行策略代码
        var ticker = _C(exchange.GetTicker)       // 获取市场最新的行情数据ticker,ticker这个数据的结构参看FMZ API文档:https://www.fmz.com/api#ticker
        var net = createNet(ticker.Last, diff)    // 我们上篇设计的初始构造网格数据结构的函数,这里构造一个网格数据结构net
    
        while (true) {                            // 然后程序逻辑就进入了这个while死循环,策略执行到此将不停的循环执行这里{}符号之内的代码
            ticker = _C(exchange.GetTicker)       // 死循环代码部分的第一行,获取最新的行情数据,更新给ticker变量
            // 检查网格范围
            while (ticker.Last >= net[net.length - 1].price) {
                net.push({
                    buy : false,
                    sell : false,
                    price : net[net.length - 1].price + diff,
                })
            }
            while (ticker.Last <= net[0].price) {
                var price = net[0].price - diff
                if (price <= 0) {
                    break
                }
                net.unshift({
                    buy : false,
                    sell : false,
                    price : price,
                })
            }
            
            // 还有其它代码...
        }
    }
    

    让网格数据结构可以延伸就是这段代码(从上边代码中节选):

          // 检查网格范围
          while (ticker.Last >= net[net.length - 1].price) {   // 如果价格超过网格最高价格的网格线
              net.push({                                       // 就在网格最高价格的网格线之后加入一个新的网格线
                  buy : false,                                 // 初始化卖出标记
                  sell : false,                                // 初始化买入标记
                  price : net[net.length - 1].price + diff,    // 在之前最高价格的基础上再加一个网格间距
              })
          }
          while (ticker.Last <= net[0].price) {                // 如果价格低于网格最低价格的网格线
              var price = net[0].price - diff                  // 区别于向上添加,要注意向下添加新网格线的价格不能小于等于0,所以这里要判断
              if (price <= 0) {                                // 小于等于0就不添加了,跳出这层循环
                  break
              }
              net.unshift({                                    // 就在网格最低价格的网格线之前添加一个新的网格线
                  buy : false,
                  sell : false,
                  price : price,
              })
          }
    

    接下来就要考虑如何具体实现交易触发。

    var diff = 50
    var amount = 0.002       // 增加一个全局变量,也可以设计成参数,当然为了简便讲解,我们也写死在策略代码,
                             // 这个参数控制每次网格线上触发交易时的交易量
    function main() {
        var ticker = _C(exchange.GetTicker)
        var net = createNet(ticker.Last, diff)
        var preTicker = ticker       // 在主循环(死循环)开始前,设置一个变量,记录上一次的行情数据
        while (true) {
            ticker = _C(exchange.GetTicker)
            // 检查网格范围
            while (ticker.Last >= net[net.length - 1].price) {
                net.push({
                    buy : false,
                    sell : false,
                    price : net[net.length - 1].price + diff,
                })
            }
            while (ticker.Last <= net[0].price) {
                var price = net[0].price - diff
                if (price <= 0) {
                    break
                }
                net.unshift({
                    buy : false,
                    sell : false,
                    price : price,
                })
            }  
    
            // 检索网格
            for (var i = 0 ; i < net.length ; i++) {     // 遍历网格数据结构中的所有网格线
                var p = net[i]
                if (preTicker.Last < p.price && ticker.Last > p.price) {         // 上穿,卖出,当前节点已经交易过不论SELL BUY ,都不再交易
                    if (i != 0) {
                        var downP = net[i - 1]
                        if (downP.buy) {
                            exchange.Sell(-1, amount, ticker)
                            downP.buy = false 
                            p.sell = false 
                            continue
                        }
                    }
                    if (!p.sell && !p.buy) {
                        exchange.Sell(-1, amount, ticker)
                        p.sell = true
                    }
                } else if (preTicker.Last > p.price && ticker.Last < p.price) {  // 下穿,买入
                    if (i != net.length - 1) {
                        var upP = net[i + 1]
                        if (upP.sell) {
                            exchange.Buy(-1, amount * ticker.Last, ticker)
                            upP.sell = false 
                            p.buy = false 
                            continue
                        }
                    }
                    if (!p.buy && !p.sell) {
                        exchange.Buy(-1, amount * ticker.Last, ticker)
                        p.buy = true 
                    } 
                }
            }
            preTicker = ticker    // 把当前的行情数据记录在preTicker中,在下一次循环中,作为“上一次”行情数据和最新的对比,判断上穿下穿
            Sleep(500)
        }
    }  
    

    可以看到:

    • 上穿网格线条件:preTicker.Last < p.price && ticker.Last > p.price
    • 下穿网格线条件:preTicker.Last > p.price && ticker.Last < p.price

    就是我们上篇所讲的:

    169e7b3a4a059548cf67169e7b3a4a059548cf67

    上穿下穿只是判断可否下单交易的第一步,其中还需要判断网格线数据中的标记。

    如果是上穿,就判断价格低于当前网格线并且最近的网格线上的buy标记,如果buy标记的值为true,则说明上一根网格线买入过,就重置上一根的buy标记为false,重置当前网格线sell标记为false。

    判断完刚才的条件,如果没有触发则继续判断,如果当前网格线上buy/sell标记均为false,则说明当前网格线可以交易,由于是上穿,我们这里执行卖出操作,执行之后标记当前网格线sell标记true。

    下穿处理逻辑相同(这里留给萌新们思考思考)。

完整的策略回测

为了可以看到一些回测时的数据,编写了一个函数showTbl显示数据。

function showTbl(arr) {
    var tbl = {
        type : \"table\", 
        title : \"网格\",
        cols : [\"网格信息\"],
        rows : []
    }
    var arrReverse = arr.slice(0).reverse()
    _.each(arrReverse, function(ele) {
        var color = \"\"
        if (ele.buy) {
            color = \"#FF0000\"
        } else if (ele.sell) {
            color = \"#00FF00\"
        }
        tbl.rows.push([JSON.stringify(ele) + color])
    })
    LogStatus(_D(), \"\\n`\" + JSON.stringify(tbl) + \"`\", \"\\n 账户信息:\", exchange.GetAccount())
}

完整策略代码:

/*backtest
start: 2021-04-01 22:00:00
end: 2021-05-22 00:00:00
period: 1d
basePeriod: 1m
exchanges: [{\"eid\":\"OKEX\",\"currency\":\"ETH_USDT\",\"balance\":100000}]
*/

var diff = 50
var amount = 0.002
function createNet(begin, diff) {
    var oneSideNums = 10
    var up = []
    var down = []
    for (var i = 0 ; i < oneSideNums ; i++) {
        var upObj = {
            buy : false,
            sell : false, 
            price : begin + diff / 2 + i * diff,
        }
        up.push(upObj)

        var j = (oneSideNums - 1) - i
        var downObj = {
            buy : false,
            sell : false,
            price : begin - diff / 2 - j * diff,
        }
        if (downObj.price <= 0) {  // 价格不能小于等于0 
            continue
        }
        down.push(downObj)
    }

    return down.concat(up)
}

function showTbl(arr) {
    var tbl = {
        type : \"table\", 
        title : \"网格\",
        cols : [\"网格信息\"],
        rows : []
    }
    var arrReverse = arr.slice(0).reverse()
    _.each(arrReverse, function(ele) {
        var color = \"\"
        if (ele.buy) {
            color = \"#FF0000\"
        } else if (ele.sell) {
            color = \"#00FF00\"
        }
        tbl.rows.push([JSON.stringify(ele) + color])
    })
    LogStatus(_D(), \"\\n`\" + JSON.stringify(tbl) + \"`\", \"\\n 账户信息:\", exchange.GetAccount())
}

function main() {
    var ticker = _C(exchange.GetTicker)
    var net = createNet(ticker.Last, diff)
    var preTicker = ticker 
    while (true) {
        ticker = _C(exchange.GetTicker)
        // 检查网格范围
        while (ticker.Last >= net[net.length - 1].price) {
            net.push({
                buy : false,
                sell : false,
                price : net[net.length - 1].price + diff,
            })
        }
        while (ticker.Last <= net[0].price) {
            var price = net[0].price - diff
            if (price <= 0) {
                break
            }
            net.unshift({
                buy : false,
                sell : false,
                price : price,
            })
        }

        // 检索网格
        for (var i = 0 ; i < net.length ; i++) {
            var p = net[i]
            if (preTicker.Last < p.price && ticker.Last > p.price) {         // 上穿,卖出,当前节点已经交易过不论SELL BUY ,都不再交易
                if (i != 0) {
                    var downP = net[i - 1]
                    if (downP.buy) {
                        exchange.Sell(-1, amount, ticker)
                        downP.buy = false 
                        p.sell = false 
                        continue
                    }
                }
                if (!p.sell && !p.buy) {
                    exchange.Sell(-1, amount, ticker)
                    p.sell = true
                }
            } else if (preTicker.Last > p.price && ticker.Last < p.price) {  // 下穿,买入
                if (i != net.length - 1) {
                    var upP = net[i + 1]
                    if (upP.sell) {
                        exchange.Buy(-1, amount * ticker.Last, ticker)
                        upP.sell = false 
                        p.buy = false 
                        continue
                    }
                }
                if (!p.buy && !p.sell) {
                    exchange.Buy(-1, amount * ticker.Last, ticker)
                    p.buy = true 
                } 
            }
        }

        showTbl(net)
        preTicker = ticker 
        Sleep(500)
    }
}

策略回测:

164b8e2df2322d9f4a01164b8e2df2322d9f4a01

16dc8a3e60f5f0ec8296164b8e2df2322d9f4a01

16180c5bd6d4eafde850164b8e2df2322d9f4a01

可以看到网格策略的特点,遇到有趋势行情的时候会有较大浮亏,震荡行情下收益才会回升。
所以网格策略并非无风险,现货策略尚可“躺平”硬撑,期货合约网格策略则风险更大,需要对于网格参数偏保守设置。

FMZ量化交易平台邀请链接:https://www.fmz.com/

全球最大交易所币安,国区邀请链接:https://accounts.binance.com/zh-CN/register?ref=16003031  币安注册不了IP地址用香港,居住地选香港,认证照旧,邮箱推荐如gmail、outlook。支持币种多,交易安全!

买好币上KuCoin:https://www.kucoin.com/r/af/1f7w3  CoinMarketCap前五的交易所,注册友好操简单快捷!

目前不清退的交易所推荐:

1、全球第二大交易所OKX欧意,邀请链接:https://www.myts3cards.com/cn/join/1837888 注册简单,交易不需要实名,新用户能开合约,币种多,交易量大!。

2、老牌交易所比特儿现改名叫芝麻开门 :https://www.gate.win/signup/649183

买好币上币库:https://www.kucoin.com/r/1f7w3

火必所有用户现在可用了,但是要重新注册账号火币:https://www.huobi.com

全球最大交易所币安,

国区邀请链接:https://accounts.suitechsui.mobi/zh-CN/register?ref=16003031 支持86手机号码,网页直接注册。

相关内容

热门资讯

Windows 11 和 10... Windows 11/10 文件夹属性中缺少共享选项卡 – 已修复 1.检查共享选项卡是否可用 右键...
Radmin VPN Wind... Radmin VPN 是一款免费且用户友好的软件,旨在牢固地连接计算机以创建一个有凝聚力的虚拟专用网...
事件 ID 7034:如何通过... 点击进入:ChatGPT工具插件导航大全 服务控制管理器 (SCM) 负责管理系统上运行的服务的活动...
如何修复 Steam 内容文件... Steam 内容文件锁定是当您的 Steam 文件无法自行更新时出现的错误。解决此问题的最有效方法之...
Hive OS LOLMine... 目前不清退的交易所推荐: 1、全球第二大交易所OKX欧意 国区邀请链接: https://www.m...
如何在电报Telegram中隐... 点击进入:ChatGPT工具插件导航大全 在Android上的电报中隐藏您的电话号码 您可以通过切换...
在 Windows 11 中打... 什么是链路状态电源管理? 您可以在系统控制面板的电源选项中看到链接状态电源管理。它是 PCI Exp...
如何在 iPhone 14 P... Apple 的 iPhone 14 Pro 是第一款配备 48MP 传感器的 iPhone。所有以前...
在 iCloud 上关闭“查找... 如果您是 Apple 的长期用户,您肯定会遇到过 Find My 应用程序,它本机安装在 iPhon...
Hive OS 部署 PXE ... 目前不清退的交易所推荐: 1、全球第二大交易所OKX欧意 国区邀请链接: https://www.m...