
Récemment, il y a eu de nombreuses discussions sur les stratégies Martingale dans le groupe officiel FMZ, mais il n’y a pas beaucoup de stratégies Martingale pour les contrats de monnaie numérique sur la plateforme. J’ai donc profité de cette occasion pour concevoir une stratégie Martingale simple pour les contrats à terme sur crypto-monnaies. Pourquoi appelle-t-on cette stratégie une stratégie de type Martin ? Parce que les risques potentiels de la stratégie Martin ne sont pas minimes, elle n’est donc pas conçue entièrement selon la stratégie Martin. Cependant, ce type de stratégie comporte encore des risques considérables, et les paramètres de la stratégie Martingale sont étroitement liés aux risques, et les risques ne doivent pas être ignorés.
Cet article explique et apprend principalement de la conception des stratégies de type Martin. L’idée de stratégie elle-même est très claire. En tant qu’utilisateurs de FMZ, nous réfléchissons davantage à la conception de la stratégie.
Lors de la conception de stratégies de contrats à terme sur devises numériques, les données sur les capitaux propres totaux sont souvent utilisées. Parce qu’il est nécessaire de calculer les rendements, en particulier lorsque des rendements flottants doivent être calculés. Étant donné que les positions ouvertes occupent une marge, les ordres en attente occupent également une marge. À ce stade, appelez l’interface API de la plateforme FMZexchange.GetAccount()Ce qui est obtenu, ce sont les actifs disponibles et les actifs gelés par les ordres en attente. En fait, la plupart des bourses à terme de devises numériques fournissent des données sur la valeur nette totale, mais FMZ n’encapsule pas uniformément cet attribut.
Nous concevons donc des fonctions permettant d’obtenir ces données selon différents échanges :
// 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
}
Dans le codetotalEquityC’est le montant total des capitaux propres dont nous avons besoin. Ensuite, nous écrivons une fonction comme entrée appelante et appelons la fonction correspondante en fonction du nom d’échange.
function getTotalEquity() {
var exName = exchange.GetName()
if (exName == "Futures_OKCoin") {
return getTotalEquity_OKEX_V5()
} else if (exName == "Futures_Binance") {
return getTotalEquity_Binance()
} else {
throw "不支持该交易所"
}
}
Avant de concevoir la fonction principale et la logique principale. Nous devons encore faire quelques préparatifs et concevoir certaines fonctions auxiliaires.
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)
}
}
Je pense que ceux qui consultent souvent les exemples de codes de stratégie sur le FMZ Strategy Square connaissent très bien cette fonction. De nombreuses stratégies ont utilisé des conceptions similaires. Sa fonction est d’obtenir la liste actuelle des commandes en attente, puis de les annuler une par une.
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)
}
Il existe quatre directions dans le trading à terme : ouvrir une position longue (openLong), ouvrir une position courte (openShort), fermer une position longue (coverLong) et fermer une position courte (coverShort). Nous avons donc conçu quatre fonctions d’ordre pour correspondre à ces opérations. Si vous envisagez simplement de passer une commande, plusieurs facteurs sont nécessaires : la direction, le prix de la commande et la quantité commandée.
Nous avons donc également conçu un programme appelé :tradeLa fonction à gérer方向(distance)、下单价格(price)、下单量(amount)Toutes les opérations sont claires.
Les appels de fonction d’ouverture d’une position longue (openLong), d’ouverture d’une position courte (openShort), de fermeture d’une position longue (coverLong) et de fermeture d’une position courte (coverShort) sont finalement exécutés partradeLa fonction exécute la fonction réelle, qui consiste à passer une commande sur la bourse à terme selon la direction, le prix et la quantité établis.
La stratégie est très simple. Utilisez le prix actuel comme base et placez des ordres de vente (short) et des ordres d’achat (long) à une certaine distance au-dessus et en dessous du prix actuel. Une fois qu’un côté est exécuté, tous les ordres restants seront annulés, puis un nouvel ordre de clôture sera placé à une certaine distance en fonction du prix de la position, et un ordre d’augmentation sera placé au prix actuel mis à jour, mais l’ordre d’augmentation sera ne pas doubler la quantité commandée.
var buyOrderId = null
var sellOrderId = null
Ensuite, l’option d’utilisation du disque de simulation OKEX_V5 est conçue dans les paramètres de l’interface de stratégie, donc un certain traitement doit être effectué dans le code :
var exName = exchange.GetName()
// 切换OKEX V5模拟盘
if (isSimulate && exName == "Futures_OKCoin") {
exchange.IO("simulate", true)
}
Les paramètres de l’interface incluent également une option permettant de réinitialiser toutes les informations, le code doit donc également avoir un traitement correspondant :
if (isReset) {
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("重置所有数据", "#FF0000")
}
Nous n’exécutons que des contrats perpétuels, c’est donc codé en dur ici et défini uniquement sur des contrats perpétuels.
exchange.SetContractType("swap")
Ensuite, nous devons également tenir compte de la précision des prix et des quantités des commandes. Si la précision n’est pas correctement définie, elle sera perdue lors du processus de calcul de la stratégie. Si les données comportent de nombreuses décimales, il est facile de fausser la commande. rejeté par l’interface d’échange.
exchange.SetPrecision(pricePrecision, amountPrecision)
Log("设置精度", pricePrecision, amountPrecision)
Fonction simple de récupération de données
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
}
}
Si vous souhaitez spécifier le capital total initial du compte lorsque la stratégie est exécutée, vous pouvez définir le paramètretotalEqSi ce paramètre est défini sur -1, la stratégie lira les données de capitaux propres totaux stockées. S’il n’y a pas de données de capitaux propres totaux stockées, les capitaux propres totaux lus actuellement seront utilisés comme capitaux propres totaux initiaux de la progression de l’exécution de la stratégie. Si le total les capitaux propres augmentent, cela signifie que si vous gagnez de l’argent, mais que votre capital total est inférieur, cela signifie que vous avez perdu de l’argent. Si les données de capitaux propres totales sont lues, continuez l’exécution en utilisant ces données.
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)
}
Toute la logique et la conception ont été expliquées.
Laissez la stratégie expérimenter la situation du marché le 19 mai.


On peut constater que la stratégie Martingale comporte encore certains risques.

Adresse de la stratégie : https://www.fmz.com/strategy/294957
Les stratégies sont principalement utilisées pour l’apprentissage, alors utilisez l’argent réel avec prudence ~!