Русский
preview
Trading por algoritmo: IA e seu caminho para os topos dourados

Trading por algoritmo: IA e seu caminho para os topos dourados

MetaTrader 5Sistemas de negociação |
64 12
Maxim Dmitrievsky
Maxim Dmitrievsky

Introdução

A evolução do entendimento das capacidades dos métodos de aprendizado de máquina no trading levou à criação de diferentes algoritmos que resolvem igualmente bem a mesma tarefa, mas que são essencialmente distintos. Nesta artigo será novamente analisado um sistema de trading de tendência unidirecional, usando o ouro como exemplo, mas aplicando um algoritmo de clusterização.

Ao observarmos esse importante método de análise e previsão de séries temporais sob diferentes ângulos, é possível identificar suas vantagens e desvantagens em comparação com outras formas de criação de sistemas de trading baseadas exclusivamente na análise e previsão de séries temporais financeiras. Em alguns casos, esses algoritmos se mostram bastante eficientes, superando os métodos clássicos tanto na velocidade de criação quanto na qualidade dos sistemas de negociação resultantes.

Neste artigo, vamos nos concentrar no trading unidirecional, quando o algoritmo abre apenas operações de compra ou de venda. Como algoritmos básicos, serão usados o CatBoost e o K-Means. O CatBoost é o modelo base, atuando como um classificador binário para classificar operações. Já o K-Means é utilizado para identificar regimes de mercado na etapa de preprocessamento.


Preparação para o trabalho e importação dos módulos

import math
import pandas as pd
from datetime import datetime
from catboost import CatBoostClassifier

from sklearn.model_selection import train_test_split
from sklearn.cluster import KMeans

from bots.botlibs.labeling_lib import *
from bots.botlibs.tester_lib import tester_one_direction
from bots.botlibs.export_lib import export_model_to_ONNX

import time

No código são usados apenas pacotes confiáveis e amplamente acessíveis, tais como:

  • Pandas: responsável pelo trabalho com tabelas de dados (dataframes)
  • Scikit-learn: contém várias funções para preprocessamento e aprendizado de máquina, incluindo algoritmos de clusterização
  • CatBoost: algoritmo poderoso de gradient boosting da empresa Yandex

Foram importados módulos separados que eu mesmo criei:

  • labeling_lib: contém funções de amostradores para a rotulação de operações
  • tester_lib: nele estão localizados os testadores de estratégias baseadas em aprendizado de máquina
  • export_lib: módulo para exportar modelos treinados para o terminal Meta Trader 5 no formato ONNX


Obtenção dos dados e criação das características

def get_prices() -> pd.DataFrame:
    p = pd.read_csv('files/'+hyper_params['symbol']+'.csv', sep='\s+')
    pFixed = pd.DataFrame(columns=['time', 'close'])
    pFixed['time'] = p['<DATE>'] + ' ' + p['<TIME>']
    pFixed['time'] = pd.to_datetime(pFixed['time'], format='mixed')
    pFixed['close'] = p['<CLOSE>']
    pFixed.set_index('time', inplace=True)
    pFixed.index = pd.to_datetime(pFixed.index, unit='s')
    return pFixed.dropna()

No código está implementado o carregamento de cotações a partir de arquivo, para facilitar a obtenção de dados de diferentes fontes. São usados apenas preços de fechamento. Com base nesses dados são criadas as características.

def get_features(data: pd.DataFrame) -> pd.DataFrame:
    pFixed = data.copy()
    pFixedC = data.copy()
    count = 0

    for i in hyper_params['periods']:
        pFixed[str(count)] = pFixedC.rolling(i).std()
        count += 1
    
    for i in hyper_params['periods_meta']:
        pFixed[str(count)+'meta_feature'] = pFixedC.rolling(i).std()
        count += 1

As características são divididas em dois grupos:

  1. Características principais para o treinamento do modelo base, que prevê a direção do trading.
  2. Meta-características adicionais para a clusterização. Elas são usadas para dividir os dados brutos em clusters (regimes de mercado).

