Если не делать сильно большой форвард, то работает как конфета. Потом переобучать, с учетом новых данных.
Признаки любые, но лучше поближе к ценам. Много не надо, 10 достаточно. Желательно нормализовать для кластеризации, если сильно отличаются по значениям.
Разметку желательно делать плотную, то есть для каждого изменения признаков должна быть метка. Но можно и на разреженном датасете. Метки должны соответствовать прибыльным сделкам в большинстве своем. Иначе будет обратный эффект.
Maxim Dmitrievsky #: Видимо никто не читает, один флуд в теме.
Вроде я уже писал тогда, что у меня есть нечто такое же...
# Настройка источника данных
#dir_cb = "C:\\FX\\MT5_02\\MQL5\\Files\\00_Standart_50_Test"
dir_cb = "E:\\FX\\MT5_CB\\MQL5\\Files\\iDelta_D1_TP_4_SL_4_Random\\CB_Bi_Setup"
arr_viborka = ["train.csv", "test.csv", "exam.csv"]
strok_total_data = 1000
stolb_total_data = 100
Load_Strok = strok_total_data
Load_Stolb = stolb_total_data
# Задание параметров кластеризации
arr_Tree = [3,3,3] # Звдвём структуру дерева - указывается число кластеров/листьев на каждом уровне/ярусе
n_level = len(arr_Tree)
arr_filter = []
arr_centroid_arhiv = []
scaler = None #Переменная для нормализации
Variant_Norm=3 #Вариант нормализации
iteration=0;# Переменная для индексации перебора
# Задание параметров обработки данных
Use_Convert_CSV = True # Если необходимо открыть CSV и пробразовать и сохранить потом в формат Use_Convert_CSV
Use_Load_CSV = True # Загружать данные из файла или использовать случайно сгенерированные - для отладки кода
Not_Bi_Data = False # Если выборка не бинарная, то True
Use_Creat_New_Viborka = True # Создать новую выборку через фильтрацию по стат. данным кластеризации
Variant_Filter_Klaster = 2 # Вариант условий фильтрации
# Проверяем наличие директории
directory = dir_cb+'\\Rez_Tree_K-Means'if not os.path.exists(directory):# Если директории нет, создаем ее
os.makedirs(directory)
print(f"Директория {directory} была создана.")
else:
print(f"Директория {directory} уже существует.")
if Use_Load_CSV == False:# Генерация случайных данных
np.random.seed(100)
arr_data_load = np.random.randint(0, 2, (strok_total_data, stolb_total_data))
arr_Target = np.random.randint(2, size=Load_Strok)
if Use_Convert_CSV == True:
f_Load_Data(dir_cb+'\\Setup','train',False,True,Not_Bi_Data)#Открыть в CSV и преобразовать в feather
f_Load_Data(dir_cb+'\\Setup','test',False,True,Not_Bi_Data)#Открыть в CSV и преобразовать в feather
f_Load_Data(dir_cb+'\\Setup','exam',False,True,Not_Bi_Data)#Открыть в CSV и преобразовать в feather
# Создаем пустой DataFrame для хранения статистики
#columns = ['random_state', 'cluster',
# 'Precision_train','Recall_train',#Итерация 0
# 'Precision_test','Recall_test', #Итерация 1
# 'Precision_exam','Recall_exam'] #Итерация 2
columns = ['random_state', 'cluster',
'Precision_train','Precision_test','Precision_exam',
'Recall_train','Recall_test', 'Recall_exam',
'Balans_train','Balans_test', 'Balans_exam'
]
table_stat = pd.DataFrame(columns=columns)
#Svod_Statistics
arr_Name_Pred=[]
arr_Set_Viborka=['train','test','exam']
n_Set = len(arr_Set_Viborka)
for Set in range(n_Set):
if Use_Load_CSV == True:
arr_data_load,arr_Target,arr_Info=f_Load_Data(dir_cb+'\\Setup',arr_Set_Viborka[Set],True,False,Not_Bi_Data)#Открыть файл в формате feather
arr_Name_Pred=arr_data_load.columns.tolist()
if Not_Bi_Data == True: #Если выборка не бинарная, то проведём нормализацию
arr_data_load,scaler=f_Normalization(arr_data_load, Variant_Norm, Set, scaler)
if Variant_Norm==0:
arr_data_load = arr_data_load.values # Преобразуем полученные данные из Pandas в массив NumPy
else:
arr_data_load = arr_data_load.values # Преобразуем полученные данные из Pandas в массив NumPy
#if Set == 0:#Нормализая - для не бинарной выборки
# scaler.fit(arr_data_load)
#arr_data_load = scaler.transform(arr_data_load)
#!!!arr_data_load = arr_data_load.values # Преобразуем полученные данные из Pandas в массив NumPy
Load_Strok, Load_Stolb = arr_data_load.shape
if Set == 0:
# Вызовим функцию для кластеризации
arr_filter=clusterize_and_filter(arr_data_load, arr_Tree, Load_Stolb, Load_Strok, n_level)
arr_filter = arr_filter.reshape(-1, n_level)#Преобразуем в двухмерный массив
# Сохранение результата кластеризации на каждом уровне дерева в файл CSV
csv_data = pd.DataFrame(arr_filter)
csv_data.to_csv(dir_cb + "\\Rez_Tree_K-Means\\f_Klaster_" + "train" + ".csv", index=False, header=False,sep=';', decimal='.')
# Сохранение всех центройдов в файл CSV
csv_data = pd.DataFrame(arr_centroid_arhiv)
csv_data.to_csv(dir_cb + "\\Rez_Tree_K-Means\\f_Centroid_Arhiv.csv", index=False, header=False,sep=';', decimal='.')
if Use_Load_CSV == False:#Только для тестов без чтения выборки!
arr_data_load = arr_data_load.reshape(Load_Stolb, -1)# Преобразуем одномерный массив в двумерный массив, обратите внимание на то, что мы используем метод reshape()
arr_data_load = pd.DataFrame(arr_data_load)# Преобразуем массив в DataFrame
# Применяем модель на входных данных
arr_Otvet = np.full(Load_Strok, -1)
for s in range(Load_Strok):
arr_Get_Data = arr_data_load[s]#Копируем всю строку двухмерного массива
Get_Klaster_N = f_Klaster_Load(arr_Tree, arr_Get_Data, Load_Stolb,arr_centroid_arhiv)
arr_Otvet[s]=Get_Klaster_N
# Сохранение результата применения модели в файл CSV
csv_data = pd.DataFrame(arr_Otvet)
csv_data.to_csv(dir_cb + "\\Rez_Tree_K-Means\\f_Klaster_Load_" + arr_Set_Viborka[Set] + ".csv", index=False, header=False,sep=';', decimal='.')
# Добавление сохраненной информации к таблицам
#arr_Info = pd.concat([arr_Info, csv_data], axis=1)
# Находим уникальные значения и их количество
#unique_values = np.unique(arr_Otvet)
#num_unique_values = len(unique_values)
# Подсчитываем статистику откликов и смещения процента целевой "1"
df_Statistics=f_Statistics(arr_Otvet,arr_Target)
#print("df_Statistics=",df_Statistics)
# Сохранение результата в файл CSV
csv_data = pd.DataFrame(df_Statistics)
csv_data.to_csv(dir_cb + "\\Rez_Tree_K-Means\\f_Klaster_Statistics_" + arr_Set_Viborka[Set] + ".csv", index=False, header=False,sep=';', decimal='.')
if Set == 0:
# Получаем все уникальные значения из столбца 'Номер кластера'
unique_clusters = df_Statistics['Номер кластера'].unique()
# Создаем новый DataFrame
new_data = pd.DataFrame({
'random_state': [iteration]* len(unique_clusters), # Заполняем первый столбец константой
'cluster': unique_clusters # Заполняем второй столбец уникальными значениями
})
table_stat = pd.concat([table_stat, new_data], ignore_index=True)# Используем pd.concat() для объединения DataFrame
# Добавляем статистику для данных train в DataFrame
matching_rows = table_stat[table_stat['random_state'] == iteration].index#Находим строки, где значение в столбце "random_state" равно переменной iteration
for index in matching_rows:# Для каждой найденной строки в table_stat
second_column_value = table_stat.loc[index, 'cluster']
matching_row = df_Statistics[df_Statistics['Номер кластера'] == second_column_value]# Находим строку во второй таблице, где значение в столбце "Номер кластера" равно значению из столбца "cluster" в первой таблице
if not matching_row.empty: # Если соответствующая строка найдена, копируем значения из второй таблицы в соответствующие столбцы первой таблицы
table_stat.loc[index, 'Precision_'+arr_Set_Viborka[Set]] = matching_row.iloc[0]['Процент значений 1']
table_stat.loc[index, 'Recall_'+arr_Set_Viborka[Set]] = matching_row.iloc[0]['Процент строк в кластере']
# Добавляем значение баланса для каждего кластера в DataFrame
max_value = np.max(arr_Otvet)
arr_Klaster_Balans = np.full(max_value+1, 0.0)
print("arr_Info=",arr_Info)
for i in range(arr_Info.shape[0]):
Target_P=arr_Info['Target_P'][i]
if Target_P>0:
arr_Klaster_Balans[arr_Otvet[i]]+=arr_Info['Target_100_Buy'][i]
else:
arr_Klaster_Balans[arr_Otvet[i]]+=arr_Info['Target_100_Sell'][i]
print('arr_Klaster_Balans=',arr_Klaster_Balans)
for index in table_stat.index:#Перебор по строкам таблицы table_stat
cluster_value = table_stat.loc[index, 'cluster']
if cluster_value < len(arr_Klaster_Balans):#Если значение кластера меньше размера массива, то выполняем код
table_stat.loc[index, 'Balans_'+arr_Set_Viborka[Set]] = arr_Klaster_Balans[cluster_value]
else:
# Обработка случаев, когда индекс выходит за границы массива
pass # Здесь можно добавить какую-то логику или значения по умолчанию для таких случаев
if Set == 2:
# Применяем lambda-функцию для преобразования столбцов в тип "float"
table_stat = table_stat.apply(lambda col: pd.to_numeric(col, errors='coerce'))# Выводим информацию о типах данных
# Сохранение результата в файл CSV
csv_data = pd.DataFrame(table_stat)
csv_data.to_csv(dir_cb + "\\Rez_Tree_K-Means\\f_Klaster_Svod_Statistics.csv", index=False, header=True,sep=';', decimal=',')
if Use_Creat_New_Viborka==True:
#Создадим отфильтрованную выборку
arr_Data_New=f_Creat_Viborka(arr_data_load, arr_Name_Pred, arr_Info,table_stat,arr_Otvet,Load_Strok, Load_Stolb,Variant_Filter_Klaster)
# Применяем lambda-функцию для преобразования столбцов в тип "float"
#arr_Data_New = arr_Data_New.apply(lambda col: pd.to_numeric(col, errors='coerce'))# Выводим информацию о типах данных
#arr_Data_New = arr_Data_New.apply(lambda col: pd.to_numeric(col, errors='coerce') if col.name != 'Time'else col)
# Сохранение результата в файл CSV
csv_data = pd.DataFrame(arr_Data_New)
csv_data.to_csv(dir_cb + "\\Rez_Tree_K-Means\\"+arr_viborka[Set], index=False, header=True,sep=';', decimal='.')
del csv_data
if Use_Load_CSV == True:
del arr_data_load
del arr_Target
У меня там была задача найти не средний, а средний-максимальный спред - внутри минутки брался максимальный спред, а потом они усреднялись для каждой минуты дня.
В подсвеченном месте подставлял средневзвешенный по времени спред минуты. Всплески в 15:15, 15:30 и 17:00.
Как понять, что уже не работает - извечный вопрос.
Понять очень просто - перестает работать :)
Идею подхода уже описывал по ссылкам. Датасет группируется на похожие кластеры (паттерны, если угодно) по признакам. И метки для заданного (subset_size) кол-во кластеров исправляются, то есть все метки становятся 1 или 0, в зависимости от того, чего больше в кластере. Это убирает неоднозначность для финальной модели, она перестает переобучаться на шум и делать лишние сплиты.
В сортированном списке кластеров, по "смещению вероятности", в самом начале стоят самые смещенные. Они исправляются в первую очередь, чтобы стать полностью однозначными для последующего обучения модели. Остальные, которые в хвосте и вероятность в которых близка к 0.5, никак не трогаются и продолжают вносить шум в модель.
Варьируя кол-во кластеров и subset_size, ищется баланс между хорошими кластерами и плохими, который удовлетворяет юзера.
Работа функции прозрачна в том смысле, что дает проверенно ожидаемый результат: чем больше кластеров исправляется, тем модель более устойчива, но менее "красива" и наоборот. Поэтому добавлена доп. настройка, для регулировки.
В итоге, такая маленькая ф-я делает практически всю работу по поиску устойчивых паттернов в данных и улучшению модели. Если устойчивых паттернов нет, всегда будет предсказуемый (плохой) результат даже на трейне, тогда как без этой функции модель бы переобучилась и показала грааль на трейне.
Начинает работать статистика, зависящая от кол-ва данных. Чем больше трейн/тест, тем больше доверия. Без этой ф-ии такой критерий бесмысленный, потому что деревья растут при увеличении длины выборки (всегда переобучаются).
Ваш код убирает строки из выборки, которые относятся к кластерам, имеющих среднее значение (как Вы пишите 0,5) единиц.
Мой код делает то ж самое, если кратко:
1. Открываем выборку train
2. Делаем кластеризацию по принципу дерева, т.е. последовательно углубляясь в каждый кластер.
3. Оцениваем метрики каждого кластера на всех выборках.
4. Отбираем кластеры по выборке train, где есть смещение вероятности встретить целевую "1" больше 5%.
5. Формируем новую выборку по отобранным кластерам.
Но, я не пробовал так много кластеров использовать...
Особой стабильности после этого нет, улучшения есть, если хорошо разметка прошла рандомной кластеризацией, но без подглядывания в новые данные это не гарантированно.
Maxim Dmitrievsky #: И метки для заданного (subset_size) кол-во кластеров исправляются, то есть все метки становятся 1 или 0, в зависимости от того, чего больше в кластере. Это убирает неоднозначность для финальной модели, она перестает переобучаться на шум и делать лишние сплиты.
Вот этого я не делал. Но, по сути это бинаризация уже получается. Опять же, если вероятности сохраняться на новых данных - эффект будет, а если нет, то трындец.
Схожий эффект я получаю через квантование по сути.
Вот этого я не делал. Но, по сути это бинаризация уже получается. Опять же, если вероятности сохраняться на новых данных - эффект будет, а если нет, то трындец.
Схожий эффект я получаю через квантование по сути.
Имхо, ещё должно хотеться сделать нормальный форум по МО в трейдинге))
Хочется сделать, но не хочется делать )
Если не делать сильно большой форвард, то работает как конфета. Потом переобучать, с учетом новых данных.
Признаки любые, но лучше поближе к ценам. Много не надо, 10 достаточно. Желательно нормализовать для кластеризации, если сильно отличаются по значениям.
Разметку желательно делать плотную, то есть для каждого изменения признаков должна быть метка. Но можно и на разреженном датасете. Метки должны соответствовать прибыльным сделкам в большинстве своем. Иначе будет обратный эффект.Видимо никто не читает, один флуд в теме.
Вроде я уже писал тогда, что у меня есть нечто такое же...
Чем больше subset_size
За что отвечает этот параметр?
Параметры можно подбирать.
Так когда перебираешь, то обязательно найдётся хороший результат - надо же, что бы всегда был хороший с любыми параметрами :)
Потом переобучать, с учетом новых данных.
Как понять, что уже не работает - извечный вопрос.
Много не надо, 10 достаточно.
В планах было сделать вариант выбора случайных признаков из моего ассортимента - пока не сделал - вообще почти месяц питон не трогал.
У меня там была задача найти не средний, а средний-максимальный спред - внутри минутки брался максимальный спред, а потом они усреднялись для каждой минуты дня.
В подсвеченном месте подставлял средневзвешенный по времени спред минуты. Всплески в 15:15, 15:30 и 17:00.
Вроде я уже писал тогда, что у меня есть нечто такое же...
Мой код исправляет метки, ваш код что делает?
Сейчас опять начнем ходить кругами, потому что ищем аналогии там, где их нет :)Как понять, что уже не работает - извечный вопрос.
Понять очень просто - перестает работать :)
Идею подхода уже описывал по ссылкам. Датасет группируется на похожие кластеры (паттерны, если угодно) по признакам. И метки для заданного (subset_size) кол-во кластеров исправляются, то есть все метки становятся 1 или 0, в зависимости от того, чего больше в кластере. Это убирает неоднозначность для финальной модели, она перестает переобучаться на шум и делать лишние сплиты.
В сортированном списке кластеров, по "смещению вероятности", в самом начале стоят самые смещенные. Они исправляются в первую очередь, чтобы стать полностью однозначными для последующего обучения модели. Остальные, которые в хвосте и вероятность в которых близка к 0.5, никак не трогаются и продолжают вносить шум в модель.
Варьируя кол-во кластеров и subset_size, ищется баланс между хорошими кластерами и плохими, который удовлетворяет юзера.
Работа функции прозрачна в том смысле, что дает проверенно ожидаемый результат: чем больше кластеров исправляется, тем модель более устойчива, но менее "красива" и наоборот. Поэтому добавлена доп. настройка, для регулировки.
В итоге, такая маленькая ф-я делает практически всю работу по поиску устойчивых паттернов в данных и улучшению модели. Если устойчивых паттернов нет, всегда будет предсказуемый (плохой) результат даже на трейне, тогда как без этой функции модель бы переобучилась и показала грааль на трейне.
Мой код исправляет метки, ваш код что делает?
Ваш код убирает строки из выборки, которые относятся к кластерам, имеющих среднее значение (как Вы пишите 0,5) единиц.
Мой код делает то ж самое, если кратко:
1. Открываем выборку train
2. Делаем кластеризацию по принципу дерева, т.е. последовательно углубляясь в каждый кластер.
3. Оцениваем метрики каждого кластера на всех выборках.
4. Отбираем кластеры по выборке train, где есть смещение вероятности встретить целевую "1" больше 5%.
5. Формируем новую выборку по отобранным кластерам.
Но, я не пробовал так много кластеров использовать...
Особой стабильности после этого нет, улучшения есть, если хорошо разметка прошла рандомной кластеризацией, но без подглядывания в новые данные это не гарантированно.
И метки для заданного (subset_size) кол-во кластеров исправляются, то есть все метки становятся 1 или 0, в зависимости от того, чего больше в кластере. Это убирает неоднозначность для финальной модели, она перестает переобучаться на шум и делать лишние сплиты.
Вот этого я не делал. Но, по сути это бинаризация уже получается. Опять же, если вероятности сохраняться на новых данных - эффект будет, а если нет, то трындец.
Схожий эффект я получаю через квантование по сути.Ваш код убирает строки из выборки, которые относятся к кластерам, имеющих среднее значение (как Вы пишите 0,5) единиц.
Вот этого я не делал. Но, по сути это бинаризация уже получается. Опять же, если вероятности сохраняться на новых данных - эффект будет, а если нет, то трындец.
Схожий эффект я получаю через квантование по сути.