Type/to search
8
Follow
1364
Followers
Design de estratégia de Martingale para futuros de criptomoedas
Discussions
Created 2021-07-02 14:35:34  Updated 2023-09-21 21:09:56
 22
 6556

img

Design de estratégia de Martingale para futuros de criptomoedas

Ultimamente, tem havido muitas discussões sobre estratégias de Martingale no grupo oficial da FMZ, mas não há muitas estratégias de Martingale para contratos de moeda digital na plataforma. Por isso, aproveitei a oportunidade para criar uma estratégia simples de Martingale para futuros de criptomoedas. Por que é chamada de estratégia Martingale? Porque os riscos potenciais da estratégia Martingale não são realmente pequenos, então ela não é projetada inteiramente de acordo com a estratégia Martingale. No entanto, esse tipo de estratégia ainda apresenta riscos consideráveis, e as configurações dos parâmetros da estratégia Martingale estão intimamente relacionadas aos riscos, portanto, os riscos não devem ser ignorados.

Este artigo explica e estuda principalmente o design de estratégias do tipo Martin. A ideia da estratégia em si é muito clara. Como usuários do FMZ, consideramos mais o design da estratégia.

Obtenha patrimônio total

Ao projetar estratégias de futuros de moeda digital, os dados de patrimônio líquido total são frequentemente usados. Porque é necessário calcular os retornos, especialmente quando é necessário calcular retornos flutuantes. Como as posições abertas ocupam margem, as ordens pendentes também ocupam margem. Neste momento, chame a interface API da plataforma FMZexchange.GetAccount()O que é obtido são os ativos disponíveis e os ativos congelados por ordens pendentes. De fato, a maioria das bolsas de futuros de moeda digital fornece dados sobre o patrimônio total, mas a FMZ não encapsula esse atributo de maneira uniforme.

Portanto, projetamos funções para obter esses dados de acordo com diferentes trocas:

// OKEX V5 获取总权益 function getTotalEquity_OKEX_V5() { var totalEquity = null var ret = exchange.IO("api", "GET", "/api/v5/account/balance", "ccy=USDT") if (ret) { try { totalEquity = parseFloat(ret.data[0].details[0].eq) } catch(e) { Log("获取账户总权益失败!") return null } } return totalEquity } // 币安期货 function getTotalEquity_Binance() { var totalEquity = null var ret = exchange.GetAccount() if (ret) { try { totalEquity = parseFloat(ret.Info.totalWalletBalance) } catch(e) { Log("获取账户总权益失败!") return null } } return totalEquity }

No códigototalEquityEsse é o patrimônio total que precisamos. Em seguida, escrevemos uma função como entrada de chamada e chamamos a função correspondente de acordo com o nome da troca.

function getTotalEquity() { var exName = exchange.GetName() if (exName == "Futures_OKCoin") { return getTotalEquity_OKEX_V5() } else if (exName == "Futures_Binance") { return getTotalEquity_Binance() } else { throw "不支持该交易所" } }

Projete algumas funções auxiliares

Antes de projetar a função principal e a lógica principal. Ainda precisamos fazer alguma preparação e projetar algumas funções auxiliares.

  • Cancelar todos os pedidos pendentes atuais

    function cancelAll() { while (1) { var orders = _C(exchange.GetOrders) if (orders.length == 0) { break } for (var i = 0 ; i < orders.length ; i++) { exchange.CancelOrder(orders[i].Id, orders[i]) Sleep(500) } Sleep(500) } }

    Acredito que aqueles que frequentemente olham para os códigos de exemplo de estratégia no FMZ Strategy Square estão muito familiarizados com essa função. Muitas estratégias usaram designs semelhantes. Sua função é obter a lista atual de pedidos pendentes e então cancelá-los um por um.

  • Operação de ordem de futuros

    function trade(distance, price, amount) { var tradeFunc = null if (distance == "buy") { tradeFunc = exchange.Buy } else if (distance == "sell") { tradeFunc = exchange.Sell } else if (distance == "closebuy") { tradeFunc = exchange.Sell } else { tradeFunc = exchange.Buy } exchange.SetDirection(distance) return tradeFunc(price, amount) } function openLong(price, amount) { return trade("buy", price, amount) } function openShort(price, amount) { return trade("sell", price, amount) } function coverLong(price, amount) { return trade("closebuy", price, amount) } function coverShort(price, amount) { return trade("closesell", price, amount) }

    Existem quatro direções na negociação de futuros: abrir uma posição longa (openLong), abrir uma posição curta (openShort), fechar uma posição longa (coverLong) e fechar uma posição curta (coverShort). Então, projetamos quatro funções de ordem para corresponder a essas operações. Se você estiver pensando apenas em fazer um pedido, há vários fatores necessários: direção, preço do pedido e quantidade do pedido.
    Então também criamos um programa chamado:tradeA função para manipular方向(distance)下单价格(price)下单量(amount)Todas as operações são claras.
    As chamadas de função de abertura de uma posição longa (openLong), abertura de uma posição curta (openShort), fechamento de uma posição longa (coverLong) e fechamento de uma posição curta (coverShort) são executadas portradeA função executa a função real, que é colocar uma ordem na bolsa de futuros de acordo com a direção, preço e quantidade estabelecidos.