Neste exemplo, como características é usada a volatilidade (desvios padrão dos preços em janelas móveis de período definido). Mas também vamos testar outras características, por exemplo médias móveis e inclinações das distribuições.


Clusterização dos regimes de mercado

def clustering(dataset, n_clusters: int) -> pd.DataFrame:
    data = dataset[(dataset.index < hyper_params['forward']) & (dataset.index > hyper_params['backward'])].copy()
    meta_X = data.loc[:, data.columns.str.contains('meta_feature')]
    data['clusters'] = KMeans(n_clusters=n_clusters).fit(meta_X).labels_
    return data

A função recebe um dataframe com preços e características e usa as meta-características adicionais para a clusterização em uma quantidade definida de clusters (geralmente 10). Para a clusterização é usado o algoritmo K-Means. Depois disso, para cada linha do dataframe é atribuída uma etiqueta de cluster que corresponde àquela observação. E retorna-se o dataframe com a coluna adicional "clusters".


Função para o treinamento dos classificadores

def fit_final_models(clustered, meta) -> list:
    # features for model\meta models. We learn main model only on filtered labels 
    X, X_meta = clustered[clustered.columns[:-1]], meta[meta.columns[:-1]]
    X = X.loc[:, ~X.columns.str.contains('meta_feature')]
    X_meta = X_meta.loc[:, X_meta.columns.str.contains('meta_feature')]
    
    # labels for model\meta models
    y = clustered['labels']
    y_meta = meta['clusters']
    
    y = y.astype('int16')
    y_meta = y_meta.astype('int16')

    # train\test split
    train_X, test_X, train_y, test_y = train_test_split(
        X, y, train_size=0.7, test_size=0.3, shuffle=True)
    
    train_X_m, test_X_m, train_y_m, test_y_m = train_test_split(
        X_meta, y_meta, train_size=0.7, test_size=0.3, shuffle=True)


    # learn main model with train and validation subsets
    model = CatBoostClassifier(iterations=500,
                               custom_loss=['Accuracy'],
                               eval_metric='Accuracy',
                               verbose=False,
                               use_best_model=True,
                               task_type='CPU',
                               thread_count=-1)
    model.fit(train_X, train_y, eval_set=(test_X, test_y),
              early_stopping_rounds=25, plot=False)
    
    # learn meta model with train and validation subsets
    meta_model = CatBoostClassifier(iterations=300,
                                    custom_loss=['F1'],
                                    eval_metric='F1',
                                    verbose=False,
                                    use_best_model=True,
                                    task_type='CPU',
                                    thread_count=-1)
    meta_model.fit(train_X_m, train_y_m, eval_set=(test_X_m, test_y_m),
              early_stopping_rounds=15, plot=False)

    
    R2 = test_model_one_direction([model, meta_model],
                                hyper_params['stop_loss'], 
                                hyper_params['take_profit'],
                                hyper_params['full forward'],
                                hyper_params['backward'],
                                hyper_params['markup'],
                                hyper_params['direction'],
                                plt=False)
    if math.isnan(R2):
        R2 = -1.0
        print('R2 is fixed to -1.0')
    print('R2: ' + str(R2))

    return [R2, model, meta_model]

Para o treinamento são usados dois modelos. O primeiro é treinado nas características principais e nas etiquetas, e o segundo é treinado nas meta-características e nas meta-etiquetas. Se para o primeiro modelo as etiquetas são as direções das operações, para o segundo modelo as etiquetas são os números dos clusters. 1 — quando os dados correspondem ao cluster necessário, e 0 — quando os dados correspondem a todos os demais clusters. 

Antes do treinamento os dados são divididos em treinamento e validação na proporção 70/30, para que o CatBoost tenha menos sobreajuste. Ele usa os dados de validação para parada antecipada, quando o erro neles deixa de cair durante o treinamento. Em seguida é escolhido o melhor modelo, que tem o menor erro de previsão nos dados de validação.

Depois do treinamento dos modelos, eles são enviados para a função de teste, para avaliar a curva de balanço por meio de R^2. Isso é necessário para posterior ordenação dos modelos e escolha do melhor.


Função de teste dos modelos

