English 中文 Español Deutsch 日本語 Português
preview
Методы Уильяма Ганна (Часть III): Работает ли астрология?

Методы Уильяма Ганна (Часть III): Работает ли астрология?

MetaTrader 5Тестер | 29 августа 2024, 11:33
1 245 2
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Введение

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

Мы уже затрагивали инструменты Ганна в прошлых статьях. Вот первая и вторая части. Сейчас мы сделаем фокус на исследовании влияния положения планет и звезд на рынки мира.

Попробуем объединить самые современные технологии и старинные знания, будем использовать язык программирования Python, а также платформу MetaTrader 5 для поиска связи астрономических явлений и движений пары евро-доллар. Охватим и теоретическую часть астрологии в финансах, попробуем себя и в практической части по разработке системы прогнозирования.

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


Теоретическая база астрологии в финансах

Знаете, я давно интересуюсь этой темой, и сегодня хочу поделиться своими мыслями о влиянии астрологии на финансовые рынки. Это действительно увлекательная область, хотя и весьма противоречивая.

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

Я много размышлял над основными принципами этой теории. Например, идее о цикличности — что движения звезд и планет имеют циклическую природу, как и движения рынков. Или взять планетарные аспекты — некоторые считают, что определенные положения планет сильно влияют на рынки. А знаки зодиака? Говорят, что прохождение планет через разные зодиакальные созвездия тоже как-то воздействует на рынок.

Отдельно стоит упомянуть лунные циклы и солнечную активность. Я встречал мнения, что фазы Луны связаны с краткосрочными колебаниями на рынке, а солнечные вспышки — с долгосрочными трендами. Интересные гипотезы, не правда ли?

Уильям Ганн, кстати, был настоящим первопроходцем в этой области. Он разработал целый ряд инструментов, вроде его знаменитого квадрата 9, основанных на астрономии, геометрии и числовых последовательностях. Его работы до сих пор вызывают жаркие споры.

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

Но знаете что? Несмотря на это, есть немало трейдеров, которые горячо отстаивают идеи финансовой астрологии. Это заставляет задуматься, не так ли?

Вот почему я решил провести собственное исследование. Хочу попробовать дать объективную оценку влияния астрологии на финансовые рынки, используя статистические методы и большие данные. Кто знает, может быть, мы обнаружим что-то интересное? В любом случае, это будет увлекательное путешествие в мир, где пересекаются звезды и биржевые графики.


Обзор используемых библиотек Python

Мне потребуется целый арсенал Python-библиотек. 

Для начала, я решил использовать пакет Skyfield для получения астрономических данных. Знаете, я долго выбирал подходящий инструмент, и Skyfield меня впечатлил своей точностью. С его помощью я смогу собрать информацию о положениях небесных тел и фазах луны с очень высокой точностью по десятичным знакам — все то, что нужно для моих датасетов.

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

Pandas станет нашим верным спутником в работе с данными. Я часто использовал эту библиотеку раньше, и она просто незаменима для работы с временными рядами. Буду применять ее для предобработки и синхронизации всех собранных данных.

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

А вот для визуализации результатов я решил использовать старых добрых друзей — Matplotlib и Seaborn. Люблю эти библиотеки за их гибкость в создании графиков. Уверен, они помогут нам наглядно представить все находки.

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


Сбор астрономических данных

import pandas as pd
import numpy as np
from skyfield.api import load, wgs84, utc
from skyfield.data import mpc
from datetime import datetime, timedelta
import requests

# Loading planet ephemerides
planets = load('de421.bsp')
earth = planets['earth']
ts = load.timescale()

def get_planet_positions(date):
    t = ts.from_datetime(date.replace(tzinfo=utc))
    planet_positions = {}
    planet_ids = {
        'mercury': 'MERCURY BARYCENTER',
        'venus': 'VENUS BARYCENTER',
        'mars': 'MARS BARYCENTER',
        'jupiter': 'JUPITER BARYCENTER',
        'saturn': 'SATURN BARYCENTER',
        'uranus': 'URANUS BARYCENTER',
        'neptune': 'NEPTUNE BARYCENTER'
    }
    for planet, planet_id in planet_ids.items():
        planet_obj = planets[planet_id]
        astrometric = earth.at(t).observe(planet_obj)
        ra, dec, _ = astrometric.radec()
        planet_positions[planet] = {'ra': ra.hours, 'dec': dec.degrees}
    return planet_positions

