Type/to search
8
Follow
1364
Followers
Débutants dans le trading quantitatif dans les cercles de crypto-monnaie, veuillez jeter un œil à ceci - Vous rapprocher du trading quantitatif dans les cercles de crypto-monnaie (V)
Discussions
Created 2021-05-28 09:50:12  Updated 2023-09-21 21:06:08
 14
 2764

img

Débutants dans le trading quantitatif dans les cercles de crypto-monnaie, veuillez jeter un œil à ceci - Vous rapprocher du trading quantitatif dans les cercles de crypto-monnaie (V)

Dans l'article précédent, nous avons expliqué l'analyse de la logique de trading d'une stratégie de grille simple. Dans cet article, nous continuerons à compléter la conception de cette stratégie d'enseignement.

  • Analyse de la logique des transactions
    Dans l'article précédent, nous avons mentionné que tant que nous parcourons chaque ligne de la grille et déterminons si le prix actuel passe au-dessus ou en dessous de la ligne de la grille, l'action de transaction peut être déclenchée. Mais en fait, il y a encore beaucoup de détails logiques. Les débutants qui ne comprennent pas l'écriture de stratégie ont souvent la fausse perception que « la logique est très simple et que le code ne devrait contenir que quelques lignes, mais dans l'écriture réelle, il y a encore " beaucoup de détails. "

    Le premier détail que nous devons prendre en compte est la conception de la grille infinie. Rappelez-vous que dans le dernier article, nous avons conçu une fonction pour générer la structure de données de la grille initialecreateNetQuoi? Cette fonction génère une structure de données de grille avec un nombre fini de lignes de grille. Et alors si le prix dépasse les limites de cette structure de données de grille (au-delà de la ligne supérieure de la grille avec le prix le plus élevé et de la ligne inférieure de la grille avec le prix le plus bas) lorsque la stratégie est en cours d'exécution ?
    Nous devons donc d’abord ajouter un mécanisme d’extension à la structure de données de la grille.

    Commencez à écrire la fonction principale de la stratégie, qui est le code qui démarre l’exécution de la stratégie.

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

    Le code qui rend la structure de données de la grille extensible est le suivant (extrait du code ci-dessus) :

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

    L’étape suivante consiste à réfléchir à la manière de mettre en œuvre le déclenchement des transactions en détail.

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

    Vous pouvez voir:

    • Condition de franchissement de la ligne de grille :preTicker.Last < p.price && ticker.Last > p.price
    • Condition de franchissement de la ligne de grille :preTicker.Last > p.price && ticker.Last < p.price

    C'est ce dont nous avons parlé dans l'article précédent :

    img

    Le croisement vers le haut ou vers le bas n'est que la première étape pour déterminer si une transaction peut être placée, ce qui nécessite également de déterminer les marques dans les données de la ligne de grille.

    S'il s'agit d'un croisement à la hausse, le prix est jugé inférieur à la ligne de grille actuelle et à la marque d'achat sur la ligne de grille la plus proche. Si la valeur de la marque d'achat est vraie, cela signifie que la ligne de grille précédente a été achetée, et la ligne de grille précédente est réinitialisée. La marque d'achat de la racine est fausse et la marque de vente de la ligne de grille actuelle est réinitialisée à faux.

    Après avoir jugé la condition précédente, si elle n'est pas déclenchée, continuez à juger. Si les marques d'achat/vente sur la ligne de grille actuelle sont toutes les deux fausses, cela signifie que la ligne de grille actuelle peut être négociée. Comme il s'agit d'un croisement à la hausse, nous Exécutez l'opération de vente ici. Après l'exécution, marquez l'indicateur de vente de la ligne de grille actuelle comme vrai.

    La logique de gestion des passages souterrains est la même (c'est aux débutants d'y réfléchir).

Backtesting complet de la stratégie

Afin de voir certaines données de backtesting, une fonction est écriteshowTblAfficher les données.

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

Code de stratégie complet :

/*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 stratégie :

img

img

img

On peut voir que les caractéristiques de la stratégie de grille sont qu’il y aura d’importantes pertes flottantes lorsqu’il y a un marché en tendance, et les rendements ne rebondiront que dans un marché volatil.
La stratégie de grille n'est donc pas sans risque. La stratégie spot peut toujours être maintenue, mais la stratégie de grille des contrats à terme est plus risquée et nécessite des réglages conservateurs pour les paramètres de grille.

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)