
Цель данной статьи — поделиться некоторым опытом и советами по разработке стратегии, которые позволят читателям быстро освоить опыт разработки торговой стратегии. При столкновении с аналогичными подробными проблемами при разработке стратегии можно сразу же придумать разумные решения. Количественная торговая платформа Inventor используется в качестве платформы для объяснения, тестирования и практики. Язык программирования политики: JavaScript Торговый рынок: рынок блокчейн-активов (BTC, ETH и т. д.)
Обычно, в зависимости от логики стратегии, можно использовать следующие различные интерфейсы для получения рыночных данных, поскольку торговая логика стратегии обычно управляется рыночными данными (конечно, есть некоторые стратегии, которые не смотрят на рынок) , например, стратегии инвестирования в фиксированный капитал).
GetTicker: получайте информацию о тиках в режиме реального времени. Обычно он используется для быстрого получения актуальной последней цены, цены покупки и цены продажи.
GetDepth: получение котировок глубины книги заявок. Обычно он используется для получения цены каждого уровня и размера заказа. Используется для стратегий хеджирования, стратегий создания рынка и т. д.
GetTrade: получите последние записи о транзакциях на рынке. Обычно он используется для анализа поведения рынка за короткий период времени и анализа микроизменений на рынке. Обычно используется в высокочастотных стратегиях и алгоритмических стратегиях.
GetRecords: получение данных K-линии рынка. Часто используется в стратегиях следования за трендом. Используется для расчета показателей.
При разработке стратегий новички обычно игнорируют различные ошибочные ситуации и интуитивно полагают, что результаты каждого звена стратегии предопределены. Но на самом деле это не так. При запросе рыночных данных во время работы стратегической программы будут возникать различные непредвиденные ситуации. Например, некоторые рыночные интерфейсы возвращают аномальные данные:
var depth = exchange.GetDepth()
// depth.Asks[0].Price < depth.Bids[0].Price 卖一价格低于了买一价格,这种情况不可能存在于盘面上,
// 因为卖出的价格低于买入的价格,必定已经成交了。
// depth.Bids[n].Amount = 0 订单薄买入列表第n档,订单量为0
// depth.Asks[m].Price = 0 订单薄卖出列表第m档,订单价格为0
Или exchange.GetDepth() напрямую возвращает нулевое значение.
Таких странных ситуаций много. Поэтому для этих предсказуемых проблем должна быть выполнена соответствующая обработка, и такой тип решения обработки называется отказоустойчивой обработкой.
Обычный подход к обеспечению отказоустойчивости заключается в удалении данных и повторном их извлечении.
Например:
function main () {
while (true) {
onTick()
Sleep(500)
}
}
function GetTicker () {
while (true) {
var ticker = exchange.GetTicker()
if (ticker.Sell > ticker.Buy) { // 以 检测卖一价格是不是小于买一价这个错误的容错处理为例,
// 排除这个错误,当前函数返回 ticker 。
return ticker
}
Sleep(500)
}
}
function onTick () {
var ticker = GetTicker() // 确保获取到的 ticker 不会存在 卖一价格小于买一价格这种数据错误的情况。
// ... 具体的策略逻辑
}
Аналогичным образом можно реализовать и другие предсказуемые отказоустойчивые процессы. Принцип проектирования заключается в том, что неверные данные ни в коем случае не должны использоваться для реализации логики стратегии.
Сбор данных по K-линии, вызов:
var r = exchange.GetRecords()
Полученные данные K-линии представляют собой массив, например такой:
[
{"Time":1562068800000,"Open":10000.7,"High":10208.9,"Low":9942.4,"Close":10058.8,"Volume":6281.887000000001},
{"Time":1562072400000,"Open":10058.6,"High":10154.4,"Low":9914.5,"Close":9990.7,"Volume":4322.099},
...
{"Time":1562079600000,"Open":10535.1,"High":10654.6,"Low":10383.6,"Close":10630.7,"Volume":5163.484000000004}
]
Вы можете видеть, что каждая фигурная скобка{}Он включает в себя время, цену открытия (open), самую высокую цену (high), самую низкую цену (low), цену закрытия (close) и объем торгов (volum).
Это подсвечник. Обычно данные K-линии используются для расчета индикаторов, таких как скользящая средняя MA, MACD и т. д.
Введите данные K-линии в качестве параметров (данные о сырье), затем задайте параметры индикатора и рассчитайте функцию данных индикатора, которую мы называем функцией индикатора.
На платформе количественной торговли Inventor имеется множество индикаторных функций.
Например, когда мы рассчитываем индикатор скользящей средней на основе различных периодов данных K-линии, которые мы передаем, мы рассчитываем скользящую среднюю соответствующего периода. Например, если данные ежедневной линии К передаются (один столбик линии К представляет один день), то вычисляемый индикатор является дневной скользящей средней. Аналогично, если данные линии К передаются в функцию индикатора скользящей средней, период 1 час, то рассчитанный показатель представляет собой скользящую среднюю за 1 час.
Обычно, когда мы вычисляем индикаторы, мы часто упускаем проблему. Если я хочу вычислить 5-дневный индикатор скользящей средней, то сначала мы подготавливаем дневные данные K-line:
var r = exchange.GetRecords(PERIOD_D1) // 给GetRecords 函数传入参数 PERIOD_D1就是指定获取日K线,
// 具体函数使用可以参看:https://www.fmz.com/api#GetRecords
С ежедневными данными K-line мы можем рассчитать индикатор скользящей средней. Если мы хотим рассчитать 5-дневную скользящую среднюю, нам нужно установить параметр индикатора функции индикатора на 5.
var ma = TA.MA(r, 5) // TA.MA() 就是指标函数,用来计算均线指标,第一个参数设置刚才获取的日K线数据r,
// 第二个参数设置5,计算出来的就是5日均线,其它指标函数同理。
Мы упустили из виду потенциальную проблему. Что, если количество баров K-line в данных K-line r-day меньше 5? Можем ли мы рассчитать действительный индикатор 5-дневной скользящей средней? Ответ однозначно — нет. Потому что индикатор скользящей средней предназначен для нахождения среднего значения цен закрытия определенного количества баров K-линии.