def get_moon_phase(date):
    t = ts.from_datetime(date.replace(tzinfo=utc))
    eph = load('de421.bsp')
    moon, sun, earth = eph['moon'], eph['sun'], eph['earth']
    
    e = earth.at(t)
    _, m, _ = e.observe(moon).apparent().ecliptic_latlon()
    _, s, _ = e.observe(sun).apparent().ecliptic_latlon()
    
    phase = (m.degrees - s.degrees) % 360
    return phase

def get_solar_activity(date):
    # Get solar activity data from NOAA API
    url = f"https://services.swpc.noaa.gov/json/solar-cycle/observed-solar-cycle-indices.json"
    response = requests.get(url)
    data = response.json()
    
    # Convert date to 'YYYY-MM' format
    target_date = date.strftime("%Y-%m")
    
    # Find the closest date in the data
    closest_data = min(data, key=lambda x: abs(datetime.strptime(x['time-tag'], "%Y-%m") - datetime.strptime(target_date, "%Y-%m")))
    
    return {
        'sunspot_number': closest_data.get('ssn', None),
        'f10.7_flux': closest_data.get('f10.7', None)
    }

def calculate_aspects(positions):
    aspects = {}
    planets = list(positions.keys())
    for i in range(len(planets)):
        for j in range(i+1, len(planets)):
            planet1 = planets[i]
            planet2 = planets[j]
            ra1 = positions[planet1]['ra']
            ra2 = positions[planet2]['ra']
            angle = abs(ra1 - ra2) % 24
            angle = min(angle, 24 - angle) * 15  # Convert to degrees
            
            if abs(angle - 0) <= 10 or abs(angle - 180) <= 10:
                aspects[f"{planet1}_{planet2}"] = "conjunction" if abs(angle - 0) <= 10 else "opposition"
            elif abs(angle - 90) <= 10:
                aspects[f"{planet1}_{planet2}"] = "square"
            elif abs(angle - 120) <= 10:
                aspects[f"{planet1}_{planet2}"] = "trine"
    
    return aspects

start_date = datetime(2024, 4, 1, tzinfo=utc)
end_date = datetime(2024, 5, 31, tzinfo=utc)
current_date = start_date
astronomical_data = []

while current_date <= end_date:
    planet_positions = get_planet_positions(current_date)
    moon_phase = get_moon_phase(current_date)
    try:
        solar_activity = get_solar_activity(current_date)
    except Exception as e:
        print(f"Error getting solar activity for {current_date}: {e}")
        solar_activity = {'sunspot_number': None, 'f10.7_flux': None}
    aspects = calculate_aspects(planet_positions)
    
    data = {
        'date': current_date,
        'moon_phase': moon_phase,
        'sunspot_number': solar_activity.get('sunspot_number'),
        'f10.7_flux': solar_activity.get('f10.7_flux'),
        **planet_positions,
        **aspects
    }
    astronomical_data.append(data)
    
    current_date += timedelta(days=1)
    print(f"Processed: {current_date}")

# Convert data to DataFrame
df = pd.DataFrame(astronomical_data)

# Save data to CSV file
df.to_csv('astronomical_data_2018_2024.csv', index=False)
print("Data saved to astronomical_data_2018_2024.csv")

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

Код берет периоды с 1 января 2018 года, по 31 мая 2024 года, и собирает ряд данных, таких как:

  • Позиции планет – Венера, Меркурий, Марс, Юпитер, Сатурн, Уран, Нептун
  • Фазы Луны
  • Солнечная активность
  • Аспекты планет (это когда планеты выстраиваются особым образом по отношению друг к другу)

Скрипт включает в себя импорты библиотек, основной цикл, и сохранение данных в формате Эксель. Код применяет уже озвученную библиотеку Skyfield для вычисления положений планет, Pandas для данных, и реквесты для получения данных по активности Солнца.

Из функций можно отметить get_planet_positions() для получения позиций планет (прямое восхождение и склонение), get_moon_phase() для определения текущей лунной фазы, get_solar_activity() для прямой поставки данных по солнечной активности из API NOAA, и calculate_aspects() для вычисления аспектов – положений планет по отношению друг к другу.

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


Получение финансовых данных через MetaTrader 5

Чтобы получать финансовые данные, мы будем использовать библиотеку MetaTrader 5 для Python. Библиотека позволит нам загружать финансовые данные сразу от брокера, и получать временные ряды цен по любому инструменту. Вот наш код для загрузки исторических данных: 

import MetaTrader5 as mt5
import pandas as pd
from datetime import datetime

# Connect to MetaTrader5
if not mt5.initialize():
    print("initialize() failed")
    mt5.shutdown()

# Set query parameters
symbol = "EURUSD"
timeframe = mt5.TIMEFRAME_D1
start_date = datetime(2018, 1, 1)
end_date = datetime(2024, 12, 31)

