
В предыдущей статье мы реализовали простую стратегию хеджирования. Далее давайте узнаем, как модернизировать эту стратегию. Изменения в стратегии не будут значительными, но детали изменений требуют внимания. Определения некоторых частей кода изменились по сравнению с предыдущими версиями и требуют понимания.
A交易所->B交易所,B交易所->A交易所Нарисуйте горизонтальную линию, которая запускает спред. Мы напрямую используем画线类库Преимущество в том, что он прост и удобен в использовании. Здесь мы также научимся использовать FMZ模版类库Функция.Далее давайте реализуем эти проекты по одному.
Взяв в качестве примера спотовую торговлю Binance, переключитесь в режим спотового кредитного плеча с помощью кодаexchanges[i].IO, передайте параметрыtrade_normalПереключитесь на кредитное плечо позиция за позицией и передайтеtrade_super_marginПереключиться на полную позицию с кредитным плечом, бэктестинг не поддерживается. Используется только в реальной торговле.
существоватьmainДобавьте следующую подготовительную фазу в начало функции:
// 切换杠杆模式
for (var i = 0 ; i < exchanges.length ; i++) { // 遍历检测所有添加的交易所对象
if (exchanges[i].GetName() == "Binance" && marginType != 0) { // 如果当前i索引代表的交易所对象是币安现货,并且策略界面参数marginType选择的不是「普通币币」选项,执行切换
if (marginType == 1) {
Log(exchanges[i].GetName(), "设置为杠杆逐仓")
exchanges[i].IO("trade_normal")
} else if (marginType == 2) {
Log(exchanges[i].GetName(), "设置为杠杆全仓")
exchanges[i].IO("trade_super_margin")
}
}
}
Стратегия здесь добавляет только код для переключения режима кредитного плеча «монета-монета» для спотовой торговли Binance, поэтому переключатель, установленный в параметрах стратегии, действителен только для спотовой торговли Binance.
Использовать готовые шаблоны чертежей очень просто. Шаблон, который мы используем, называется画线类库. Вы можете найти и получить его непосредственно на стратегическом квадрате платформы FMZ.

Или нажмите ссылку: https://www.fmz.com/strategy/27293, чтобы перейти на страницу копирования этого шаблона.

Нажмите кнопку, чтобы скопировать эту библиотеку шаблонов в вашу собственную библиотеку стратегий.

Затем на странице редактирования политики вы можете проверить нужную вам библиотеку шаблонов в столбце шаблонов. После установки флажка и сохранения политики эта политика будет ссылаться на этот шаблон. Здесь мы просто кратко объясним использование библиотеки шаблонов. Эта стратегия уже ссылается на этот шаблон, поэтому нет необходимости повторять операцию. После копирования этой стратегии в Strategy Square вы увидите ее в столбце шаблона на странице редактирования стратегии.画线类库Уже цитировалось.
В основном мы учимся использовать画线类库функция для рисования графика.

Мы планируемA->BРазница в цене,B->AРисуются разница в цене и линия срабатывания разницы в цене. Вам необходимо нарисовать две кривые (текущую разницу цен от A до B и от B до A) и две горизонтальные линии (линии разницы цен триггера), как показано на рисунке выше.
Поскольку нам необходимо разработать одностороннюю хедж-систему,A->BиB->AЛиния срабатывания другая. Дизайн из предыдущей статьи использовать нельзя.
В предыдущей статье:
var targetDiffPrice = hedgeDiffPrice
if (diffAsPercentage) {
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
}
Существует только один триггерный спредtargetDiffPrice。
Итак, здесь нам нужно изменить код, для начала изменив параметры.

Затем измените код:
var targetDiffPriceA2B = hedgeDiffPriceA2B
var targetDiffPriceB2A = hedgeDiffPriceB2A
if (diffAsPercentage) {
targetDiffPriceA2B = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentageA2B
targetDiffPriceB2A = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentageB2A
}
Таким образом, линия срабатывания разницы в цене изменяется по сравнению с предыдущейtargetDiffPriceОдин, стал двумяtargetDiffPriceA2B、targetDiffPriceB2A。
Далее вы можете использовать функцию рисования линий из библиотеки рисования линий, чтобы изобразить эти данные на диаграмме.
// 画图
$.PlotHLine(targetDiffPriceA2B, "A->B") // 该函数第一个参数是水平线在Y轴方向上的值,第二个参数是显示文本
$.PlotHLine(targetDiffPriceB2A, "B->A")
Когда стратегия запущена, появится такая диаграмма.

