preview
Исследуем регрессионные модели для причинно-следственного вывода и трейдинга

Исследуем регрессионные модели для причинно-следственного вывода и трейдинга

MetaTrader 5Интеграция |
436 3
Maxim Dmitrievsky
Maxim Dmitrievsky

Введение

На протяжении ряда статей мы исследовали различные способы классификации временных рядов, но при этом не затрагивали регрессионные модели. Регрессионные модели, в отличие от бинарной классификации, позволяют предсказывать не вероятность принадлежности наблюдения к тому или иному классу, а непрерывные значения, что расширяет возможности их применения для создания автоматических торговых систем.

Бинарная классификация представляет собой фундаментальную задачу машинного обучения, целью которой является классификация входных данных по одной из двух различных категорий или классов. В контексте торгового бота на Форекс это обычно означает прогнозирование сигнала "купить" (представленного как 0) или "продать" (представленного как 1). Такой подход упрощает сложную динамику рынка до простого направленного решения.

Наиболее существенным внутренним ограничением бинарной классификации для количественной торговли является ее неспособность количественно оценить величину или интенсивность прогнозируемого движения цены. Бинарный классификатор лишь утверждает, будет ли цена двигаться вверх или вниз, не предоставляя никакой информации о том, насколько она, как ожидается, изменится. Отсутствие такой детализации принципиально ограничивает сложность торговых решений.

Точность предсказаний классификатора сама по себе не учитывает величину изменения, и поэтому не очень полезна для торговли. Этот аспект является ключевым, поскольку он подчеркивает, что высокая точность определения направления (например, прогнозирование правильного направления в 70% случаев) не автоматически приводит к прибыльности торговли. 

Существует важное наблюдение, что высокая точность определения направления не гарантирует прибыльности. Например, можно быть правым в 30% случаев и быть прибыльным, или быть правым в 70% случаев и быть убыточным. Это демонстрирует, что чистый результат торговой стратегии определяется величиной прибыли по выигрышным сделкам по сравнению с величиной убытков по проигрышным сделкам, а не просто процентом выигрышей.

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

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


Модификация функции разметки

Предположим следующий сценарий: имеется финансовый временной ряд, который требуется предсказать по набору признаков. В случае бинарной классификации можно определить направление будущей сделки (покупка или продажа), и эти метки всегда фиксированы. Мы не можем разметить их иным образом, чтобы получить более точную оценку величины будущих ценовых отклонений. Сделки получаются равнозначными вне зависимости от того, насколько действительно изменилась цена.

Теперь представим, что мы можем прогнозировать не только направление сделки, но и величину будущего изменения. Это позволит более тонко настроить торговую систему путем создания дополнительных фильтров, которые помогут определить только значимые для торговли прогнозируемые ценовые колебания и исключить незначительные.

Чтобы обучить регрессионную модель, необходимо подготовить признаки и целевые для ее обучения. Признаки могут быть общими для классификатора и регрессора, тогда как целевые будут отличаться.

Напишем простую функцию, которая реализует разметку примеров для регрессионной модели:

@njit
def calculate_labels_r(close_data, min_val, max_val):
    labels = []
    for i in range(len(close_data) - max_val):
        rand = random.randint(min_val, max_val)
        labels.append(close_data[i + rand] - close_data[i])
    return labels

def get_labels_r(dataset, min = 1, max = 15) -> pd.DataFrame:
    # Extract closing prices from the dataset
    close_data = dataset['close'].values
    labels = calculate_labels_r(close_data, min, max)
    # Trim the dataset to match the length of calculated labels
    dataset = dataset.iloc[:len(labels)].copy() 
    # Add the calculated labels as a new column
    dataset['labels'] = labels
    # Remove rows with NaN values (potentially introduced in 'calculate_labels')
    dataset = dataset.dropna()
    return dataset

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

Вышеприведенная функция учитывает разницу только между случайно выбранной будущей ценой в диапазоне {min_val; max_val} и текущей. Это может быть не совсем корректно, поскольку не учтены промежуточные отклонения, которые могут быть значительными. Я предлагаю еще одну модификацию функции расчета отклонений, которая представлена ниже.

@njit
def calculate_labels_mean_r(close_data, min_val, max_val):
    labels = []
    for i in range(len(close_data) - max_val):
        # Вычисляем среднее значение цен в окне от min_val до max_val
        future_prices = close_data[i + min_val : i + max_val + 1]
        mean_future_price = np.mean(future_prices)
        # Вычисляем разницу между средним будущим значением и текущей ценой
        labels.append(mean_future_price - close_data[i])
    return labels