# Request historical data
rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)

# Convert data to DataFrame
df = pd.DataFrame(rates)
df['time'] = pd.to_datetime(df['time'], unit='s')

# Save data to CSV file
df.to_csv(f'{symbol}_data.csv', index=False)

# Terminate the connection to MetaTrader5
mt5.shutdown()

Скрипт подключается к торговому терминалу, получает данные по паре евро-доллара с таймфреймом D1, затем создает датафрейм, и сохраняет его в один CSV файл. 


Синхронизация астрономических и финансовых данных

Итак, у нас есть данные по астрономии, у нас также есть данные по евро-доллару. Встает задача их синхронизации. Объединим данные по датам, чтобы единый датасет у нас содержал всю нужную информацию, как финансовую, так и астрономическую.

import pandas as pd

# Load data
astro_data = pd.read_csv('astronomical_data_2018_2024.csv')
financial_data = pd.read_csv('EURUSD_data.csv')

# Convert date columns to datetime
astro_data['date'] = pd.to_datetime(astro_data['date'])
financial_data['time'] = pd.to_datetime(financial_data['time'])

# Merge data
merged_data = pd.merge(financial_data, astro_data, left_on='time', right_on='date', how='inner')

# Save merged data
merged_data.to_csv('merged_astro_financial_data.csv', index=False)

Скрипт грузит все сохраненные данные, форматирует столбцы дат в формат datetime, и соединяет датасеты по дате. В итоге получаем CSV файл, который содержит в себе все нужные нам для будущего анализа данные.


Статистический анализ корреляций

Итак, едем дальше. У нас есть общий набор, общий датасет, и пришло время выяснить, есть ли в данных хоть какие-то зависимости между данными астрономии и движениями на рынках. Будем использовать функции corr() из библиотеки pandas. Ну и объединим оба наших кода в один.

Вот итоговый скрипт:

import pandas as pd
import numpy as np
from skyfield.api import load, wgs84, utc
from skyfield.data import mpc
from datetime import datetime, timedelta
import requests
import MetaTrader5 as mt5
import seaborn as sns
import matplotlib.pyplot as plt

# Part 1: Collecting astronomical data

# Loading planetary ephemerides
planets = load('de421.bsp')
earth = planets['earth']
ts = load.timescale()

def get_planet_positions(date):
    t = ts.from_datetime(date.replace(tzinfo=utc))
    planet_positions = {}
    planet_ids = {
        'mercury': 'MERCURY BARYCENTER',
        'venus': 'VENUS BARYCENTER',
        'mars': 'MARS BARYCENTER',
        'jupiter': 'JUPITER BARYCENTER',
        'saturn': 'SATURN BARYCENTER',
        'uranus': 'URANUS BARYCENTER',
        'neptune': 'NEPTUNE BARYCENTER'
    }
    for planet, planet_id in planet_ids.items():
        planet_obj = planets[planet_id]
        astrometric = earth.at(t).observe(planet_obj)
        ra, dec, _ = astrometric.radec()
        planet_positions[planet] = {'ra': ra.hours, 'dec': dec.degrees}
    return planet_positions

def get_moon_phase(date):
    t = ts.from_datetime(date.replace(tzinfo=utc))
    eph = load('de421.bsp')
    moon, sun, earth = eph['moon'], eph['sun'], eph['earth']

    e = earth.at(t)
    _, m, _ = e.observe(moon).apparent().ecliptic_latlon()
    _, s, _ = e.observe(sun).apparent().ecliptic_latlon()

    phase = (m.degrees - s.degrees) % 360
    return phase

def get_solar_activity(date):
    url = f"https://services.swpc.noaa.gov/json/solar-cycle/observed-solar-cycle-indices.json"
    response = requests.get(url)
    data = response.json()

    target_date = date.strftime("%Y-%m")

    closest_data = min(data, key=lambda x: abs(datetime.strptime(x['time-tag'], "%Y-%m") - datetime.strptime(target_date, "%Y-%m")))

    return {
        'sunspot_number': closest_data.get('ssn', None),
        'f10.7_flux': closest_data.get('f10.7', None)
    }

def calculate_aspects(positions):
    aspects = {}
    planets = list(positions.keys())
    for i in range(len(planets)):
        for j in range(i+1, len(planets)):
            planet1 = planets[i]
            planet2 = planets[j]
            ra1 = positions[planet1]['ra']
            ra2 = positions[planet2]['ra']
            angle = abs(ra1 - ra2) % 24
            angle = min(angle, 24 - angle) * 15  # Convert to degrees

            if abs(angle - 0) <= 10 or abs(angle - 180) <= 10:
                aspects[f"{planet1}_{planet2}"] = "conjunction" if abs(angle - 0) <= 10 else "opposition"
            elif abs(angle - 90) <= 10:
                aspects[f"{planet1}_{planet2}"] = "square"
            elif abs(angle - 120) <= 10:
                aspects[f"{planet1}_{planet2}"] = "trine"

    return aspects