def test_model_one_direction( 
               result: list, 
               stop: float, 
               take: float, 
               forward: float, 
               backward: float, 
               markup: float,
               direction: str, 
               plt = False):
    
    pr_tst = get_features(get_prices())
    X = pr_tst[pr_tst.columns[1:]]
    X_meta = X.copy()
    X = X.loc[:, ~X.columns.str.contains('meta_feature')]
    X_meta = X_meta.loc[:, X_meta.columns.str.contains('meta_feature')]

    pr_tst['labels'] = result[0].predict_proba(X)[:,1]
    pr_tst['meta_labels'] = result[1].predict_proba(X_meta)[:,1]
    pr_tst['labels'] = pr_tst['labels'].apply(lambda x: 0.0 if x < 0.5 else 1.0)
    pr_tst['meta_labels'] = pr_tst['meta_labels'].apply(lambda x: 0.0 if x < 0.5 else 1.0)
    return tester_one_direction(pr_tst, stop, take, forward, backward, markup, direction, plt)

A função recebe dois modelos treinados (o principal e o meta-modelo), além dos demais parâmetros necessários para o teste no testador customizado de estratégias. Depois disso, é criado novamente um dataframe com preços e características, que são alimentadas nesses modelos para gerar previsões. As previsões obtidas são registradas nas colunas "labels" e "meta_labels" desse dataframe.

No final é chamada a função do testador customizado, que está localizada no módulo conectado tester_lib.py, que executa o teste dos modelos no histórico e retorna a métrica R^2.


Função de rotulação das operações

O módulo labeling_lib.py contém um amostrador destinado à rotulação das operações somente no sentido selecionado:

@njit
def calculate_labels_one_direction(close_data, markup, min, max, direction):
    labels = []
    for i in range(len(close_data) - max):
        rand = random.randint(min, max)
        curr_pr = close_data[i]
        future_pr = close_data[i + rand]

        if direction == "sell":
            if (future_pr + markup) < curr_pr:
                labels.append(1.0)
            else:
                labels.append(0.0)
        if direction == "buy":
            if (future_pr - markup) > curr_pr:
                labels.append(1.0)
            else:
                labels.append(0.0)
    return labels

def get_labels_one_direction(dataset, markup, min = 1, max = 15, direction = 'buy') -> pd.DataFrame:
    close_data = dataset['close'].values
    labels = calculate_labels_one_direction(close_data, markup, min, max, direction)
    dataset = dataset.iloc[:len(labels)].copy()
    dataset['labels'] = labels
    dataset = dataset.dropna()
    return dataset


Ciclo principal de treinamento

# LEARNING LOOP
dataset = get_features(get_prices()) 	// получение цен и признаков
models = [] 				// создание пустого списка моделей

for i in range(1):							// цикл задает сколько попыток обучения нужно выполнить
    start_time = time.time()
    data = clustering(dataset, n_clusters=hyper_params['n_clusters']) 	// добавление номеров кластеров к данным
    sorted_clusters = data['clusters'].unique() 			// определение уникальных кластеров
    sorted_clusters.sort() 						// сортировка кластеров по возрастанию
    for clust in sorted_clusters: 					// цикл по всем кластерам
        clustered_data = data[data['clusters'] == clust].copy()		// выбор данных для одного гластера
        if len(clustered_data) < 500:
            print('too few samples: {}'.format(len(clustered_data)))	// проверка на достаточность обучающих примеров
            continue
    
        clustered_data = get_labels_one_direction(clustered_data,	// разметка сделок для выбранного кластера
                                       markup=hyper_params['markup'],
                                       min=1,
                                       max=15,
                                       direction=hyper_params['direction'])
        
        print(f'Iteration: {i}, Cluster: {clust}')
        clustered_data = clustered_data.drop(['close', 'clusters'], axis=1)// удаление цен закрытия и номеров кластеров

        meta_data = data.copy()	// создание данных для мета-модели
        meta_data['clusters'] = meta_data['clusters'].apply(lambda x: 1 if x == clust else 0) // размтка текущего кластера как "1"
        models.append(fit_final_models(clustered_data, meta_data.drop(['close'], axis=1))) // обучение моделей и добавление их в список
    end_time = time.time()
    print("Время выполнения: ", end_time - start_time)

