Recentemente, há muitas estratégias Martingale discutidas no grupo oficial da FMZ, e não há muitas estratégias Martingale de contratos de criptomoedas na plataforma. Portanto, aproveitei esta oportunidade para projetar uma estratégia Martingale simples para futuros de criptomoedas. Por que é chamada de estratégia Martingale? Porque os riscos potenciais da estratégia Martin não são pequenos, ela não é projetada exatamente de acordo com a estratégia Martin. No entanto, esse tipo de estratégia ainda tem muitos riscos, e as configurações de parâmetros da estratégia tipo Martin estão intimamente relacionadas ao risco, e o risco não deve ser ignorado.
Este artigo explica principalmente e aprende com o design de estratégias do tipo Martin.
O equidade total é frequentemente usado ao projetar estratégias de futuros de criptomoedas. Isso ocorre porque os retornos precisam ser calculados, especialmente quando você precisa calcular retornos flutuantes. Como a posição é ocupada com margem, a ordem pendente também é ocupada.exchange.GetAccount()
Na verdade, a maioria das exchanges de futuros de criptomoedas fornece os dados do patrimônio total, mas esse atributo não é uniformemente empacotado na FMZ.
Então nós projetamos funções para obter esses dados de acordo com diferentes trocas:
// OKEX V5 obtain total equity
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("failed to obtain the total equity of the account!")
return null
}
}
return totalEquity
}
// Binance futures
function getTotalEquity_Binance() {
var totalEquity = null
var ret = exchange.GetAccount()
if (ret) {
try {
totalEquity = parseFloat(ret.Info.totalWalletBalance)
} catch(e) {
Log("failed to obtain the total equity of the account!")
return null
}
}
return totalEquity
}
OtotalEquity
Então nós escrevemos uma função como a 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 "This exchange is not supported"
}
}
Antes de projetar a função principal e a lógica principal, precisamos fazer alguns preparativos e projetar algumas funções auxiliares.
Cancelar todas as ordens pendentes
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)
}
}
Esta função é familiar para aqueles que frequentemente lêem o código de exemplo de estratégia no quadrado de estratégia FMZ, e muitas estratégias usaram projetos semelhantes.
Operações de colocação 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 para a negociação de futuros: openLong, openShort, coverLong e coverShort. Então, projetamos quatro funções de ordem correspondentes a essas operações. Se você considerar apenas a ordem, então há vários fatores necessários: direção, preço da ordem e volume da ordem.
Então nós também projetamos uma função chamada:trade
para lidar com a operação quandodistance
, price
, amount
são especificados.
As chamadas de função para openLong, openShort, coverLong e coverShort são finalmente completadas pelotrade
função, isto é, colocar uma ordem numa bolsa de futuros com base na distância, preço e quantidade estabelecidos.
A ideia da estratégia é muito simples, tomar o preço atual como a linha de base, e colocar ordens de venda (curto) e compra (longo) em uma certa distância para cima ou para baixo.
Trabalhos iniciais Por causa da ordem pendente, precisamos de duas variáveis globais para registrar a ID da ordem.
var buyOrderId = null
var sellOrderId = null
Em seguida, os parâmetros da interface de estratégia são projetados para usar a opção de bot simulado OKEX_V5, então algum processamento precisa ser feito no código:
var exName = exchange.GetName()
// Switch OKEX V5 simulated bot
if (isSimulate && exName == "Futures_OKCoin") {
exchange.IO("simulate", true)
}
Há também uma opção para redefinir todas as informações nos parâmetros da interface, de modo que deve haver um processamento correspondente no código:
if (isReset) {
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("reset all data", "#FF0000")
}
Só temos contratos perpétuos, por isso a escrita está fixa aqui e definida para perpétuo.
exchange.SetContractType("swap")
Então, também precisamos considerar a precisão do preço da ordem e do valor da ordem. 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 um grande número de casas decimais, é fácil fazer com que a ordem seja rejeitada pela interface de troca.
exchange.SetPrecision(pricePrecision, amountPrecision)
Log("set precision", pricePrecision, amountPrecision)
Recuperação de dados simples por projeto
if (totalEq == -1 && !IsVirtual()) {
var recoverTotalEq = _G("totalEq")
if (!recoverTotalEq) {
var currTotalEq = getTotalEquity()
if (currTotalEq) {
totalEq = currTotalEq
_G("totalEq", currTotalEq)
} else {
throw "failed to obtain initial equity"
}
} else {
totalEq = recoverTotalEq
}
}
Se você quiser especificar o patrimônio líquido total inicial da conta quando a estratégia está sendo executada, você pode definir o parâmetrototalEq
Se este parâmetro for definido em -1, a estratégia lerá os dados de patrimônio total armazenados. Se não houver dados de patrimônio total armazenados, o patrimônio total lido atual é usado como o patrimônio total inicial da estratégia em andamento. Depois disso, um aumento no patrimônio total indica um lucro e uma diminuição no patrimônio total indica uma perda. Se os dados de patrimônio total forem lidos, a estratégia continuará a ser executada com esses dados.
lógica principal Após o trabalho inicial ser feito, finalmente chegamos à parte lógica principal da estratégia. Para facilitar a explicação, escrevi as instruções diretamente nos comentários do código.
while (1) { // The main logic of the strategy is designed as an infinite loop
var ticker = _C(exchange.GetTicker) // Read the current market information first, mainly using the latest transaction price
var pos = _C(exchange.GetPosition) // Read current position data
if (pos.length > 1) { // Judging the position data, because of the logic of this strategy, it is unlikely that long and short positions will appear at the same time, so if there are long and short positions at the same time, an error will be thrown
Log(pos)
throw "Simultaneous long and short positions" // Throw an error to stop the strategy
}
//Depends on status
if (pos.length == 0) { // Make different operations according to the position status, when there is no position, pos.length == 0
// If you have not held a position, count the profit once
if (!IsVirtual()) {
var currTotalEq = getTotalEquity()
if (currTotalEq) {
LogProfit(currTotalEq - totalEq, "current total equity:", currTotalEq)
}
}
buyOrderId = openLong(ticker.Last - targetProfit, amount) // Open a buy order for a long position
sellOrderId = openShort(ticker.Last + targetProfit, amount) // Open a short sell order
} else if (pos[0].Type == PD_LONG) { // For long positions, the position and quantity of pending orders are different
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) { // For short positions, the position and quantity of pending orders are different
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) { // If one side of the pending order fails, cancel all pending orders and start over
cancelAll()
buyOrderId = null
sellOrderId = null
continue
}
while (1) { // The pending order is completed, start monitoring the order
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) { // Detected that both buy and sell orders have been filled
cancelAll()
break
} else if (!isFindBuyId) { // Detected buy order closing
Log("buy order closing")
cancelAll()
break
} else if (!isFindSellId) { // Detected sell order closing
Log("sell order closing")
cancelAll()
break
}
LogStatus(_D())
Sleep(3000)
}
Sleep(500)
}
Toda a lógica e o desenho são explicados.
Deixe a estratégia passar por um mercado de 19 de Maio.
Pode-se ver que a estratégia Martingale ainda apresenta certos riscos.
O bot real pode ser executado com o bot de simulação OKEX V5
Endereço estratégico:https://www.fmz.com/strategy/294957
As estratégias são usadas principalmente para aprender, e o dinheiro real deve ser usado com cautela~!