# Collecting astronomical data
start_date = datetime(2024, 3, 1, tzinfo=utc)
end_date = datetime(2024, 7, 30, tzinfo=utc)
current_date = start_date
astronomical_data = []

while current_date <= end_date:
    planet_positions = get_planet_positions(current_date)
    moon_phase = get_moon_phase(current_date)
    try:
        solar_activity = get_solar_activity(current_date)
    except Exception as e:
        print(f"Error getting solar activity for {current_date}: {e}")
        solar_activity = {'sunspot_number': None, 'f10.7_flux': None}
    aspects = calculate_aspects(planet_positions)

    data = {
        'date': current_date,
        'moon_phase': moon_phase,
        'sunspot_number': solar_activity.get('sunspot_number'),
        'f10.7_flux': solar_activity.get('f10.7_flux'),
        **planet_positions,
        **aspects
    }
    astronomical_data.append(data)

    current_date += timedelta(days=1)
    print(f"Processed: {current_date}")

# Convert data to DataFrame and save
astro_df = pd.DataFrame(astronomical_data)
astro_df.to_csv('astronomical_data_2018_2024.csv', index=False)
print("Astronomical data saved to astronomical_data_2018_2024.csv")

# Part 2: Retrieving financial data via MetaTrader5

# Initialize connection to MetaTrader5
if not mt5.initialize():
    print("initialize() failed")
    mt5.shutdown()

# Set request parameters
symbol = "EURUSD"
timeframe = mt5.TIMEFRAME_D1
start_date = datetime(2024, 3, 1)
end_date = datetime(2024, 7, 30)

# Request historical data
rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)

# Convert data to DataFrame
financial_df = pd.DataFrame(rates)
financial_df['time'] = pd.to_datetime(financial_df['time'], unit='s')

# Save financial data
financial_df.to_csv(f'{symbol}_data.csv', index=False)
print(f"Financial data saved to {symbol}_data.csv")

# Shutdown MetaTrader5 connection
mt5.shutdown()

# Part 3: Synchronizing astronomical and financial data

# Load data
astro_df = pd.read_csv('astronomical_data_2018_2024.csv')
financial_df = pd.read_csv('EURUSD_data.csv')

# Convert date columns to datetime
astro_df['date'] = pd.to_datetime(astro_df['date']).dt.tz_localize(None)
financial_df['time'] = pd.to_datetime(financial_df['time'])

# Merge data
merged_data = pd.merge(financial_df, astro_df, left_on='time', right_on='date', how='inner')

# Save merged data
merged_data.to_csv('merged_astro_financial_data.csv', index=False)
print("Merged data saved to merged_astro_financial_data.csv")

# Part 4: Statistical analysis of correlations

# Select numeric columns for correlation analysis
numeric_columns = merged_data.select_dtypes(include=[np.number]).columns

# Create lags for astronomical data
for col in numeric_columns:
    if col not in ['open', 'high', 'low', 'close', 'tick_volume', 'spread', 'real_volume']:
        for lag in range(1, 6):  # Create lags from 1 to 5
            merged_data[f'{col}_lag{lag}'] = merged_data[col].shift(lag)

# Update list of numeric columns
numeric_columns = merged_data.select_dtypes(include=[np.number]).columns

# Calculate correlation matrix
correlation_matrix = merged_data[numeric_columns].corr()

# Create heatmap of correlations
plt.figure(figsize=(20, 16))
sns.heatmap(correlation_matrix, annot=False, cmap='coolwarm', vmin=-1, vmax=1, center=0)
plt.title('Correlation Matrix of Astronomical Factors (with Lags) and EURUSD Prices')
plt.tight_layout()
plt.savefig('correlation_heatmap_with_lags.png')
plt.close()

# Output the most significant correlations with the closing price
significant_correlations = correlation_matrix['close'].sort_values(key=abs, ascending=False)
print("Most significant correlations with the closing price:")
print(significant_correlations)

# Create a separate correlation matrix for astronomical data with lags and the current price
astro_columns = [col for col in numeric_columns if col not in ['open', 'high', 'low', 'tick_volume', 'spread', 'real_volume']]
astro_columns.append('close')  # Add the current closing price
astro_correlation_matrix = merged_data[astro_columns].corr()