No ciclo de treinamento são usadas sequencialmente todas as funções descritas anteriormente:

  • Do arquivo são carregadas as cotações para um dataframe e são criadas as características
  • Cria-se uma lista vazia que armazenará os modelos treinados
  • Define-se a quantidade de iterações (tentativas) de treinamento nos mesmos dados, para eliminar flutuações aleatórias dos modelos
  • Acontece a clusterização das meta-características e é adicionada a coluna "clusters" aos dados
  • No ciclo, para cada cluster, são selecionados os dados que pertencem somente a ele
  • Os dados de cada cluster são rotulados, ou seja, são criadas etiquetas de classes para o modelo principal
  • Cria-se um dataset adicional para o meta-modelo, que aprende a identificar o cluster definido entre todos os outros
  • Ambos os datasets são enviados para a função de treinamento, que executa o treinamento dos dois classificadores
  • Os modelos treinados são adicionados à lista


Processo de treinamento e teste dos modelos

Os hiperparâmetros (configurações gerais) do algoritmo são colocados em um dicionário:

hyper_params = {
    'symbol': 'XAUUSD_H1',
    'export_path': '/drive_c/Program Files/MetaTrader 5/MQL5/Include/Mean reversion/',
    'model_number': 0,
    'markup': 0.2,
    'stop_loss':  10.000,
    'take_profit': 5.000,
    'periods': [i for i in range(5, 300, 30)],
    'periods_meta': [5],
    'backward': datetime(2020, 1, 1),
    'forward': datetime(2024, 1, 1),
    'full forward': datetime(2026, 1, 1),
    'direction': 'buy',
    'n_clusters': 10,
}

O treinamento será feito no período de 2020 a 2024, e o período de teste, desde o início de 2024 até hoje.

É muito importante definir corretamente os seguintes parâmetros:

  • markup - 0.2 — este é o spread médio do símbolo XAUUSD. Se você definir um spread muito pequeno ou muito grande, os resultados dos testes podem ficar irreais. Além disso, aqui também entram perdas adicionais ligadas a slippage e comissões, caso existam.
  • stop loss — tamanho do stop em pontos do símbolo.
  • take profit — tamanho do take em pontos. Deve-se considerar que as operações são fechadas tanto pelos sinais do modelo quanto pelo atingimento de stop-loss ou take-profit.
  • periods — lista com os valores de períodos para as características principais. No geral, dez períodos são suficientes, começando em cinco e com passo de 30.
  • periods meta — lista com valores de períodos para as meta-características. Para determinar regimes de mercado não é necessário um grande número de características. Normalmente é uma única característica, por exemplo o desvio padrão dos últimos 5 barras.
  • direction — vamos usar apenas "buy", porque o ouro tem tendência de alta. 
  • n_clusters — quantidade de regimes (clusters) para a clusterização. Normalmente uso 10.

Treinamento nos desvios padrão

Primeiro vamos usar apenas os desvios padrão como características, portanto a função de criação das características ficará assim:

def get_features(data: pd.DataFrame) -> pd.DataFrame:
    pFixed = data.copy()
    pFixedC = data.copy()
    count = 0

    for i in hyper_params['periods']:
        pFixed[str(count)] = pFixedC.rolling(i).std()
        count += 1
    
    for i in hyper_params['periods_meta']:
        pFixed[str(count)+'meta_feature'] = pFixedC.rolling(i).std()
        count += 1

    return pFixed.dropna()

Vamos executar um ciclo de treinamento, no qual obteremos as seguintes informações:

Iteration: 0, Cluster: 0
R2: 0.989543793954197
Iteration: 0, Cluster: 1
R2: 0.9697821077241253
too few samples: 19
too few samples: 238
Iteration: 0, Cluster: 4
R2: 0.9852770333065658
Iteration: 0, Cluster: 5
R2: 0.7723040599270985
too few samples: 87
Iteration: 0, Cluster: 7
R2: 0.9970885055361235
Iteration: 0, Cluster: 8
R2: 0.9524980839809385
too few samples: 446
Время выполнения:  2.140070915222168