def get_labels_r(dataset, min = 1, max = 15) -> pd.DataFrame:
    # Extract closing prices from the dataset
    close_data = dataset['close'].values
    # Calculate buy/hold labels based on future price movements
    labels = calculate_labels_mean_r(close_data, min, max)
    # Trim the dataset to match the length of calculated labels
    dataset = dataset.iloc[:len(labels)].copy() 
    # Add the calculated labels as a new column
    dataset['labels'] = labels
    # Remove rows with NaN values (potentially introduced in 'calculate_labels')
    dataset = dataset.dropna()
    return dataset

Теперь функция учитывает все отклонения в заданном интервале, вычисляя среднее значение. После этого вычисляется разница между средним значением будущих цен и текущей ценой. Соответственно, функция get_labels_r() теперь вызывает функцию-разметчик calculate_labels_mean_r(), а не calculate_labels_r(), как раньше. Вы можете экспериментировать, вызывая разные функции-разметчики.


Добавление системы причинно-следственного вывода

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

def meta_learners(data, models_number: int, iterations: int, depth: int):
    data = data.copy()
    data = data[(data.index < hyper_params['forward']) & (data.index > hyper_params['backward'])].copy()

    X = data[data.columns[1:-1]]
    y = data['labels']
    data['meta_labels'] = 0

    for i in range(models_number):
        X_train, X_val, y_train, y_val = train_test_split(
            X, y, train_size = 0.5, test_size = 0.5, shuffle = True)
        
        # fit debias model with train and validation subsets
        meta_m = CatBoostRegressor(iterations = iterations,
                                depth = depth,
                                verbose = False,
                                use_best_model = True)
        
        meta_m.fit(X_train, y_train, eval_set = (X_val, y_val), plot = False)
        
        coreset = X.copy()
        coreset['labels'] = y
        coreset['labels_pred'] = meta_m.predict(X)
        data['meta_labels'] += abs(coreset['labels'] - coreset['labels_pred'])

    data['meta_labels'] = data['meta_labels'] / models_number
    return data

Функция обучает несколько регрессоров на случайных подмножествах данных из исходного датасета, а затем сравнивает фактические целевые с предсказанными. Таким образом, мета-модель будет предсказывать не 0 или 1 (торговать или не торговать), а усредненные значения отклонений прогнозов от фактических. Так мы сможем фильтровать прогнозы, которые сильно отклоняются от ожидаемых значений.


Обучение и тестирование обученных моделей

Для тестирования регрессионных моделей тестер был модифицирован и имеет суффикс "r". Настало время обучить несколько моделей. В данной статье я обучу 10 моделей и выберу из них наиболее понравившуюся.

hyper_params = {
    'symbol': 'EURUSD_H1',
    'export_path': '/Users/dmitrievsky/drive_c/Program Files/MetaTrader 5/MQL5/Include/Trend following/',
    'model_number': 0,
    'markup': 0.00010,
    'stop_loss':  0.00500,
    'take_profit': 0.00200,
    'periods': [i for i in range(5, 100, 30)],
    'backward': datetime(2010, 1, 1),
    'forward': datetime(2024, 1, 1),
}

models = []
for i in range(10):
    print('Learn ' + str(i) + ' model')
    data = get_labels_r(get_features(get_prices()), min=1, max=15)
    dataset = meta_learners(data=data, models_number=5, iterations=15, depth=3)
    models.append(fit_final_models(dataset, tol=3e-2))

Здесь следует заострить внимание на параметре tol, который передается в функцию обучения финальных моделей. Поскольку мы хотим сделать основную модель максимально надежной, то нет смысла обучать ее на всех примерах. Мы будем обучать ее только на тех примерах, предсказания которых отклоняются от фактических меньше, чем на величину tol.

Поскольку отклонения от предсказаний считаются фактически в пунктах, то tol=3e-2 будет означать максимальную разницу 0.03 или 300 4-х значных пунктов. Она может показаться слишком большой в качестве фильтра, но стоит учесть, что это разница в абсолютных значениях, так как предсказания могут быть как положительными, так и отрицательными. Вы можете экспериментировать с этим параметром. Ниже приведена сама функция.