# Create heatmap of correlations for astronomical data with lags and the current price
import seaborn as sns
import matplotlib.pyplot as plt

# Увеличиваем шрифт заголовка и меток осей
plt.figure(figsize=(18, 14))
sns.heatmap(astro_correlation_matrix, annot=False, cmap='coolwarm', vmin=-1, vmax=1, center=0, cbar_kws={'label': 'Correlation'})
plt.title('Correlation matrix of astronomical factors (with lags) and current EURUSD price', fontsize=24)
plt.xlabel('X-axis Label', fontsize=30)
plt.ylabel('Y-axis Label', fontsize=30)
plt.xticks(fontsize=30)
plt.yticks(fontsize=30)
plt.tight_layout()
plt.savefig('astro_correlation_heatmap_with_lags.png')
plt.close()

print("Analysis completed. Results saved in CSV and PNG files.")

Данный скрипт выводит карту корреляций между всеми числами в наборе данных, а также выводит матрицу всех корреляций в формате heatmap, и выдает список самых значимых корреляций с ценами close.

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


Если прийти ближе к теме, то какой-либо значимой корреляции в данных нам обнаружить не удается. Среди прошлых показателей астрономии нет явно коррелирующих с показателями рынка.


Призываем на помощь машинное обучение

Затем я задумался, что же делать дальше, и было решено призвать на помощь модель машинного обучения. Я сделал два скрипта на библиотеке CatBoost, которые пытаются прогнозировать будущие цены, используя в качестве признаков данные датасета. Вот первая из моделей  — регрессионная: 

import pandas as pd
import numpy as np
from catboost import CatBoostRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder

# Loading data
data = pd.read_csv('merged_astro_financial_data.csv')

# Converting date to datetime
data['date'] = pd.to_datetime(data['date'])

# Creating lags for financial data
for col in ['open', 'high', 'low', 'close']:
    for lag in range(1, 6):  # Creating lags from 1 to 5
        data[f'{col}_lag{lag}'] = data[col].shift(lag)

# Creating lags for astronomical data
astro_cols = ['mercury', 'venus', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune']
for col in astro_cols:
    data[f'{col}_ra'] = data[col].apply(lambda x: eval(x)['ra'] if pd.notna(x) else np.nan)
    data[f'{col}_dec'] = data[col].apply(lambda x: eval(x)['dec'] if pd.notna(x) else np.nan)
    for lag in range(1, 6):  # Lags from 1 to 5
        data[f'{col}_ra_lag{lag}'] = data[f'{col}_ra'].shift(lag)
        data[f'{col}_dec_lag{lag}'] = data[f'{col}_dec'].shift(lag)
    data.drop(columns=[col, f'{col}_ra', f'{col}_dec'], inplace=True)

# Converting aspects to numerical features
aspect_cols = ['mercury_saturn', 'venus_mars', 'venus_jupiter', 'venus_uranus', 
               'mars_jupiter', 'mars_uranus', 'jupiter_uranus', 'mercury_neptune', 
               'venus_saturn', 'venus_neptune', 'mars_saturn', 'mercury_venus', 
               'mars_neptune', 'mercury_uranus', 'saturn_neptune', 'mercury_jupiter', 
               'mercury_mars', 'jupiter_saturn']

# Using LabelEncoder for encoding aspects
label_encoders = {}
for col in aspect_cols:
    label_encoders[col] = LabelEncoder()
    data[col] = label_encoders[col].fit_transform(data[col].astype(str))

# Filling missing values with mean values for numeric columns
numeric_cols = data.select_dtypes(include=[np.number]).columns
data[numeric_cols] = data[numeric_cols].fillna(data[numeric_cols].mean())

# Removing rows with missing values
data = data.dropna()

# Preparing features and target variable
features = [col for col in data.columns if col not in ['date', 'time', 'close']]
X = data[features]
y = data['close']

# Splitting data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1)

# Creating and training the CatBoost model
model = CatBoostRegressor(iterations=500, learning_rate=0.1, depth=9, random_state=1)
model.fit(X_train, y_train, eval_set=(X_test, y_test), early_stopping_rounds=200, verbose=100)

# Evaluating the model
y_pred = model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"Mean Squared Error: {mse}")
print(f"Mean Absolute Error: {mae}")
print(f"R-squared Score: {r2}")

# Visualizing feature importance
feature_importance = model.feature_importances_
feature_names = X.columns
sorted_idx = np.argsort(feature_importance)
pos = np.arange(sorted_idx.shape[0]) + 0.5
plt.figure(figsize=(12, 6))
plt.barh(pos, feature_importance[sorted_idx], align='center')
plt.yticks(pos, np.array(feature_names)[sorted_idx])
plt.xlabel('Feature Importance')
plt.title('Feature Importance')
plt.show()