Foi feita a tentativa de treinar dez modelos para dez regimes de mercado. Nem todos os regimes se mostraram úteis, porque alguns deles continham exemplos de treinamento (operações) em quantidade muito pequena. Eles não passaram pelo filtro de quantidade mínima de operações, por isso não foram usados no treinamento.

O melhor regime de mercado (cluster) de número 7 mostrou R^2 0.99. Este é um bom candidato a melhor modelo para trading. O tempo de execução de todo o ciclo de treinamento foi de apenas 2 segundos, o que é muito rápido.

Depois de ordenar os modelos, vamos testar o melhor:

Figura 1. Teste do melhor modelo após ordenação

O modelo seguinte também se mostrou suficientemente bom e tem uma grande quantidade de operações:

Figura 2. Teste do segundo modelo no ranking

Como o treinamento e os testes dos modelos são muito rápidos, é possível reiniciar o ciclo muitas vezes, obtendo modelos da mais alta qualidade. Por exemplo, depois da próxima reinicialização e ordenação, foi obtida esta variante:

Figura 3. Teste do melhor modelo após o ciclo de treinamento repetido

Treinamento em médias móveis e desvios padrão

Vamos alterar nossas características e observar como os modelos vão se comportar.

def get_features(data: pd.DataFrame) -> pd.DataFrame:
    pFixed = data.copy()
    pFixedC = data.copy()
    count = 0

    for i in hyper_params['periods']:
        pFixed[str(count)] = pFixedC.rolling(i).mean()
        count += 1
    
    for i in hyper_params['periods_meta']:
        pFixed[str(count)+'meta_feature'] = pFixedC.rolling(i).std()
        count += 1

    return pFixed.dropna()

Como características principais serão usadas médias móveis simples, e como meta-características, os desvios padrão.

Vamos iniciar o ciclo de treinamento e observar os melhores modelos:

Iteration: 0, Cluster: 0
R2: 0.9312180471969619
Iteration: 0, Cluster: 1
R2: 0.9839766532391275
too few samples: 101
Iteration: 0, Cluster: 3
R2: 0.9643925934007344
too few samples: 299
Iteration: 0, Cluster: 5
R2: 0.9960009821184868
too few samples: 19
Iteration: 0, Cluster: 7
R2: 0.9557947960449501
Iteration: 0, Cluster: 8
R2: 0.9747160963596306
Iteration: 0, Cluster: 9
R2: 0.5526910449937035
Время выполнения:  2.8627688884735107

Assim se apresenta no testador o melhor modelo:

Figura 4. Teste do melhor modelo em médias móveis

O segundo melhor modelo também demonstra um bom resultado:

Figura 5. Teste do segundo modelo em médias móveis

Após reiniciar o ciclo de treinamento mais algumas vezes, obtive um gráfico de balanço mais suave:

Figura 6. Teste da melhor modelo após o ciclo de treinamento repetido

Aqui eu não experimentei com a quantidade de características (lista dos seus períodos), portanto, na prática, é possível obter uma grande variedade desses modelos. As capturas de tela apresentadas apenas demonstram algumas das variantes.


Combate ao sobreajuste

Muitas vezes acontece de a complexidade excessiva do modelo causar um efeito negativo na sua capacidade de generalização. Mesmo considerando a parte de validação e a parada antecipada. Nesse caso, é possível tentar reduzir a quantidade de características e/ou diminuir a complexidade do modelo. Na terminologia do CatBoost, a complexidade do modelo significa a quantidade de iterações ou árvores de decisão construídas de forma sequencial. Na função fit_final_models() tente alterar os seguintes valores:

