Type/to search
8
Follow
1364
Followers
Novatos em negociação quantitativa nos círculos de criptomoedas, dêem uma olhada nisso - Levando você mais perto da negociação quantitativa nos círculos de criptomoedas (V)
Discussions
Created 2021-05-28 09:50:12  Updated 2023-09-21 21:06:08
 14
 2764

img

Novatos em negociação quantitativa nos círculos de criptomoedas, dêem uma olhada nisso - Levando você mais perto da negociação quantitativa nos círculos de criptomoedas (V)

No artigo anterior, explicamos a análise da lógica de negociação de uma estratégia de grade simples. Neste artigo, continuaremos a concluir o design desta estratégia de ensino.

  • Análise lógica de transação
    No artigo anterior, mencionamos que, desde que percorramos cada linha da grade e determinemos se o preço atual cruza acima ou abaixo da linha da grade, a ação da transação pode ser acionada. Mas, na verdade, ainda há muitos detalhes lógicos. Novatos que não entendem a escrita de estratégia geralmente formam uma percepção errada de que "a lógica é muito simples e o código deve ter apenas algumas linhas, mas na escrita real, ainda há muitos detalhes."

    O primeiro detalhe que precisamos considerar é o design da grade infinita. Lembre-se de que no último artigo projetamos uma função para gerar a estrutura de dados da grade inicialcreateNetO que? Esta função gera uma estrutura de dados de grade com um número finito de linhas de grade. E daí se o preço exceder os limites dessa estrutura de dados da grade (além da linha superior da grade com o preço mais alto e da linha inferior da grade com o preço mais baixo) quando a estratégia estiver em execução?
    Então, primeiro precisamos adicionar um mecanismo de extensão à estrutura de dados da grade.

    Comece a escrever a função principal da estratégia, que é o código que inicia a execução da estratégia.

    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, }) } // 还有其它代码... } }

    O código que torna a estrutura de dados da grade extensível é este (extraído do código acima):

    // 检查网格范围 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, }) }

    O próximo passo é considerar como implementar o acionamento de transações em detalhes.

    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) } }

    Você pode ver:

    • Condição de cruzamento da linha de grade:preTicker.Last < p.price && ticker.Last > p.price
    • Condição de cruzamento da linha de grade:preTicker.Last > p.price && ticker.Last < p.price

    Foi sobre isso que falamos no artigo anterior:

    img

    Cruzar para cima ou para baixo é apenas o primeiro passo para determinar se uma negociação pode ser feita, o que também requer a determinação das marcas nos dados da linha da grade.

    Se for um cruzamento ascendente, o preço é julgado como sendo menor do que a linha de grade atual e a marca de compra na linha de grade mais próxima. Se o valor da marca de compra for verdadeiro, significa que a linha de grade anterior foi comprada, e a linha de grade anterior é redefinida. A marca de compra da raiz é falsa, e a marca de venda da linha de grade atual é redefinida como falsa.

    Após julgar a condição anterior, se ela não for acionada, continue a julgar. Se as marcas de compra/venda na linha de grade atual forem ambas falsas, significa que a linha de grade atual pode ser negociada. Como é um cruzamento ascendente, nós execute a operação sell aqui. Após a execução, marque o sinalizador sell da linha de grade atual como true.

    A lógica para lidar com passagens subterrâneas é a mesma (isso fica para os novatos pensarem).

Backtesting de estratégia completa

Para ver alguns dados de backtesting, uma função é escritashowTblExibir os dados.

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()) }

Código de estratégia completo:

/*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) } }

Backtesting de estratégia:

img

img

img

Pode-se observar que as características da estratégia de grade são que haverá grandes perdas flutuantes quando houver uma tendência de mercado, e os retornos só se recuperarão em um mercado volátil.
Portanto, a estratégia de grade não é isenta de risco. A estratégia spot ainda pode ser sustentada, mas a estratégia de grade de contrato futuro é mais arriscada e requer configurações conservadoras para os parâmetros de grade.

Related Recommendations
Comment
All comments (12)

    这是c++语言吧

    5 years ago

    策略是JavaScript语言。

    5 years ago

    为啥判断上穿下穿条件的时候,每根网格线都要判断啊,这里面感觉有个逻辑漏洞啊,不应该是上穿卖出的时候只要遍历高于目前价格的网格线吗? 还有exchange.Sell(-1, amount, ticker)这个函数怎么和api文档里的不一样啊,我看api文档里写的是exchange.Sell(Price, Amount),为啥你有三个参数啊,搞不懂啊,好复杂啊,我人都晕了~

    5 years ago

    FMZ的API函数中可以产生日志输出的函数例如:Log(...)、exchange.Buy(Price, Amount)、exchange.CancelOrder(Id)等都可以在必要参数后跟一些附带输出参数。https://www.fmz.com/api#exchange.cancelorderid

    5 years ago

    大佬,再问一个问题,这个啥意思啊 。注意:需要交易所的下单接口支持市价单(下单类型为买单时,下单量参数为计价币为单位的金额)。数字货币期货市价单方式下单,下单量参数的单位为合约张数。 我看你这篇帖子回测的是okex的eth的usdt永续合约,这不算期货市价单的下单方式吗?为啥买单的下单参数不是合约张数呢?

    5 years ago

    期货都是合约张数, 现货市价单买单才是金额。现货卖单都是币数。

    5 years ago

    文中的永续合约不算期货吗?

    5 years ago

    哦,我明白了

    5 years ago

    好难啊

    5 years ago

    上穿和下跌时,exchange.Buy(-1, amount * ticker.Last, ticker),amount*ticker.Last是啥意思,为啥sell没有呢?

    5 years ago

    https://www.fmz.com/strategy/291160
    last_tick = []
    line = []
    grid_buy_list = []

    def net(now_price):
    global line
    print(now_price)
    line = [now_price*(1+0.003*i) for i in range(-1000,1000)]
    Log(line)

    def ontick():
    global last_tick
    global line
    global grid_buy_list
    account = exchange.GetAccount()
    ticker = exchange.GetTicker()
    last_tick.append(ticker['Last'])
    if len(last_tick) == 1:return
    elif len(last_tick) == 100:del last_tick[0]
    for i in range(len(line)):
    if last_tick[-1] > line[i] and last_tick[-2] < line[i] and len(grid_buy_list)!= 0 and i > min(grid_buy_list) and account['Stocks'] >= 0.001:
    exchange.Sell(last_tick[-1],0.01)
    del grid_buy_list[grid_buy_list.index(min(grid_buy_list))]
    Log(exchange.GetAccount())
    elif last_tick[-1] < line[i] and last_tick[-2] > line[i] and i not in grid_buy_list:
    exchange.Buy(last_tick[-1],0.01)
    grid_buy_list.append(i)
    Log(exchange.GetAccount())

    def main():
    net(exchange.GetTicker()['Last'])
    Log(exchange.GetAccount())
    while(True):
    ontick()
    Sleep(1000)

    5 years ago

    感谢梦神,讲的好详细,去重买入都解释了,仿着写了个py版

    5 years ago
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)