
Я уже некоторое время занимаюсь количественной торговлей. Честно говоря, большую часть времени я просто читаю стратегии, которыми делятся другие, а затем пытаюсь изменить параметры. Не так много возможностей действительно начать стратегию с нуля, в основном потому, что у меня нет хороших идей, а расстояние от идей до кода кажется довольно большим.
Некоторое время назад я освободился, поэтому снова начал просматривать B-станцию. Я случайно увидел прямую трансляцию Brother Z. Я просто смотрел ее мимоходом, но не ожидал, что она даст мне неожиданное вдохновение.
Когда брат Z говорил об индикаторе KDJ, один из пользователей сети в комментариях опубликовал предложение, которое мне показалось особенно интересным:

«Если цена немного падает, крюк быстро опускается, что может указывать на бычий тренд в будущем. Если цена резко падает, крюк опускается медленно, что может указывать на дальнейшее падение в будущем. Если цена немного растёт, бороздка быстро растёт, что может указывать на медвежий тренд в будущем. Если цена резко растёт, крюк растёт медленно, что может указывать на дальнейший рост в будущем».
Я был ошеломлен в то время. Это резюме настолько проницательно! Хотя это всего лишь одно предложение, оно, кажется, ясно объясняет изменяющиеся правила значения J KDJ.
Я подумал: «Брат Зет работает на фондовом рынке, но эта логика должна быть применима и к криптовалютам, верно?» Чем больше я думал об этом, тем яснее мне это становилось. Масштаб изменения цены и скорость реакции индикаторов – эти два параметра, в совокупности позволяющие судить о последующей тенденции, – звучали очень логично. Более того, рынок криптовалют торгуется круглосуточно, и волатильность выше, так что, возможно, эта теория окажется более убедительной.
В тот момент я был немного взволнован и чувствовал, что нашел направление для исследования. Но затем я снова забеспокоился, как я могу превратить эту идею в код? Раньше, когда я сталкивался с такой ситуацией, я либо думал об этом, либо откладывал и делал это, когда у меня было время.
Но на этот раз всё по-другому. Я вспомнил, что в Inventor Platform есть ИИ-помощник, и решил проверить, смогу ли я напрямую «перевести» эту идею в код.
Желая попробовать, я открыл помощника ИИ платформы Inventor, дословно ввел идею и добавил: это теория, применяемая к акциям, и я хочу опробовать ее на криптовалютах.
Я не ожидал, что ИИ так быстро поймет и быстро сгенерирует для меня полный набор кодов стратегии. В тот момент я был весьма удивлен и подумал, неужели ИИ теперь такой сильный?
Первая версия кода стратегии:
/*backtest
start: 2024-07-18 09:40:00
end: 2025-06-30 15:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/
//@version=5
strategy("KDJ J值变化速度策略", overlay=false)
// 输入参数 - 针对加密货币市场优化
kdj_length = input.int(14, "KDJ周期", minval=1)
k_smooth = input.int(3, "K平滑", minval=1)
d_smooth = input.int(3, "D平滑", minval=1)
j_speed_period = input.int(5, "J值变化速度计算周期", minval=1)
price_change_threshold = input.float(3.0, "价格变化幅度阈值(%)-区分大小幅", minval=0.1) / 100
j_speed_threshold = input.float(12.0, "J值变化速度阈值-区分快慢", minval=0.1)
j_change_threshold = input.float(1.5, "J值变化忽略阈值-过滤噪音", minval=0.1)
price_change_ignore_threshold = input.float(0.5, "价格变化忽略阈值(%)-过滤噪音", minval=0.01) / 100
// 加密货币风控参数
max_consecutive_trades = input.int(5, "最大连续交易次数", minval=1)
min_bars_between_signals = input.int(3, "信号间最小间隔K线数", minval=1)
enable_stop_loss = input.bool(true, "启用止损")
stop_loss_pct = input.float(5.0, "止损百分比(%)", minval=0.5) / 100
// 计算KDJ指标
highest_high = ta.highest(high, kdj_length)
lowest_low = ta.lowest(low, kdj_length)
rsv = (close - lowest_low) / (highest_high - lowest_low) * 100
k = ta.sma(rsv, k_smooth)
d = ta.sma(k, d_smooth)
j = 3 * k - 2 * d
// 计算J值连续变化速度
// 统计连续上涨或下跌的累计变化量和持续时间
var float j_cumulative_change = 0.0
var int j_consecutive_bars = 0
var int j_direction = 0 // 1为上涨,-1为下跌,0为初始
j_current_change = j - j[1]
// 只有变化幅度超过阈值才认为有方向性
j_current_direction = math.abs(j_current_change) > j_change_threshold ?
(j_current_change > 0 ? 1 : -1) : 0
// 检测J值方向是否改变
if j_current_direction != j_direction and j_current_direction != 0
// 方向改变,重新开始统计
j_cumulative_change := math.abs(j_current_change)
j_consecutive_bars := 1
j_direction := j_current_direction
else if j_current_direction == j_direction and j_current_direction != 0
// 方向相同,累计变化
j_cumulative_change += math.abs(j_current_change)
j_consecutive_bars += 1
else if j_current_direction == 0
// 变化太小忽略,但时间继续累计
j_consecutive_bars += 1
// J值变化速度 = 累计变化量 / 持续时间
j_speed = j_consecutive_bars > 0 ? j_cumulative_change / j_consecutive_bars : 0
// 计算价格连续变化幅度
// 统计连续上涨或下跌的累计变化量和持续时间
var float price_cumulative_change = 0.0
var int price_consecutive_bars = 0
var int price_direction = 0 // 1为上涨,-1为下跌,0为初始
price_current_change = (close - close[1]) / close[1]
// 只有变化幅度超过阈值才认为有方向性
price_current_direction = math.abs(price_current_change) > price_change_ignore_threshold ?
(price_current_change > 0 ? 1 : -1) : 0
// 检测价格方向是否改变
if price_current_direction != price_direction and price_current_direction != 0
// 方向改变,重新开始统计
price_cumulative_change := math.abs(price_current_change)
price_consecutive_bars := 1
price_direction := price_current_direction
else if price_current_direction == price_direction and price_current_direction != 0
// 方向相同,累计变化
price_cumulative_change += math.abs(price_current_change)
price_consecutive_bars += 1
else if price_current_direction == 0
// 变化太小忽略,但时间继续累计
price_consecutive_bars += 1
// 价格变化幅度 = 累计变化量
price_change = price_cumulative_change
// 判断价格变化类型
is_small_change = price_change < price_change_threshold
is_large_change = price_change >= price_change_threshold
// 判断价格方向(基于当前连续变化方向)
is_price_up = price_direction == 1
is_price_down = price_direction == -1
// 判断J值变化速度
is_j_fast = j_speed > j_speed_threshold
is_j_slow = j_speed <= j_speed_threshold
// 交易控制变量
var float entry_price = 0.0
// 策略信号逻辑
// 1. 小幅下跌钩掉的快,可能后期看涨
signal_small_down_fast_j = is_small_change and is_price_down and is_j_fast
// 2. 大幅下跌勾下的慢,后期还要再跌
signal_large_down_slow_j = is_large_change and is_price_down and is_j_slow
// 3. 小幅上涨沟长得快,可能后期看跌
signal_small_up_fast_j = is_small_change and is_price_up and is_j_fast
// 4. 大幅上涨勾长得慢,后期可能还要再涨
signal_large_up_slow_j = is_large_change and is_price_up and is_j_slow
// 交易信号
long_signal = (signal_small_down_fast_j or signal_large_up_slow_j)
short_signal = (signal_small_up_fast_j or signal_large_down_slow_j)
// 执行交易
if long_signal and strategy.position_size == 0
strategy.entry("Long", strategy.long, comment="多头信号开仓")
entry_price := close
if short_signal and strategy.position_size == 0
strategy.entry("Short", strategy.short, comment="空头信号开仓")
entry_price := close
// 平仓条件
// 1. 信号反转平仓
if strategy.position_size > 0 and short_signal
strategy.close("Long", comment="信号反转平多仓")
if strategy.position_size < 0 and long_signal
strategy.close("Short", comment="信号反转平空仓")
// 2. 止损平仓
if enable_stop_loss
if strategy.position_size > 0 and close <= entry_price * (1 - stop_loss_pct)
strategy.close("Long", comment="止损平多仓")
if strategy.position_size < 0 and close >= entry_price * (1 + stop_loss_pct)
strategy.close("Short", comment="止损平空仓")
// 绘制指标
plot(j, "J值", color=color.blue, linewidth=2)
hline(80, "超买线", color=color.red, linestyle=hline.style_dashed)
hline(20, "超卖线", color=color.green, linestyle=hline.style_dashed)
hline(50, "中线", color=color.gray, linestyle=hline.style_dotted)
// 绘制信号标记
plotshape(signal_small_down_fast_j, title="小跌快勾-看涨", location=location.belowbar, color=color.lime, style=shape.triangleup, size=size.small,overlay=true)
plotshape(signal_large_down_slow_j, title="大跌慢勾-看跌", location=location.belowbar, color=color.red, style=shape.triangledown, size=size.small,overlay=true)
plotshape(signal_small_up_fast_j, title="小涨快勾-看跌", location=location.abovebar, color=color.red, style=shape.triangledown, size=size.small,overlay=true)
plotshape(signal_large_up_slow_j, title="大涨慢勾-看涨", location=location.abovebar, color=color.lime, style=shape.triangleup, size=size.small,overlay=true)
Получив код, я первым делом проверил его работоспособность. Редактор Inventor Platform сообщил об отсутствии синтаксических ошибок, и я успокоился.
Затем наступает самый захватывающий момент — онлайн-бэктестинг. На странице Inventor AI Assistant вы можете провести онлайн-бэктестинг в режиме реального времени в правом нижнем углу сгенерированной страницы. Выберите валюту для бэктестинга (BTC), задайте временной диапазон и нажмите кнопку «Онлайн-бэктестинг».

После того, как результаты вышли, я почувствовал себя немного смешанным. Код запустился, и логика казалась правильной, но результаты бэктеста были неидеальными. Хотя были времена, когда я получал прибыль, общий процент выигрышей был невысоким, и я часто терпел последовательные убытки.

Было такое ощущение, будто самолёт, который теоретически должен был летать, построили, но так и не смогли. Я немного расстроился, но не собирался сдаваться.
Если теория имеет смысл и код работает, в чем проблема?
Я начал внимательно изучать логику кода. Я обнаружил, что код, написанный ИИ, в основном оценивает «быстро» и «медленно», вычисляя скорость изменения значения J:
j_speed = j_cumulative_change / j_consecutive_bars
is_j_fast = j_speed > j_speed_threshold
is_j_slow = j_speed <= j_speed_threshold
На первый взгляд, все в порядке, но мне всегда кажется, что что-то не так.
Подумав об этом, я внезапно осознал проблему: этот расчет учитывает только скорость изменения в определенный момент, но не принимает во внимание изменениеНепрерывность。
Например, значение J может быстро меняться в определенный день, но это всего лишь вспышка на сковородке, которая возвращается к норме на следующий день. В этом случае оценка по скорости одной точки может быть неточной.
В оригинальной теории говорится: «быстро отцепляем» и «медленно отцепляем». «Быстро» и «медленно» должны относиться к непрерывному процессу, а не к состоянию в определенной точке.
Осознав эту проблему, я снова немного воодушевился. Мне показалось, что я нашёл ключ, но я не знал, как сменить код.
В это время я вспомнил некоторые комментарии, которые я видел раньше, кто-то упомянул, что мы должны обращать внимание на непрерывность изменений индикаторов. Да, непрерывность! Это именно то, о чем я думал.

Я вернулся к помощнику ИИ и на этот раз высказал четкую просьбу: мы должны подчеркнуть непрерывность изменения значения J. Мы не можем просто смотреть на скорость изменения одной точки, а должны смотреть на тенденцию изменения за несколько последовательных дней.
Решение, предоставленное ИИ на этот раз, было ошеломляющим. Оно переработало всю логику анализа значения J:
Вторая улучшенная версия кода стратегии:
/*backtest
start: 2024-07-18 09:40:00
end: 2025-06-30 15:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/
//@version=5
strategy("KDJ J值连续性变化策略", overlay=false)
// 输入参数
kdj_length = input.int(9, "KDJ周期", minval=1)
k_smooth = input.int(3, "K平滑", minval=1)
d_smooth = input.int(3, "D平滑", minval=1)
j_speed_period = input.int(3, "J值变化速度计算周期", minval=1)
price_change_threshold = input.float(1.5, "价格变化幅度阈值(%)-区分大小幅", minval=0.1) / 100
j_fast_threshold = input.float(15.0, "J值快速变化阈值", minval=1.0)
j_slow_threshold = input.float(5.0, "J值慢速变化阈值", minval=0.1)
j_change_threshold = input.float(1.0, "J值变化忽略阈值-过滤噪音", minval=0.1)
price_change_ignore_threshold = input.float(0.2, "价格变化忽略阈值(%)-过滤噪音", minval=0.01) / 100
min_consecutive_bars = input.int(3, "最小连续K线数", minval=2)
// 风控参数
max_consecutive_trades = input.int(3, "最大连续交易次数", minval=1)
min_bars_between_signals = input.int(5, "信号间最小间隔K线数", minval=1)
enable_stop_loss = input.bool(true, "启用止损")
stop_loss_pct = input.float(3.0, "止损百分比(%)", minval=0.5) / 100
// 计算KDJ指标
highest_high = ta.highest(high, kdj_length)
lowest_low = ta.lowest(low, kdj_length)
rsv = (close - lowest_low) / (highest_high - lowest_low) * 100
k = ta.sma(rsv, k_smooth)
d = ta.sma(k, d_smooth)
j = 3 * k - 2 * d
// 改进的J值连续性变化分析
var float j_cumulative_change = 0.0 // 保持方向性的累计变化(正负数)
var int j_consecutive_bars = 0
var int j_direction = 0 // 1为连续上涨,-1为连续下跌,0为无明确方向
var float j_start_value = 0.0
j_current_change = j - j[1]
// 只有变化幅度超过阈值才认为有方向性
j_current_direction = math.abs(j_current_change) > j_change_threshold ?
(j_current_change > 0 ? 1 : -1) : 0
// 重新设计J值连续性检测逻辑
if j_current_direction != 0
if j_current_direction == j_direction
// 方向相同,继续累计(保持正负号)
j_cumulative_change += j_current_change
j_consecutive_bars += 1
else
// 方向改变,重新开始统计
j_cumulative_change := j_current_change
j_consecutive_bars := 1
j_direction := j_current_direction
j_start_value := j[1]
else
// 变化很小,视为横盘,重置连续性
if j_consecutive_bars > 0
j_consecutive_bars += 1
else
j_cumulative_change := 0.0
j_consecutive_bars := 0
j_direction := 0
// 计算J值连续性指标(保持方向性)
j_total_change = j - j_start_value // 从起始点到当前的总变化(带正负号)
j_avg_speed = j_consecutive_bars > 0 ? j_cumulative_change / j_consecutive_bars : 0 // 平均变化速度(带正负号)
j_abs_avg_speed = math.abs(j_avg_speed) // 变化速度的绝对值
// 新的J值变化判断逻辑
is_j_continuous_up = j_direction == 1 and j_consecutive_bars >= min_consecutive_bars
is_j_continuous_down = j_direction == -1 and j_consecutive_bars >= min_consecutive_bars
// 基于连续性和变化幅度的快慢判断
is_j_fast_up = is_j_continuous_up and j_abs_avg_speed > j_fast_threshold
is_j_slow_up = is_j_continuous_up and j_abs_avg_speed <= j_slow_threshold and j_abs_avg_speed > 0
is_j_fast_down = is_j_continuous_down and j_abs_avg_speed > j_fast_threshold
is_j_slow_down = is_j_continuous_down and j_abs_avg_speed <= j_slow_threshold and j_abs_avg_speed > 0
// 计算价格连续变化幅度(保持原有逻辑)
var float price_cumulative_change = 0.0
var int price_consecutive_bars = 0
var int price_direction = 0
price_current_change = (close - close[1]) / close[1]
price_current_direction = math.abs(price_current_change) > price_change_ignore_threshold ?
(price_current_change > 0 ? 1 : -1) : 0
if price_current_direction != price_direction and price_current_direction != 0
price_cumulative_change := math.abs(price_current_change)
price_consecutive_bars := 1
price_direction := price_current_direction
else if price_current_direction == price_direction and price_current_direction != 0
price_cumulative_change += math.abs(price_current_change)
price_consecutive_bars += 1
else if price_current_direction == 0
price_consecutive_bars += 1
price_change = price_cumulative_change
// 判断价格变化类型
is_small_change = price_change < price_change_threshold
is_large_change = price_change >= price_change_threshold
is_price_up = price_direction == 1
is_price_down = price_direction == -1
// 交易控制变量
var float entry_price = 0.0
// 重新设计策略信号逻辑,强调J值的连续性
// 1. 小幅下跌 + J值连续快速下钩 = 看涨(下跌惯性减弱,J值快速回落)
signal_small_down_fast_j_down = is_small_change and is_price_down and is_j_fast_down
// 2. 大幅下跌 + J值连续慢速下钩 = 看跌(下跌惯性仍在,J值缓慢下行)
signal_large_down_slow_j_down = is_large_change and is_price_down and is_j_slow_down
// 3. 小幅上涨 + J值连续快速上钩 = 看跌(上涨惯性减弱,J值快速回落)
signal_small_up_fast_j_up = is_small_change and is_price_up and is_j_fast_up
// 4. 大幅上涨 + J值连续慢速上钩 = 看涨(上涨惯性仍在,J值持续上行)
signal_large_up_slow_j_up = is_large_change and is_price_up and is_j_slow_up
// 交易信号
long_signal = (signal_small_down_fast_j_down or signal_large_up_slow_j_up)
short_signal = (signal_small_up_fast_j_up or signal_large_down_slow_j_down)
// 执行交易
if long_signal and strategy.position_size == 0
strategy.entry("Long", strategy.long, comment="多头信号开仓")
entry_price := close
if short_signal and strategy.position_size == 0
strategy.entry("Short", strategy.short, comment="空头信号开仓")
entry_price := close
// 平仓条件
if strategy.position_size > 0 and short_signal
strategy.close("Long", comment="信号反转平多仓")
if strategy.position_size < 0 and long_signal
strategy.close("Short", comment="信号反转平空仓")
// 止损平仓
if enable_stop_loss
if strategy.position_size > 0 and close <= entry_price * (1 - stop_loss_pct)
strategy.close("Long", comment="止损平多仓")
if strategy.position_size < 0 and close >= entry_price * (1 + stop_loss_pct)
strategy.close("Short", comment="止损平空仓")
// 绘制指标
plot(j, "J值", color=color.blue, linewidth=2)
hline(80, "超买线", color=color.red, linestyle=hline.style_dashed)
hline(20, "超卖线", color=color.green, linestyle=hline.style_dashed)
hline(50, "中线", color=color.gray, linestyle=hline.style_dotted)
// 在下方子图显示连续性指标
plot(j_consecutive_bars, "连续K线数", color=color.orange)
plot(j_avg_speed, "平均变化速度(带方向)", color=color.purple)
plot(j_abs_avg_speed, "平均变化速度(绝对值)", color=color.yellow)
// 绘制信号标记
plotshape(signal_small_down_fast_j_down, title="小跌+J快速下钩-看涨", location=location.belowbar, color=color.lime, style=shape.triangleup, size=size.small, overlay=true)
plotshape(signal_large_down_slow_j_down, title="大跌+J慢速下钩-看跌", location=location.belowbar, color=color.red, style=shape.triangledown, size=size.small, overlay=true)
plotshape(signal_small_up_fast_j_up, title="小涨+J快速上钩-看跌", location=location.abovebar, color=color.red, style=shape.triangledown, size=size.small, overlay=true)
plotshape(signal_large_up_slow_j_up, title="大涨+J慢速上钩-看涨", location=location.abovebar, color=color.lime, style=shape.triangleup, size=size.small, overlay=true)
// 背景色显示J值连续性状态
bgcolor(is_j_continuous_up ? color.new(color.green, 95) : is_j_continuous_down ? color.new(color.red, 95) : na, title="J值连续性背景")
Увидев это улучшение, я почувствовал волнение, что «это то самое чувство». Теперь изменение значения J больше не является изолированной точкой, а представляет собой непрерывную тенденцию.
Получив новый код, я с нетерпением ждал возможности провести еще одно тестирование.

На этот раз я был немного удивлен результатом. Хотя это нельзя назвать идеальной стратегией, она, очевидно, намного лучше первой версии:
Глядя на кривую прибыли в отчете бэктеста, я испытываю сильное чувство выполненного долга. Хотя это не потрясающая стратегия, она началась с идеи и была исследована шаг за шагом.
Это исследование дало мне новое понимание ИИ-помощников и количественных платформ.
Ценность ИИ-помощников:
Раньше я думал, что ИИ — это просто слабый вспомогательный инструмент интеллекта, но на этот раз я обнаружил, что он действительно полезен в количественной торговле. Наибольшая ценность заключается не в замене человеческого мышления, а в значительном сокращении расстояния от идеи до кода.
Удобство количественной платформы: Я также впечатлен функцией бэктестинга платформы Inventor. Раньше, если вы хотели провести бэктестинг стратегии, вам приходилось находить исторические данные, писать фреймворк бэктестинга и разбираться с различными техническими деталями. Теперь вам нужно только вставить код, задать параметры, и вы можете немедленно увидеть результаты.
Благодаря мгновенной обратной связи весь процесс исследования становится очень гладким, а идеи, код, проверка и улучшение формируют очень быстрый цикл.
Конечно, это исследование также заставило меня задуматься над некоторыми вопросами:
Ограничения ИИ: Хотя ИИ оказал значительную помощь, он всё же не всемогущ. ИИ не может самостоятельно обнаружить проблемы в первой версии кода и по-прежнему нуждается в людях для анализа и оценки. Более того, решение, предложенное ИИ, может быть не лучшим и его необходимо корректировать в соответствии с реальной ситуацией.
Ограничения самой стратегии: Хотя эта стратегия хорошо работает в бэктестинге, она не панацея. Она в целом хорошо работает на волатильных рынках, и параметры необходимо корректировать в соответствии с различными валютами.
Самое важное то, что если стратегия хорошо работает на исторических данных, нельзя предполагать, что она будет эффективна в будущем. Рынок меняется, и стратегии нужно постоянно корректировать.
Помимо самой стратегии, это исследование также принесло некоторые неожиданные выгоды:
Понимание количественной торговли изменилось: Раньше я считал, что порог для количественной торговли очень высок и требует хороших навыков программирования и математической базы. Этот опыт убедил меня, что идеи и логика важнее. С хорошими инструментами технический порог не так высок, как кажется.
Измененное отношение к обучению: Раньше, когда я сталкивался с интересными идеями, я всегда думал: «Я попробую, когда у меня будет время», но это часто оказывалось тщетным. Этот опыт заставил меня понять, что современные инструменты сделали «попробовать» простым, главное — иметь смелость действовать.
Изменился взгляд на неудачу: Когда первая версия стратегии не сработала, я не сдался сразу, а проанализировал проблему. Этот процесс заставил меня понять, что неудача часто не конец, а подсказка для поиска правильного направления.
Эта стратегия всё ещё присутствует в моей кодовой базе, и я иногда её использую. Хотя она не идеальна, я ею вполне доволен, поскольку провёл исследование.
Что еще важнее, этот опыт придал мне уверенности. Оказывается, расстояние от идеи до стратегии не так велико, как я себе представлял.
Далее я хотел бы продолжить это исследование:
Если вас также интересует количественная торговля, вот мои предложения:
Оглядываясь назад, я понимаю, что отправной точкой этого исследования был всего лишь комментарий к Bilibili, но он позволил мне ощутить весь процесс разработки количественной стратегии.
В эпоху, когда знания и инструменты доступны буквально каждому, возможно, нам просто не хватает смелой попытки. Независимо от того, новичок ли вы в количественном анализе или опытный трейдер, вы можете начать с простой идеи и посмотреть, куда можно двигаться.
Следующая интересная стратегия может быть скрыта в комментарии, который вы только что прочитали.
Отказ от ответственности: это всего лишь личный опыт, а не инвестиционный совет. Торговля рискованна, поэтому принимайте решения обдуманно.