def fit_final_models(clustered, meta) -> list:
    # features for model\meta models. We learn main model only on filtered labels 
    X, X_meta = clustered[clustered.columns[:-1]], meta[meta.columns[:-1]]
    X = X.loc[:, ~X.columns.str.contains('meta_feature')]
    X_meta = X_meta.loc[:, X_meta.columns.str.contains('meta_feature')]
    
    # labels for model\meta models
    y = clustered['labels']
    y_meta = meta['clusters']
    
    y = y.astype('int16')
    y_meta = y_meta.astype('int16')

    # train\test split
    train_X, test_X, train_y, test_y = train_test_split(
        X, y, train_size=0.8, test_size=0.2, shuffle=True)
    
    train_X_m, test_X_m, train_y_m, test_y_m = train_test_split(
        X_meta, y_meta, train_size=0.8, test_size=0.2, shuffle=True)


    # learn main model with train and validation subsets
    model = CatBoostClassifier(iterations=100,
                               custom_loss=['Accuracy'],
                               eval_metric='Accuracy',
                               verbose=False,
                               use_best_model=True,
                               task_type='CPU',
                               thread_count=-1)
    model.fit(train_X, train_y, eval_set=(test_X, test_y),
              early_stopping_rounds=15, plot=False)

Reduza a quantidade de iterações para 100 e o valor da parada antecipada para 15. Isso permitirá construir um modelo menos complexo.

Figura 7. Testando um modelo menos complexo


Exportação dos modelos para o terminal MetaTrader 5

A função export_model_to_ONNX() do módulo export_lib() contém as seguintes linhas:

# get features
    code += 'void fill_arays' + symbol + '_' + str(model_number) + '( double &features[]) {\n'
    code += '   double pr[], ret[];\n'
    code += '   ArrayResize(ret, 1);\n'
    code += '   for(int i=ArraySize(Periods'+ symbol + '_' + str(model_number) + ')-1; i>=0; i--) {\n'
    code += '       CopyClose(NULL,PERIOD_H1,1,Periods' + symbol + '_' + str(model_number) + '[i],pr);\n'
    code += '       ret[0] = MathMean(pr);\n'
    code += '       ArrayInsert(features, ret, ArraySize(features), 0, WHOLE_ARRAY); }\n'
    code += '   ArraySetAsSeries(features, true);\n'
    code += '}\n\n'

    # get features
    code += 'void fill_arays_m' + symbol + '_' + str(model_number) + '( double &features[]) {\n'
    code += '   double pr[], ret[];\n'
    code += '   ArrayResize(ret, 1);\n'
    code += '   for(int i=ArraySize(Periods_m' + symbol + '_' + str(model_number) + ')-1; i>=0; i--) {\n'
    code += '       CopyClose(NULL,PERIOD_H1,1,Periods_m' + symbol + '_' + str(model_number) + '[i],pr);\n'
    code += '       ret[0] = MathSkewness(pr);\n'
    code += '       ArrayInsert(features, ret, ArraySize(features), 0, WHOLE_ARRAY); }\n'
    code += '   ArraySetAsSeries(features, true);\n'
    code += '}\n\n'

As linhas destacadas são responsáveis pelo cálculo das características no código MQL5. Caso você altere as características no script Python na função get_features(), como foi descrito acima, será necessário alterar o cálculo delas nesse código, ou você pode fazer isso diretamente no arquivo .mqh já exportado.

Por exemplo, no arquivo exportado XAUUSD_H1 ONNX include 0.mqh é necessário corrigir as seguintes linhas:

#include <Math\Stat\Math.mqh>
#resource "catmodel XAUUSD_H1 0.onnx" as uchar ExtModel_XAUUSD_H1_0[]
#resource "catmodel_m XAUUSD_H1 0.onnx" as uchar ExtModel2_XAUUSD_H1_0[]

int PeriodsXAUUSD_H1_0[10] = {5,35,65,95,125,155,185,215,245,275};
int Periods_mXAUUSD_H1_0[1] = {5};

void fill_araysXAUUSD_H1_0( double &features[]) {
   double pr[], ret[];
   ArrayResize(ret, 1);
   for(int i=ArraySize(PeriodsXAUUSD_H1_0)-1; i>=0; i--) {
       CopyClose(NULL,PERIOD_H1,1,PeriodsXAUUSD_H1_0[i],pr);
       ret[0] = MathStandardDeviation(pr);
       // ret[0] = MathMean(pr);
       ArrayInsert(features, ret, ArraySize(features), 0, WHOLE_ARRAY); }
   ArraySetAsSeries(features, true);
}

