Соответствующая статья:https://www.fmz.com/bbs-topic/7457Следующая статья:https://www.fmz.com/digest-topic/8902
// 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 } // dYdX function getTotalEquity_dYdX() { var totalEquity = null var ret = exchange.GetAccount() if (ret) { totalEquity = ret.Balance } return totalEquity } // BitMEX function getTotalEquity_BitMEX() { var currency = exchange.GetCurrency() var arr = currency.split("_") if (arr.length != 2) { throw "交易对配置错误" } var baseCurrency = arr[0] var quoteCurrency = arr[1] var coinName = "" var scale = 0.0 if (quoteCurrency == "USDT") { coinName = "USDt" scale = 6 } else if (quoteCurrency == "USD") { coinName = "XBt" scale = 8 } else { throw "不支持" } var ret = exchange.IO("api", "GET", "/api/v1/user/margin", "currency=all") if (ret) { for (var i = 0 ; i < ret.length ; i++) { if (coinName == ret[i].currency) { var equity = ret[i]["marginBalance"] if (equity) { return parseFloat(equity) / Math.pow(10, scale) } } } } else { Log("获取账户总权益失败!") return null } } function getTotalEquity() { var exName = exchange.GetName() if (exName == "Futures_OKCoin") { return getTotalEquity_OKEX_V5() } else if (exName == "Futures_Binance") { return getTotalEquity_Binance() } else if (exName == "Futures_dYdX") { return getTotalEquity_dYdX() } else if (exName == "Futures_BitMEX") { return getTotalEquity_BitMEX() } else { throw "不支持该交易所" } } 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) } } 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) } var buyOrderId = null var sellOrderId = null var chartUpdateTS = 0 function riskControl() { var ts = new Date().getTime() var data = _G("riskControData") if (!data) { // 没有风控数据,初始化 data = {timeStamp : ts, tradeTimes : 0} _G("riskControData", data) } if (ts - data.timeStamp > 1000 * 60 * 60 * 24) { data.tradeTimes = 0 data.timeStamp = ts Log("风控模块重置时间:", data) } data.tradeTimes++ _G("riskControData", data) if (data.tradeTimes > 10) { Log("触发风控:", data) return false } return true } function main() { var exName = exchange.GetName() // 切换OKEX V5模拟盘 if (isSimulate && exName == "Futures_OKCoin") { exchange.IO("simulate", true) } if (isReset) { _G(null) LogReset(1) LogProfitReset() LogVacuum() Log("重置所有数据", "#FF0000") } Log("当前模式:", ["双向", "只做多", "只做空"][mode]) exchange.SetContractType("swap") exchange.SetPrecision(pricePrecision, amountPrecision) Log("设置精度", pricePrecision, amountPrecision) 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 } } var n = 1 // 加仓系数 while (1) { // 风控 /* if (!riskControl()) { Sleep(1000 * 60 * 30) continue } */ var ticker = _C(exchange.GetTicker) var pos = _C(exchange.GetPosition) if (pos.length > 1) { Log(pos) throw "同时有多空持仓" } // 根据状态而定 if (pos.length == 0) { // 未持仓了,统计一次收益 if (!IsVirtual()) { var currTotalEq = getTotalEquity() if (currTotalEq) { LogProfit(currTotalEq - totalEq, "当前总权益:", currTotalEq) } } if (mode == 0) { buyOrderId = openLong(ticker.Last - targetProfit, amount) sellOrderId = openShort(ticker.Last + targetProfit, amount) } else if (mode == 1) { buyOrderId = openLong(ticker.Last - targetProfit, amount) } else if (mode == 2) { sellOrderId = openShort(ticker.Last + targetProfit, amount) } n = 1 // 初始为1 } else if (pos[0].Type == PD_LONG) { // 有多头持仓 n += increment 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) { // 有空头持仓 n += increment var price = ticker.Last buyOrderId = coverShort(pos[0].Price - targetProfit, pos[0].Amount) sellOrderId = openShort(price + targetProfit * n, amount) } if (mode == 0 && (!sellOrderId || !buyOrderId)) { cancelAll() buyOrderId = null sellOrderId = null continue } else if (mode == 1 && pos.length == 0 && !buyOrderId) { cancelAll() buyOrderId = null sellOrderId = null continue } else if (mode == 2 && pos.length == 0 && !sellOrderId) { cancelAll() buyOrderId = null sellOrderId = null continue } else if (pos.length != 0 && (!sellOrderId || !buyOrderId)) { cancelAll() buyOrderId = null sellOrderId = null continue } while (1) { // 监控订单 var isFindBuyId = false var isFindSellId = false var buyOrder = null var sellOrder = null var orders = _C(exchange.GetOrders) var t = exchange.GetTicker() for (var i = 0 ; i < orders.length ; i++) { if (buyOrderId == orders[i].Id) { isFindBuyId = true buyOrder = orders[i] } if (sellOrderId == orders[i].Id) { isFindSellId = true sellOrder = orders[i] } } if (!isFindSellId && !isFindBuyId) { // 买卖单都成交了 cancelAll() break } else if (!isFindBuyId && (mode == 0 || (mode == 2 && pos.length != 0))) { // 买单成交 // 双向模式,只做空模式有持仓,找不到买单时 Log("买单成交") cancelAll() break } else if (!isFindSellId && (mode == 0 || (mode == 1 && pos.length != 0))) { // 卖单成交 // 双向模式,只做多模式有持仓,找不到卖单时 Log("卖单成交") cancelAll() break } else if (mode == 1 && pos.length == 0 && isFindBuyId && t && buyOrder && t.Last - buyOrder.Price > maxPendingDiff) { // 只做多模式,没有持仓,查询到买单存在,订单价格超出 Log("当前价格超出最大距离,撤销买单订单!当前价格:", t.Last, "订单价格:", buyOrder.Price, "最大距离:", maxPendingDiff) cancelAll() break } else if (mode == 2 && pos.length == 0 && isFindSellId && t && sellOrder && sellOrder.Price - t.Last > maxPendingDiff) { // 只做空模式,没有持仓,查询到卖单存在,订单价格超出 Log("当前价格超出最大距离,撤销卖单订单!当前价格:", t.Last, "订单价格:", buyOrder.Price, "最大距离:", maxPendingDiff) cancelAll() break } else if (!isFindBuyId && pos.length != 0) { // 有持仓,找不到买单时,条件可以合并 Log("买单成交") cancelAll() break } else if (!isFindSellId && pos.length != 0) { // 有持仓,找不到卖单时 Log("卖单成交") cancelAll() break } if (!IsVirtual()) { var currTotalEq = getTotalEquity() var pos = exchange.GetPosition() if (currTotalEq && pos) { // LogStatus(_D(), "当前总权益:", currTotalEq, "持仓:", pos) var tblPos = { "type" : "table", "title" : "持仓", "cols" : ["持仓数量", "持仓方向", "持仓均价", "持仓盈亏", "合约代码", "自定义字段 / " + SpecifyPosField], "rows" : [] } var descType = ["多头仓位", "空头仓位"] for (var posIndex = 0 ; posIndex < pos.length ; posIndex++) { tblPos.rows.push([pos[posIndex].Amount, descType[pos[posIndex].Type], pos[posIndex].Price, pos[posIndex].Profit, pos[posIndex].ContractType, SpecifyPosField == "" ? "--" : pos[posIndex].Info[SpecifyPosField]]) } var tbl = { "type" : "table", "title" : "数据", "cols" : ["当前总权益", "实际盈亏", "当前价格", "买单价格/数量", "卖单价格/数量"], "rows" : [] } /* var buyOrder = null var sellOrder = null for (var orderIndex = 0 ; orderIndex < orders.length ; orderIndex++) { if (orders[orderIndex].Type == ORDER_TYPE_BUY) { buyOrder = orders[orderIndex] } else { sellOrder = orders[orderIndex] } } */ var realProfit = currTotalEq - totalEq if (exchange.GetName() == "Futures_Binance") { _.each(pos, function(p) { realProfit += parseFloat(p.Info.unRealizedProfit) }) } // var t = exchange.GetTicker() tbl.rows.push([currTotalEq, realProfit, t ? t.Last : "--", buyOrder ? (buyOrder.Price + "/" + buyOrder.Amount) : "--/--", sellOrder ? (sellOrder.Price + "/" + sellOrder.Amount) : "--/--"]) // 更新图表数据 if (t && showLine) { var ts = new Date().getTime() if (ts - chartUpdateTS > 60 * 1000 * 5) { chartUpdateTS = ts $.PlotLine("当前价格", t.Last) } } // 更新状态栏数据 LogStatus("时间:" + _D() + "\n" + "`" + JSON.stringify(tblPos) + "`" + "\n" + "`" + JSON.stringify(tbl) + "`") } } Sleep(5000) } Sleep(500) } } function onexit() { Log("扫尾,撤销所有挂单") cancelAll() }
xyiiЕсли бы мы добавили условия остановки, это было бы лучше.
Тургай75Привет, это очень хороший бот. Я использую его, но я должен сделать stoploss сам. Можете ли вы добавить параметр stoploss к этому боту пожалуйста. Я буду очень доволен автоматическим stoploss. Спасибо заранее. turgay75@gmx.de Простите мой английский
ПросящиеПродолжайте просматривать, двусторонний режим будет ошибочным.
ДеколярДа, я только что проверил, почему я не могу настроить на 0.1, чтобы использовать
xjjНо, в общем-то, почему разрыв в открытии 20 долларов, ETH и BNB, которые я тестировал, были 20 долларов?
Они просто хотят заработать деньги.О Боже, как я могу связаться с тобой, если я могу написать стратегию?
ИвдалейСколько раз рычаг?
Изобретатели количественного измерения - мечтыНе за что.
Тургай75Спасибо за вашу работу, установка стоплосса поможет мне много
Изобретатели количественного измерения - мечтыСпасибо, что поддержали квантификацию FMZ. Я взял время, чтобы добавить функцию остановки потери.
Изобретатели количественного измерения - мечтыВозможно, параметры установлены неправильно, целевая прибыль установлена большая, возможно, установлена слишком маленькая, что приводит к двустороннему держанию.
ПросящиеКогда рекурс проводится в течение всего года, рекурс застрял в первом месяце, сообщение было двусторонним.
Изобретатели количественного измерения - мечтыО, что конкретно было ошибочным?
Изобретатели количественного измерения - мечтыХорошо.
ДеколярНо я не могу, я должен посмотреть, это моя проблема.
Изобретатели количественного измерения - мечтыУстройство с точностью в параметрах неправильно настроено.
ДеколярETH_USDT постоянный контракт, показывающий следующую сумму 0.
Изобретатели количественного измерения - мечтыЯ думаю, что это может быть 0.1. Какая пара транзакций работает, контракт?
Изобретатели количественного измерения - мечтыВ этом случае, мы не можем выполнить задание.
xy5335Установленные позиции, разные рычаги