Função principal

A estratégia é muito simples. Use o preço atual como base e coloque ordens de venda (curta) e ordens de compra (longa) a uma certa distância acima e abaixo do preço atual. Uma vez que um lado é executado, todas as ordens restantes serão canceladas e, em seguida, uma nova ordem de fechamento será colocada a uma certa distância com base no preço da posição, e uma ordem de aumento será colocada no preço atual atualizado, mas a ordem de aumento será não dobrar a quantidade do pedido.

  • Trabalho inicial
    Como precisamos fazer pedidos, precisamos de duas variáveis ​​globais para registrar os IDs dos pedidos.

    var buyOrderId = null var sellOrderId = null

    Então a opção de usar o disco de simulação OKEX_V5 é projetada nos parâmetros da interface de estratégia, então algum processamento precisa ser feito no código:

    var exName = exchange.GetName() // 切换OKEX V5模拟盘 if (isSimulate && exName == "Futures_OKCoin") { exchange.IO("simulate", true) }

    Os parâmetros da interface também incluem uma opção para redefinir todas as informações, então o código também deve ter o processamento correspondente:

    if (isReset) { _G(null) LogReset(1) LogProfitReset() LogVacuum() Log("重置所有数据", "#FF0000") }

    Executamos apenas contratos perpétuos, por isso está codificado aqui e definido apenas para contratos perpétuos.

    exchange.SetContractType("swap")

    Então também precisamos considerar a precisão dos preços dos pedidos e das quantidades dos pedidos. Se a precisão não for definida corretamente, a precisão será perdida durante o processo de cálculo da estratégia. Se os dados tiverem muitas casas decimais, é fácil que o pedido seja rejeitado pela interface de troca.

    exchange.SetPrecision(pricePrecision, amountPrecision) Log("设置精度", pricePrecision, amountPrecision)

    Função simples de recuperação de dados

    if (totalEq == -1 && !IsVirtual()) { var recoverTotalEq = _G("totalEq") if (!recoverTotalEq) { var currTotalEq = getTotalEquity() if (currTotalEq) { totalEq = currTotalEq _G("totalEq", currTotalEq) } else { throw "获取初始权益失败" } } else { totalEq = recoverTotalEq } }

    Se você quiser especificar o patrimônio total inicial da conta quando a estratégia for executada, você pode definir o parâmetrototalEqSe este parâmetro for definido como -1, a estratégia lerá os dados de patrimônio líquido total armazenados. Se não houver dados de patrimônio líquido total armazenados, o patrimônio líquido total lido atual será usado como o patrimônio líquido total inicial do progresso da estratégia em execução. Se o total o patrimônio líquido aumenta, significa que se você ganha dinheiro, mas seu patrimônio líquido total é menor, significa que você perdeu dinheiro. Se os dados de patrimônio líquido total forem lidos, continue a execução usando esses dados.

  • Lógica principal
    Depois que o trabalho inicial foi feito, finalmente chegamos à lógica principal da estratégia. Para a conveniência da explicação, escrevi a explicação diretamente nos comentários do código.

    while (1) { // 策略主要逻辑设计为一个死循环 var ticker = _C(exchange.GetTicker) // 首先读取当前行情信息,主要用到最新成交价 var pos = _C(exchange.GetPosition) // 读取当前持仓数据 if (pos.length > 1) { // 判断持仓数据,由于这个策略的逻辑,是不太可能同时出现多空持仓的,所以发现同时出现多空持仓就抛出错误 Log(pos) throw "同时有多空持仓" // 抛出错误,让策略停止 } // 根据状态而定 if (pos.length == 0) { // 根据持仓状态做出不同操作,pos.length == 0是当没有持仓时 // 未持仓了,统计一次收益 if (!IsVirtual()) { var currTotalEq = getTotalEquity() if (currTotalEq) { LogProfit(currTotalEq - totalEq, "当前总权益:", currTotalEq) } } buyOrderId = openLong(ticker.Last - targetProfit, amount) // 挂开多仓的买单 sellOrderId = openShort(ticker.Last + targetProfit, amount) // 挂开空仓的卖单 } else if (pos[0].Type == PD_LONG) { // 有多头持仓,挂单位置、数量有所不同 var n = 1 var price = ticker.Last buyOrderId = openLong(price - targetProfit * n, amount) sellOrderId = coverLong(pos[0].Price + targetProfit, pos[0].Amount) } else if (pos[0].Type == PD_SHORT) { // 有空头持仓,挂单位置、数量有所不同 var n = 1 var price = ticker.Last buyOrderId = coverShort(pos[0].Price - targetProfit, pos[0].Amount) sellOrderId = openShort(price + targetProfit * n, amount) } if (!sellOrderId || !buyOrderId) { // 如果有一边挂单失败就取消所有挂单,重来 cancelAll() buyOrderId = null sellOrderId = null continue } while (1) { // 挂单完成,开始监控订单 var isFindBuyId = false var isFindSellId = false var orders = _C(exchange.GetOrders) for (var i = 0 ; i < orders.length ; i++) { if (buyOrderId == orders[i].Id) { isFindBuyId = true } if (sellOrderId == orders[i].Id) { isFindSellId = true } } if (!isFindSellId && !isFindBuyId) { // 检测到买卖单都成交了 cancelAll() break } else if (!isFindBuyId) { // 检测到买单成交 Log("买单成交") cancelAll() break } else if (!isFindSellId) { // 检测到卖单成交 Log("卖单成交") cancelAll() break } LogStatus(_D()) Sleep(3000) } Sleep(500) }

Toda a lógica e design foram explicados.

Testes retrospectivos

Deixe a estratégia experimentar a situação do mercado em 19 de maio.

img

img

Pode-se observar que a estratégia Martingale ainda apresenta certos riscos.

Você pode experimentar a demonstração do OKEX V5 para negociações reais.

img

Endereço estratégico: https://www.fmz.com/strategy/294957

As estratégias são usadas principalmente para aprendizado, então use dinheiro real com cautela ~!

Related Recommendations
Comment
All comments (20)

    梦大,想要請問這句
    if (!isFindSellId && !isFindBuyId) { // 检测到买卖单都成交了
    偵測訂單時,若快速上下插針同時成交了買賣單,那是否會拋出錯誤?

    4 years ago

    不会抛出错误。依然会取消所有挂单,跳出当前循环,然后继续挂单逻辑。瞬间都成交了就相当于吃到价差利润了。

    4 years ago

    另外就是合约的模式是全仓还是逐仓怎么设定?目前是啥模式呢

    5 years ago

    一般要用全仓吧。

    5 years ago

    杠杆可以在交易所具体设置,根据自身风险偏好。

    5 years ago

    既然开的是合约,为啥没有合约倍数的设定呢?买卖都是多少倍的呢

    5 years ago

    谢谢梦大,我终于能从头到尾看懂了,
    然后学会了挂单监控了,然后写了一个双边的马丁。两天时间,写了580行。。。。。
    谢谢梦大[抱拳]

    5 years ago

    云总 666!

    5 years ago

    img
    false true 交换下?

    5 years ago

    那这个变量名叫 isFindBuyId 就不合适了吧。 应该叫isNotFindBuyId了。

    5 years ago

    如果

    5 years ago

    所有者权益合计

    5 years ago

    需要止损吗

    5 years ago

    这策略没设计止损。。所以跑出来的曲线可以看到是一路往上的。

    5 years ago

    马丁,回测下了,归0

    5 years ago

    哈哈,马丁的归宿。
    本篇主要是教学策略设计,不用太在意收益。

    5 years ago

    开单数量那里没看懂呢= =,那个n永远都等于1

    5 years ago

    那个N是为了做之后的改动用的,比如想n倍距离加仓,暂时可以定1。

    5 years ago

    策略是多空双开吗?还是单开的

    5 years ago

    单开的。

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