void fill_arays_mXAUUSD_H1_0( double &features[]) {
   double pr[], ret[];
   ArrayResize(ret, 1);
   for(int i=ArraySize(Periods_mXAUUSD_H1_0)-1; i>=0; i--) {
       CopyClose(NULL,PERIOD_H1,1,Periods_mXAUUSD_H1_0[i],pr);
       ret[0] = MathStandardDeviation(pr);
       ArrayInsert(features, ret, ArraySize(features), 0, WHOLE_ARRAY); }
   ArraySetAsSeries(features, true);
}

Agora o cálculo das características corresponde ao cálculo da função get_features(), na qual eram usados apenas os desvios padrão. Se forem usadas médias móveis, então deve-se substituir por MathMean().

Depois da compilação, já é possível testar o bot diretamente no terminal MetaTrader 5.

Figura 8. Testando no intervalo de treinamento + teste

Figura 9. Testando apenas no período de teste


Considerações finais

Nesta artigo foi demonstrado mais um método de criação de estratégias de tendência unidirecional, agora baseado em clusterização. A principal diferença desse método é sua intuitividade e alta velocidade de treinamento. A qualidade dos modelos obtidos é comparável àquela da artigo anterior.

O arquivo Python files.zip contém os seguintes arquivos para desenvolvimento no ambiente Python:

Nome do arquivo Descrição
one direction clusters.py 
Script principal para o treinamento dos modelos
labeling_lib.py
Módulo atualizado com rotuladores de operações
tester_lib.py
Testador customizado atualizado para estratégias baseadas em aprendizado de máquina
export_lib.py Módulo para exportação dos modelos para o terminal
XAUUSD_H1.csv
Arquivo com cotações, exportado do terminal MetaTrader 5

O arquivo MQL5 files.zip contém arquivos para o terminal MetaTrader 5:

Nome do arquivo Descrição
one direction clusters.ex5
Bot compilado desta artigo
one direction clusters.mq5
Código-fonte do bot da artigo
pasta Include//Trend following
Estão localizados os modelos ONNX e o arquivo de cabeçalho para conexão ao bot

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/17755

Arquivos anexados |
MQL5_files.zip (103.29 KB)
Python_files.zip (547.58 KB)
Últimos Comentários | Ir para discussão (12)
Aliaksandr Kazunka
Aliaksandr Kazunka | 26 mai. 2025 em 12:01
Portanto, você tem o R2, um índice modificado, cuja eficiência é baseada no lucro em pips. E quanto ao drawdown e outros indicadores de desempenho? Se obtivermos um modelo que dê mais de 90% no treinamento e pelo menos 85% no teste, então seu índice apresentará números impressionantes. Não importa quantas vezes eu tenha executado o testador no MT5, nunca recebi um lucro no histórico. O depósito é drenado. Isso ocorre apesar do fato de seu testador em Python fornecer 0,97-0,98
Maxim Dmitrievsky
Maxim Dmitrievsky | 26 mai. 2025 em 12:17
sportoman #:
Portanto, você tem o R2, um índice modificado, cuja eficiência é baseada no lucro em pips. E quanto ao drawdown e outros indicadores de desempenho? Se obtivermos um modelo que dê mais de 90% no treinamento e pelo menos 85% no teste, então seu índice apresentará números impressionantes. Não importa quantas vezes eu tenha executado o testador no MT5, nunca recebi um lucro no histórico. O depósito é drenado. Isso ocorre apesar do fato de seu testador em Python fornecer 0,97-0,98

Não entendo o que isso tem a ver com o CV.

Todas essas estratégias têm baixo poder de comprovação, porque se baseiam apenas no histórico de cotações não estacionárias. Mas você pode detectar tendências.

Qualquer verificação dupla no histórico não aumenta as chances de que as tendências mudem. Ou seja, você não pode provar algo sobre o futuro com base no histórico, você só pode estimar o grau de generalização do modelo com base nos dados disponíveis. Há um período de teste para isso.