# Predicting the next value
def predict_next():
    # Selecting the last row of data
    last_data = data.iloc[-1]
    input_features = last_data[features].values.reshape(1, -1)
    # Prediction
    prediction = model.predict(input_features)
    print(f"Prediction for the next closing price: {prediction[0]}")

# Example of using the function to predict the next value
predict_next()


Вторая модель — классификация:

import pandas as pd
import numpy as np
from skyfield.api import load, utc
from datetime import datetime, timedelta
import requests
import MetaTrader5 as mt5
import seaborn as sns
import matplotlib.pyplot as plt
from catboost import CatBoostClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
from sklearn.preprocessing import LabelEncoder

# Part 1: Collecting astronomical data

planets = load('de421.bsp')
earth = planets['earth']
ts = load.timescale()

def get_planet_positions(date):
    t = ts.from_datetime(date.replace(tzinfo=utc))
    planet_positions = {}
    planet_ids = {
        'mercury': 'MERCURY BARYCENTER',
        'venus': 'VENUS BARYCENTER',
        'mars': 'MARS BARYCENTER',
        'jupiter': 'JUPITER BARYCENTER',
        'saturn': 'SATURN BARYCENTER',
        'uranus': 'URANUS BARYCENTER',
        'neptune': 'NEPTUNE BARYCENTER'
    }
    for planet, planet_id in planet_ids.items():
        planet_obj = planets[planet_id]
        astrometric = earth.at(t).observe(planet_obj)
        ra, dec, _ = astrometric.radec()
        planet_positions[planet] = {'ra': ra.hours, 'dec': dec.degrees}
    return planet_positions

def get_moon_phase(date):
    t = ts.from_datetime(date.replace(tzinfo=utc))
    eph = load('de421.bsp')
    moon, sun, earth = eph['moon'], eph['sun'], eph['earth']
    
    e = earth.at(t)
    _, m, _ = e.observe(moon).apparent().ecliptic_latlon()
    _, s, _ = e.observe(sun).apparent().ecliptic_latlon()
    
    phase = (m.degrees - s.degrees) % 360
    return phase

def get_solar_activity(date):
    url = f"https://services.swpc.noaa.gov/json/solar-cycle/observed-solar-cycle-indices.json"
    response = requests.get(url)
    data = response.json()
    
    target_date = date.strftime("%Y-%m")
    
    closest_data = min(data, key=lambda x: abs(datetime.strptime(x['time-tag'], "%Y-%m") - datetime.strptime(target_date, "%Y-%m")))
    
    return {
        'sunspot_number': closest_data.get('ssn', None),
        'f10.7_flux': closest_data.get('f10.7', None)
    }

def calculate_aspects(positions):
    aspects = {}
    planets = list(positions.keys())
    for i in range(len(planets)):
        for j in range(i+1, len(planets)):
            planet1 = planets[i]
            planet2 = planets[j]
            ra1 = positions[planet1]['ra']
            ra2 = positions[planet2]['ra']
            angle = abs(ra1 - ra2) % 24
            angle = min(angle, 24 - angle) * 15  # Convert to degrees
            
            if abs(angle - 0) <= 10 or abs(angle - 180) <= 10:
                aspects[f"{planet1}_{planet2}"] = "conjunction" if abs(angle - 0) <= 10 else "opposition"
            elif abs(angle - 90) <= 10:
                aspects[f"{planet1}_{planet2}"] = "square"
            elif abs(angle - 120) <= 10:
                aspects[f"{planet1}_{planet2}"] = "trine"
    
    return aspects

# Part 2: Obtaining financial data through MetaTrader5

def get_financial_data(symbol, start_date, end_date):
    if not mt5.initialize():
        print("initialize() failed")
        mt5.shutdown()
        return None

    timeframe = mt5.TIMEFRAME_D1

    rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)
    mt5.shutdown()

    financial_df = pd.DataFrame(rates)
    financial_df['time'] = pd.to_datetime(financial_df['time'], unit='s')
    return financial_df

# Part 3: Synchronizing astronomical and financial data

def sync_data(astro_df, financial_df):
    astro_df['date'] = pd.to_datetime(astro_df['date']).dt.tz_localize(None)
    financial_df['time'] = pd.to_datetime(financial_df['time'])
    merged_data = pd.merge(financial_df, astro_df, left_on='time', right_on='date', how='inner')
    return merged_data

