Расчет MFE и MAE в Python для анализа торговых систем

Расчет MFE и MAE в Python для анализа торговых систем ЛИЧНЫЙ БЮДЖЕТ и ЭКОНОМИЯ
Подробное руководство по расчету метрик Maximum Favorable Excursion (MFE) и Maximum Adverse Excursion (MAE) для торговых систем на Python с использованием pandas. Улучшите анализ стратегий и управление рисками.
Суть: Maximum Favorable Excursion (MFE) и Maximum Adverse Excursion (MAE) измеряют максимальное движение цены в пользу и против открытой позиции соответственно, от точки входа до точки выхода или текущего момента, предоставляя критически важные данные для анализа эффективности торговой системы и управления рисками.

Исходный код

Как Senior Quant Developer, я часто сталкиваюсь с необходимостью глубокого анализа эффективности торговых стратегий. Одним из ключевых аспектов такого анализа является понимание того, как цена движется относительно нашей позиции после входа в сделку. Здесь на помощь приходят метрики Maximum Favorable Excursion (MFE) и Maximum Adverse Excursion (MAE).

MFE (Maximum Favorable Excursion) показывает максимальную прибыль, которую можно было бы получить от сделки, если бы она была закрыта в самый благоприятный момент до фактического выхода. MAE (Maximum Adverse Excursion), напротив, измеряет максимальный убыток, который сделка испытала от точки входа до фактического выхода или до текущего момента, прежде чем цена, возможно, развернулась в нашу сторону. Эти метрики критически важны для:

  • Оценки качества точек входа: Высокий MAE может указывать на плохие точки входа.
  • Оптимизации стоп-лоссов: MAE помогает определить адекватные уровни стоп-лосса.
  • Оптимизации тейк-профитов: MFE помогает понять потенциал прибыли и установить реалистичные цели.
  • Понимания психологии трейдинга: Сравнение MFE с фактической прибылью/убытком показывает, насколько эффективно трейдер управляет сделками.

В этой статье мы рассмотрим, как эффективно рассчитать MFE и MAE для вашей торговой системы с использованием Python и библиотеки pandas. Для более сложных сценариев, где требуется ускорение вычислений для больших объемов данных, можно рассмотреть подходы, описанные в статье Ускорение финансовых индикаторов на Python с помощью Numba JIT.


import pandas as pd
import numpy as np

def calculate_mfe_mae(trades_df: pd.DataFrame, price_series_df: pd.DataFrame, price_col: str = 'Close') -> pd.DataFrame:
    """
    Рассчитывает Maximum Favorable Excursion (MFE) и Maximum Adverse Excursion (MAE)
    для каждой сделки в торговой системе.

    Параметры:
    ----------
    trades_df : pd.DataFrame
        DataFrame, содержащий информацию о сделках. Должен включать столбцы:
        'entry_time' (datetime), 'exit_time' (datetime), 'entry_price' (float),
        'trade_type' (str, 'long' или 'short').
    price_series_df : pd.DataFrame
        DataFrame с ценовыми данными. Должен иметь datetime индекс и столбец с ценой.
    price_col : str, optional
        Название столбца с ценой в price_series_df. По умолчанию 'Close'.

    Возвращает:
    ----------
    pd.DataFrame
        Исходный DataFrame trades_df с добавленными столбцами 'mfe' и 'mae'.
    """
    # Убедимся, что столбцы времени в trades_df имеют тип datetime
    trades_df_copy = trades_df.copy() # Работаем с копией, чтобы не изменять оригинал
    trades_df_copy['entry_time'] = pd.to_datetime(trades_df_copy['entry_time'])
    trades_df_copy['exit_time'] = pd.to_datetime(trades_df_copy['exit_time'])

    # Убедимся, что индекс price_series_df имеет тип datetime
    if not isinstance(price_series_df.index, pd.DatetimeIndex):
        raise ValueError("Индекс price_series_df должен быть DatetimeIndex.")

    mfe_values = []
    mae_values = []

    for idx, trade in trades_df_copy.iterrows():
        entry_time = trade['entry_time']
        exit_time = trade['exit_time']
        entry_price = trade['entry_price']
        trade_type = trade['trade_type']

        # Извлекаем ценовой ряд для текущей сделки
        # Используем .loc для точного среза по времени, включая entry_time и exit_time
        trade_prices = price_series_df.loc[entry_time:exit_time, price_col]

        if trade_prices.empty:
            mfe_values.append(np.nan)
            mae_values.append(np.nan)
            continue

        if trade_type == 'long':
            # Для длинной позиции:
            # MFE = max(цена_в_сделке) - цена_входа
            # MAE = цена_входа - min(цена_в_сделке)
            mfe = trade_prices.max() - entry_price
            mae = entry_price - trade_prices.min()
        elif trade_type == 'short':
            # Для короткой позиции:
            # MFE = цена_входа - min(цена_в_сделке)
            # MAE = max(цена_в_сделке) - цена_входа
            mfe = entry_price - trade_prices.min()
            mae = trade_prices.max() - entry_price
        else:
            mfe = np.nan
            mae = np.nan
            print(f"Предупреждение: Неизвестный тип сделки '{trade_type}' для сделки с ID {idx}. MFE/MAE будут NaN.")

        mfe_values.append(mfe)
        mae_values.append(mae)

    trades_df_copy['mfe'] = mfe_values
    trades_df_copy['mae'] = mae_values

    return trades_df_copy