Поэтому перед использованием данных K-line и индикаторных функций для расчета индикаторных данных необходимо определить, соответствует ли количество столбцов K-line в данных K-line условиям расчета индикатора (параметрам индикатора).
Поэтому, прежде чем вычислять 5-дневную скользящую среднюю, необходимо вынести суждение. Полный код выглядит следующим образом:
function CalcMA () {
var r = _C(exchange.GetRecords, PERIOD_D1) // _C() 是容错函数,目的就是避免 r 为 null , 具体可以查询文档:https://www.fmz.com/api#_C
if (r.length > 5) {
return TA.MA(r, 5) // 用均线指标函数 TA.MA 计算出均线数据,做为函数返回值,返回。
}
return false
}
function main () {
var ma = CalcMA()
Log(ma)
}

Бэктестинг показывает: [null,null,null,null,4228.7,4402.9400000000005, … ]
Видно, что первые четыре из рассчитанных показателей 5-дневной скользящей средней равны нулю, поскольку количество столбцов K-линии меньше 5 и среднее значение не может быть рассчитано. По 5-й свече это можно рассчитать.
При написании некоторых стратегий часто возникает такая ситуация, что нам необходимо обработать некоторые операции или распечатать некоторые журналы после завершения каждого цикла K-линии. Как достичь этой функциональности? Для новичков, у которых нет опыта программирования, они могут не додуматься, какой механизм использовать, чтобы справиться с этим. Здесь мы прямо дадим вам несколько советов.
Мы можем судить о том, что цикл столбца K-line завершен, начав с атрибута времени в данных K-line. Каждый раз, когда мы получаем данные K-line, мы судим об атрибуте времени в данных последнего столбца K-line этих данных K-line. Изменилось ли значение этого атрибута. Если оно изменилось, это означает, что был сгенерирован новый столбец K-line (доказывая, что предыдущий цикл столбца K-line нового сгенерированного столбца K-line был Если он не изменился, это означает, что нет Генерируется новая свеча (текущий последний цикл свечи еще не завершен).
Поэтому нам нужна переменная для записи времени последнего столбца свечных данных.
var r = exchange.GetRecords()
var lastTime = r[r.length - 1].Time // lastTime 用来记录最后一根K线柱的时间。
В практических приложениях структура обычно выглядит следующим образом:
function main () {
var lastTime = 0
while (true) {
var r = _C(exchange.GetRecords)
if (r[r.length - 1].Time != lastTime) {
Log("新K线柱产生")
lastTime = r[r.length - 1].Time // 一定要更新 lastTime ,这个至关重要。
// ... 其它处理逻辑
// ...
}
Sleep(500)
}
}

Видно, что в бэктесте период K-line установлен на день (функция exchange.GetRecords вызывается без указания параметров, а период K-line, установленный в соответствии с бэктестом, является параметром по умолчанию). Всякий раз, когда Появляется новый столбец K-line, он печатается как A log.
Если вы хотите отобразить или контролировать время, необходимое стратегии для доступа к интерфейсу биржи, вы можете использовать следующий код:
function main () {
while (true) {
var beginTime = new Date().getTime()
var ticker = exchange.GetTicker()
var endTime = new Date().getTime()
LogStatus(_D(), "GetTicker() 函数耗时:", endTime - beginTime, "毫秒")
Sleep(1000)
}
}
Проще говоря, временная метка, записанная после вызова функции GetTicker, вычитается из временной метки до вызова для расчета количества прошедших миллисекунд, то есть времени, необходимого функции GetTicker для выполнения и возврата результата.
Если вам нужен числовой верхний предел, вы обычно используете Math.min для ограничения
Например, при размещении ордера на продажу сумма ордера не должна превышать количество монет на счете. Так как если оно больше, чем количество доступных монет на счете, при оформлении заказа будет выдано сообщение об ошибке.
Обычно контролируют так: Например, вы планируете разместить ордер на продажу 0,2 монеты.
var planAmount = 0.2
var account = _C(exchange.GetAccount)
var amount = Math.min(account.Stocks, planAmount)
Это гарантирует, что сумма размещаемого заказа не превысит количество монет, имеющихся на счете.
Аналогично, Math.max используется для обеспечения нижнего предела значения. К каким сценариям это обычно применяется? Обычно биржи имеют минимальный лимит количества ордеров для определенных торговых пар. Если количество ордеров меньше этого минимального количества ордеров, ордер будет отклонен. В этом случае заказ не будет выполнен. Предположим, что минимальный размер заказа для BTC обычно составляет 0,01. Иногда торговая стратегия может рассчитать, что размер ордера меньше 0,01, поэтому мы можем использовать Math.max, чтобы гарантировать минимальный размер ордера.
Можно использовать_Функция N() или функция SetPrecision для управления точностью.
Функцию SetPrecision() необходимо задать только один раз, и система автоматически удалит лишние десятичные знаки в значениях количества и цены заказа.
_Функция N() используется для усечения значения до определенного количества десятичных знаков (контроль точности).
Например:
var pi = _N(3.141592653, 2)
Log(pi)
Значение числа Пи округляется до двух знаков после запятой, что составляет: 3,14.
Подробную информацию смотрите в документации API.
Вы можете использовать этот механизм, чтобы использовать метод обнаружения временной метки, чтобы определить текущую временную метку за вычетом временной метки последнего времени выполнения запланированной задачи и рассчитать прошедшее время в реальном времени. Когда прошедшее время превышает определенную заданную продолжительность времени После этого , выполняется новая операция.
Например, он используется в стратегиях инвестирования в фиксированный капитал.
var lastActTime = 0
var waitTime = 1000 * 60 * 60 * 12 // 一天的毫秒数
function main () {
while (true) {
var nowTime = new Date().getTime()
if (nowTime - lastActTime > waitTime) {
Log("执行定投")
// ... 具体的定投操作,买入操作。
lastActTime = nowTime
}
Sleep(500)
}
}
Это простой пример.
Используя квантованную функцию _G() изобретателя и функцию сохранения выхода, очень удобно разработать стратегию выхода и сохранения прогресса, а также перезапустить для автоматического восстановления состояния.
var hold = {
price : 0,
amount : 0,
}
function main () {
if (_G("hold")) {
var ret = _G("hold")
hold.price = ret.price
hold.amount = ret.amount
Log("恢复 hold:", hold)
}
var count = 1
while (true) {
// ... 策略逻辑
// ... 策略运行中,可能开仓,交易,把开仓的持仓价格赋值给 hold.price ,开仓的数量赋值给 hold.amount,用以记录持仓信息。
hold.price = count++ // 模拟一些数值
hold.amount = count/10 // 模拟一些数值
Sleep(500)
}
}
function onexit () { // 点击机器人上的停止按钮,会触发执行这个函数,执行完毕机器人停止。
_G("hold", hold)
Log("保存 hold:", JSON.stringify(hold))
}

Видно, что каждый раз, когда робот останавливается, данные в объекте удержания сохраняются. Каждый раз, когда он перезапускается, данные считываются, и значение удержания восстанавливается до состояния на предыдущей остановке. Конечно, вышеприведенный пример является простым. Если он используется в реальной стратегии, он должен быть разработан в соответствии с ключевыми данными, которые необходимо восстановить в стратегии (обычно это информация о счете, позиции, значения прибыли, направления торговли и т. д.) .). Конечно, вы также можете задать некоторые условия, которые позволят определить, следует ли выполнять восстановление.
Выше приведены некоторые советы по разработке стратегий. Надеюсь, они будут полезны новичкам и разработчикам стратегий! Самый быстрый способ улучшить свои навыки — это практика! Желаю вам всем дальнейших прибылей.