# Part 4: Training the model and making predictions

def train_and_predict(merged_data):
    # Converting aspects to numerical features
    aspect_cols = [col for col in merged_data.columns if '_' in col and col not in ['date', 'time']]
    label_encoders = {}
    for col in aspect_cols:
        label_encoders[col] = LabelEncoder()
        merged_data[col] = label_encoders[col].fit_transform(merged_data[col].astype(str))
    
    # Creating lags for financial data
    for col in ['open', 'high', 'low', 'close']:
        for lag in range(1, 6):
            merged_data[f'{col}_lag{lag}'] = merged_data[col].shift(lag)
    
    # Creating lags for astronomical data
    astro_cols = ['mercury', 'venus', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune']
    for col in astro_cols:
        merged_data[f'{col}_ra'] = merged_data[col].apply(lambda x: eval(x)['ra'] if pd.notna(x) else np.nan)
        merged_data[f'{col}_dec'] = merged_data[col].apply(lambda x: eval(x)['dec'] if pd.notna(x) else np.nan)
        for lag in range(1, 6):
            merged_data[f'{col}_ra_lag{lag}'] = merged_data[f'{col}_ra'].shift(lag)
            merged_data[f'{col}_dec_lag{lag}'] = merged_data[f'{col}_dec'].shift(lag)
        merged_data.drop(columns=[col, f'{col}_ra', f'{col}_dec'], inplace=True)

    # Filling missing values with mean values for numeric columns
    numeric_cols = merged_data.select_dtypes(include=[np.number]).columns
    merged_data[numeric_cols] = merged_data[numeric_cols].fillna(merged_data[numeric_cols].mean())

    merged_data = merged_data.dropna()

    # Creating binary target variable
    merged_data['price_change'] = (merged_data['close'].shift(-1) > merged_data['close']).astype(int)

    # Removing rows with missing values in the target variable
    merged_data = merged_data.dropna(subset=['price_change'])

    features = [col for col in merged_data.columns if col not in ['date', 'time', 'close', 'price_change']]
    X = merged_data[features]
    y = merged_data['price_change']

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1)

    model = CatBoostClassifier(iterations=500, learning_rate=0.1, depth=9, random_state=1)
    model.fit(X_train, y_train, eval_set=(X_test, y_test), early_stopping_rounds=200, verbose=100)

    y_pred = model.predict(X_test)
    accuracy = accuracy_score(y_test, y_pred)
    clf_report = classification_report(y_test, y_pred)
    conf_matrix = confusion_matrix(y_test, y_pred)

    print(f"Accuracy: {accuracy}")
    print("Classification Report:")
    print(clf_report)
    print("Confusion Matrix:")
    print(conf_matrix)

    # Visualizing feature importance
    feature_importance = model.feature_importances_
    feature_names = X.columns
    sorted_idx = np.argsort(feature_importance)
    pos = np.arange(sorted_idx.shape[0]) + 0.5

    plt.figure(figsize=(12, 6))
    plt.barh(pos, feature_importance[sorted_idx], align='center')
    plt.yticks(pos, np.array(feature_names)[sorted_idx])
    plt.xlabel('Feature Importance')
    plt.title('Feature Importance')
    plt.show()

    # Predicting the next value
    def predict_next():
        last_data = merged_data.iloc[-1]
        input_features = last_data[features].values.reshape(1, -1)
        prediction = model.predict(input_features)
        print(f"Price change prediction (0: will decrease, 1: will increase): {prediction[0]}")

    predict_next()

# Main program
start_date = datetime(2023, 3, 1)
end_date = datetime(2024, 7, 30)

astro_data = []
current_date = start_date

while current_date <= end_date:
    planet_positions = get_planet_positions(current_date)
    moon_phase = get_moon_phase(current_date)
    try:
        solar_activity = get_solar_activity(current_date)
    except Exception as e:
        print(f"Error getting solar activity for {current_date}: {e}")
        solar_activity = {'sunspot_number': None, 'f10.7_flux': None}
    aspects = calculate_aspects(planet_positions)
    
    astro_data.append({
        'date': current_date,
        'mercury': str(planet_positions['mercury']),
        'venus': str(planet_positions['venus']),
        'mars': str(planet_positions['mars']),
        'jupiter': str(planet_positions['jupiter']),
        'saturn': str(planet_positions['saturn']),
        'uranus': str(planet_positions['uranus']),
        'neptune': str(planet_positions['neptune']),
        'moon_phase': moon_phase,
        **solar_activity,
        **aspects
    })
    
    current_date += timedelta(days=1)

astro_df = pd.DataFrame(astro_data)