def fit_final_models(dataset, tol=1e-2) -> list:
    # features for model\meta models. We learn main model only on filtered labels
    X = dataset[dataset['meta_labels'] < tol]
    X, X_meta = X[X.columns[1:-2]], dataset[dataset.columns[1:-2]]
    # labels for model\meta models
    y = dataset[dataset['meta_labels'] < tol]
    y, y_meta = y[y.columns[-2]], dataset[dataset.columns[-1]]
    
    # fit main model with train and validation subsets
    model = RandomForestRegressor(n_estimators=50, max_depth=10)
    model.fit(X, y)
    # fit meta model with train and validation subsets
    meta_model = RandomForestRegressor(n_estimators=50, max_depth=10)
    meta_model.fit(X_meta, y_meta)

    data = get_features(get_prices())
    R2 = test_model_r(data, 
                    [model, meta_model], 
                    hyper_params['stop_loss'], 
                    hyper_params['take_profit'],
                    hyper_params['forward'],
                    hyper_params['backward'],
                    hyper_params['markup'],
                    plt=False)
    
    if math.isnan(R2):
        R2 = -1.0
        print('R2 is fixed to -1.0')
    print('R2: ' + str(R2))
    result = [R2, model, meta_model]
    return result

Теперь отсортируем модели и вызовем функцию кастомного тестера:

models.sort(key=lambda x: x[0])
data = get_features(get_prices())
test_model_r(data, 
        models[-1][1:], 
        hyper_params['stop_loss'], 
        hyper_params['take_profit'],
        hyper_params['forward'],
        hyper_params['backward'],
        hyper_params['markup'],
        plt=True)

Модель переобучена и плохо работает на новых данных:

Рис 1. тестирование модели с базовой разметкой

Проведем точно такие же манипуляции, но за основу возьмем разметчик сделок calculate_labels_mean_r(), который считает средние будущие цены.

Рис 2. тестирование модели с усредненной разметкой

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

В кастомном тестере не добавлена возможность определения порогов для основной регрессионной модели, поэтому он просто разделяет предсказания моделей на положительные и отрицательные, значит, сигналы являются все еще достаточно грубыми. Но мы исправим это уже непосредственно в терминале Meta Trader 5.


Экспорт моделей в терминал Meta Trader 5

Теперь нам нужно экспортировать модели в терминал в формате ONNX и произвести настройку торговой системы. Функция экспорта выглядит привычным образом:

export_model_to_ONNX(model = models[-1],
                     symbol = hyper_params['symbol'],
                     periods = hyper_params['periods'],
                     periods_meta = hyper_params['periods'],
                     model_number = hyper_params['model_number'],
                     export_path = hyper_params['export_path'])

Следует учесть, что мне не удалось подключить регрессионные модели CatBoost в формате ONNX к терминалу, поэтому я использовал случайный лес.

Размерность входного тензора настраивается автоматически в зависимости от гиперпараметров (количества признаков), которые задаются перед началом обучения. Далее модели конвертируются в формат ONNX с помощью функции convert_sklearn() и сохраняются на диск в директорию, указанную вами в гиперпараметрах.

def export_model_to_ONNX(**kwargs):
    model = kwargs.get('model')
    symbol = kwargs.get('symbol')
    periods = kwargs.get('periods')
    periods_meta = kwargs.get('periods_meta')
    model_number = kwargs.get('model_number')
    export_path = kwargs.get('export_path')

    initial_type = [('float_input', FloatTensorType([None, len(hyper_params['periods'])]))]
    onnx_model = convert_sklearn(model[1], initial_types=initial_type)
    # save main model to ONNX
    with open(export_path +'catmodel ' + symbol + ' ' + str(model_number) +'.onnx', "wb") as f:
        f.write(onnx_model.SerializeToString())
    onnx_model_meta = convert_sklearn(model[2], initial_types=initial_type)
    # save meta model to ONNX
    with open(export_path +'catmodel_m ' + symbol + ' ' + str(model_number) +'.onnx', "wb") as f:
        f.write(onnx_model_meta.SerializeToString())
    
    code = '#include <Math\Stat\Math.mqh>'
    code += '\n'
    code += '#resource "catmodel '+ symbol + ' '+str(model_number)+'.onnx" as uchar ExtModel_' + symbol + '_' + str(model_number) + '[]'
    code += '\n'
    code += '#resource "catmodel_m '+ symbol + ' '+str(model_number)+'.onnx" as uchar ExtModel2_' + symbol + '_' + str(model_number) + '[]'
    code += '\n\n'
    code += 'int Periods' + symbol + '_' + str(model_number) + '[' + str(len(periods)) + \
        '] = {' + ','.join(map(str, periods)) + '};'
    code += '\n'
    code += 'int Periods_m' + symbol + '_' + str(model_number) + '[' + str(len(periods_meta)) + \
        '] = {' + ','.join(map(str, periods_meta)) + '};'
    code += '\n\n'

    # 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] = MathStandardDeviation(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] = MathStandardDeviation(pr);\n'
    code += '       ArrayInsert(features, ret, ArraySize(features), 0, WHOLE_ARRAY); }\n'
    code += '   ArraySetAsSeries(features, true);\n'
    code += '}\n\n'

    file = open(export_path + str(symbol) + ' ONNX include' + ' ' + str(model_number) + '.mqh', "w")
    file.write(code)

    file.close()
    print('The file ' + 'ONNX include' + '.mqh ' + 'has been written to disk')