Se alguma nova maneira eficiente de testar modelos em séries não estacionárias já tiver sido inventada, informe-me :).
Maxim Dmitrievsky
Maxim Dmitrievsky | 26 mai. 2025 em 13:00
Há também um artigo sobre estratégias de reversão à média. Lá, fazemos a suposição mais forte de que uma série temporal retorna à média quase sempre. Ao contrário das tendências, que mudam.
Vladimir Perervenko
Vladimir Perervenko | 29 mai. 2025 em 08:47

Então, onde está a IA nisso? Vocês fizeram um upgrade para esse nível? Ou isso é apenas um truque comum de marketing para atrair o público?

Notei esse recurso estranho em várias publicações recentes de diferentes autores.

Não há outros modelos decentes além do catbust?

Maxim Dmitrievsky
Maxim Dmitrievsky | 29 mai. 2025 em 09:19
Vladimir Perervenko #:

Então, onde está a IA nisso? Vocês fizeram um upgrade para esse nível? Ou isso é apenas um truque de marketing comum para atrair o público?

Notei esse recurso estranho em várias publicações recentes de diferentes autores.

Não há outros modelos decentes além do catbust?

Clickbait (abreviação popular). Não sou defensor do termo de forma alguma.

As pessoas estão acostumadas a se referir ao MO como "IA". Além disso, o TC é um complexo de diferentes algoritmos de MO, por exemplo, agrupamento e classificação.
Otimização de recifes de coral — Coral Reefs Optimization (CRO) Otimização de recifes de coral — Coral Reefs Optimization (CRO)
Neste artigo é apresentada uma análise abrangente do algoritmo de otimização de recifes de coral (CRO), um método meta-heurístico inspirado nos processos biológicos de formação e desenvolvimento de recifes de coral. Ele modela aspectos-chave da evolução dos corais: reprodução externa e interna, fixação de larvas, reprodução assexuada e competição por espaço limitado no recife. É dada atenção especial à versão aprimorada do algoritmo.
Desenvolvimento do Kit de Ferramentas de Análise de Price Action (Parte 5): Volatility Navigator EA Desenvolvimento do Kit de Ferramentas de Análise de Price Action (Parte 5): Volatility Navigator EA
Determinar a direção do mercado pode ser simples, mas saber quando entrar pode ser desafiador. Como parte da série intitulada "Desenvolvimento do Kit de Ferramentas de Análise de Price Action", tenho o prazer de apresentar mais uma ferramenta que fornece pontos de entrada, níveis de take profit e definições de stop loss. Para isso, utilizamos a linguagem de programação MQL5. Vamos nos aprofundar em cada etapa neste artigo.
Redes neurais em trading: Ator–Diretor–Crítico (Actor–Director–Critic) Redes neurais em trading: Ator–Diretor–Crítico (Actor–Director–Critic)
Propomos conhecer o framework Actor-Director-Critic, que combina aprendizado hierárquico e uma arquitetura com múltiplos componentes para criar estratégias de trading adaptativas. Neste artigo, analisamos em detalhe como o uso do Diretor para classificar as ações do Ator ajuda a otimizar decisões de trading de forma eficiente e a aumentar a robustez dos modelos nas condições dos mercados financeiros.
Construindo um Modelo de Restrição de Tendência com Candlesticks (Parte 9): Expert Advisor de Múltiplas Estratégias (III) Construindo um Modelo de Restrição de Tendência com Candlesticks (Parte 9): Expert Advisor de Múltiplas Estratégias (III)
Bem-vindo à terceira parte da nossa série sobre tendências! Hoje, vamos nos aprofundar no uso de divergência como estratégia para identificar pontos de entrada ideais dentro da tendência diária predominante. Também apresentaremos um mecanismo personalizado de proteção de lucro, semelhante a um trailing stop-loss, mas com melhorias exclusivas. Além disso, vamos atualizar o Trend Constraint Expert para uma versão mais avançada, incorporando uma nova condição de execução de trade para complementar as já existentes. À medida que avançamos, continuaremos explorando a aplicação prática do MQL5 no desenvolvimento algorítmico, fornecendo a você percepções mais detalhadas e técnicas acionáveis.