symbol = "EURUSD"
financial_data = get_financial_data(symbol, start_date, end_date)

if financial_data is not None:
    merged_data = sync_data(astro_df, financial_data)
    train_and_predict(merged_data)

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


Возможно, результат можно улучшить, возможно, что регрессионной модели было уделено мало внимания, и на самом деле прогнозировать цены с помощью положения планет и активности Луны и Солнца все же возможно. Если будет настроение — я сделаю еще одну статью на эту тему.


Результаты исследования

Итак, пришло время подвести результаты исследования. После проведения простого анализа, и написания двух моделей прогнозирования, мы видим результаты исследования о потенциальном влиянии астрологии на рынок. 

Корреляционный анализ. Полученная нами карта взаимозависимостей не выявила какой-либо плотной корреляции между положением планет и ценой закрытия EURUSD. Все корреляции у нас – слабее 0.3, что дает основания полагать, что положение звезд или планет вообще никак не связано с финансовыми рынками.

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

Полученные метрики результатов модели, такие как MSE, MAE, и R квадрат – очень слабы, и объясняют данные очень слабо. При этом, модель показывает, что самыми важными признаками являются лаги, предыдущие значения цен, а вовсе не положения планет. Значит, что цена является лучшим признаком, нежели положение любой планеты нашей солнечной системы?

Модель классификации CatBoost. Модель классификации крайне плохо прогнозирует будущий рост или падение цен. Точность едва превышает 50%, что также подтверждает — астрономия никак не работает на реальном рынке.


Заключение

Итоги исследования вполне явны — методы астрологии, и попытки прогноза цен на реальном рынке по данным астрономических признаков — полностью бесполезны. Возможно, я еще вернусь к этой теме, но пока что учение Уильяма Ганна выглядит как «наведение тени на плетень», и попытки замаскировать за сложностью по факту нерабочие решения, которые были созданы лишь с целью продажи книг и курсов по трейдингу.

Может ли быть, что улучшенная модель, где также использовались бы значения углов Ганна, значения из выстроенного квадрата 9, а также значения сетки Ганна, показала бы себя лучше? Пока что мы этого не знаем. Я немного разочарован результатами исследования.

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

Прикрепленные файлы |
GannAstroClass.py (9.39 KB)
AstroCorr.py (7.62 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (2)
Maxim Kuznetsov
Maxim Kuznetsov | 29 авг. 2024 в 11:51

по стопам Гана, для чистоты опыта стоило брать не EURUSD а например фьюч на хлопок. И инструмент примерно тот-же и астрономические циклы в нём могут быть, всё-же сельхоз.

spalsule
spalsule | 10 мар. 2025 в 07:19
Похоже, вы не изучали временные циклы Ганна. Поэтому вы считаете, что они не работают. Он работает на всех акциях и товарах.
Нейросети в трейдинге: Анализ облака точек (PointNet) Нейросети в трейдинге: Анализ облака точек (PointNet)
Прямой анализ облака точек позволяет избежать излишнего увеличения объема данных и повышает эффективность моделей в задачах классификации и сегментации. Подобные подходы демонстрируют высокую производительность и устойчивость к возмущениям в исходных данных.
Графики индекса доллара и индекса евро — пример сервиса в MetaTrader 5 Графики индекса доллара и индекса евро — пример сервиса в MetaTrader 5
На примере программы-сервиса рассмотрим создание и обновление графиков индекса доллара (USDX) и индекса евро (EURX). При запуске сервиса будем проверять наличие нужного синтетического инструмента, создавать его при его отсутствии и размещать в окне Обзор рынка. Далее будет создана история синтетического инструмента — минутная и тиковая, и будет открыт график созданного инструмента.
Решение проблем интеграции ONNX Решение проблем интеграции ONNX
ONNX — отличный инструмент для интеграции сложного ИИ-кода на разных платформах. Однако при его использовании возникают некоторые сложности, которые необходимо преодолеть, чтобы извлечь из него максимальную пользу. В этой статье мы обсудим распространенные проблемы, с которыми вы можете столкнуться, и способы их устранения.
Универсальная формула оптимизации (GOF) при реализации режима Custom Max с ограничениями Универсальная формула оптимизации (GOF) при реализации режима Custom Max с ограничениями
В статье представлен способ реализации задач оптимизации с несколькими целями и ограничениями при выборе режима Custom Max в настройках терминала MetaTrader 5. Например, задача оптимизации может быть следующей: максимизировать фактор прибыли, чистую прибыль и фактор восстановления таким образом, чтобы просадка была менее 10%, количество последовательных убытков было менее 5, а количество сделок в неделю было более 5.