Машинное обучение в трейдинге: теория, модели, практика и алготорговля - страница 3600

 
Aleksey Vyazmikin #:

Я открыт к новым идеям. Можно просто скрипт скинуть и не заморачиваться.

В последнем примере - в целом да. В остальных вариантах - метки строит стратегия. Основывается на логике поведения цены и моих наблюдениях.

from sklearn.cluster import KMeans, BisectingKMeans
import pandas as pd


def fix_labels_subset_mean(dataset, n_clusters=100, subset_size=10) -> pd.DataFrame:
    # Применяем KMeans для кластеризации
    dataset['clusters'] = KMeans(n_clusters=n_clusters).fit(dataset[dataset.columns[:-1]]).labels_

    # Вычисляем среднее значение 'labels' для каждого кластера
    cluster_means = dataset.groupby('clusters')['labels'].mean()

    # Сортируем кластеры по их средним значениям и выбираем те, которые наиболее далеки от 0.5
    sorted_clusters = cluster_means.sub(0.5).abs().sort_values(ascending=False).index[:subset_size]

    # Создаем словарь для отображения средних значений в новые значения только для выбранных кластеров
    mean_to_new_value = {cluster: 0.0 if mean < 0.5 else 1.0 for cluster, mean in cluster_means.items() if cluster in sorted_clusters}

    # Применяем изменения к исходным значениям 'labels' только для выбранных кластеров
    dataset['labels'] = dataset.apply(lambda row: mean_to_new_value[row['clusters']] if row['clusters'] in mean_to_new_value else row['labels'], axis=1)

    dataset = dataset.drop(columns=['clusters'])
    return dataset


Форвард 5 лет:


Видимо никто не читает, один флуд в теме.

З.Ы. Остальные способы (ссылка на чат с промптами) слетели. Но по аналогии можно придумать свои.

Машинное обучение в трейдинге: теория, модели, практика и алготорговля
Машинное обучение в трейдинге: теория, модели, практика и алготорговля
  • 2024.07.30
  • Aleksey Nikolayev
  • www.mql5.com
Добрый день всем, Знаю, что есть на форуме энтузиасты machine learning и статистики...
 
Aleksey Nikolayev #:
Открыл два терминала (в смысле консоль а не МТ), в каждом запустил R. Работают оба, отдельно. Линукс.

Работает и у меня много лет.

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

Причина не понятна.

 

Чем больше subset_size по отношению к кол-ву кластеров, тем кривульки более кривые, но более устойчиывые в своей кривизне на новых данных. И наоборот.

Например:

n_clusters=500, subset_size=200

Обратная ситуация:

n_clusters=500, subset_size=50

n_clusters=500, subset_size=10


 

Последний пример. Параметры можно подбирать.

n_clusters=20, subset_size=5


 
Библа от гугла (или не от гугла, у гугла тоже похожая была) для тех же целей (не пробовал, руки не доходили). Люблю сам придумывать.
GitHub - cleanlab/cleanlab: The standard data-centric AI package for data quality and machine learning with messy, real-world data and labels.
GitHub - cleanlab/cleanlab: The standard data-centric AI package for data quality and machine learning with messy, real-world data and labels.
  • cleanlab
  • github.com
cleanlab helps you clean data and lab els by automatically detecting issues in a ML dataset. To facilitate machine learning with messy, real-world data , this data-centric AI package uses your existing models to estimate dataset...
 
Aleksey Nikolayev #:
Имхо, ещё должно хотеться сделать нормальный форум по МО в трейдинге))

Хочется сделать, но не хочется делать )

 

Если не делать сильно большой форвард, то работает как конфета. Потом переобучать, с учетом новых данных.

Признаки любые, но лучше поближе к ценам. Много не надо, 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
Maxim Dmitrievsky #:
Чем больше subset_size

За что отвечает этот параметр?

Maxim Dmitrievsky #:
Параметры можно подбирать.

Так когда перебираешь, то обязательно найдётся хороший результат - надо же, что бы всегда был хороший с любыми параметрами :)

Maxim Dmitrievsky #:
Потом переобучать, с учетом новых данных.

Как понять, что уже не работает - извечный вопрос.

Maxim Dmitrievsky #:
Много не надо, 10 достаточно.

В планах было сделать вариант выбора случайных признаков из моего ассортимента - пока не сделал - вообще почти месяц питон не трогал.

 
Aleksey Nikolayev #:

У меня там была задача найти не средний, а средний-максимальный спред - внутри минутки брался максимальный спред, а потом они усреднялись для каждой минуты дня.

В подсвеченном месте подставлял средневзвешенный по времени спред минуты. Всплески в 15:15, 15:30 и 17:00.