Настройка порогов в терминале Meta Trader 5

Теперь, когда мы получили две регрессионные модели вместо двух классифиаторов, у нас появляется возможность установки конкретных числовых порогов.

Рис 3. настройка порогов активации сигналов в терминале

  • Пороги buy_threshhold и sell_threshhold отвечают за фильтрацию сигналов основной регрессионной модели. Если сигнал ниже этого порога, то сделки не открываются. Например, если прогнозируемое ценовое изменение менее 10 пунктов, то открывать такую сделку не имеет большого смысла, поскольку она не покроет спред и комиссию.
  • Порог meta_threshhold фильтрует сигналы основной модели на основе причинно-следственного вывода, описанного ранее. Он проверяет насколько прогноз, вероятно, отличается от будущего фактического изменения. Если разница слишком велика, то сделки тоже не будут открыты.

Теперь проверим нашу модель в тестере Meta Trader 5 c заданными порогами:

Рис 4. тестирование модели с заданными порогами

Напомню, что форвард-период начинается на начала 2024 года и теперь модель его проходит достаточно стабильно. Это подчеркивает важность правильного определения и настройки порогов. Вы можете оптимизировать значения порогов самостоятельно.

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

Рис 5. обучение и тестирование другой модели, с другими параметрами обучения

А после настройки порогов, модель показала устойчивый рост с начала 2024 года.

Рис 6. тестирование модели в терминале после настройки порогов

Тестирование моделей на основе второго разметчика сделок и фильтрации по порогам дает еще более интересные и аккуратные результаты:

Рис 7. тестирование модели на основе усредненного разметчика сделок и tol = 1e-2

Если изменить параметр tol при обучении с 1e-2 до 1e-3, то результаты окажутся еще лучше:

Рис 8. тестирование модели на основе усредненного разметчика сделок и tol = 1e-3


Дополнительная информация

Для экспорта моделей необходимо установить и импортировать пакет skl2onnx:

from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType

Также изменен код запуска ONNX моделей в коде торгового бота, чтобы он корректно обрабатывал новые регрессионные модели:

vectorf y_main(1), y_meta(1);
   
OnnxRun(ExtHandle, ONNX_DEBUG_LOGS, f, y_main);
OnnxRun(ExtHandle2, ONNX_DEBUG_LOGS, f_m, y_meta);

float sig = y_main[0];
float meta_sig = y_meta[0];

Добавлены новые "input" переменные для корректировки и оптимизации порогов:

input double buy_threshold = 0.00001;
input double sell_threshold = -0.00001;
input double meta_threshold = 0.001;

 Доступ к именам ONNX моделей теперь осуществляется через директивы #define, это упрощает подключение моделей с другими именами:

#define model ExtModel_EURUSD_H1_0
#define model_m ExtModel2_EURUSD_H1_0
#define periods PeriodsEURUSD_H1_0
#define periods_m Periods_mEURUSD_H1_0
#define fill_arrays fill_araysEURUSD_H1_0
#define fill_arrays_m fill_arays_mEURUSD_H1_0

Торговые сигналы формируются при срабатывании условий в зависимости от порогов:

if((Ask-Bid < max_spread*_Point) && MathAbs(meta_sig) < meta_threshold &&
      AllowTrade(OrderMagic))
      if(countOrders(OrderMagic) < max_orders &&
         CheckMoneyForTrade(_Symbol, LotsOptimized(), ORDER_TYPE_BUY))
        {
         double l = LotsOptimized();
         if(sig > buy_threshold && Allow_Buy)
           {
            int res = -1;
            do
              {
               double stop = Bid - stoploss * _Point;
               double take = Ask + takeprofit * _Point;
               res = mytrade.PositionOpen(_Symbol, ORDER_TYPE_BUY, l, Ask, stop, take, bot_comment);
               Sleep(50);
              }
            while(res == -1);
           }
         else
           {
            if(sig < sell_threshold && Allow_Sell)
              {
               int res = -1;
               do
                 {
                  double stop = Ask + stoploss * _Point;
                  double take = Bid - takeprofit * _Point;
                  res = mytrade.PositionOpen(_Symbol, ORDER_TYPE_SELL, l, Bid, stop, take, bot_comment);
                  Sleep(50);
                 }
               while(res == -1);
              }
           }
        }

