Гребневая регрессия

 

Сидел как-то глубокой ночью, общался с чатом по душам. Этот кретин тем хуже не понимает, чем чаще на него злишься. После самовольного накидывания на себя обязанности исправиться и написать мне то, что я хочу, он делает ровно наоборот - добавляет ошибок. 

И вдруг я его прошу: "Давай чё-нибудь попроще. Что ещё может предсказывать временной ряд?"

Тот мне: "Попробуй гребневую регрессию". 

Ну окей, я ему "пиши давай, болтаешь больше", он мне написал простейший код на Питоне, 


import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error, r2_score
import matplotlib.pyplot as plt

# Загрузка данных из оригинального файла
data = pd.read_csv('C:/Users/iboot/Desktop/PYTHON/Цены/EURUSD D1 2012-2022.csv', encoding='cp1251', header=None)

# Разделение данных на признаки и целевую переменную
X = data.iloc[:, 0:20]  # Первые столбцы (по позиции)
y = data.iloc[:, 20]  # Последний столбец (по позиции)

# Разделение данных на обучающий и тестовый наборы
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Создание и обучение модели гребневой регрессии
alpha = 1.0  # Коэффициент регуляризации (подберите под ваши данные)
ridge = Ridge(alpha=alpha)
ridge.fit(X_train, y_train)

# Прогнозирование на тестовом наборе
y_pred = ridge.predict(X_test)

# Оценка модели
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)


Спрашиваю: "Ну и чё это? Где вывод инфы? Как я пойму, где мои миллионы?"

Чат предлагает такой вывод

# Вывод информации для оригинальных данных
print("ПОСЛЕДНИЙ ВХОД (Оригинальные данные)")
print(f"Среднеквадратичная ошибка (MSE): {mse}")
print(f"Коэффициент детерминации (R^2): {r2}")


Я продолжаю невдуплять, что это за язык эльфов и прошу его создать вывод по моему правилу:

Если предикт БОЛЬШЕ текущего значения и факт БОЛЬШЕ текущего значения, то добавляй в счётчик +1. Иначе — вычитай из счётчика -1. 
Тоже самое и МЕНЬШЕ. 

Чат написал мне код

# Создание графика для оригинальных данных
plt.figure(figsize=(10, 6))
plt.plot(range(len(y_test)), y_test, label="ФАКТ (Оригинальные данные)")
plt.plot(range(len(y_test)), y_pred, label="ПРОГНОЗ (Оригинальные данные)")
plt.xlabel("Номер наблюдения")
plt.ylabel("Значение")
plt.legend()
plt.title("Гребневая регрессия: ФАКТ vs ПРОГНОЗ (Оригинальные данные)")
plt.show()

# Создание графика для оригинальных данных
plt.figure(figsize=(10, 6))
plt.plot(y_test, y_pred, 'bo')  # Синие точки для ФАКТ и ПРОГНОЗ (Оригинальные данные)
plt.plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], 'r-', lw=2)  # Линия регрессии
plt.xlabel("ФАКТ (Оригинальные данные)")
plt.ylabel("ПРОГНОЗ (Оригинальные данные)")
plt.title("Гребневая регрессия: ФАКТ vs ПРОГНОЗ (Оригинальные данные)")
plt.show()


# Создаем счетчик
counter = 0

# Сравниваем вход, прогноз и целевую переменную для тестовых данных
# Сравниваем последний вход, прогноз и целевую переменную для тестовых данных
for i in range(len(y_pred)):
    last_input = X_test.iloc[i, -1]  # Получаем последний элемент столбца для текущего наблюдения
    prediction = y_pred[i]
    target = y_test.iloc[i]
    
    if (prediction > last_input and target > last_input) or (prediction < last_input and target < last_input):
        counter += 1
    else:
        counter -= 1

# Выводим результат счетчика
print("Результат счетчика для тестовых данных:", counter)


Я подготовил данные: скриптом создал csv-файл, всё по заветам древней статьи про NeuroPro. 

