Name
数字货币期货类马丁策略
Author
小小梦
Strategy Description
相关文章1:https://www.fmz.com/bbs-topic/7457 相关文章2:https://www.fmz.com/digest-topic/8902
Strategy Arguments
Argument | Default | Description |
---|---|---|
targetProfit | 10 | 目标利润 |
amount | true | 下单量 |
totalEq | -1 | 初始总权益 |
isReset | false | 重置 |
pricePrecision | 2 | 价格精度 |
amountPrecision | 2 | 下单量精度 |
isSimulate | false | OKEX_V5模拟盘选项 |
SpecifyPosField | 指定显示的持仓字段 | |
showLine | false | 显示曲线图表 |
mode | 0 | 模式: 双向 |
maxPendingDiff | 60 | 最大挂单距离 |
increment | false | 加仓系数增量 |
Source (javascript)
// 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()
}
Detail
https://www.fmz.com/strategy/294957
Last Modified
2023-12-01 10:57:21