Модифицированный тестер стратегий и функция экспорта моделей добавлены в соответствующие модули и прикреплены к статье.


Заключение

В этой статье я описал возможный, но не единственный способ построения торговых систем на базе регрессионных моделей. Такой подход позволяет более тонко настраивать ботов, основанных на машинном обучении. Также он позволяет превращать заведомо убыточные модели в прибыльные через настройку порогов. Вы можете использовать любые признаки и/или разметчики сделок, а также проверять этот алгоритм на других торговых инструментах и других временных масштабах.

Архив Python files.zip содержит следующие файлы для разработки в среде Python:

Имя файла Описание
causal regression.py 
Основной скрипт для обучения моделей
labeling_lib.py
Обновленный модуль с разметчиками сделок
tester_lib.py
Обновленный кастомный тестер стратегий, основанных на машинном обучении
export_lib.py Модуль для экспорта моделей в терминал
EURUSD_H1.csv
Файл с котировками, экспортированный из терминала MetaTrader 5

Архив MQL5 files.zip cодержит файлы для терминала MetaTrader 5:

Имя файла Описание
regression trader.ex5
Скомпилированный бот из данной статьи
regression trader.mq5
Исходник бота из статьи
папка Include//Trend following
Расположены модели ONNX и заголовочный файл для подключения к боту
Прикрепленные файлы |
MQL5_files.zip (950.36 KB)
Python_files.zip (1094.44 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (3)
zhainan
zhainan | 12 июл. 2025 в 14:12
我觉得你的方法中,测试数据参与进模型筛选的过程,容易存在过拟合的可能性,最好是两份足够多的数据做两份测试,一份做筛选模型,另一份检验筛选出来的最优模型,不参与模型筛选。
Alexey Viktorov
Alexey Viktorov | 12 июл. 2025 в 17:57

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Обсуждение статьи "Разрабатываем мультивалютный советник (Часть 25): Подключаем новую стратегию (II)"

Rashid Umarov, 2025.07.05 12:58

Пишите по-русски в русской части.  Все комментарии к статьям автоматические переводятся в каждой языковой ветке. 

И также зедсь есть кнопка "Автоперевод", если ему так хочется прочитать на английском имеено здесь.


Maxim Dmitrievsky
Maxim Dmitrievsky | 13 июл. 2025 в 04:57
zhainan #:
我觉得你的方法中,测试数据参与进模型筛选的过程,容易存在过拟合的可能性,最好是两份足够多的数据做两份测试,一份做筛选模型,另一份检验筛选出来的最优模型,不参与模型筛选。

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

Переосмысление индикаторов MQL5 и MetaTrader 5 Переосмысление индикаторов MQL5 и MetaTrader 5
Инновационный подход к сбору информации с индикаторов на MQL5 обеспечивает более гибкий и оптимизированный анализ данных, позволяя разработчикам вводить пользовательские данные в индикаторы для осуществления немедленных расчетов. Этот подход особенно полезен для алгоритмической торговли, поскольку он обеспечивает повышенный контроль над информацией, обрабатываемой индикаторами, выходя за рамки традиционных ограничений.
Нейросети в трейдинге: Вероятностное прогнозирование временных рядов (Энкодер) Нейросети в трейдинге: Вероятностное прогнозирование временных рядов (Энкодер)
Предлагаем познакомиться с новым подходом, который объединяет классические методы и современные нейросети для анализа временных рядов. В статье подробно раскрыта архитектура и принципы работы модели K²VAE.
Компоненты View и Controller для таблиц в парадигме MVC на MQL5: Контейнеры Компоненты View и Controller для таблиц в парадигме MVC на MQL5: Контейнеры
В статье рассмотрим создание элемента управления "Контейнер" с возможностью прокрутки его содержимого. В процессе будут доработаны уже готовые классы элементов управления графической библиотеки.
Связь торговых роботов MetaTrader 5 с внешними брокерами через API и Python Связь торговых роботов MetaTrader 5 с внешними брокерами через API и Python
В настоящей статье мы обсудим реализацию MQL5 в партнерстве с Python для выполнения связанных с брокером операций. Представьте, что у вас есть постоянно работающий советник (EA), размещенный на VPS и совершающий сделки от вашего имени. В какой-то момент способность советника управлять средствами становится первостепенной. Она включает в себя такие операции, как пополнение вашего торгового счета и инициирование вывода средств. В данном обсуждении мы прольем свет на преимущества и практическую реализацию этих функций, обеспечивающих плавную интеграцию управления средствами в вашу торговую стратегию. Следите за обновлениями!