Далее нарисуйте кривую спреда в реальном времени, чтобы избежать перерасхода. Добавьте код для построения кривых данных о разнице цен в реальном времени в проверку баланса.
if (ts - lastKeepBalanceTS > keepBalanceCyc * 1000) {
nowAccs = _C(updateAccs, exchanges)
var isBalance = keepBalance(initAccs, nowAccs, [depthA, depthB])
cancelAll()
if (isBalance) {
lastKeepBalanceTS = ts
if (isTrade) {
var nowBalance = _.reduce(nowAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
var initBalance = _.reduce(initAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
LogProfit(nowBalance - initBalance, nowBalance, initBalance, nowAccs)
isTrade = false
}
}
$.PlotLine("A2B", depthA.Bids[0].Price - depthB.Asks[0].Price) // 画实时差价曲线
$.PlotLine("B2A", depthB.Bids[0].Price - depthA.Asks[0].Price) // 第一个参数是曲线名称,第二个参数是曲线当前时刻的值,即当前时刻Y轴方向上的值
}
Таким образом, используя всего 4 строки кода рисования, стратегия может отображать диаграмму во время работы.
Как упоминалось выше, линия триггера разницы цен была преобразована в две, контролирующиеA->BТриггер хеджированияB->AСрабатывает хеджирование. Таким образом, алгоритм предыдущей цены ордера не может быть использован, и вместо него применяется метод рыночной цены плюс проскальзывание.
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPriceA2B && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B 盘口条件满足
var priceSell = depthA.Bids[0].Price - slidePrice
var priceBuy = depthB.Asks[0].Price + slidePrice
var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount)
if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance * 0.8 / priceSell > minHedgeAmount) {
amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance * 0.8 / priceSell, maxHedgeAmount)
Log("触发A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[1].Balance * 0.8 / priceSell, nowAccs[0].Stocks) // 提示信息
hedge(exB, exA, priceBuy, priceSell, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
} else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPriceB2A && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) { // B -> A 盘口条件满足
var priceBuy = depthA.Asks[0].Price + slidePrice
var priceSell = depthB.Bids[0].Price - slidePrice
var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount)
if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance * 0.8 / priceBuy > minHedgeAmount) {
amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance * 0.8 / priceBuy, maxHedgeAmount)
Log("触发B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[0].Balance * 0.8 / priceBuy, nowAccs[1].Stocks) // 提示信息
hedge(exA, exB, priceBuy, priceSell, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
Поскольку цены покупки и продажи разделены на два типа данных, функция хеджированияhedgeЕго также необходимо изменить.
function hedge(buyEx, sellEx, priceBuy, priceSell, amount) {
var buyRoutine = buyEx.Go("Buy", priceBuy, amount)
var sellRoutine = sellEx.Go("Sell", priceSell, amount)
Sleep(500)
buyRoutine.wait()
sellRoutine.wait()
}
Также есть некоторые незначительные корректировки, основанные на этих изменениях, в которые я не буду вдаваться здесь. Вы можете посмотреть код для получения подробностей.
Добавьте в стратегию интерактивности, чтобы стратегия могла изменять линию срабатывания спреда в режиме реального времени. Это требование к конструкции полуавтоматической стратегии, которая также реализована здесь в качестве обучающей демонстрации. Дизайн взаимодействия стратегии также очень прост. Сначала добавьте интерактивные элементы управления к стратегии на странице редактирования стратегии.

Добавляются два элемента управления: один называется A2B, а другой — B2A. После ввода значения в поле ввода нажмите кнопку в правой части поля ввода. Инструкция будет немедленно отправлена в стратегию, например: введите значение в поле ввода.123, нажмитеA2BЭта кнопка немедленно отправит инструкции стратегии.
A2B:123
Разработать код обнаружения и обработки взаимодействия в коде стратегии.
// 交互
var cmd = GetCommand() // 每次循环执行到这里时,都检测有没有交互指令过来,没有则返回空字符串
if (cmd) { // 检测到有交互指令,例如:A2B:123
Log("接收到命令:", cmd)
var arr = cmd.split(":") // 拆分出交互控件名称和输入框中的值,arr[0]就是A2B,arr[1]就是123
if (arr[0] == "A2B") { // 判断触发的交互控件是不是A2B
Log("修改A2B的参数,", diffAsPercentage ? "参数为差价百分比" : "参数为差价:", arr[1])
if (diffAsPercentage) {
hedgeDiffPercentageB2A = parseFloat(arr[1]) // 修改触发差价线
} else {
hedgeDiffPriceA2B = parseFloat(arr[1]) // 修改触发差价线
}
} else if (arr[0] == "B2A") { // 检测到触发的控件是B2A
Log("修改B2A的参数,", diffAsPercentage ? "参数为差价百分比" : "参数为差价:", arr[1])
if (diffAsPercentage) {
hedgeDiffPercentageA2B = parseFloat(arr[1])
} else {
hedgeDiffPriceB2A = parseFloat(arr[1])
}
}
}
Сделайте данные в строке состояния более организованными и удобными для просмотра.
var tbl = {
"type" : "table",
"title" : "数据",
"cols" : ["交易所", "币", "冻结币", "计价币", "冻结计价币", "触发差价", "当前差价"],
"rows" : [],
}
tbl.rows.push(["A:" + exA.GetName(), nowAccs[0].Stocks, nowAccs[0].FrozenStocks, nowAccs[0].Balance, nowAccs[0].FrozenBalance, "A->B:" + targetDiffPriceA2B, "A->B:" + (depthA.Bids[0].Price - depthB.Asks[0].Price)])
tbl.rows.push(["B:" + exB.GetName(), nowAccs[1].Stocks, nowAccs[1].FrozenStocks, nowAccs[1].Balance, nowAccs[1].FrozenBalance, "B->A:" + targetDiffPriceB2A, "B->A:" + (depthB.Bids[0].Price - depthA.Asks[0].Price)])
LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`")

Backtesting — это всего лишь проверка стратегии и предварительное обнаружение функций. Многие ошибки можно проверить на этапе backtesting. Нет необходимости уделять слишком много внимания результатам бэктеста. Окончательную стратегию все равно нужно протестировать в реальной среде.


Исходный код стратегии: https://www.fmz.com/strategy/302834