Разбор параметров

  • trades_df:

    DataFrame, содержащий информацию о сделках. Ожидаются столбцы: 'entry_time' (время входа, datetime), 'exit_time' (время выхода, datetime), 'entry_price' (цена входа, float) и 'trade_type' (тип сделки, str, значения 'long' или 'short').

  • price_series_df:

    DataFrame с ценовыми данными. Должен иметь индекс типа pd.DatetimeIndex и содержать столбец с ценой, по которой будут рассчитываться MFE/MAE.

  • price_col:

    Строка, указывающая название столбца с ценой в price_series_df. По умолчанию 'Close'.

Как запустить

Для запуска функции calculate_mfe_mae вам потребуется два основных DataFrame: один для ваших торговых сделок и один для исторических ценовых данных. Давайте создадим примеры таких данных и продемонстрируем использование.

Понимание MFE и MAE является частью более широкого спектра инструментов для оценки рисков. Например, для оценки общего рыночного риска портфеля часто используются метрики вроде Value at Risk, о расчете которых можно узнать в статье Расчет Value at Risk (VaR) через Монте-Карло и GARCH на Python.

Пример данных:


# Создаем пример ценового ряда
dates = pd.date_range(start='2023-01-01', periods=100, freq='H')
prices = np.random.rand(100) * 100 + 100 # Базовая цена 100, колебания до 200
price_series_df = pd.DataFrame({'Close': prices}, index=dates)

# Создаем пример DataFrame с торговыми сделками
trades_data = [
    {'entry_time': '2023-01-01 05:00:00', 'exit_time': '2023-01-01 10:00:00', 'entry_price': 105.0, 'trade_type': 'long'},
    {'entry_time': '2023-01-02 12:00:00', 'exit_time': '2023-01-02 18:00:00', 'entry_price': 150.0, 'trade_type': 'short'},
    {'entry_time': '2023-01-03 08:00:00', 'exit_time': '2023-01-03 09:00:00', 'entry_price': 120.0, 'trade_type': 'long'},
    {'entry_time': '2023-01-04 01:00:00', 'exit_time': '2023-01-04 01:00:00', 'entry_price': 130.0, 'trade_type': 'long'}, # Сделка без длительности
    {'entry_time': '2023-01-05 00:00:00', 'exit_time': '2023-01-05 02:00:00', 'entry_price': 110.0, 'trade_type': 'invalid_type'}, # Некорректный тип
]
trades_df = pd.DataFrame(trades_data)

# Выполняем расчет MFE и MAE
trades_with_mfe_mae = calculate_mfe_mae(trades_df, price_series_df)

print(trades_with_mfe_mae)

После выполнения этого кода вы получите DataFrame trades_with_mfe_mae, который будет содержать исходные данные о сделках, а также два новых столбца: 'mfe' и 'mae', отражающие рассчитанные значения Maximum Favorable Excursion и Maximum Adverse Excursion для каждой сделки.

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

Оцените статью
FinFluct