И такой: "Ну давай, грааль, работай". Запускаю, — чухня. График предиктов размазанный, сглаженный, как скользящая средняя с периодом 3500.

Думаю, жалко выбрасывать. Да и лень. Спрашиваю у чата: "А можно как-то улучшить это полено?", тот отвечает: "Добавьте ещё исходных данных". 

Думаю, ну чё ещё добавить? Я же добавил чистые цены, без преобразований.

Потом думаю: "А пофиг, добавлю все валютные пары, пусть подавится".

Начинаю добавлять по одной паре и тестировать. Чухня. Чухня. Ещё Чухня. ВО! Тут чухня получше. Тут похуже...

Добавляя 15-ую валютную пару, начинаю замечать, как график предикта.... постепенно прилипает к графику факта. 

Окей, меня не проведёшь, я такое уже проходил. Любая нейросеть в Питоне в любой архитектуре, с любыми данными, да хоть с 100 000 нейронов - превращается по итогу в скользящую среднюю с периодом 5. Эта скользящая средняя бегает, как хвост за ценой, но.... постоянно ОПАЗДЫВАЕТ, как вы уже догадались. Этот фокус, или "мошеннический" эффект можно неплохо так юзать инфоцыганам. Ведь на макроуровне, когда вы смотрите выборку из сотни и тысячи угадываний (сделок потенциальных) - то складывается ощущение, что перед нами ГРААЛЬ! Который угадывает пункт в пункт. Но на микроуровне, рассматривая каждую "сделку", вы неприятно разочаруетесь - предикт тупо бегает за ценой с опозданием. И, если цена развернётся на 500 пипсов, то вы потеряете эти 500 пипсов, ведь предикт опаздывает и показывает предыдущее значение. А уже на следующем шаге он покажет разворот на 499 пипсов, когда вы уже на юг отправились. 

И тут я начинаю проверять своё правило вывода информации (счётчик). Он начинает показывать +20. То есть, из 260 D1-свечей форварда, он 140 угадал, а 100 не угадал (правильно я посчитал?...)

Вы скажете - фигня. 

Окей, начинаю добавлять ещё пары, ещё.... Счетчик показывает +40. +60. +75...

УБираю йеновские пары, он мне 


Счетчик +166

У меня с математикой не очень, но мне кажется это 213 правильных угадываний и 47 неправильных

Форвард в файле "EURUSD D1 2012-2022 2.csv" - это период с 2022-2023, просто название лень было переписывать, добавил двоечку. В оригинальном файле цена заканчивается на 1.0343, а в проверочном начинается на 1.0377 - это первое или четвёртое января, то есть, это не тот же самый файл, это новые данные. 



18.08% - процент неправильных угадываний

81.92% - процент правильных угадываний



Ну а теперь самое главное - ваш выход. Есть ли у меня ошибка, которую я не заметил?

Или эта канитель реально работает. Справедливо отметить, что я не тестировал другие периоды, но, для затравки начну с этого. 

 

Допустим, если ошибок нет, подводных камней нет. 

Тогда явление похоже на то, о чём говорит Ренат: все пары управляются какой-то формулой. 

И, если нет никаких форс-мажоров, эту формулу можно эксплуатировать. 

Как мне известно нейросети, в том числе и всякого рода регрессии - это тоже формула, которая строится путём подборов коэффициентов (весов). Одна валютная пара ничего не даёт, но если взять все - то их соотношения показывают какая куда пойдёт в следующем временном шаге. 

Неправильные ответы - это либо недостаточная корректность формулы, либо разного рода форс-мажоры (неконтролируемые покупки-продажи или что-нибудь ещё), в любом случае формула сейчас сырая, если она вообще имеет место быть. 

 
Ivan Butko #:

Допустим, если ошибок нет, подводных камней нет. 

Тогда явление похоже на то, о чём говорит Ренат: все пары управляются какой-то формулой. 

И, если нет никаких форс-мажоров, эту формулу можно эксплуатировать. 

