1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.
Source Code
2,443 lines of Pine Script v6 from supertrend_dqn.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.
supertrend_dqn.pine
//@version=6
indicator("ML SuperTrend (Ultimate) - Auto-Optimized AI with LSTM", overlay=true, max_labels_count=100, max_lines_count=500, max_bars_back=5000)
// 1. Параметры (ОПТИМИЗИРОВАННЫЕ)
resample_freq = input.int(3, "Resample Frequency", minval=1, maxval=10, group="Optimization")
atr_length = input.int(10, "ATR Length", group="SuperTrend", minval=1)
base_fact = input.float(1.3, "Base Factor", minval=0.1, step=0.01, group="SuperTrend")
use_base_factor = input.bool(true, "Use Base Factor", group="SuperTrend")
src_select = input.string("HLC3", "Source", options=["Close", "HL2", "HLC3", "OHLC4"], group="SuperTrend")
training_period_input = input.int(500, "Training Period", minval=10, maxval=4000, group="ML Settings")
smooth_factor = input.int(3, "Smoothing", minval=1, group="ML Settings")
debug_mode = input.bool(false, "Enable Debug Info", group="Debug")
color_bull = input.color(color.new(color.green, 0), "Bullish Color", group="Visual")
color_bear = input.color(color.new(color.red, 0), "Bearish Color", group="Visual")
norm_length = input.int(100, "Normalization Length", minval=10, maxval=500, group="Neural Network")
// =======================================================================
// ПАРАМЕТРЫ DEBUG ПАНЕЛИ
// =======================================================================
show_debug = input.bool(false, "Show Debug Panel", group="ML Settings")
debug_table_size = input.string("Normal", "Debug Panel Size",
options=["Tiny", "Small", "Normal", "Large", "Huge"],
group="ML Settings")
debug_table_position = input.string("Top Left", "Debug Panel Position",
options=["Top Left", "Top Right", "Bottom Left", "Bottom Right"],
group="ML Settings")
//**********************************************************************************************************************************************
// --- Новые inputs для улучшения отзывчивости (вставь в начало, с остальными input) ---
use_roc_adapt = input.bool(true, "Use ROC for Adaptive Factor", group="SuperTrend")
roc_length = input.int(5, "ROC Length for Volatility", minval=3, group="SuperTrend")
// 1.1. Параметры точности
//use_adaptive_vol_filter = input.bool(true, "Use Adaptive Volatility Filter", group="Accuracy Boost")
vol_filter = input.float(1.1, "Volatility Filter", minval=1.0, step=0.1, group="Accuracy Boost")
adaptive_factor = input.bool(false, " Adaptive Factor", group="Accuracy Boost")
auto_optimize = input.bool(true, "Auto-Optimize Training", group="Accuracy Boost")
// 1.2. Оптимизация ML параметров
auto_train = input.bool(true, "Auto Training Period", group="ML Settings")
min_train_bars = input.int(250, "Min Training Bars", minval=10, maxval=4000, group="ML Settings")
var float learning_rate = input.float(0.01, "Initial Learning Rate", group="Neural Network", step=0.001)
//****************************************************************************************************************
// =================== ПАРАМЕТРЫ РЕГУЛЯЦИИ ===================
float dropout_rate = 0.3 // Вероятность отключения нейрона
float l2_lambda = 0.0008 // Коэффициент L2 для MLP
float l2_lambda_lstm = 0.0003 // Коэффициент L2 для LSTM
var float best_basefact = base_fact // Объявление
// Счётчики для статистики
var float total_l2_loss = 0.0
var float total_train_loss = 0.0
var int update_count = 0
//*****************ишимоку*****************************************************************************************
// Входные параметры для Ichimoku (добавьте в input группу)
var int ichi_conversion_period = 9
var int ichi_base_period = 26
var int ichi_span_period = 52
var int ichi_displacement = 26
ichi_htf = input.timeframe("5", "Higher Timeframe for Ichimoku", group="Ichimoku Settings") // Мульти-таймфрейм
float nn_output_val = na
//**************************************************************************************************************************************
// 🆕 Параметры Adam
var float adam_beta1 = 0.9
var float adam_beta2 = 0.999
var float adam_epsilon = 1e-8
var int adam_timestep = 1
// 🆕 Параметры для LSTM
lstm_hidden_size = input.int(8, "LSTM Hidden Size", minval=1, maxval=32, group="Neural Network")
use_lstm = input.bool(true, "Use LSTM Layer", group="Neural Network")
//***************************************************************************************************************************************************
var float sum_abs_td_error_total = 0.0 // 🆕 Глобальная переменная
var int td_error_count = 0 // 🆕 Счетчик TD-errors
// --- ИНИЦИАЛИЗАЦИЯ ПЕРЕМЕННЫХ ---
// Переменные состояния (var) должны иметь начальное значение.
// Мы установим их на 0.0, предполагая, что они будут обновлены позже.
var float td_error = 0.0 // Абсолютная ошибка (MAE/TD-error)
var float ema_error = 0.0 // EMA ошибки
var float td_error_slow = 0.0
// Константа alpha_slow перенесена для лучшей области видимости
// --- 1. ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ---
my_clamp(float value, float min_val, float max_val) =>
int clamped_val = math.round(math.max(min_val, math.min(max_val, value)))
clamped_val
// --- 2. НАСТРОЙКИ СЕТИ И ГРАНИЦЫ ---
int lstm_timesteps_min = 8
int lstm_timesteps_max = 20
var int lstm_timesteps = input.int(10, "LSTM initial step count", minval=lstm_timesteps_min, maxval=lstm_timesteps_max, group="A. Параметры NN")
int vol_std_length = 20
float atr_val = ta.atr(atr_length)
float atr_ema = ta.ema(atr_val, smooth_factor)
float vol_std = ta.stdev(atr_val, vol_std_length)
// --- 4. ПАРАМЕТРЫ ДИНАМИКИ ---
float threshold = input.float(0.1, "Normalized ATR threshold", minval=0.01, group="C. Логика Адаптации")
float step_factor = input.float(2.0, "ATR step multiplier", minval=1.0, group="C. Логика Адаптации")
// Гистерезис ATR
int min_bars_between_changes = 5
var int bars_since_change = 0
// Параметры TD-Error
float td_error_surge_threshold = 0.1
float td_error_plateau_level = 0.5
float td_error_plateau_diff = 0.01 //minval=0.001
int min_bars_td_cooldown = 10
var int td_cooldown_counter = 0
// 1.3. Параметры KNN/K-Means++
var int k_value = input.int(3, "K-Means Clusters", minval=2, maxval=10, group="KNN Settings")
use_kmeans = input.bool(true, "Use K-Means++ Clustering", group="KNN Settings")
auto_k_value = input.bool(true, "Auto-Select K-Value", group="KNN Settings")
min_k = input.int(2, "Min K-Value", minval=2, maxval=5, group="KNN Settings")
max_k = input.int(5, "Max K-Value", minval=3, maxval=10, group="KNN Settings")
// 1.4. Параметры нейросети
use_nn_filter = input.bool(true, "Use Neural Network Filter", group="Neural Network")
nn_hidden_neurons = input.int(5, "Hidden Neurons", minval=1, maxval=10, group="Neural Network")
use_cnn_filter = input.bool(true, "Use CNN-like Filter", group="Neural Network")
cnn_window_size = input.int(10, "CNN Window Size", minval=5, maxval=50, group="Neural Network")
cnn_filter_size = input.int(3, "CNN Filter Size", minval=2, maxval=10, group="Neural Network")
int num_features = 5 // сколько признаков в каждом State
int num_actions = 8 // количество Q-действий
var int input_size_nn4 = 20
var int hidden_neurons1_nn4 = 24
var int hidden_neurons2_nn4 = 16
var int hidden_neurons3_nn4 = 8
var int hidden_neurons4_nn4 = 4
int input_dim = num_features
var array<float> hidden1_deltas = array.new<float>(hidden_neurons1_nn4, 0.0) // Массив дельт для hidden1
// LSTM веса (расширены для multivariate)
var array<float> lstm_forget_weights_i = array.new<float>(lstm_hidden_size * input_dim, 0.5)
var array<float> lstm_forget_weights_h = array.new<float>(lstm_hidden_size, 0.5)
var array<float> lstm_forget_bias = array.new<float>(lstm_hidden_size, 0.0)
var array<float> lstm_input_weights_i = array.new<float>(lstm_hidden_size * input_dim, 0.5)
var array<float> lstm_input_weights_h = array.new<float>(lstm_hidden_size, 0.5)
var array<float> lstm_input_bias = array.new<float>(lstm_hidden_size, 0.0)
var array<float> lstm_cell_weights_i = array.new<float>(lstm_hidden_size * input_dim, 0.5)
var array<float> lstm_cell_weights_h = array.new<float>(lstm_hidden_size, 0.5)
var array<float> lstm_cell_bias = array.new<float>(lstm_hidden_size, 0.0)
var array<float> lstm_output_weights_i = array.new<float>(lstm_hidden_size * input_dim, 0.5)
var array<float> lstm_output_weights_h = array.new<float>(lstm_hidden_size, 0.5)
var array<float> lstm_output_bias = array.new<float>(lstm_hidden_size, 0.0)
// Adam-массивы (расширены аналогично)
var array<float> m_lstm_forget_weights_i = array.new<float>(lstm_hidden_size * input_dim, 0.0)
var array<float> v_lstm_forget_weights_i = array.new<float>(lstm_hidden_size * input_dim, 0.0)
var array<float> m_lstm_forget_weights_h = array.new<float>(lstm_hidden_size, 0.0)
var array<float> v_lstm_forget_weights_h = array.new<float>(lstm_hidden_size, 0.0)
var array<float> m_lstm_forget_bias = array.new<float>(lstm_hidden_size, 0.0)
var array<float> v_lstm_forget_bias = array.new<float>(lstm_hidden_size, 0.0)
var array<float> m_lstm_input_weights_i = array.new<float>(lstm_hidden_size * input_dim, 0.0)
var array<float> v_lstm_input_weights_i = array.new<float>(lstm_hidden_size * input_dim, 0.0)
var array<float> m_lstm_input_weights_h = array.new<float>(lstm_hidden_size, 0.0)
var array<float> v_lstm_input_weights_h = array.new<float>(lstm_hidden_size, 0.0)
var array<float> m_lstm_input_bias = array.new<float>(lstm_hidden_size, 0.0)
var array<float> v_lstm_input_bias = array.new<float>(lstm_hidden_size, 0.0)
var array<float> m_lstm_cell_weights_i = array.new<float>(lstm_hidden_size * input_dim, 0.0)
var array<float> v_lstm_cell_weights_i = array.new<float>(lstm_hidden_size * input_dim, 0.0)
var array<float> m_lstm_cell_weights_h = array.new<float>(lstm_hidden_size, 0.0)
var array<float> v_lstm_cell_weights_h = array.new<float>(lstm_hidden_size, 0.0)
var array<float> m_lstm_cell_bias = array.new<float>(lstm_hidden_size, 0.0)
var array<float> v_lstm_cell_bias = array.new<float>(lstm_hidden_size, 0.0)
var array<float> m_lstm_output_weights_i = array.new<float>(lstm_hidden_size * input_dim, 0.0)
var array<float> v_lstm_output_weights_i = array.new<float>(lstm_hidden_size * input_dim, 0.0)
var array<float> m_lstm_output_weights_h = array.new<float>(lstm_hidden_size, 0.0)
var array<float> v_lstm_output_weights_h = array.new<float>(lstm_hidden_size, 0.0)
var array<float> m_lstm_output_bias = array.new<float>(lstm_hidden_size, 0.0)
var array<float> v_lstm_output_bias = array.new<float>(lstm_hidden_size, 0.0)
// 🆕 Инициализация полноценной нейросети с плоскими массивами
var float[] weights_input_hidden1 = array.new_float(hidden_neurons1_nn4 * input_size_nn4, 0.0)
var float[] biases_hidden1 = array.new_float(hidden_neurons1_nn4, 0.1)
var float[] weights_hidden1_hidden2 = array.new_float(hidden_neurons2_nn4 * hidden_neurons1_nn4, 0.0)
var float[] biases_hidden2 = array.new_float(hidden_neurons2_nn4, 0.05)
var float[] weights_hidden2_hidden3 = array.new_float(hidden_neurons3_nn4 * hidden_neurons2_nn4, 0.0)
var float[] biases_hidden3 = array.new_float(hidden_neurons3_nn4, 0.02)
var float[] weights_hidden3_output = array.new_float(hidden_neurons3_nn4, 0.3)
var float[] weights_hidden3_hidden4 = array.new_float(hidden_neurons4_nn4 * hidden_neurons3_nn4, 0.0)
var float[] biases_hidden4 = array.new_float(hidden_neurons4_nn4 , 0.02)
var float[] weights_hidden4_output = array.new_float(hidden_neurons4_nn4 , 0.3) // Теперь output подключается к hidden4
var float[] bias_output = array.new_float(1, 0.01) // Wrapped in array
// 🆕 Adam моменты для MLP весов и biases
var float[] m_weights_input_hidden1 = array.new_float(array.size(weights_input_hidden1), 0.0)
var float[] v_weights_input_hidden1 = array.new_float(array.size(weights_input_hidden1), 0.0)
var float[] m_biases_hidden1 = array.new_float(array.size(biases_hidden1), 0.0)
var float[] v_biases_hidden1 = array.new_float(array.size(biases_hidden1), 0.0)
var float[] m_weights_hidden1_hidden2 = array.new_float(array.size(weights_hidden1_hidden2), 0.0)
var float[] v_weights_hidden1_hidden2 = array.new_float(array.size(weights_hidden1_hidden2), 0.0)
var float[] m_biases_hidden2 = array.new_float(array.size(biases_hidden2), 0.0)
var float[] v_biases_hidden2 = array.new_float(array.size(biases_hidden2), 0.0)
var float[] m_weights_hidden2_hidden3 = array.new_float(array.size(weights_hidden2_hidden3), 0.0)
var float[] v_weights_hidden2_hidden3 = array.new_float(array.size(weights_hidden2_hidden3), 0.0)
var float[] m_weights_hidden3_hidden4 = array.new_float(array.size(weights_hidden3_hidden4), 0.0)
var float[] v_weights_hidden3_hidden4 = array.new_float(array.size(weights_hidden3_hidden4), 0.0)
var float[] m_biases_hidden3 = array.new_float(array.size(biases_hidden3), 0.0)
var float[] v_biases_hidden3 = array.new_float(array.size(biases_hidden3), 0.0)
var float[] m_weights_hidden3_output = array.new_float(array.size(weights_hidden3_output), 0.0)
var float[] v_weights_hidden3_output = array.new_float(array.size(weights_hidden3_output), 0.0)
var float[] m_biases_hidden4 = array.new_float(array.size(biases_hidden4), 0.0)
var float[] v_biases_hidden4 = array.new_float(array.size(biases_hidden4), 0.0)
var float[] m_weights_hidden4_output = array.new_float(array.size(weights_hidden4_output), 0.0)
var float[] v_weights_hidden4_output = array.new_float(array.size(weights_hidden4_output), 0.0)
var float[] m_bias_output = array.new_float(1, 0.0)
var float[] v_bias_output = array.new_float(1, 0.0)
// 🆕 Массивы для весов и biases гейтов LSTM (теперь с biases)
var float[] lstm_cell_state = array.new_float(lstm_hidden_size, 0.0)
var float[] lstm_hidden_state = array.new_float(lstm_hidden_size, 0.0)
// 🆕 Массив для истории входов (для нескольких временных шагов)
var float[] input_history = array.new_float(lstm_timesteps, 0.0)
// 🆕 Хранение состояний для BPTT (плоские массивы для каждого timestep)
history_len = input.int(18, "State History Length (bars)", minval=8, maxval=20, group="DQN")
int max_seq_len = math.max(lstm_timesteps, history_len)
var float[] lstm_hidden_history = array.new_float(max_seq_len * lstm_hidden_size, 0.0)
var float[] lstm_cell_history = array.new_float(max_seq_len * lstm_hidden_size, 0.0)
var float[] lstm_forget_history = array.new_float(max_seq_len * lstm_hidden_size, 0.0)
var float[] lstm_input_history = array.new_float(max_seq_len * lstm_hidden_size, 0.0)
var float[] lstm_candidate_history = array.new_float(max_seq_len * lstm_hidden_size, 0.0)
var float[] lstm_output_history = array.new_float(max_seq_len * lstm_hidden_size, 0.0)
// Дельты для LSTM (как в твоём коде)
var array<float> delta_hidden = array.new_float(max_seq_len * lstm_hidden_size, 0.0)
var array<float> delta_cell = array.new_float(max_seq_len * lstm_hidden_size, 0.0)
// Аккумуляторы для градиентов MLP (весов и bias)
var array<float> acc_weights_h4_q = array.new_float(num_actions * hidden_neurons4_nn4, 0.0)
var array<float> acc_biases_q = array.new_float(num_actions, 0.0)
var array<float> acc_weights_h3_h4 = array.new_float(hidden_neurons4_nn4 * hidden_neurons3_nn4, 0.0)
var array<float> acc_biases_h4 = array.new_float(hidden_neurons4_nn4, 0.0)
var array<float> acc_weights_h2_h3 = array.new_float(hidden_neurons3_nn4 * hidden_neurons2_nn4, 0.0)
var array<float> acc_biases_h3 = array.new_float(hidden_neurons3_nn4, 0.0)
var array<float> acc_weights_h1_h2 = array.new_float(hidden_neurons2_nn4 * hidden_neurons1_nn4, 0.0)
var array<float> acc_biases_h2 = array.new_float(hidden_neurons2_nn4, 0.0)
var array<float> acc_weights_in_h1 = array.new_float(hidden_neurons1_nn4 * input_size_nn4, 0.0)
var array<float> acc_biases_h1 = array.new_float(hidden_neurons1_nn4, 0.0)
// Аккумуляторы для градиентов LSTM (весов и bias)
var array<float> acc_lstm_forget_weights_i = array.new_float(lstm_hidden_size * input_dim, 0.0)
var array<float> acc_lstm_forget_weights_h = array.new_float(lstm_hidden_size, 0.0)
var array<float> acc_lstm_forget_bias = array.new_float(lstm_hidden_size, 0.0)
var array<float> acc_lstm_input_weights_i = array.new_float(lstm_hidden_size * input_dim, 0.0)
var array<float> acc_lstm_input_weights_h = array.new_float(lstm_hidden_size, 0.0)
var array<float> acc_lstm_input_bias = array.new_float(lstm_hidden_size, 0.0)
var array<float> acc_lstm_cell_weights_i = array.new_float(lstm_hidden_size * input_dim, 0.0)
var array<float> acc_lstm_cell_weights_h = array.new_float(lstm_hidden_size, 0.0)
var array<float> acc_lstm_cell_bias = array.new_float(lstm_hidden_size, 0.0)
var array<float> acc_lstm_output_weights_i = array.new_float(lstm_hidden_size * input_dim, 0.0)
var array<float> acc_lstm_output_weights_h = array.new_float(lstm_hidden_size, 0.0)
var array<float> acc_lstm_output_bias = array.new_float(lstm_hidden_size, 0.0)
// Аккумуляторы дельт (как в твоём коде)
var array<float> acc_hidden4_deltas = array.new_float(hidden_neurons4_nn4, 0.0)
var array<float> acc_hidden3_deltas = array.new_float(hidden_neurons3_nn4, 0.0)
var array<float> acc_hidden2_deltas = array.new_float(hidden_neurons2_nn4, 0.0)
var array<float> acc_hidden1_deltas = array.new_float(hidden_neurons1_nn4, 0.0)
// =======================================================================
// DEBUG ПЕРЕМЕННЫЕ (ОБЪЯВЛЕНИЕ - ДОЛЖНО БЫТЬ В НАЧАЛЕ)
// =======================================================================
var float debug_avg_reward = 0.0
var float debug_avg_max_q = 0.0
var float debug_avg_target_q = 0.0
var float debug_avg_old_q = 0.0
var float debug_min_td = 0.0
var float debug_max_td = 0.0
var int debug_zero_td_count = 0
var bool debug_all_q_zero = false
var bool debug_all_reward_zero = false
var float debug_grad_norm = 0.0
var int debug_clipping_count = 0
var float debug_max_grad = 0.0
var float debug_mlp_grad_norm = 0.0
var float debug_lstm_grad_norm = 0.0
var float debug_l2_loss = 0.0
var float debug_avg_weight_magnitude = 0.0
var float debug_weight_change_rate = 0.0
var float debug_replay_diversity = 0.0
// 2. Адаптивный расчет периода обучения
max_training_bars = 5000
training_period = auto_train ? math.min(math.max(bar_index, min_train_bars), max_training_bars) : math.min(training_period_input, max_training_bars)
// 3. Выбор источника цены
src = src_select == "Close" ? close : src_select == "HL2" ? hl2 : src_select == "HLC3" ? hlc3 : ohlc4
// 4. Расчет волатильности
atr10 = ta.atr(10)
atr50 = ta.atr(50)
volatility = nz(ta.atr(atr_length), ta.tr)
smooth_vol = nz(ta.ema(volatility, smooth_factor), volatility)
safe_smooth_vol = math.max(nz(smooth_vol, ta.tr), 0.0001)
filtered_vol = math.max(safe_smooth_vol, ta.ema(volatility, 5) * vol_filter)
//******************************************************************************************************************************
//*********************************************************************************************************************************
// 2. КЭШИРОВАНИЕ
var array<float> htf_values_cached = array.new<float>(5, na)
var int last_htf_bar_idx = -1
// 3. DONCHIAN HELPER
f_donchian(int len) =>
(ta.highest(high, len) + ta.lowest(low, len)) / 2.0
// 4. ВЫЧИСЛЯЕМ ТЕКУЩИЙ HTF БАР (ИСПРАВЛЕННО - БЕЗ TRY)
float seconds_in_htf = timeframe.in_seconds(ichi_htf)
int minutes_in_htf = int(seconds_in_htf / 60)
float seconds_in_current = timeframe.in_seconds() // Текущий ТФ
int minutes_in_current = int(seconds_in_current / 60)
int current_htf_bar = 0
if minutes_in_current > 0
int divisor = math.max(1, minutes_in_htf / minutes_in_current)
current_htf_bar := bar_index / divisor
else
current_htf_bar := bar_index
// 5. ICHIMOKU UPDATE
if current_htf_bar != last_htf_bar_idx
// Обновляем request.security только когда htf бар меняется
[t, k, sa, sb, c] = request.security(syminfo.tickerid,ichi_htf,[f_donchian(ichi_conversion_period),f_donchian(ichi_base_period),(f_donchian(ichi_conversion_period) + f_donchian(ichi_base_period)) / 2.0,f_donchian(ichi_span_period),close[ichi_displacement]])
// Сохраняем в кэш
array.set(htf_values_cached, 0, t)
array.set(htf_values_cached, 1, k)
array.set(htf_values_cached, 2, sa)
array.set(htf_values_cached, 3, sb)
array.set(htf_values_cached, 4, c)
last_htf_bar_idx := current_htf_bar
// 6. ИСПОЛЬЗУЕМ КЭШИРОВАННЫЕ ЗНАЧЕНИЯ
htf_tenkan = array.get(htf_values_cached, 0)
htf_kijun = array.get(htf_values_cached, 1)
htf_span_a = array.get(htf_values_cached, 2)
htf_span_b = array.get(htf_values_cached, 3)
htf_chikou = array.get(htf_values_cached, 4)
// 7. ИСПОЛЬЗУЙ ДЛЯ ВХОДОВ NN
raw_ichi_cloud_diff = close - math.max(htf_span_a, htf_span_b)
raw_ichi_above_below = htf_tenkan > htf_kijun ? 1.0 : -1.0
//********************************************************************************************************************
// 🆕 Добавление Heidelberg Volatility (как stdev шума: close - sma)
float raw_heidelberg_vol = ta.stdev(close - ta.sma(close, 20), 20)
// 5. Оптимизированный K-Means++
var float[] kmeans_centroids = array.new_float(k_value)
var bool kmeans_initialized = false
var int best_k = k_value
float assigned_centroid = na
var int last_calculation_bar = 0
// 🆕 Новая упрощенная эвристика для выбора k
if auto_k_value and use_kmeans
volatility_ratio = atr10 / atr50
if volatility_ratio > 1.2
best_k := math.min(5, max_k) // Ограничение max_k
else if volatility_ratio < 0.8
best_k := math.max(2, min_k) // Ограничение min_k
else
best_k := 3 // Средняя волатильность -> стандартные 3 кластера
// Break down the long condition into smaller parts
bool condition1 = use_kmeans
bool condition2 = (bar_index - last_calculation_bar >= 100 or not kmeans_initialized)
bool condition3 = bar_index > training_period
if condition1 and condition2 and condition3
last_calculation_bar := bar_index
float[] vol_data = array.new_float(0)
for i = 0 to training_period - 1
val = filtered_vol[i]
if not na(val)
array.push(vol_data, val)
if array.size(vol_data) > 0
array.clear(kmeans_centroids)
// K-Means++ инициализация
if array.size(kmeans_centroids) == 0
kmeans_centroids := array.new_float(best_k, 0)
array.set(kmeans_centroids, 0, array.get(vol_data, int(math.random() * array.size(vol_data))))
for k = 1 to best_k - 1
float[] distances = array.new_float(array.size(vol_data))
float sum_dist_sq = 0.0
for i = 0 to array.size(vol_data) - 1
float point = array.get(vol_data, i)
float min_dist_sq = 1e10
for cc = 0 to k - 1 // Исправлено: до k-1, так как текущий k еще не установлен
float centroid = array.get(kmeans_centroids, cc)
float dist_sq = (point - centroid) * (point - centroid)
if dist_sq < min_dist_sq
min_dist_sq := dist_sq
array.set(distances, i, min_dist_sq)
sum_dist_sq += min_dist_sq
float target_dist = math.random() * sum_dist_sq
float cumulative_sum = 0.0
int new_centroid_idx = 0
for i = 0 to array.size(distances) - 1
cumulative_sum += array.get(distances, i)
if cumulative_sum >= target_dist
new_centroid_idx := i
break
array.set(kmeans_centroids, k, array.get(vol_data, new_centroid_idx))
// 🆕 Ограничиваем количество итераций для K-Means
for iter = 1 to 5 // Фиксированное количество итераций
float[] new_centroids = array.new_float(best_k, 0)
int[] counts = array.new_int(best_k, 0)
for i = 0 to array.size(vol_data) - 1
float point = array.get(vol_data, i)
float min_dist = 1e10
int label = -1
for c = 0 to best_k - 1
float centroid = array.get(kmeans_centroids, c)
float dist = math.abs(point - centroid)
if dist < min_dist
min_dist := dist
label := c
array.set(new_centroids, label, array.get(new_centroids, label) + point)
array.set(counts, label, array.get(counts, label) + 1)
bool centroids_changed = false
for c = 0 to best_k - 1
if array.get(counts, c) > 0
new_centroid = array.get(new_centroids, c) / array.get(counts, c)
if math.abs(new_centroid - array.get(kmeans_centroids, c)) > 1e-5
centroids_changed := true
array.set(kmeans_centroids, c, new_centroid)
if not centroids_changed
break
kmeans_initialized := true
if use_kmeans
float min_dist = 1e10
for c = 0 to array.size(kmeans_centroids) - 1
float centroid = array.get(kmeans_centroids, c)
float dist = math.abs(filtered_vol - centroid)
if dist < min_dist
min_dist := dist
assigned_centroid := centroid
else
q25 = ta.percentile_nearest_rank(filtered_vol, training_period, 25)
q50 = ta.percentile_nearest_rank(filtered_vol, training_period, 50)
q75 = ta.percentile_nearest_rank(filtered_vol, training_period, 75)
d1 = math.abs(filtered_vol - q25)
d2 = math.abs(filtered_vol - q50)
d3 = math.abs(filtered_vol - q75)
min_dist_q = math.min(d1, math.min(d2, d3))
assigned_centroid := min_dist_q == d1 ? q25 : min_dist_q == d2 ? q50 : q75
assigned_centroid := nz(assigned_centroid, atr10)
final_centroid = assigned_centroid
// 🆕 Leaky ReLU activation
leaky_relu(x) =>
x > 0 ? x : 0.01 * x
// 🆕 Derivative of Leaky ReLU for backprop
leaky_relu_deriv(y) =>
y > 0 ? 1.0 : 0.01
// Пользовательская функция tanh для совместимости (оставляем для LSTM cell)
my_tanh(x) =>
// Защита от переполнения (inf/inf = NaN)
if x > 20.0
1.0
else if x < -20.0
-1.0
else
(math.exp(x) - math.exp(-x)) / (math.exp(x) + math.exp(-x))
// 🆕 Сигмоид для гейтов LSTM
sigmoid(x) =>
1.0 / (1.0 + math.exp(-x))
//********************************************************************************************************************************
// Вычисление attention весов ---
weight_func(offset) =>
base_weight = math.exp(-offset * 0.15)
sharpness = 0.8
base_weight * sharpness
// Функция для вычисления attention весов с учетом скрытых состояний
calc_attention_weights(int seq_len, array<float> current_hidden, array<float> lstm_hidden_history) =>
attn_weights = array.new_float(seq_len, 0.0)
attn_sum = 0.0
for t = 0 to seq_len - 1
dot_product = 0.0
for i = 0 to lstm_hidden_size - 1
dot_product += array.get(current_hidden, i) * array.get(lstm_hidden_history, t * lstm_hidden_size + i)
// 🛡️ ЗАЩИТА: Ограничиваем dot_product, чтобы math.exp не стал бесконечностью
dot_product := math.max(-10.0, math.min(10.0, dot_product))
w = math.exp(dot_product) * 0.8
array.set(attn_weights, t, w)
attn_sum += w
// Нормализация (с защитой от деления на 0)
if attn_sum > 1e-10
for t = 0 to seq_len - 1
old_w = array.get(attn_weights, t)
array.set(attn_weights, t, old_w / attn_sum)
attn_weights
//==================================================================================================================================
lstm_forward(array<float> inputs, int seq_len, int input_dim, bool is_training = false) =>
// 1. ИНИЦИАЛИЗАЦИЯ СОСТОЯНИЯ
// Важно: при обучении всегда начинаем с чистого листа (нулевая память).
// При инференсе (реальная торговля) продолжаем с прошлого состояния.
array<float> current_hidden = is_training ? array.new_float(lstm_hidden_size, 0.0) : array.copy(lstm_hidden_state)
array<float> current_cell = is_training ? array.new_float(lstm_hidden_size, 0.0) : array.copy(lstm_cell_state)
// 2. Расчет весов Attention
// Примечание: Для обучения attention лучше считать на лету или не использовать его совсем,
// так как он добавляет сложность в Backprop. Но если оставляем:
array<float> attn_weights = calc_attention_weights(seq_len, current_hidden, lstm_hidden_history)
float attn_sum = 0.0
for t = 0 to seq_len - 1
attn_sum += array.get(attn_weights, t)
if attn_sum > 0
for t = 0 to seq_len - 1
array.set(attn_weights, t, array.get(attn_weights, t) / attn_sum)
// 3. ПОДГОТОВКА ГЛОБАЛЬНЫХ МАССИВОВ
// Всегда очищаем перед заполнением, так как backpropagation читает именно их
array.fill(lstm_hidden_history, 0.0)
array.fill(lstm_cell_history, 0.0)
array.fill(lstm_forget_history, 0.0)
array.fill(lstm_input_history, 0.0)
array.fill(lstm_candidate_history, 0.0)
array.fill(lstm_output_history, 0.0)
// 4. Основной цикл Forward Pass
for t = 0 to seq_len - 1
array<float> forget_gates = array.new_float(lstm_hidden_size, 0.0)
array<float> input_gates = array.new_float(lstm_hidden_size, 0.0)
array<float> cell_candidates = array.new_float(lstm_hidden_size, 0.0)
array<float> output_gates = array.new_float(lstm_hidden_size, 0.0)
array<float> new_cell = array.new_float(lstm_hidden_size, 0.0)
array<float> new_hidden = array.new_float(lstm_hidden_size, 0.0)
float attn_weight = array.get(attn_weights, t)
for i = 0 to lstm_hidden_size - 1
// --- Forget Gate ---
float input_sum_forget = 0.0
for j = 0 to input_dim - 1
float input_val = array.get(inputs, t * input_dim + j) * attn_weight
input_sum_forget += input_val * array.get(lstm_forget_weights_i, i * input_dim + j)
float forget = sigmoid(input_sum_forget + array.get(current_hidden, i) * array.get(lstm_forget_weights_h, i) + array.get(lstm_forget_bias, i))
array.set(forget_gates, i, forget)
// --- Input Gate ---
float input_sum_input = 0.0
for j = 0 to input_dim - 1
float input_val = array.get(inputs, t * input_dim + j) * attn_weight
input_sum_input += input_val * array.get(lstm_input_weights_i, i * input_dim + j)
float input_gate = sigmoid(input_sum_input + array.get(current_hidden, i) * array.get(lstm_input_weights_h, i) + array.get(lstm_input_bias, i))
array.set(input_gates, i, input_gate)
// --- Cell Candidate ---
float input_sum_cell = 0.0
for j = 0 to input_dim - 1
float input_val = array.get(inputs, t * input_dim + j) * attn_weight
input_sum_cell += input_val * array.get(lstm_cell_weights_i, i * input_dim + j)
float cell_candidate = my_tanh(input_sum_cell + array.get(current_hidden, i) * array.get(lstm_cell_weights_h, i) + array.get(lstm_cell_bias, i))
array.set(cell_candidates, i, cell_candidate)
// --- Output Gate ---
float input_sum_output = 0.0
for j = 0 to input_dim - 1
float input_val = array.get(inputs, t * input_dim + j) * attn_weight
input_sum_output += input_val * array.get(lstm_output_weights_i, i * input_dim + j)
float output_gate = sigmoid(input_sum_output + array.get(current_hidden, i) * array.get(lstm_output_weights_h, i) + array.get(lstm_output_bias, i))
array.set(output_gates, i, output_gate)
// --- Update State ---
float cell = array.get(current_cell, i) * forget + input_gate * cell_candidate
array.set(new_cell, i, cell)
array.set(new_hidden, i, output_gate * my_tanh(cell))
// 5. Сохраняем историю в ГЛОБАЛЬНЫЕ массивы
// Это работает и для обучения, и для инференса
for i = 0 to lstm_hidden_size - 1
array.set(lstm_hidden_history, t * lstm_hidden_size + i, array.get(new_hidden, i))
array.set(lstm_cell_history, t * lstm_hidden_size + i, array.get(new_cell, i))
array.set(lstm_forget_history, t * lstm_hidden_size + i, array.get(forget_gates, i))
array.set(lstm_input_history, t * lstm_hidden_size + i, array.get(input_gates, i))
array.set(lstm_candidate_history, t * lstm_hidden_size + i, array.get(cell_candidates, i))
array.set(lstm_output_history, t * lstm_hidden_size + i, array.get(output_gates, i))
current_cell := new_cell
current_hidden := new_hidden
// 6. Финальное обновление
// ТОЛЬКО при инференсе обновляем глобальную память агента.
// При обучении НЕ трогаем глобальную память, чтобы не ломать текущее состояние агента.
if not is_training
for i = 0 to lstm_hidden_size - 1
array.set(lstm_cell_state, i, array.get(current_cell, i))
array.set(lstm_hidden_state, i, array.get(current_hidden, i))
current_hidden
//====================================================================================
var float raw_input1 = na
var float raw_input2 = na
var float raw_input3 = na
var float raw_input4 = na
var float raw_input5 = na
var float entropy_ma = na
var float entropy_stdev = na
var float raw_input6_entropy = na
var float raw_input9_stoch_k = na
var float raw_input9_stoch_d = na
var float raw_obv_roc = na
var float raw_vwap_diff = na
var float raw_hurst_proxy = na
//**************************************************************************************************************************
// Объявление входных данных для NN в глобальной области (до нормализации)
raw_input1 := ta.roc(close, 10)
raw_input2 := ta.roc(atr10, 5)
raw_input3 := ta.mom(close, 10)
raw_input4 := ta.rsi(close, 14)
raw_input5 := ta.sma(volume, 20) / ta.sma(volume, 50) - 1
entropy_ma := ta.sma(close, cnn_window_size)
entropy_stdev := ta.stdev(close, cnn_window_size)
raw_input6_entropy := entropy_stdev > 0 ? math.sum(math.abs(close - entropy_ma), cnn_window_size) / (entropy_stdev * cnn_window_size) : 0.0
// 🆕 Новый вход: Объёмный осциллятор
vol_osc = (ta.ema(volume, 10) - ta.ema(volume, 20)) / ta.ema(volume, 20)
float raw_input8_vol_osc = vol_osc
// 🆕 Новый вход: Стохастический осциллятор
k = ta.stoch(close, high, low, 14)
d = ta.sma(k, 3)
raw_input9_stoch_k := k / 100 // Нормализация
raw_input9_stoch_d := d / 100 // Нормализация (не используется, но сохранено)
// 🆕 ДОБАВЛЕНИЕ НОВЫХ ПРИЗНАКОВ: OBV, VWAP и прокси Херста
raw_obv_roc := ta.roc(ta.obv, 1)
raw_vwap_diff := (close - ta.vwap) / ta.atr(20)
raw_hurst_proxy := ta.stdev(close, 50) / ta.ema(close, 50)
// ╔════════════════════════════════════════════════════════╗
// ║ УЛУЧШЕННЫЙ CNN ФИЛЬТР (Dual-Kernel v2.0) - Pine v6 ║
// ╚════════════════════════════════════════════════════════╝
// HELPER ФУНКЦИЯ: Z-Score с защитой от нулевого StDev
f_zscore(float value, int length) =>
float mean = ta.sma(value, length)
float dev = ta.stdev(value, length)
dev > 1e-10 ? (value - mean) / dev : 0.0
// НОВАЯ ФУНКЦИЯ: Exponential Weighted Moving Average
f_ewma(float src, float alpha) =>
var float ewma = na
ewma := na(ewma) ? src : alpha * src + (1 - alpha) * ewma
ewma
// ═══ CNN ФИЛЬТР ПАРАМЕТРЫ ═══
cnn_short_window = input.int(5, "CNN Short Window", minval=2, maxval=10, group="CNN Filter")
cnn_long_window = input.int(10, "CNN Long Window", minval=5, maxval=20, group="CNN Filter")
cnn_use_dual = input.bool(true, "Use Dual Kernel", group="CNN Filter")
cnn_activation = input.string("ReLU", "Activation Function", options=["ReLU", "Tanh", "Sigmoid"], group="CNN Filter")
cnn_pooling = input.string("Max", "Pooling Type", options=["Max", "Average", "L2"], group="CNN Filter")
cnn_normalize_length = input.int(20, "Normalization Length", minval=10, maxval=100, group="CNN Filter")
cnn_smooth_output = input.bool(true, "Smooth Output (EWMA)", group="CNN Filter")
cnn_smooth_alpha = input.float(0.3, "Smoothing Alpha", minval=0.01, maxval=0.99, step=0.01, group="CNN Filter")
var float raw_input7 = 0.0
if use_cnn_filter
// ═══ ОПТИМИЗАЦИЯ: Предрасчет весов ═══
float short_weight_sum = 0.0
float long_weight_sum = 0.0
for i = 0 to cnn_short_window - 1
short_weight_sum += math.pow(0.9, i)
if cnn_use_dual
for i = 0 to cnn_long_window - 1
long_weight_sum += math.pow(0.85, i)
// ═══ KERNEL 1: SHORT-TERM FEATURE EXTRACTION ═══
float pooled_short = 0.0
float sum_short = 0.0
int valid_short = 0
for i = 0 to cnn_short_window - 1
// V6: Улучшенная короткозамкнутая оценка (short-circuit)
if bar_index > i and not na(close[i]) and not na(close[i + 1])
float price_change = close[i] - close[i + 1]
float weight = math.pow(0.9, i) / short_weight_sum
float weighted_feature = price_change * weight
float atr_short = ta.atr(14)
// Применение функции активации с защитой от деления на ноль
float activated = cnn_activation == "ReLU" ? math.max(0, weighted_feature) :cnn_activation == "Tanh" and atr_short > 0 ? my_tanh(weighted_feature / atr_short) :atr_short > 0 ? 1 / (1 + math.exp(-weighted_feature / atr_short)) : 0.0
// Pooling
if cnn_pooling == "Max"
pooled_short := math.max(pooled_short, math.abs(activated))
else if cnn_pooling == "Average"
sum_short += activated
valid_short += 1
else // L2 (RMS)
sum_short += math.pow(activated, 2)
valid_short += 1
if cnn_pooling == "Average" and valid_short > 0
pooled_short := sum_short / valid_short
else if cnn_pooling == "L2" and valid_short > 0
pooled_short := math.sqrt(sum_short / valid_short)
// ═══ KERNEL 2: LONG-TERM TREND EXTRACTION ═══
float pooled_long = 0.0
if cnn_use_dual
float sum_long = 0.0
int valid_long = 0
for i = 0 to cnn_long_window - 1
// V6: Короткозамкнутая оценка для оптимизации
if bar_index > i and not na(close[i]) and not na(close[i + 1])
float price_change = close[i] - close[i + 1]
float weight = math.pow(0.85, i) / long_weight_sum
float weighted_feature = price_change * weight
float atr_long = ta.atr(21)
// Активация
float activated = cnn_activation == "ReLU" ? math.max(0, weighted_feature) :cnn_activation == "Tanh" and atr_long > 0 ? my_tanh(weighted_feature / atr_long) :atr_long > 0 ? 1 / (1 + math.exp(-weighted_feature / atr_long)) : 0.0
// Pooling
if cnn_pooling == "Max"
pooled_long := math.max(pooled_long, math.abs(activated))
else if cnn_pooling == "Average"
sum_long += activated
valid_long += 1
else // L2
sum_long += math.pow(activated, 2)
valid_long += 1
if cnn_pooling == "Average" and valid_long > 0
pooled_long := sum_long / valid_long
else if cnn_pooling == "L2" and valid_long > 0
pooled_long := math.sqrt(sum_long / valid_long)
// ═══ FEATURE FUSION: Адаптивное комбинирование ═══
float combined_feature = 0.0
if cnn_use_dual
// Адаптивные веса на основе волатильности
float short_vol = ta.stdev(close, cnn_short_window)
float long_vol = ta.stdev(close, cnn_long_window)
// V6: Короткозамкнутая оценка предотвращает деление на ноль
float vol_ratio = long_vol > 1e-10 ? short_vol / long_vol : 1.0
// Если краткосрочная волатильность высока - больше вес короткому ядру
float adaptive_short_weight = math.min(0.85, 0.5 + vol_ratio * 0.2)
float adaptive_long_weight = 1.0 - adaptive_short_weight
combined_feature := pooled_short * adaptive_short_weight + pooled_long * adaptive_long_weight
else
combined_feature := pooled_short
// ═══ NORMALIZATION ═══
float normalized = f_zscore(combined_feature, cnn_normalize_length)
// ═══ OUTPUT SMOOTHING (опционально) ═══
raw_input7 := cnn_smooth_output ? f_ewma(normalized, cnn_smooth_alpha) : normalized
// V6: Упрощенная защита от NaN (bool не может быть na)
raw_input7 := na(raw_input7) ? 0.0 : raw_input7
// Клиппинг экстремальных значений
raw_input7 := math.max(-5.0, math.min(5.0, raw_input7))
else
raw_input7 := 0.0
// Z-score нормализация входов
float input1 = ta.stdev(raw_input1, norm_length) > 0 ? (raw_input1 - ta.sma(raw_input1, norm_length)) / ta.stdev(raw_input1, norm_length) : 0.0
float input2 = ta.stdev(raw_input2, norm_length) > 0 ? (raw_input2 - ta.sma(raw_input2, norm_length)) / ta.stdev(raw_input2, norm_length) : 0.0
float input3 = ta.stdev(raw_input3, norm_length) > 0 ? (raw_input3 - ta.sma(raw_input3, norm_length)) / ta.stdev(raw_input3, norm_length) : 0.0
float input4 = ta.stdev(raw_input4, norm_length) > 0 ? (raw_input4 - ta.sma(raw_input4, norm_length)) / ta.stdev(raw_input4, norm_length) : 0.0
float input5 = ta.stdev(raw_input5, norm_length) > 0 ? (raw_input5 - ta.sma(raw_input5, norm_length)) / ta.stdev(raw_input5, norm_length) : 0.0
float input6_entropy = ta.stdev(raw_input6_entropy, norm_length) > 0 ? (raw_input6_entropy - ta.sma(raw_input6_entropy, norm_length)) / ta.stdev(raw_input6_entropy, norm_length) : 0.0
float input7 = ta.stdev(raw_input7, norm_length) > 0 ? (raw_input7 - ta.sma(raw_input7, norm_length)) / ta.stdev(raw_input7, norm_length) : 0.0
float input8_vol_osc = ta.stdev(raw_input8_vol_osc, norm_length) > 0 ? (raw_input8_vol_osc - ta.sma(raw_input8_vol_osc, norm_length)) / ta.stdev(raw_input8_vol_osc, norm_length) : 0.0
float input9_stoch_k = ta.stdev(raw_input9_stoch_k, norm_length) > 0 ? (raw_input9_stoch_k - ta.sma(raw_input9_stoch_k, norm_length)) / ta.stdev(raw_input9_stoch_k, norm_length) : 0.0
float obv_roc = ta.stdev(raw_obv_roc, norm_length) > 0 ? (raw_obv_roc - ta.sma(raw_obv_roc, norm_length)) / ta.stdev(raw_obv_roc, norm_length) : 0.0
float vwap_diff = ta.stdev(raw_vwap_diff, norm_length) > 0 ? (raw_vwap_diff - ta.sma(raw_vwap_diff, norm_length)) / ta.stdev(raw_vwap_diff, norm_length) : 0.0
float hurst_proxy = ta.stdev(raw_hurst_proxy, norm_length) > 0 ? (raw_hurst_proxy - ta.sma(raw_hurst_proxy, norm_length)) / ta.stdev(raw_hurst_proxy, norm_length) : 0.0
float heidelberg_vol = ta.stdev(raw_heidelberg_vol, norm_length) > 0 ? (raw_heidelberg_vol - ta.sma(raw_heidelberg_vol, norm_length)) / ta.stdev(raw_heidelberg_vol, norm_length) : 0.0
// 🆕 Нормализация ишимоку
float ichi_cloud_diff = ta.stdev(raw_ichi_cloud_diff, norm_length) != 0 ? (raw_ichi_cloud_diff - ta.sma(raw_ichi_cloud_diff, norm_length)) / ta.stdev(raw_ichi_cloud_diff, norm_length) : 0.0
// Собираем входы в массив (теперь с новыми фичами)
float[] nn_inputs = array.new_float(input_size_nn4, 0.0)
array.set(nn_inputs, 0, input1)
array.set(nn_inputs, 1, input2)
array.set(nn_inputs, 2, input3)
array.set(nn_inputs, 3, input4)
array.set(nn_inputs, 4, input5)
array.set(nn_inputs, 5, input6_entropy)
array.set(nn_inputs, 6, input7)
array.set(nn_inputs, 7, input8_vol_osc)
array.set(nn_inputs, 8, input9_stoch_k)
array.set(nn_inputs, 9, obv_roc)
array.set(nn_inputs, 10, vwap_diff)
array.set(nn_inputs, 11, hurst_proxy)
array.set(nn_inputs, 12, ichi_cloud_diff) // Было ichi_feature1, теперь расширенная
array.set(nn_inputs, 13, heidelberg_vol)
array.set(nn_inputs, 14, 0.0) // Резерв для будущих фич, если нужно
// Position 15 remains 0 if not used
// 8. Полноценная многослойная нейронная сеть (MLP) для фильтрации с LSTM
float nn_filter_value = 1.0
float[] hidden1_output = array.new_float(hidden_neurons1_nn4, na)
float[] hidden2_output = array.new_float(hidden_neurons2_nn4, na)
float[] hidden3_output = array.new_float(hidden_neurons3_nn4, na)
float[] hidden4_output = array.new_float(hidden_neurons4_nn4, na)
// Хранение предыдущих выходов для backprop
var float[] hidden1_output_prev = array.new_float(hidden_neurons1_nn4, 0.0)
var float[] hidden2_output_prev = array.new_float(hidden_neurons2_nn4, 0.0)
var float[] hidden3_output_prev = array.new_float(hidden_neurons3_nn4, 0.0)
var float[] hidden4_output_prev = array.new_float(hidden_neurons4_nn4, 0.0)
var float nn_output_val_prev = na
var float[] nn_inputs_prev = array.new_float(input_size_nn4, 0.0)
if use_nn_filter
// 🆕 Добавляем в историю (shift и push) - для combined_input, но теперь используем сумму для простоты или адаптировать
float combined_input = array.sum(nn_inputs)
array.shift(input_history)
array.push(input_history, combined_input)
//**************************************************************************************************************************************************
//── Inputs DQN (расширенные от вашего RL)
episode_len = input.int(200, "Episode Length (bars)", minval=50, group="DQN")
float epsilon_start = input.float(0.10, "ε-greedy exploration", minval=0.0, maxval=1.0, group="DQN")
var float epsilon = epsilon_start
rl_gamma = input.float(0.99, "Discount γ", group="DQN")
//history_len = input.int(20, "State History Length (bars)", minval=8, maxval=20, group="DQN")
replay_size = input.int(1000, "Replay Buffer Size", minval=100, group="DQN")
train_freq = input.int(10, "Training Frequency (bars)", minval=1, group="DQN")
batch_size = input.int(6, "Batch Size", group="SuperTrend", minval=1)
type State
array<float> features
type StateHistory
array<State> history
//── Веса Main сети
var float[] weights_hidden4_q = array.new_float(hidden_neurons4_nn4 * num_actions, math.random(0.2) - 0.1)
var float[] biases_q = array.new_float(num_actions, 0.01)
//── Веса Target сети (для стабильности DQN)
var float[] weights_hidden4_q_target = array.new_float(hidden_neurons4_nn4 * num_actions, 0.0)
var float[] biases_q_target = array.new_float(num_actions, 0.0)
// Target MLP weights
var float[] weights_input_hidden1_target = array.new_float(hidden_neurons1_nn4 * input_size_nn4, 0.0)
var float[] biases_hidden1_target = array.new_float(hidden_neurons1_nn4, 0.0)
var float[] weights_hidden1_hidden2_target = array.new_float(hidden_neurons2_nn4 * hidden_neurons1_nn4, 0.0)
var float[] biases_hidden2_target = array.new_float(hidden_neurons2_nn4, 0.0)
var float[] weights_hidden2_hidden3_target = array.new_float(hidden_neurons3_nn4 * hidden_neurons2_nn4, 0.0)
var float[] biases_hidden3_target = array.new_float(hidden_neurons3_nn4, 0.0)
var float[] weights_hidden3_hidden4_target = array.new_float(hidden_neurons4_nn4 * hidden_neurons3_nn4, 0.0)
var float[] biases_hidden4_target = array.new_float(hidden_neurons4_nn4, 0.0)
//── Счётчик состояний
var bool[] visited_states = array.new_bool(100000, false)
var int visited_count = 0
//── Persistent
var int rl_bar = 0
var State prev_state = na
var int rl_prevA = na
var int replay_pointer = 0
var int replay_fill = 0
// Adam-arrays для Q-layer
var float[] m_weights_hidden4_q = array.new_float(array.size(weights_hidden4_q), 0.0)
var float[] v_weights_hidden4_q = array.new_float(array.size(weights_hidden4_q), 0.0)
var float[] m_biases_q = array.new_float(array.size(biases_q), 0.0)
var float[] v_biases_q = array.new_float(array.size(biases_q), 0.0)
array<float> q_next = na
float ignored1 = na
float ignored2 = na
var array<float> q_pred = na
// =======================================================================
// ИНИЦИАЛИЗАЦИЯ ВЕСОВ
// =======================================================================
force_reinit = input.bool(false, "Force Reinit Weights", group="ML Settings")
var bool weights_initialized = false
bool should_init = (barstate.isfirst and not weights_initialized) or (force_reinit and not weights_initialized)
if should_init
weights_initialized := true
// ═══ MLP ВЕСА (MAIN) ═══
array.clear(weights_input_hidden1)
for i = 0 to (input_size_nn4 * hidden_neurons1_nn4) - 1
array.push(weights_input_hidden1, math.random(-0.1, 0.1))
array.clear(biases_hidden1)
for i = 0 to hidden_neurons1_nn4 - 1
array.push(biases_hidden1, 0.0)
array.clear(weights_hidden1_hidden2)
for i = 0 to (hidden_neurons1_nn4 * hidden_neurons2_nn4) - 1
array.push(weights_hidden1_hidden2, math.random(-0.1, 0.1))
array.clear(biases_hidden2)
for i = 0 to hidden_neurons2_nn4 - 1
array.push(biases_hidden2, 0.0)
array.clear(weights_hidden2_hidden3)
for i = 0 to (hidden_neurons2_nn4 * hidden_neurons3_nn4) - 1
array.push(weights_hidden2_hidden3, math.random(-0.1, 0.1))
array.clear(biases_hidden3)
for i = 0 to hidden_neurons3_nn4 - 1
array.push(biases_hidden3, 0.0)
array.clear(weights_hidden3_hidden4)
for i = 0 to (hidden_neurons3_nn4 * hidden_neurons4_nn4) - 1
array.push(weights_hidden3_hidden4, math.random(-0.1, 0.1))
array.clear(biases_hidden4)
for i = 0 to hidden_neurons4_nn4 - 1
array.push(biases_hidden4, 0.0)
array.clear(weights_hidden4_q)
for i = 0 to (hidden_neurons4_nn4 * num_actions) - 1
array.push(weights_hidden4_q, math.random(-0.1, 0.1))
array.clear(biases_q)
for i = 0 to num_actions - 1
array.push(biases_q, 0.0)
// ═══ ИНИЦИАЛИЗАЦИЯ TARGET ВЕСОВ (Копия Main) ═══
// ИСПРАВЛЕНО: Используем присваивание результата array.copy()
weights_input_hidden1_target := array.copy(weights_input_hidden1)
biases_hidden1_target := array.copy(biases_hidden1)
weights_hidden1_hidden2_target := array.copy(weights_hidden1_hidden2)
biases_hidden2_target := array.copy(biases_hidden2)
weights_hidden2_hidden3_target := array.copy(weights_hidden2_hidden3)
biases_hidden3_target := array.copy(biases_hidden3)
weights_hidden3_hidden4_target := array.copy(weights_hidden3_hidden4)
biases_hidden4_target := array.copy(biases_hidden4)
weights_hidden4_q_target := array.copy(weights_hidden4_q)
biases_q_target := array.copy(biases_q)
//*****************************************************************************************************************************************************
// Функция Forward Pass (Main/Target switch)
// ИСПРАВЛЕНО: Добавлено значение по умолчанию false
dqn_forward(array<State> state_seq, bool use_target_weights = false, bool is_training_pass = false) =>
int seq_len = array.size(state_seq)
if seq_len == 0
[array.new<float>(num_actions, 0.0), 0.5]
array<float> flat_input = array.new<float>(seq_len * num_features, 0.0)
for i = 0 to seq_len - 1
State s = array.get(state_seq, i)
for j = 0 to num_features - 1
float val = 0.0
if not na(s) and array.size(s.features) > j
val := nz(array.get(s.features, j), 0.0)
array.set(flat_input, i * num_features + j, val)
// LSTM Forward - передаем флаг обучения
array<float> lstm_out = array.new<float>(0)
if use_lstm
lstm_out := lstm_forward(flat_input, seq_len, input_dim, is_training_pass)
if array.size(lstm_out) > 0
for i = 0 to math.min(array.size(lstm_out) - 1, lstm_hidden_size - 1)
int input_idx = 12 + i
if input_idx < input_size_nn4
while array.size(nn_inputs) <= input_idx
array.push(nn_inputs, 0.0)
array.set(nn_inputs, input_idx, nz(array.get(lstm_out, i), 0.0))
if array.size(lstm_out) == 0
for i = 0 to math.min(array.size(flat_input) - 1, input_size_nn4 - 1)
while array.size(nn_inputs) <= i
array.push(nn_inputs, 0.0)
array.set(nn_inputs, i, nz(array.get(flat_input, i), 0.0))
for i = 0 to array.size(nn_inputs) - 1
if i < array.size(nn_inputs_prev)
array.set(nn_inputs_prev, i, array.get(nn_inputs, i))
// MLP Forward
float[] h1_out = array.new_float(hidden_neurons1_nn4, 0.0)
float[] h2_out = array.new_float(hidden_neurons2_nn4, 0.0)
float[] h3_out = array.new_float(hidden_neurons3_nn4, 0.0)
float[] h4_out = array.new_float(hidden_neurons4_nn4, 0.0)
// Layer 1
for i = 0 to hidden_neurons1_nn4 - 1
float sum = use_target_weights ? nz(array.get(biases_hidden1_target, i), 0.0) : nz(array.get(biases_hidden1, i), 0.0)
for j = 0 to input_size_nn4 - 1
float w = use_target_weights ? nz(array.get(weights_input_hidden1_target, i * input_size_nn4 + j), 0.0) : nz(array.get(weights_input_hidden1, i * input_size_nn4 + j), 0.0)
if j < array.size(nn_inputs)
sum += nz(array.get(nn_inputs, j), 0.0) * w
float activated = leaky_relu(sum)
array.set(h1_out, i, activated)
if not use_target_weights and i < array.size(hidden1_output_prev)
array.set(hidden1_output_prev, i, activated)
// Layer 2
for i = 0 to hidden_neurons2_nn4 - 1
float sum = use_target_weights ? nz(array.get(biases_hidden2_target, i), 0.0) : nz(array.get(biases_hidden2, i), 0.0)
for j = 0 to hidden_neurons1_nn4 - 1
float w = use_target_weights ? nz(array.get(weights_hidden1_hidden2_target, i * hidden_neurons1_nn4 + j), 0.0) : nz(array.get(weights_hidden1_hidden2, i * hidden_neurons1_nn4 + j), 0.0)
sum += nz(array.get(h1_out, j), 0.0) * w
float activated = leaky_relu(sum)
array.set(h2_out, i, activated)
if not use_target_weights and i < array.size(hidden2_output_prev)
array.set(hidden2_output_prev, i, activated)
// Layer 3
for i = 0 to hidden_neurons3_nn4 - 1
float sum = use_target_weights ? nz(array.get(biases_hidden3_target, i), 0.0) : nz(array.get(biases_hidden3, i), 0.0)
for j = 0 to hidden_neurons2_nn4 - 1
float w = use_target_weights ? nz(array.get(weights_hidden2_hidden3_target, i * hidden_neurons2_nn4 + j), 0.0) : nz(array.get(weights_hidden2_hidden3, i * hidden_neurons2_nn4 + j), 0.0)
sum += nz(array.get(h2_out, j), 0.0) * w
float activated = leaky_relu(sum)
array.set(h3_out, i, activated)
if not use_target_weights and i < array.size(hidden3_output_prev)
array.set(hidden3_output_prev, i, activated)
// Layer 4
for i = 0 to hidden_neurons4_nn4 - 1
float sum = use_target_weights ? nz(array.get(biases_hidden4_target, i), 0.0) : nz(array.get(biases_hidden4, i), 0.0)
for j = 0 to hidden_neurons3_nn4 - 1
float w = use_target_weights ? nz(array.get(weights_hidden3_hidden4_target, i * hidden_neurons3_nn4 + j), 0.0) : nz(array.get(weights_hidden3_hidden4, i * hidden_neurons3_nn4 + j), 0.0)
sum += nz(array.get(h3_out, j), 0.0) * w
float activated = leaky_relu(sum)
array.set(h4_out, i, activated)
if not use_target_weights and i < array.size(hidden4_output_prev)
array.set(hidden4_output_prev, i, activated)
// Q-VALUES
array<float> q_values = array.new<float>(num_actions, 0.0)
for a = 0 to num_actions - 1
float sum = use_target_weights ? nz(array.get(biases_q_target, a), 0.0) : nz(array.get(biases_q, a), 0.0)
for i = 0 to hidden_neurons4_nn4 - 1
float w = use_target_weights ? nz(array.get(weights_hidden4_q_target, a * hidden_neurons4_nn4 + i), 0.0) : nz(array.get(weights_hidden4_q, a * hidden_neurons4_nn4 + i), 0.0)
sum += nz(array.get(h4_out, i), 0.0) * w
array.set(q_values, a, sum)
float nn_output = 0.5
if array.size(q_values) > 0
nn_output := my_tanh(array.sum(q_values) / num_actions)
[q_values, nn_output]
//********************************************************************************************************************************************************
// Resize Input Logic
needed_size_nn_inputs = lstm_timesteps * input_dim
current_size = array.size(nn_inputs)
if current_size < needed_size_nn_inputs
for _ = current_size to needed_size_nn_inputs - 1
array.push(nn_inputs, 0.0)
else if current_size > needed_size_nn_inputs
new_nn_inputs = array.new_float(needed_size_nn_inputs, 0.0)
for idx = 0 to needed_size_nn_inputs - 1
array.set(new_nn_inputs, idx, array.get(nn_inputs, idx))
nn_inputs := new_nn_inputs
//***********************************************************************************************************************************************************
array_argmax(array<float> arr) =>
int idx = 0
float maxVal = array.get(arr, 0)
for i = 1 to array.size(arr) - 1
float val = array.get(arr, i)
if val > maxVal
maxVal := val
idx := i
idx
//*******************************************************************
// 10. SuperTrend: pine_supertrend
// Функция определена ЗДЕСЬ, чтобы была доступна до вызова
pine_supertrend(factor, atr) =>
src = hlc3
atr_val = nz(atr, 0.0001)
up = src + factor * atr_val
dn = src - factor * atr_val
var float st = na
var int dir = 0
prev_up = nz(up[1], up)
prev_dn = nz(dn[1], dn)
prev_st = nz(st[1], close)
prev_close = nz(close[1], close)
up := up < prev_up or prev_close > prev_up ? up : prev_up
dn := dn > prev_dn or prev_close < prev_dn ? dn : prev_dn
if na(prev_st)
st := up
dir := 1
else
if prev_st == prev_up
dir := close > up ? -1 : 1
st := dir == 1 ? up : dn
else
dir := close < dn ? 1 : -1
st := dir == -1 ? dn : up
[nz(st, close), nz(dir, 1)]
// 9. Адаптивный фактор ATR с NN-фильтром
float vol_roc = ta.roc(ta.atr(roc_length), roc_length)
vol_roc := nz(vol_roc, 0)
var int atr_fast_len = 3
var int atr_normal_len = 10
atr3 = ta.atr(atr_fast_len)
volatility_index = atr50 != 0 ? atr10 / atr50 : 1.0
float smoothed_index = ta.ema(volatility_index, smooth_factor)
float adjust = 1.0 + (vol_roc / 100.0) * (nn_filter_value > 0.5 ? 1.5 : 1.0)
float dynamic_multiplier = 1 / (1 + math.exp(-smoothed_index + 1.0))
dynamic_factor = adaptive_factor ? best_basefact * (0.8 + dynamic_multiplier * adjust) : best_basefact
fast_vol_boost = atr10 != 0 ? math.pow(atr3 / atr10, 0.3) : 1.0
dynamic_factor := dynamic_factor * fast_vol_boost
nn_confidence = nn_filter_value
// =======================================================================
// REINFORCEMENT LEARNING (DQN) - ИСПРАВЛЕННАЯ ВЕРСИЯ
// =======================================================================
var float[] rl_actions = array.from(0.30, 0.40, 0.50, 0.70, 0.90, 1.00, 1.20, 1.50)
float seg_w = 0.05
// Replay buffers
var array<StateHistory> replay_state = array.new<StateHistory>(replay_size)
var array<int> replay_action = array.new_int(replay_size)
var array<float> replay_reward = array.new_float(replay_size)
var array<StateHistory> replay_next_state = array.new<StateHistory>(replay_size)
var array<float> replay_priority = array.new_float(replay_size, 1.0)
var float priority_alpha = 0.6
var float priority_eps = 1e-5
var StateHistory prev_history = na
var array<State> state_history = array.new<State>(history_len)
if barstate.isfirst
State init_st = State.new()
init_st.features := array.new<float>(num_features, 0.0)
StateHistory init_hist = StateHistory.new()
init_hist.history := array.new<State>(history_len)
for j = 0 to history_len - 1
array.set(init_hist.history, j, init_st)
for i = 0 to replay_size - 1
array.set(replay_state, i, init_hist)
array.set(replay_action, i, 0)
array.set(replay_reward, i, 0.0)
array.set(replay_next_state, i, init_hist)
array.set(replay_priority, i, 1.0)
for i = 0 to history_len - 1
array.set(state_history, i, init_st)
float atr_fast = ta.atr(3)
float atr_slow = ta.atr(14)
float atr_ratio = nz(atr_slow != 0 ? atr_fast / atr_slow : 1.0, 1.0)
float nn_conf_vel = nz(nn_confidence - nn_confidence[3], 0.0)
// Используем глобальные переменные: volatility_index, input1, nn_confidence, heidelberg_vol
// Без заглушек - переменные должны быть определены выше в скрипте
array<float> cur_features = array.from(nz(volatility_index, 1.0), nz(input1, close), nz(nn_confidence, 0.5), atr_ratio, nz(heidelberg_vol, 0.0))
State cur_state = State.new()
cur_state.features := cur_features
array.shift(state_history)
array.push(state_history, cur_state)
int rl_state = 0
for i = 0 to history_len - 1
array<float> f = array.get(state_history, i).features
for j = 0 to array.size(f) - 1
rl_state := rl_state * 31 + int(nz(array.get(f, j), 0.0) * 100)
rl_state := math.abs(rl_state % array.size(visited_states))
if not array.get(visited_states, rl_state)
array.set(visited_states, rl_state, true)
visited_count += 1
float fill_pct = (visited_count / array.size(visited_states)) * 100
bool is_ready = fill_pct > 60
if barstate.islast and rl_bar % 100 == 0
label.new(bar_index, high, str.tostring(fill_pct, "%.1f") + "% visited", color=color.blue, style=label.style_label_down)
if is_ready and barstate.islast
label.new(bar_index, low, "Agent ready (>60%)", color=color.green, style=label.style_label_up)
// Action Selection
epsilon := math.max(0.02, epsilon * 0.999)
int rl_act = 0
var array<float> q_vals = na
var float nn_out = na
if math.random() < epsilon
rl_act := int(math.random(0, num_actions - 1))
nn_out := 0.0
else
[q_vals, nn_out] = dqn_forward(state_history, false)
rl_act := array_argmax(q_vals)
nn_filter_value := 1.0 + nn_out * 0.5
// ───────── 9. Генерация коэффициента ─────────
float base_val = rl_act <= 2 ? 0.30 + rl_act * 0.10 + math.random() * 0.10 :
rl_act <= 4 ? 0.70 + (rl_act - 3) * 0.10 + math.random() * 0.10 :
rl_act <= 5 ? 1.00 + math.random() * 0.10 :
rl_act == 6 ? 1.20 + math.random() * 0.10 :
1.40 + math.random() * 0.10
float jitter = (math.random() - 0.5) * 2 * seg_w
float rl_factor = math.max(0.30, math.min(base_val + jitter, 1.50))
dynamic_factor := dynamic_factor * rl_factor
float default_factor = base_fact
float base_cs = nz(best_basefact, default_factor)
float base_factor_to_use = default_factor
raw_factor = dynamic_factor * nn_filter_value * (use_base_factor ? base_factor_to_use : 1.0)
final_factor = math.max(0.1, math.min(raw_factor, 3.0))
// --- ВЫЗОВ SUPERTREND ---
[st, dir_val] = pine_supertrend(final_factor, final_centroid)
//******************************************************************************************************************************************************
bool crossUp = close > st and close[1] <= st[1]
bool crossDown = close < st and close[1] >= st[1]
bool buy_signal = crossUp and barstate.isconfirmed and not na(final_centroid)
bool sell_signal = crossDown and barstate.isconfirmed and not na(final_centroid)
// ВАЖНО: Обновление счетчика баров
rl_bar += 1
// 1. Пытаемся сохранить переход в буфер (используем данные с ПРОШЛОГО бара)
if not na(prev_history) and not na(rl_prevA)
// ═══ ЛОГИКА НАГРАДЫ (RISK-ADJUSTED v2 - NaN Safe) ═══
int prev_dir = dir_val[1]
bool prev_crossUp = crossUp[1]
bool prev_crossDown = crossDown[1]
// --- 1. Безопасный ATR ---
float current_atr = ta.atr(14)
current_atr := na(current_atr) or current_atr < 0.0001 ? 0.0001 : current_atr
// --- 2. Движение цены ---
float price_move = (close - close[1]) / current_atr
// --- 3. Базовая прибыль ---
float base_profit = 0.0
if prev_dir == -1 // Long
base_profit := price_move
else if prev_dir == 1 // Short
base_profit := -price_move
// --- 4. Штраф за транзакции ---
bool did_trade = prev_crossUp or prev_crossDown
float cost_per_trade = 0.3
if did_trade
base_profit -= cost_per_trade
// --- 5. Итоговая награда ---
float reward = base_profit
// 🛡️ ЗАЩИТА: Если вдруг награда NaN (мат. ошибка), ставим 0
if na(reward)
reward := 0.0
// Клиппинг (ограничение) награды
float reward_clip_val = 1.5
reward := math.max(-reward_clip_val, math.min(reward_clip_val, reward))
// Сохранение в буфер
array.set(replay_state, replay_pointer, prev_history)
array.set(replay_action, replay_pointer, rl_prevA)
array.set(replay_reward, replay_pointer, reward)
StateHistory next_hist = StateHistory.new()
next_hist.history := array.copy(state_history)
array.set(replay_next_state, replay_pointer, next_hist)
float max_pr = array.max(replay_priority)
array.set(replay_priority, replay_pointer, nz(max_pr, 1.0) + priority_eps)
replay_pointer := (replay_pointer + 1) % replay_size
replay_fill := math.min(replay_fill + 1, replay_size)
// ═══ КРИТИЧЕСКИ ВАЖНО: ОБНОВЛЕНИЕ СОСТОЯНИЙ (ВНЕ if!) ═══
// Этот код должен выполняться КАЖДЫЙ бар, чтобы на следующем баре было что сохранять.
prev_history := StateHistory.new()
prev_history.history := array.copy(state_history)
rl_prevA := rl_act
//***********************************************************************************************************************************************************
var array<State> state_seq = array.new<State>()
f_create_state(array<float> feats) =>
State st = State.new()
// Явно создаём новый массив для features
st.features := array.new_float()
for i = 0 to array.size(feats) - 1
float val = array.get(feats, i)
array.push(st.features, val)
st
if barstate.isrealtime
array.clear(state_seq)
if lstm_timesteps > 0
start_offset = lstm_timesteps - 1
if start_offset >= 0
offset = start_offset
while offset >= 0
float[] feats = array.new_float()
// Заполняем признаки, замените на ваши вычисления
array.push(feats, input1[offset])
array.push(feats, input2[offset])
array.push(feats, input3[offset])
array.push(feats, input4[offset])
array.push(feats, input5[offset])
array.push(feats, input6_entropy[offset])
array.push(feats, input7[offset])
array.push(feats, input8_vol_osc[offset])
array.push(feats, input9_stoch_k[offset])
array.push(feats, obv_roc[offset])
array.push(feats, vwap_diff[offset])
array.push(feats, hurst_proxy[offset])
array.push(feats, ichi_cloud_diff[offset])
array.push(feats, heidelberg_vol[offset])
State st = f_create_state(feats)
array.push(state_seq, st)
offset := offset - 1
// =======================================================================
// ПЕРЕМЕННЫЕ (В НАЧАЛО СКРИПТА)
// =======================================================================
smooth_len = input.int(20, "EMA Length for Error", group = "ML Settings")
decay_up = input.float(1.05, "LR Multiplier Up (error ↓)", group = "ML Settings")
decay_down = input.float(0.95, "LR Multiplier Down (error ↑)", group = "ML Settings")
min_lr = input.float(0.0001, "Min LR", group = "ML Settings")
max_lr = input.float(0.05, "Max LR", group = "ML Settings")
// Batch size
// Переменные для adaptive LR
var float smooth_error_lr = na
var float last_error_lr = na
var float adaptive_lr = learning_rate
// Переменные для обучения
var float prev_error = 0.0
var float clip_threshold = 1.0
var float alpha_ema = 0.1
var float smooth_error = 0.0
// PER параметры
var float priority_beta = 0.4
var float beta_increment = 0.0005
// =======================================================================
// FORWARD PASS (ВСЕГДА ВЫПОЛНЯЕТСЯ)
// =======================================================================
if array.size(state_seq) > 0 and not na(close)
[q_vals, nn_output_val] = dqn_forward(state_seq)
if not na(nn_output_val)
nn_output_val_prev := nn_output_val
else
nn_output_val_prev := nz(nn_output_val_prev, 0.5)
// =======================================================================
// ADAPTIVE LEARNING RATE (НА ОСНОВЕ ОШИБКИ)
// =======================================================================
// Обновляем smooth_error для gradient clipping
smooth_error := 0.9 * smooth_error + 0.1 * ema_error
// Adaptive LR на основе EMA ошибки
if not na(ema_error) and ema_error > 0
smooth_error_lr := na(smooth_error_lr) ? ema_error : ta.ema(ema_error, smooth_len)
if not na(last_error_lr)
if smooth_error_lr < last_error_lr
adaptive_lr := math.min(adaptive_lr * decay_up, max_lr)
else
adaptive_lr := math.max(adaptive_lr * decay_down, min_lr)
learning_rate := adaptive_lr
last_error_lr := smooth_error_lr
// =======================================================================
// ОСНОВНОЙ БЛОК ДИНАМИЧЕСКОЙ КОРРЕКЦИИ
// =======================================================================
float atr_change_raw = atr_ema - atr_ema[1]
float atr_change_norm = atr_change_raw / (vol_std + 1e-10)
int atr_change_ema_length = 3
float atr_change_ema = ta.ema(atr_change_norm, atr_change_ema_length)
if barstate.isconfirmed or barstate.isrealtime
// Флаг для отслеживания изменения TD-Error
var bool td_change_made = false
// Обновление медленного EMA ошибки для анализа TD-Error
td_error_slow := learning_rate * td_error + (1 - learning_rate) * td_error_slow
// --- 1. Адаптация на основе Волатильности (ATR) с Гистерезисом ---
if bars_since_change >= min_bars_between_changes
float step_size_float = math.abs(atr_change_ema) * step_factor
int step_size = math.max(1, math.round(step_size_float))
if atr_change_ema > threshold
// Растущая волатильность -> Увеличить timesteps
lstm_timesteps := my_clamp(float(lstm_timesteps) + step_size, float(lstm_timesteps_min), float(lstm_timesteps_max))
bars_since_change := 0
else if atr_change_ema < -threshold
// Падающая волатильность -> Уменьшить timesteps
lstm_timesteps := my_clamp(float(lstm_timesteps) - step_size, float(lstm_timesteps_min), float(lstm_timesteps_max))
bars_since_change := 0
// Увеличение счётчика охлаждения ATR
bars_since_change := bars_since_change + 1
// --- 2. Принудительное Сокращение / Расширение Памяти (TD-Error) ---
if td_cooldown_counter >= min_bars_td_cooldown
// **ЛОГИКА A: ВСПЛЕСК ОШИБКИ**
if td_error > td_error_slow + td_error_surge_threshold
// Всплеск ошибки -> Сокращаем timesteps (фокус на свежайших данных)
lstm_timesteps := my_clamp(float(lstm_timesteps) - 1, float(lstm_timesteps_min), float(lstm_timesteps_max))
td_change_made := true
// **ЛОГИКА B: ПЛАТО ОШИБКИ**
else if math.abs(td_error - td_error_slow) < td_error_plateau_diff and td_error > td_error_plateau_level
// Ошибка застряла на высоком уровне -> Увеличиваем timesteps (нужен новый, более широкий контекст)
lstm_timesteps := my_clamp(float(lstm_timesteps) + 1, float(lstm_timesteps_min), float(lstm_timesteps_max))
td_change_made := true
// Управление счётчиком охлаждения TD
if td_change_made
td_cooldown_counter := 0
else
td_cooldown_counter := td_cooldown_counter + 1
// 🆕 Функция для обновления с Adam (ИСПРАВЛЕНО)
adam_update(float[] param, float[] m, float[] v, float g, int idx) =>
// 1. Обновляем моменты (biased)
float m_new = adam_beta1 * array.get(m, idx) + (1 - adam_beta1) * g
float v_new = adam_beta2 * array.get(v, idx) + (1 - adam_beta2) * g * g
// 2. Коррекция смещения (bias correction)
// adam_timestep теперь инициализируется как 1, поэтому деления на ноль не будет
float m_hat = m_new / (1 - math.pow(adam_beta1, adam_timestep))
float v_hat = v_new / (1 - math.pow(adam_beta2, adam_timestep))
// 3. Обновляем веса
array.set(param, idx, array.get(param, idx) - adaptive_lr * m_hat / (math.sqrt(v_hat) + adam_epsilon))
// 4. Сохраняем моменты
array.set(m, idx, m_new)
array.set(v, idx, v_new)
// Учитываем максимум из текущего шага LSTM и глубины истории для обучения
int needed_size = math.max(lstm_timesteps, history_len) * lstm_hidden_size
// Логика изменения размера массива (безопасная)
int current_size_local = array.size(delta_hidden)
if current_size_local < needed_size
// Если массив меньше нужного - добавляем элементы
for _ = current_size_local to needed_size - 1
array.push(delta_hidden, 0.0)
else if current_size_local > needed_size
// Если массив больше нужного - создаем новый (усекаем)
float[] new_array = array.new_float(needed_size, 0.0)
for idx = 0 to needed_size - 1
array.set(new_array, idx, array.get(delta_hidden, idx))
delta_hidden := new_array
f_resize_array(arr, needed_size) =>
current_size = array.size(arr)
if current_size < needed_size
for _ = current_size to needed_size - 1
array.push(arr, 0.0)
arr
else if current_size > needed_size
new_arr = array.new_float(needed_size, 0.0)
for idx = 0 to needed_size - 1
array.set(new_arr, idx, array.get(arr, idx))
new_arr
else
arr
delta_hidden := f_resize_array(delta_hidden, needed_size)
delta_cell := f_resize_array(delta_cell, needed_size)
lstm_cell_history := f_resize_array(lstm_cell_history, needed_size)
lstm_hidden_history := f_resize_array(lstm_hidden_history, needed_size)
lstm_forget_history := f_resize_array(lstm_forget_history, needed_size)
lstm_input_history := f_resize_array(lstm_input_history, needed_size)
lstm_candidate_history := f_resize_array(lstm_candidate_history, needed_size)
lstm_output_history := f_resize_array(lstm_output_history, needed_size)
// Расчёт L2-штрафа для одного набора весов
calculate_l2_penalty(array<float> weights, float lambda) =>
float penalty = 0.0
for i = 0 to array.size(weights) - 1
float w = array.get(weights, i)
if not na(w)
penalty += lambda * w * w
penalty
// Добавляет L2-градиент к градиентам весов
add_l2_gradient(array<float> weights, float lambda) =>
array<float> l2_grads = array.new_float(array.size(weights), 0.0)
for i = 0 to array.size(weights) - 1
float w = array.get(weights, i)
if not na(w)
array.set(l2_grads, i, 2.0 * lambda * w)
l2_grads
// Применяет L2-градиент к накопленным градиентам
apply_l2_to_accumulated_gradients(array<float> acc_grads, array<float> weights, float lambda) =>
for i = 0 to array.size(acc_grads) - 1
float w = array.get(weights, i)
if not na(w)
float l2_grad = 2.0 * lambda * w
array.set(acc_grads, i, array.get(acc_grads, i) + l2_grad)
//************************************************************************************************************************************************************
// Функция для адаптивного clip_threshold_gate
adaptive_clip_threshold_gate(float error_ema) =>
float min_clip = 0.5 // Минимальный порог
float max_clip = 2.0 // Максимальный порог
float base_error = 1.0 // Базовый уровень ошибки для нормализации
float factor = math.min(1.0, error_ema / base_error) // Фактор адаптации
min_clip + (max_clip - min_clip) * factor // Динамический порог (0.5–2.0)
// Функция backpropagation для DQN с поддержкой mini-batch, LSTM (BPTT), Dropout и L2
// =======================================================================
// ФУНКЦИЯ BACKPROPAGATION (ИСПРАВЛЕННАЯ - ФИНАЛЬНАЯ ВЕРСИЯ)
// =======================================================================
backpropagation(array<int> batch_indices, array<float> is_weights, int batch_len) =>
// Если батч пуст
if batch_len == 0
[0.0, 0.0, 0.0, 0.0, 0, 0.0, 0.0, array.new_float(5, 0.0)]
float batch_len_f = float(batch_len)
float total_steps = batch_len_f * float(history_len)
// 1. Обнуляем аккумуляторы
array.fill(acc_weights_h4_q, 0.0)
array.fill(acc_biases_q, 0.0)
array.fill(acc_weights_h3_h4, 0.0)
array.fill(acc_biases_h4, 0.0)
array.fill(acc_weights_h2_h3, 0.0)
array.fill(acc_biases_h3, 0.0)
array.fill(acc_weights_h1_h2, 0.0)
array.fill(acc_biases_h2, 0.0)
array.fill(acc_weights_in_h1, 0.0)
array.fill(acc_biases_h1, 0.0)
if use_lstm
array.fill(acc_lstm_forget_weights_i, 0.0)
array.fill(acc_lstm_forget_weights_h, 0.0)
array.fill(acc_lstm_forget_bias, 0.0)
array.fill(acc_lstm_input_weights_i, 0.0)
array.fill(acc_lstm_input_weights_h, 0.0)
array.fill(acc_lstm_input_bias, 0.0)
array.fill(acc_lstm_cell_weights_i, 0.0)
array.fill(acc_lstm_cell_weights_h, 0.0)
array.fill(acc_lstm_cell_bias, 0.0)
array.fill(acc_lstm_output_weights_i, 0.0)
array.fill(acc_lstm_output_weights_h, 0.0)
array.fill(acc_lstm_output_bias, 0.0)
// Переменные
float avg_error = 0.0
float total_l2 = 0.0
// Debug counters
float debug_sum_reward = 0.0
float debug_sum_target_q = 0.0
float debug_sum_old_q = 0.0
float debug_sum_max_q = 0.0
int debug_zero_td_count_local = 0
// ═══ ЦИКЛ ПО БАТЧУ ═══
for b = 0 to batch_len - 1
int sample_idx = array.get(batch_indices, b)
float is_w = array.get(is_weights, b)
// A. Данные
StateHistory sample_state_hist = array.get(replay_state, sample_idx)
int sample_act = array.get(replay_action, sample_idx)
float sample_reward = array.get(replay_reward, sample_idx)
StateHistory sample_next_hist = array.get(replay_next_state, sample_idx)
// B. Forward Next
[q_next, _] = dqn_forward(sample_next_hist.history,true,true)
float max_q = array.max(q_next)
float target_q = sample_reward + rl_gamma * max_q
// C. Forward Current (обновляет скрытые слои)
[q_pred, _] = dqn_forward(sample_state_hist.history, false,true)
float old_q = array.get(q_pred, sample_act)
// D. Ошибка
float pure_td_error = target_q - old_q
// Сбор статистики
debug_sum_reward += sample_reward
debug_sum_target_q += target_q
debug_sum_old_q += old_q
debug_sum_max_q += max_q
if math.abs(pure_td_error) < 0.0001
debug_zero_td_count_local += 1
// PER Update
array.set(replay_priority, sample_idx, math.abs(pure_td_error) + priority_eps)
float loss = math.pow(pure_td_error, 2)
float linear_td_error = math.sqrt(math.abs(loss)) * math.sign(pure_td_error)
float scaled_td_error = linear_td_error * is_w
avg_error += math.abs(pure_td_error)
// E. Backward Pass (MLP)
float output_delta = scaled_td_error
// 1. Output Layer
for i = 0 to hidden_neurons4_nn4 - 1
float g = output_delta * array.get(hidden4_output_prev, i)
int idx = sample_act * hidden_neurons4_nn4 + i
array.set(acc_weights_h4_q, idx, array.get(acc_weights_h4_q, idx) + g)
array.set(acc_biases_q, sample_act, array.get(acc_biases_q, sample_act) + output_delta)
// 2. Hidden4
float[] hidden4_deltas = array.new_float(hidden_neurons4_nn4, 0.0)
for i = 0 to hidden_neurons4_nn4 - 1
float err = output_delta * array.get(weights_hidden4_q, sample_act * hidden_neurons4_nn4 + i)
float delta = err * leaky_relu_deriv(array.get(hidden4_output_prev, i))
array.set(hidden4_deltas, i, delta)
array.set(acc_biases_h4, i, array.get(acc_biases_h4, i) + delta)
for i = 0 to hidden_neurons4_nn4 - 1
for j = 0 to hidden_neurons3_nn4 - 1
int idx = i * hidden_neurons3_nn4 + j
float g = array.get(hidden4_deltas, i) * array.get(hidden3_output_prev, j)
array.set(acc_weights_h3_h4, idx, array.get(acc_weights_h3_h4, idx) + g)
// 3. Hidden3
float[] hidden3_deltas = array.new_float(hidden_neurons3_nn4, 0.0)
for i = 0 to hidden_neurons3_nn4 - 1
float err = 0.0
for k = 0 to hidden_neurons4_nn4 - 1
err += array.get(hidden4_deltas, k) * array.get(weights_hidden3_hidden4, k * hidden_neurons3_nn4 + i)
float delta = err * leaky_relu_deriv(array.get(hidden3_output_prev, i))
array.set(hidden3_deltas, i, delta)
array.set(acc_biases_h3, i, array.get(acc_biases_h3, i) + delta)
for i = 0 to hidden_neurons3_nn4 - 1
for j = 0 to hidden_neurons2_nn4 - 1
int idx = i * hidden_neurons2_nn4 + j
float g = array.get(hidden3_deltas, i) * array.get(hidden2_output_prev, j)
array.set(acc_weights_h2_h3, idx, array.get(acc_weights_h2_h3, idx) + g)
// 4. Hidden2
float[] hidden2_deltas = array.new_float(hidden_neurons2_nn4, 0.0)
for i = 0 to hidden_neurons2_nn4 - 1
float err = 0.0
for k = 0 to hidden_neurons3_nn4 - 1
err += array.get(hidden3_deltas, k) * array.get(weights_hidden2_hidden3, k * hidden_neurons2_nn4 + i)
float delta = err * leaky_relu_deriv(array.get(hidden2_output_prev, i))
array.set(hidden2_deltas, i, delta)
array.set(acc_biases_h2, i, array.get(acc_biases_h2, i) + delta)
for i = 0 to hidden_neurons2_nn4 - 1
for j = 0 to hidden_neurons1_nn4 - 1
int idx = i * hidden_neurons1_nn4 + j
float g = array.get(hidden2_deltas, i) * array.get(hidden1_output_prev, j)
array.set(acc_weights_h1_h2, idx, array.get(acc_weights_h1_h2, idx) + g)
// 5. Hidden1
float[] hidden1_deltas = array.new_float(hidden_neurons1_nn4, 0.0)
for i = 0 to hidden_neurons1_nn4 - 1
float err = 0.0
for k = 0 to hidden_neurons2_nn4 - 1
err += array.get(hidden2_deltas, k) * array.get(weights_hidden1_hidden2, k * hidden_neurons1_nn4 + i)
float delta = err * leaky_relu_deriv(array.get(hidden1_output_prev, i))
array.set(hidden1_deltas, i, delta)
array.set(acc_biases_h1, i, array.get(acc_biases_h1, i) + delta)
// Input -> Hidden1 Weights
for i = 0 to hidden_neurons1_nn4 - 1
for j = 0 to input_size_nn4 - 1
int idx = i * input_size_nn4 + j
float g = array.get(hidden1_deltas, i) * array.get(nn_inputs_prev, j)
array.set(acc_weights_in_h1, idx, array.get(acc_weights_in_h1, idx) + g)
// ═══ F. LSTM Backpropagation (ВЫНЕСЕНО ИЗ ЦИКЛОВ MLP) ═══
// Используем hidden1_deltas (локальный градиент текущего примера)
if use_lstm
// Используем реальную историю из sample_state_hist, а не nn_inputs_prev
int seq_len = array.size(sample_state_hist.history)
array<float> lstm_inputs_flat = array.new_float(seq_len * input_dim, 0.0)
for t = 0 to seq_len - 1
State s = array.get(sample_state_hist.history, t)
for j = 0 to input_dim - 1
float val = 0.0
if not na(s) and array.size(s.features) > j
val := nz(array.get(s.features, j), 0.0)
array.set(lstm_inputs_flat, t * input_dim + j, val)
array.fill(delta_hidden, 0.0)
array.fill(delta_cell, 0.0)
int lstm_start = 12
float[] delta_lstm_ht = array.new_float(lstm_hidden_size, 0.0)
for lstm_i = 0 to lstm_hidden_size - 1
int input_j = lstm_start + lstm_i
if input_j >= input_size_nn4
continue
float delta_input_j = 0.0
for hidden1_k = 0 to hidden_neurons1_nn4 - 1
int weight_idx = hidden1_k * input_size_nn4 + input_j
delta_input_j += array.get(hidden1_deltas, hidden1_k) * array.get(weights_input_hidden1, weight_idx)
array.set(delta_lstm_ht, lstm_i, delta_input_j)
for i = 0 to lstm_hidden_size - 1
if na(array.get(delta_lstm_ht, i))
continue
array.set(delta_hidden, (seq_len - 1) * lstm_hidden_size + i, array.get(delta_lstm_ht, i))
// Обратное распространение через время (BPTT)
for t_forward = 0 to seq_len - 1
int t = seq_len - 1 - t_forward
for i = 0 to lstm_hidden_size - 1
float cell_tanh = my_tanh(array.get(lstm_cell_history, t * lstm_hidden_size + i))
float delta_c = array.get(delta_hidden, t * lstm_hidden_size + i) * array.get(lstm_output_history, t * lstm_hidden_size + i) * (1 - cell_tanh * cell_tanh)
delta_c := math.min(math.max(delta_c, -clip_threshold), clip_threshold)
if t < seq_len - 1
delta_c += array.get(delta_cell, (t + 1) * lstm_hidden_size + i) * array.get(lstm_forget_history, (t + 1) * lstm_hidden_size + i)
array.set(delta_cell, t * lstm_hidden_size + i, delta_c)
float delta_output = array.get(delta_hidden, t * lstm_hidden_size + i) * cell_tanh * array.get(lstm_output_history, t * lstm_hidden_size + i) * (1 - array.get(lstm_output_history, t * lstm_hidden_size + i))
float delta_forget = delta_c * (t > 0 ? array.get(lstm_cell_history, (t - 1) * lstm_hidden_size + i) : 0.0) * array.get(lstm_forget_history, t * lstm_hidden_size + i) * (1 - array.get(lstm_forget_history, t * lstm_hidden_size + i))
float delta_input = delta_c * array.get(lstm_candidate_history, t * lstm_hidden_size + i) * array.get(lstm_input_history, t * lstm_hidden_size + i) * (1 - array.get(lstm_input_history, t * lstm_hidden_size + i))
float delta_candidate = delta_c * array.get(lstm_input_history, t * lstm_hidden_size + i) * (1 - math.pow(array.get(lstm_candidate_history, t * lstm_hidden_size + i), 2))
float clip_threshold_gate = adaptive_clip_threshold_gate(smooth_error)
delta_output := math.min(math.max(delta_output, -clip_threshold_gate), clip_threshold_gate)
delta_forget := math.min(math.max(delta_forget, -clip_threshold_gate), clip_threshold_gate)
delta_input := math.min(math.max(delta_input, -clip_threshold_gate), clip_threshold_gate)
delta_candidate := math.min(math.max(delta_candidate, -clip_threshold_gate), clip_threshold_gate)
float prev_hidden = (t > 0) ? array.get(lstm_hidden_history, (t - 1) * lstm_hidden_size + i) : 0.0
for j = 0 to input_dim - 1
float input_val = array.get(lstm_inputs_flat, t * input_dim + j)
int idx = i * input_dim + j
array.set(acc_lstm_forget_weights_i, idx, array.get(acc_lstm_forget_weights_i, idx) + delta_forget * input_val)
array.set(acc_lstm_input_weights_i, idx, array.get(acc_lstm_input_weights_i, idx) + delta_input * input_val)
array.set(acc_lstm_cell_weights_i, idx, array.get(acc_lstm_cell_weights_i, idx) + delta_candidate * input_val)
array.set(acc_lstm_output_weights_i, idx, array.get(acc_lstm_output_weights_i, idx) + delta_output * input_val)
array.set(acc_lstm_forget_weights_h, i, array.get(acc_lstm_forget_weights_h, i) + delta_forget * prev_hidden)
array.set(acc_lstm_forget_bias, i, array.get(acc_lstm_forget_bias, i) + delta_forget)
array.set(acc_lstm_input_weights_h, i, array.get(acc_lstm_input_weights_h, i) + delta_input * prev_hidden)
array.set(acc_lstm_input_bias, i, array.get(acc_lstm_input_bias, i) + delta_input)
array.set(acc_lstm_cell_weights_h, i, array.get(acc_lstm_cell_weights_h, i) + delta_candidate * prev_hidden)
array.set(acc_lstm_cell_bias, i, array.get(acc_lstm_cell_bias, i) + delta_candidate)
array.set(acc_lstm_output_weights_h, i, array.get(acc_lstm_output_weights_h, i) + delta_output * prev_hidden)
array.set(acc_lstm_output_bias, i, array.get(acc_lstm_output_bias, i) + delta_output)
if t > 0
float delta_h_prev = delta_forget * array.get(lstm_forget_weights_h, i) + delta_input * array.get(lstm_input_weights_h, i) + delta_candidate * array.get(lstm_cell_weights_h, i) + delta_output * array.get(lstm_output_weights_h, i)
array.set(delta_hidden, (t - 1) * lstm_hidden_size + i, array.get(delta_hidden, (t - 1) * lstm_hidden_size + i) + delta_h_prev)
// ═══ РАСЧЕТ ГРАДИЕНТОВ ═══
float mlp_grad_sum = 0.0
int grad_count_mlp = 0
for i = 0 to array.size(acc_weights_h4_q) - 1
float g = array.get(acc_weights_h4_q, i)
if not na(g) and math.abs(g) > 1e-10
mlp_grad_sum += g * g
grad_count_mlp += 1
for i = 0 to array.size(acc_weights_h3_h4) - 1
float g = array.get(acc_weights_h3_h4, i)
if not na(g) and math.abs(g) > 1e-10
mlp_grad_sum += g * g
grad_count_mlp += 1
for i = 0 to array.size(acc_weights_h2_h3) - 1
float g = array.get(acc_weights_h2_h3, i)
if not na(g) and math.abs(g) > 1e-10
mlp_grad_sum += g * g
grad_count_mlp += 1
for i = 0 to array.size(acc_weights_h1_h2) - 1
float g = array.get(acc_weights_h1_h2, i)
if not na(g) and math.abs(g) > 1e-10
mlp_grad_sum += g * g
grad_count_mlp += 1
for i = 0 to array.size(acc_weights_in_h1) - 1
float g = array.get(acc_weights_in_h1, i)
if not na(g) and math.abs(g) > 1e-10
mlp_grad_sum += g * g
grad_count_mlp += 1
float mlp_grad_norm = grad_count_mlp > 0 ? math.sqrt(mlp_grad_sum / float(grad_count_mlp)) : 0.0
float lstm_grad_sum = 0.0
int grad_count_lstm = 0
if use_lstm
for i = 0 to array.size(acc_lstm_forget_weights_i) - 1
float g = array.get(acc_lstm_forget_weights_i, i)
if not na(g) and math.abs(g) > 1e-10
lstm_grad_sum += g * g
grad_count_lstm += 1
for i = 0 to array.size(acc_lstm_input_weights_i) - 1
float g = array.get(acc_lstm_input_weights_i, i)
if not na(g) and math.abs(g) > 1e-10
lstm_grad_sum += g * g
grad_count_lstm += 1
for i = 0 to array.size(acc_lstm_cell_weights_i) - 1
float g = array.get(acc_lstm_cell_weights_i, i)
if not na(g) and math.abs(g) > 1e-10
lstm_grad_sum += g * g
grad_count_lstm += 1
for i = 0 to array.size(acc_lstm_output_weights_i) - 1
float g = array.get(acc_lstm_output_weights_i, i)
if not na(g) and math.abs(g) > 1e-10
lstm_grad_sum += g * g
grad_count_lstm += 1
float lstm_grad_norm = grad_count_lstm > 0 ? math.sqrt(lstm_grad_sum / float(grad_count_lstm)) : 0.0
float grad_norm = math.sqrt(mlp_grad_sum + lstm_grad_sum)
int clipping_count = grad_norm > clip_threshold ? 1 : 0
float max_grad = math.max(mlp_grad_norm, lstm_grad_norm)
// ═══ ОБНОВЛЕНИЕ ВЕСОВ MLP ═══
apply_l2_to_accumulated_gradients(acc_weights_h4_q, weights_hidden4_q, l2_lambda)
for i = 0 to array.size(acc_weights_h4_q) - 1
float g_avg = array.get(acc_weights_h4_q, i) / batch_len_f
adam_update(weights_hidden4_q, m_weights_hidden4_q, v_weights_hidden4_q, g_avg, i)
for i = 0 to array.size(acc_biases_q) - 1
float g_bias_avg = array.get(acc_biases_q, i) / batch_len_f
adam_update(biases_q, m_biases_q, v_biases_q, g_bias_avg, i)
apply_l2_to_accumulated_gradients(acc_weights_h3_h4, weights_hidden3_hidden4, l2_lambda)
for i = 0 to array.size(acc_weights_h3_h4) - 1
float g_avg = array.get(acc_weights_h3_h4, i) / batch_len_f
adam_update(weights_hidden3_hidden4, m_weights_hidden3_hidden4, v_weights_hidden3_hidden4, g_avg, i)
for i = 0 to array.size(acc_biases_h4) - 1
float g_bias_avg = array.get(acc_biases_h4, i) / batch_len_f
adam_update(biases_hidden4, m_biases_hidden4, v_biases_hidden4, g_bias_avg, i)
apply_l2_to_accumulated_gradients(acc_weights_h2_h3, weights_hidden2_hidden3, l2_lambda)
for i = 0 to array.size(acc_weights_h2_h3) - 1
float g_avg = array.get(acc_weights_h2_h3, i) / batch_len_f
adam_update(weights_hidden2_hidden3, m_weights_hidden2_hidden3, v_weights_hidden2_hidden3, g_avg, i)
for i = 0 to array.size(acc_biases_h3) - 1
float g_bias_avg = array.get(acc_biases_h3, i) / batch_len_f
adam_update(biases_hidden3, m_biases_hidden3, v_biases_hidden3, g_bias_avg, i)
apply_l2_to_accumulated_gradients(acc_weights_h1_h2, weights_hidden1_hidden2, l2_lambda)
for i = 0 to array.size(acc_weights_h1_h2) - 1
float g_avg = array.get(acc_weights_h1_h2, i) / batch_len_f
adam_update(weights_hidden1_hidden2, m_weights_hidden1_hidden2, v_weights_hidden1_hidden2, g_avg, i)
for i = 0 to array.size(acc_biases_h2) - 1
float g_bias_avg = array.get(acc_biases_h2, i) / batch_len_f
adam_update(biases_hidden2, m_biases_hidden2, v_biases_hidden2, g_bias_avg, i)
apply_l2_to_accumulated_gradients(acc_weights_in_h1, weights_input_hidden1, l2_lambda)
for i = 0 to array.size(acc_weights_in_h1) - 1
float g_avg = array.get(acc_weights_in_h1, i) / batch_len_f
adam_update(weights_input_hidden1, m_weights_input_hidden1, v_weights_input_hidden1, g_avg, i)
for i = 0 to array.size(acc_biases_h1) - 1
float g_bias_avg = array.get(acc_biases_h1, i) / batch_len_f
adam_update(biases_hidden1, m_biases_hidden1, v_biases_hidden1, g_bias_avg, i)
// ═══ ОБНОВЛЕНИЕ ВЕСОВ LSTM ═══
if use_lstm
apply_l2_to_accumulated_gradients(acc_lstm_forget_weights_i, lstm_forget_weights_i, l2_lambda_lstm)
for i = 0 to array.size(acc_lstm_forget_weights_i) - 1
float g_avg = array.get(acc_lstm_forget_weights_i, i) / total_steps
adam_update(lstm_forget_weights_i, m_lstm_forget_weights_i, v_lstm_forget_weights_i, g_avg, i)
apply_l2_to_accumulated_gradients(acc_lstm_forget_weights_h, lstm_forget_weights_h, l2_lambda_lstm)
for i = 0 to array.size(acc_lstm_forget_weights_h) - 1
float g_avg = array.get(acc_lstm_forget_weights_h, i) / total_steps
adam_update(lstm_forget_weights_h, m_lstm_forget_weights_h, v_lstm_forget_weights_h, g_avg, i)
for i = 0 to array.size(acc_lstm_forget_bias) - 1
float g_avg = array.get(acc_lstm_forget_bias, i) / total_steps
adam_update(lstm_forget_bias, m_lstm_forget_bias, v_lstm_forget_bias, g_avg, i)
apply_l2_to_accumulated_gradients(acc_lstm_input_weights_i, lstm_input_weights_i, l2_lambda_lstm)
for i = 0 to array.size(acc_lstm_input_weights_i) - 1
float g_avg = array.get(acc_lstm_input_weights_i, i) / total_steps
if not na(g_avg)
adam_update(lstm_input_weights_i, m_lstm_input_weights_i, v_lstm_input_weights_i, g_avg, i)
apply_l2_to_accumulated_gradients(acc_lstm_input_weights_h, lstm_input_weights_h, l2_lambda_lstm)
for i = 0 to array.size(acc_lstm_input_weights_h) - 1
float g_avg = array.get(acc_lstm_input_weights_h, i) / total_steps
if not na(g_avg)
adam_update(lstm_input_weights_h, m_lstm_input_weights_h, v_lstm_input_weights_h, g_avg, i)
for i = 0 to array.size(acc_lstm_input_bias) - 1
float g_avg = array.get(acc_lstm_input_bias, i) / total_steps
if not na(g_avg)
adam_update(lstm_input_bias, m_lstm_input_bias, v_lstm_input_bias, g_avg, i)
apply_l2_to_accumulated_gradients(acc_lstm_cell_weights_i, lstm_cell_weights_i, l2_lambda_lstm)
for i = 0 to array.size(acc_lstm_cell_weights_i) - 1
float g_avg = array.get(acc_lstm_cell_weights_i, i) / total_steps
if not na(g_avg)
adam_update(lstm_cell_weights_i, m_lstm_cell_weights_i, v_lstm_cell_weights_i, g_avg, i)
apply_l2_to_accumulated_gradients(acc_lstm_cell_weights_h, lstm_cell_weights_h, l2_lambda_lstm)
for i = 0 to array.size(acc_lstm_cell_weights_h) - 1
float g_avg = array.get(acc_lstm_cell_weights_h, i) / total_steps
if not na(g_avg)
adam_update(lstm_cell_weights_h, m_lstm_cell_weights_h, v_lstm_cell_weights_h, g_avg, i)
for i = 0 to array.size(acc_lstm_cell_bias) - 1
float g_avg = array.get(acc_lstm_cell_bias, i) / total_steps
if not na(g_avg)
adam_update(lstm_cell_bias, m_lstm_cell_bias, v_lstm_cell_bias, g_avg, i)
apply_l2_to_accumulated_gradients(acc_lstm_output_weights_i, lstm_output_weights_i, l2_lambda_lstm)
for i = 0 to array.size(acc_lstm_output_weights_i) - 1
float g_avg = array.get(acc_lstm_output_weights_i, i) / total_steps
if not na(g_avg)
adam_update(lstm_output_weights_i, m_lstm_output_weights_i, v_lstm_output_weights_i, g_avg, i)
apply_l2_to_accumulated_gradients(acc_lstm_output_weights_h, lstm_output_weights_h, l2_lambda_lstm)
for i = 0 to array.size(acc_lstm_output_weights_h) - 1
float g_avg = array.get(acc_lstm_output_weights_h, i) / total_steps
if not na(g_avg)
adam_update(lstm_output_weights_h, m_lstm_output_weights_h, v_lstm_output_weights_h, g_avg, i)
for i = 0 to array.size(acc_lstm_output_bias) - 1
float g_avg = array.get(acc_lstm_output_bias, i) / total_steps
if not na(g_avg)
adam_update(lstm_output_bias, m_lstm_output_bias, v_lstm_output_bias, g_avg, i)
// L2 Loss
float mlp_l2_loss = 0.0
mlp_l2_loss += calculate_l2_penalty(weights_hidden4_q, l2_lambda)
mlp_l2_loss += calculate_l2_penalty(weights_hidden3_hidden4, l2_lambda)
mlp_l2_loss += calculate_l2_penalty(weights_hidden2_hidden3, l2_lambda)
mlp_l2_loss += calculate_l2_penalty(weights_hidden1_hidden2, l2_lambda)
mlp_l2_loss += calculate_l2_penalty(weights_input_hidden1, l2_lambda)
float lstm_l2_loss = 0.0
if use_lstm
lstm_l2_loss += calculate_l2_penalty(lstm_forget_weights_i, l2_lambda_lstm)
lstm_l2_loss += calculate_l2_penalty(lstm_forget_weights_h, l2_lambda_lstm)
lstm_l2_loss += calculate_l2_penalty(lstm_input_weights_i, l2_lambda_lstm)
lstm_l2_loss += calculate_l2_penalty(lstm_input_weights_h, l2_lambda_lstm)
lstm_l2_loss += calculate_l2_penalty(lstm_cell_weights_i, l2_lambda_lstm)
lstm_l2_loss += calculate_l2_penalty(lstm_cell_weights_h, l2_lambda_lstm)
lstm_l2_loss += calculate_l2_penalty(lstm_output_weights_i, l2_lambda_lstm)
lstm_l2_loss += calculate_l2_penalty(lstm_output_weights_h, l2_lambda_lstm)
total_l2 := mlp_l2_loss + lstm_l2_loss
// Статистика
array<float> debug_stats = array.new_float(5, 0.0)
array.set(debug_stats, 0, debug_sum_reward / batch_len_f)
array.set(debug_stats, 1, debug_sum_max_q / batch_len_f)
array.set(debug_stats, 2, debug_sum_target_q / batch_len_f)
array.set(debug_stats, 3, debug_sum_old_q / batch_len_f)
array.set(debug_stats, 4, debug_zero_td_count_local)
[avg_error / batch_len_f, mlp_grad_norm, lstm_grad_norm, grad_norm, clipping_count, max_grad, total_l2, debug_stats]
//****************************************************************************************************************************************
// =======================================================================
// ОБУЧЕНИЕ Q-LEARNING С PER - БЛОК 1: ФУНКЦИИ И ОБУЧЕНИЕ
// =======================================================================
// ───────── Helper функция: бинарный поиск ─────────
f_binary_search(array<float> sorted_arr, float target) =>
int left = 0
int right = array.size(sorted_arr) - 1
int result = 0
while left <= right
int mid = (left + right) / 2
float mid_val = array.get(sorted_arr, mid)
if mid_val < target
left := mid + 1
else
result := mid
right := mid - 1
result
// ───────── PER функция (ОПТИМИЗИРОВАННАЯ) ─────────
per_sample_optimized(int batch_size_param, int replay_fill_param) =>
array<float> priorities = array.new<float>(replay_fill_param)
float sum_pr = 0.0
for i = 0 to replay_fill_param - 1
float pr = math.pow(array.get(replay_priority, i), priority_alpha)
array.set(priorities, i, pr)
sum_pr += pr
if sum_pr <= 0
sum_pr := 1e-8
array<float> cum_priority = array.new<float>(replay_fill_param)
float cum_sum = 0.0
for i = 0 to replay_fill_param - 1
cum_sum += array.get(priorities, i)
array.set(cum_priority, i, cum_sum)
array<int> batch_indices = array.new_int(batch_size_param)
array<float> is_weights = array.new_float(batch_size_param)
float max_weight = 0.0
for b = 0 to batch_size_param - 1
float r = math.random() * sum_pr
int idx = f_binary_search(cum_priority, r)
array.set(batch_indices, b, idx)
float prob = array.get(priorities, idx) / sum_pr
float weight = math.pow(replay_fill_param * math.max(prob, 1e-10), -priority_beta)
array.set(is_weights, b, weight)
max_weight := math.max(max_weight, weight)
if max_weight > 0
for b = 0 to batch_size_param - 1
array.set(is_weights, b, array.get(is_weights, b) / max_weight)
[batch_indices, is_weights]
// ───────── ОСНОВНОЙ БЛОК ОБУЧЕНИЯ (ОПТИМИЗИРОВАННЫЙ) ─────────
// Примечание: Этот блок должен вызываться внутри условия 'if rl_bar % train_freq == 0 ...'
// Переменные debug_... и training_error должны быть объявлены как var/global ранее.
if rl_bar % train_freq == 0 and replay_fill >= batch_size
debug_clipping_count := 0
[batch_indices, is_weights] = per_sample_optimized(batch_size, replay_fill)
// Выполняем Forward, Backward и Update внутри функции backpropagation
// Убедитесь, что функция backpropagation определена в вашем коде
[training_error, mlp_norm, lstm_norm, grad_norm, clipping, max_grad, l2_loss, debug_stats_arr] = backpropagation(batch_indices, is_weights, batch_size)
// Распаковка статистики
float avg_td_error = training_error
debug_avg_reward := array.get(debug_stats_arr, 0)
debug_avg_max_q := array.get(debug_stats_arr, 1)
debug_avg_target_q := array.get(debug_stats_arr, 2)
debug_avg_old_q := array.get(debug_stats_arr, 3)
debug_zero_td_count := int(array.get(debug_stats_arr, 4))
// Дополнительные проверки (warnings)
debug_all_q_zero := (debug_avg_old_q == 0.0 and debug_avg_max_q == 0.0)
debug_all_reward_zero := (debug_avg_reward == 0.0)
// Обновление глобальной статистики
ema_error := alpha_ema * avg_td_error + (1 - alpha_ema) * ema_error
total_l2_loss := nz(total_l2_loss) + l2_loss
total_train_loss := nz(total_train_loss) + training_error
update_count += 1
adam_timestep += 1
// Обновление метрик для панели
debug_mlp_grad_norm := mlp_norm
debug_lstm_grad_norm := lstm_norm
debug_grad_norm := grad_norm
debug_clipping_count := clipping
debug_max_grad := max_grad
debug_l2_loss := l2_loss
td_error := avg_td_error
// Приоритеты обновляются внутри backpropagation, здесь обновляем бету
priority_beta := math.min(1.0, priority_beta + beta_increment)
//***************************************************************************************************************************************
// =======================================================================
// ИНФОРМАЦИОННАЯ DEBUG ПАНЕЛЬ (МАСШТАБИРУЕМАЯ)
// =======================================================================
if barstate.islast and show_debug
// 🆕 Выбор позиции
string panel_position = debug_table_position == "Top Left" ? position.top_left :
debug_table_position == "Top Right" ? position.top_right :
debug_table_position == "Bottom Left" ? position.bottom_left :
position.bottom_right
// 🆕 Выбор размера текста
string text_size = debug_table_size == "Tiny" ? size.tiny :
debug_table_size == "Small" ? size.small :
debug_table_size == "Normal" ? size.normal :
debug_table_size == "Large" ? size.large :
size.huge
var table debug_table = table.new(panel_position, 2, 26,
bgcolor=color.new(color.black, 10),
border_width=2,
border_color=color.orange)
// Очистка таблицы
table.clear(debug_table, 0, 0, 1, 25)
// ═══ ЗАГОЛОВОК ═══
table.cell(debug_table, 0, 0, "DEBUG METRIC",
text_color=color.white,
bgcolor=color.new(color.orange, 0),
text_size=text_size)
table.cell(debug_table, 1, 0, "VALUE",
text_color=color.white,
bgcolor=color.new(color.orange, 0),
text_size=text_size)
// ═══ ОСНОВНЫЕ МЕТРИКИ ═══
table.cell(debug_table, 0, 1, "Update", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 1, str.tostring(update_count), text_color=color.white, text_size=text_size)
table.cell(debug_table, 0, 2, "RL Bar", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 2, str.tostring(rl_bar), text_color=color.white, text_size=text_size)
table.cell(debug_table, 0, 3, "Replay", text_color=color.white, text_size=text_size)
float fill_pct = replay_fill / replay_size * 100
table.cell(debug_table, 1, 3, str.tostring(replay_fill) + "/" + str.tostring(replay_size) +
" (" + str.tostring(fill_pct, "#.#") + "%)",
text_color=color.white, text_size=text_size)
table.cell(debug_table, 0, 4, "LR", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 4, str.tostring(adaptive_lr, "#.#####"),
text_color=adaptive_lr < min_lr * 1.5 ? color.orange : color.green,
text_size=text_size)
// --- SEPARATOR ---
table.cell(debug_table, 0, 5, "--- Q-VALUES ---",
text_color=color.yellow,
bgcolor=color.new(color.gray, 80),
text_size=text_size)
table.cell(debug_table, 1, 5, "", bgcolor=color.new(color.gray, 80))
// Q-Values
table.cell(debug_table, 0, 6, "Avg Reward", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 6, str.tostring(debug_avg_reward, "#.######"),
text_color=debug_avg_reward == 0.0 ? color.orange : color.green,
text_size=text_size)
table.cell(debug_table, 0, 7, "Max Q (next)", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 7, str.tostring(debug_avg_max_q, "#.######"),
text_color=debug_avg_max_q == 0.0 ? color.red : color.green,
text_size=text_size)
table.cell(debug_table, 0, 8, "Target Q", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 8, str.tostring(debug_avg_target_q, "#.######"),
text_color=debug_avg_target_q == 0.0 ? color.red : color.green,
text_size=text_size)
table.cell(debug_table, 0, 9, "Old Q", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 9, str.tostring(debug_avg_old_q, "#.######"),
text_color=color.white, text_size=text_size)
// --- SEPARATOR ---
table.cell(debug_table, 0, 10, "--- TD-ERROR ---",
text_color=color.yellow,
bgcolor=color.new(color.gray, 80),
text_size=text_size)
table.cell(debug_table, 1, 10, "", bgcolor=color.new(color.gray, 80))
table.cell(debug_table, 0, 11, "Avg TD", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 11, str.tostring(td_error, "#.######"),
text_color=td_error == 0.0 ? color.red : color.green,
text_size=text_size)
table.cell(debug_table, 0, 12, "Min TD", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 12, str.tostring(debug_min_td, "#.######"),
text_color=color.white, text_size=text_size)
table.cell(debug_table, 0, 13, "Max TD", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 13, str.tostring(debug_max_td, "#.######"),
text_color=color.white, text_size=text_size)
table.cell(debug_table, 0, 14, "Zero TD", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 14, str.tostring(debug_zero_td_count) + "/" + str.tostring(batch_size),
text_color=debug_zero_td_count == batch_size ? color.red : color.green,
text_size=text_size)
table.cell(debug_table, 0, 15, "EMA Err", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 15, str.tostring(ema_error, "#.######"),
text_color=ema_error == 0.0 ? color.red : color.green,
text_size=text_size)
// --- SEPARATOR: ГРАДИЕНТЫ ---
table.cell(debug_table, 0, 16, "--- GRADIENTS ---",
text_color=color.yellow,
bgcolor=color.new(color.gray, 80),
text_size=text_size)
table.cell(debug_table, 1, 16, "", bgcolor=color.new(color.gray, 80))
table.cell(debug_table, 0, 17, "Total Norm", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 17, str.tostring(debug_grad_norm, "#.####"),
text_color=debug_grad_norm > clip_threshold ? color.red : color.green,
text_size=text_size)
table.cell(debug_table, 0, 18, "MLP Norm", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 18, str.tostring(debug_mlp_grad_norm, "#.####"),
text_color=color.white, text_size=text_size)
table.cell(debug_table, 0, 19, "LSTM Norm", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 19, str.tostring(debug_lstm_grad_norm, "#.####"),
text_color=color.white, text_size=text_size)
table.cell(debug_table, 0, 20, "Clipping", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 20, str.tostring(debug_clipping_count),
text_color=debug_clipping_count > batch_size / 2 ? color.orange : color.green,
text_size=text_size)
table.cell(debug_table, 0, 21, "L2 Loss", text_color=color.white, text_size=text_size)
table.cell(debug_table, 1, 21, str.tostring(debug_l2_loss, "#.######"),
text_color=color.white, text_size=text_size)
// --- SEPARATOR: WARNINGS ---
table.cell(debug_table, 0, 22, "--- STATUS ---",
text_color=color.yellow,
bgcolor=color.new(color.gray, 80),
text_size=text_size)
table.cell(debug_table, 1, 22, "", bgcolor=color.new(color.gray, 80))
string warning1 = debug_all_q_zero ? "⚠️ Q=0!" : "✅ Q-OK"
table.cell(debug_table, 0, 23, warning1,
text_color=debug_all_q_zero ? color.red : color.green,
text_size=text_size)
table.cell(debug_table, 1, 23, "", text_color=color.white)
string warning2 = debug_all_reward_zero ? "⚠️ R=0!" : "✅ R-OK"
table.cell(debug_table, 0, 24, warning2,
text_color=debug_all_reward_zero ? color.red : color.green,
text_size=text_size)
table.cell(debug_table, 1, 24, "", text_color=color.white)
string warning3 = debug_grad_norm > clip_threshold * 2 ? "⚠️ EXPLODE!" : "✅ G-OK"
table.cell(debug_table, 0, 25, warning3,
text_color=debug_grad_norm > clip_threshold * 2 ? color.red : color.green,
text_size=text_size)
table.cell(debug_table, 1, 25, "", text_color=color.white)
// 🆕 АЛЬТЕРНАТИВНЫЙ СПОСОБ: Удаление таблицы если флаг выключен
if barstate.islast and not show_debug
// Удаляем таблицу если она существует
var table debug_table = na
if not na(debug_table)
table.delete(debug_table)
debug_table := na
//*******************************************************************************************************************************************************
// 13. Визуализация
plot(dir_val == -1 ? st : na, "Up", color_bull, 2, plot.style_linebr)
plot(dir_val == 1 ? st : na, "Down", color_bear, 2, plot.style_linebr)
bar_color = dir_val == -1 ? color.new(color.green, 70) : dir_val == 1 ? color.new(color.red, 70) : na
barcolor(bar_color)
// 14. Глобальные переменные для сигналов
var int last_signal_type = 0
var int last_signal_bar = 0
if buy_signal
last_signal_type := 1
last_signal_bar := bar_index
else if sell_signal
last_signal_type := -1
last_signal_bar := bar_index
// 15. Отображение сигналов
plotshape(buy_signal, text="Long", textcolor=color.white, style=shape.labelup, location=location.belowbar, color=color_bull, size=size.tiny)
plotshape(sell_signal, text="Short", textcolor=color.white, style=shape.labeldown, location=location.abovebar, color=color_bear, size=size.tiny)
bgcolor(dir_val == -1 ? color.new(color.green, 90) : dir_val == 1 ? color.new(color.red, 90) : na)
// =================== ДИАГНОСТИКА С L2 МОНИТОРИНГОМ ===================
// 16. Информационная панель
var tbl = table.new(position.top_right, 1, 1, bgcolor=color.new(color.gray, 90))
if bar_index % resample_freq == 0 or barstate.islast
signal_text = "NONE"
txt_color = color.white
signal_bar_text = "NONE"
trend_text = dir_val == -1 ? "Бычий" : dir_val == 1 ? "Медвежий" : "N/A"
// Объявляем debug_info здесь, чтобы избежать ошибки "Undeclared identifier"
string debug_info = ""
if last_signal_type == 1
signal_text := "BUY"
txt_color := color_bull
signal_bar_text := str.tostring(last_signal_bar)
else if last_signal_type == -1
signal_text := "SELL"
txt_color := color_bear
signal_bar_text := str.tostring(last_signal_bar)
method_text = use_kmeans ? "K-Means++" : "Quantile"
factor_text = adaptive_factor ? str.tostring(dynamic_factor, "#.##") + " (Adaptive)" : str.tostring(final_factor,"#.##") + "(Final Adaptive)"
//factor_text = adaptive_factor or use_dqn ? str.tostring(final_factor, "#.##") + " (Hybrid)" : str.tostring(best_basefact, "#.##")
table_content = "VOL TYPE: EMA(ATR)"
+ "\nSOURCE: " + src_select
+ "\nCLUSTER METHOD: " + method_text
+ "\nFACTOR: " + factor_text
+ "\nVOLATILITY: " + str.tostring(filtered_vol, "#.##")
+ "\nATR: " + str.tostring(final_centroid)
+ "\nBEST FACTOR: " + str.tostring(best_basefact, "#.##")
+ "\nTREND: " + trend_text
+ "\nLAST SIGNAL: " + signal_text
+ "\nSIGNAL BAR: " + signal_bar_text
+ "\nTRAIN BARS: " + str.tostring(math.min(training_period, max_training_bars))
+ "\nLEARNING RATE: " + str.tostring(adaptive_lr, "#.#####")
+ "\nFill Percent: " + str.tostring(replay_fill) + "/" + str.tostring(replay_size)
+ "\nAdaptiv lstm_timesteps: " + str.tostring(lstm_timesteps, "#.##")
+ "\nTD_Error: " + str.tostring(td_error, "#.######")
+ "\nEpsilon: " + str.tostring(epsilon, "#.######")
+ "\nEma_Error: " + str.tostring(ema_error, "#.######")
+ "\nUpdate_count: " + str.tostring(update_count)
+ "\nZero TD: " + str.tostring(debug_zero_td_count) + "/" + str.tostring(batch_size)
+ "\nGradiens"
+ "\nTotal Norm: " + str.tostring(debug_grad_norm, "#.########")
+ "\nMLP Norm: " + str.tostring(debug_mlp_grad_norm, "#.########")
+ "\nClipping: " + str.tostring(debug_clipping_count, "#.########")
+ "\nLSTM Norm: " + str.tostring(debug_lstm_grad_norm, "#.########")
+ "\nL2 Loss: " + str.tostring(debug_l2_loss, "#.########")
if debug_mode
debug_info := "\n\n--- DEBUG ---" +
"\nBUY: " + str.tostring(buy_signal) +
"\nSELL: " + str.tostring(sell_signal) +
"\nBAR INDEX: " + str.tostring(bar_index) +
"\nDIR_VAL: " + str.tostring(dir_val) +
"\nCLOSE: " + str.tostring(close) +
"\nST: " + str.tostring(st) +
"\nATR: " + str.tostring(final_centroid, "#.##") +
"\nVOL INDEX: " + str.tostring(volatility_index, "#.##") +
"\nNN INPUTS: ROC=" + str.tostring(input1, "#.##") + " | ATR_ROC=" + str.tostring(input2, "#.##") + " | MOM=" + str.tostring(input3, "#.##") + " | RSI=" + str.tostring(input4, "#.##") + " | VOL=" + str.tostring(input5, "#.##") + " | ENTROPY=" + str.tostring(input6_entropy, "#.##") + " | CNN=" + str.tostring(input7, "#.##") + " | VOL_OSC=" + str.tostring(input8_vol_osc, "#.##") + " | STOCH=" + str.tostring(input9_stoch_k, "#.##") + " | OBV=" + str.tostring(obv_roc, "#.##") + " | VWAP=" + str.tostring(vwap_diff, "#.##") + " | HURST=" + str.tostring(hurst_proxy, "#.##")
if use_nn_filter and auto_optimize
debug_info := debug_info + "\nNN OUTPUT: " + str.tostring(nn_output_val_prev, "#.##") + "\nNN FACTOR: " + str.tostring(nn_filter_value, "#.##") + "\nLEARNING RATE: " + str.tostring(learning_rate, "#.#####")
if use_kmeans
string centroids_str = ""
for c = 0 to array.size(kmeans_centroids) - 1
centroids_str += str.tostring(array.get(kmeans_centroids, c), "#.##") + " "
debug_info := debug_info + "\nK-MEANS CENTROIDS: " + centroids_str + "\nBEST K: " + str.tostring(best_k)
table_content := table_content + debug_info
table.cell(tbl, 0, 0, table_content, text_color=txt_color)
// 17. Визуальное оповещение о проблемах
bgcolor(na(final_centroid) and use_kmeans ? color.new(color.orange, 80) : na)
// 18. Инициализация
min_bars = math.max(final_centroid, math.min(training_period, max_training_bars))
if bar_index < min_bars and bar_index % 10 == 0
label.new(bar_index, high,
text="Initializing: " + str.tostring(bar_index) + "/" + str.tostring(min_bars),
style=label.style_label_down,
color=color.new(color.orange, 0),
textcolor=color.white)
Overview
Optional settings enable an adaptive factor driven by rate of change, a volatility filter, K-means clustering, and an LSTM layer with a configurable hidden size, timestep count, learning rate, and training window. Auto-optimize and auto training period controls change how the model updates. Bullish and bearish colors mark direction. A debug table can show internal values. It is an indicator only and does not place entries, exits, or stops.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Resample Frequency
int
3
resample_freq
ATR Length
int
10
atr_length
Base Factor
float
1.3
base_fact
Use Base Factor
bool
true
use_base_factor
Source
string
HLC3
src_select
Training Period
int
500
training_period_input
Smoothing
int
3
smooth_factor
Enable Debug Info
bool
false
debug_mode
Bullish Color
color
color.new(color.green, 0)
color_bull
Bearish Color
color
color.new(color.red, 0)
color_bear
Normalization Length
int
100
norm_length
Show Debug Panel
bool
false
show_debug
Show all 40 settings
Setting
Type
Default
Variable
Debug Panel Size
string
Normal
debug_table_size
Debug Panel Position
string
Top Left
debug_table_position
Use ROC for Adaptive Factor
bool
true
use_roc_adapt
ROC Length for Volatility
int
5
roc_length
Volatility Filter
float
1.1
vol_filter
Adaptive Factor
bool
false
adaptive_factor
Auto-Optimize Training
bool
true
auto_optimize
Auto Training Period
bool
true
auto_train
Min Training Bars
int
250
min_train_bars
Initial Learning Rate
float
0.01
learning_rate
Higher Timeframe for Ichimoku
timeframe
5
ichi_htf
LSTM Hidden Size
int
8
lstm_hidden_size
Use LSTM Layer
bool
true
use_lstm
LSTM initial step count
int
10
lstm_timesteps
Normalized ATR threshold
float
0.1
threshold
ATR step multiplier
float
2.0
step_factor
K-Means Clusters
int
3
k_value
Use K-Means++ Clustering
bool
true
use_kmeans
Auto-Select K-Value
bool
true
auto_k_value
Min K-Value
int
2
min_k
Max K-Value
int
5
max_k
Use Neural Network Filter
bool
true
use_nn_filter
Hidden Neurons
int
5
nn_hidden_neurons
Use CNN-like Filter
bool
true
use_cnn_filter
CNN Window Size
int
10
cnn_window_size
CNN Filter Size
int
3
cnn_filter_size
State History Length (bars)
int
18
history_len
CNN Short Window
int
5
cnn_short_window
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.
This indicator uses a Hidden Markov Model (HMM) framework combined with an adaptive dual moving average engine and Directional Movement Index (DMI) features to identify market regimes (bullish, bearish, or ranging/chop). It calculates regime probabilities on e
This overlay indicator builds many SuperTrend trailing stops across a user-set ATR length, factor range, and step. Each factor is scored with an exponential performance memory. K-means clustering groups those results, and the user chooses the best, average, or
Markov State Engine classifies market conditions into six distinct regimes, including strong trends, weak trends, compression, and expansion. It evaluates historical state transitions using an exit-conditional Markov model with Laplace smoothing to estimate re
This indicator classifies the market into gamma regime types (negative gamma trend, positive gamma mean-reversion, or transition) by calculating a composite score based on daily ATR ratios, session VWAP crossover frequencies, and opening range displacement. It
This indicator calculates standardized trend strength and historical volatility to estimate position sizes. It computes the differences between three user-defined EMAs standardized by price using MACD logic, while historical volatility is derived from the stan
Dynamic Extreme Channels & Reversals tracks evolving price highs and lows to plot adaptive channel bands and a midline directly on the price chart. When no new extremes occur within the specified lookback window, the channel boundaries interpolate toward price