Как мне известно нейросети, в том числе и всякого рода регрессии - это тоже формула, которая строится путём подборов коэффициентов (весов). Одна валютная пара ничего не даёт, но если взять все - то их соотношения показывают какая куда пойдёт в следующем временном шаге. 

Неправильные ответы - это либо недостаточная корректность формулы, либо разного рода форс-мажоры (неконтролируемые покупки-продажи или что-нибудь ещё), в любом случае формула сейчас сырая, если она вообще имеет место быть. 

Попробуйте разбить выборку не рандомно, а по хронологии.

 
очередная тема, когда попросил у чата что-то сделать и не понял, что он сделал? теперь другие должны объяснить? :)
 
Maxim Dmitrievsky #:
очередная тема, когда попросил у чата что-то сделать и не понял, что он сделал? теперь другие должны объяснить? :)

Да, так это и работает)

 
Aleksey Vyazmikin #:

Попробуйте разбить выборку не рандомно, а по хронологии.

Попробую разобраться

 
Не понятно, почему корректность предсказания проверяется за счет сверки положения прогноза prediction и реального ожидаемого значения target относительно предиктора last_input. Если идея в том, чтобы контролировать относительное движение выхода, то нужно сравнивать prediction и target относительно y_test.iloc[i - 1].
 
Stanislav Korotky #:
Не понятно, почему корректность предсказания проверяется за счет сверки положения прогноза prediction и реального ожидаемого значения target относительно предиктора last_input. Если идея в том, чтобы контролировать относительное движение выхода, то нужно сравнивать prediction и target относительно y_test.iloc[i - 1].

Уточните, пожалуйста:

Я подаю на вход текущую цену last_input

Регрессия мне предсказывает (prediction) следующую цену. 

Если следующая цена больше текущей и прогноз больше текущей, то это означает, что прогноз верный. +1
Тоже самое и с меньше "<"

Здесь ошибка в самой логике или техническая ошибка в коде?

 
Ivan Butko #:


Я подаю на вход текущую цену last_input

Регрессия мне предсказывает (prediction) следующую цену. 

На вход подается не только last_input но и еще куча предикторов. Из кода не следует, что этот предиктор (а почему не другой?) можно соотносить с целевым значением и предсказанием - вдруг тут "бананы" с "огурцами" сравниваются?

 
Stanislav Korotky #:

На вход подается не только last_input но и еще куча предикторов. Из кода не следует, что этот предиктор (а почему не другой?) можно соотносить с целевым значением и предсказанием - вдруг тут "бананы" с "огурцами" сравниваются?

А, вот такой экспорт у меня

  for (int i = 0; i >= 0; i--)
      { 
         C_1 = iClose("EURCHF", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         C_1 = iClose("EURGBP", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("EURCAD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ","; 
         
         C_1 = iClose("EURAUD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("EURNZD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         
         C_1 = iClose("GBPUSD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("USDCHF", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("USDCAD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("AUDUSD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("NZDUSD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         
         
          C_1 = iClose("GBPCHF", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("GBPCAD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("GBPAUD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("GBPNZD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("GBPNZD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         
         
         
         C_1 = iClose("NZDCAD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("NZDCHF", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         
         
         C_1 = iClose("AUDCAD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("AUDCHF", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("AUDNZD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         C_1 = iClose("EURUSD", PER_Candle, bar + i + 1); 
         row += C_1;     if (StringLen(row))     row += ",";
         
         
         
     //    row += d;     if (StringLen(row))     row += ",";
      }
  
    C_1 = iClose(symbol, PER_Candle, bar - 1 + 1); 
         row += C_1;     if (StringLen(row))   //  row += ",";

Поскольку накидываю на график EURUSD, то последнее значение - это тоже от EURUSD, только самое свежее - оно и есть целевое. 

В csv-таблице это выглядит так

Хронология сверху-вниз

 

Поскольку делал эту штуку год назад, по-моему, то проверил сейчас на новых данных 

2023-2024






Либо подсчёт неправильный, либо что-то ещё.