English Русский 中文 Español Deutsch 日本語 Português Italiano Türkçe
preview
MetaTrader 5를 사용하여 Python에서 사용자 지정 통화쌍 패턴 찾기

MetaTrader 5를 사용하여 Python에서 사용자 지정 통화쌍 패턴 찾기

MetaTrader 5트레이딩 |
19 1
Yevgeniy Koshtenko
Yevgeniy Koshtenko

외환 패턴 분석 소개

초보자는 통화쌍 차트를 처음 볼 때 무엇을 볼 수 있을까요? 장중 변동, 변동성 증가 및 감소, 추세 변화 등 많은 변동이 있습니다. 위, 아래, 지그재그 - 이 모든 것을 어떻게 알아낼 수 있을까요? 저는 가격 패턴 분석 연구에 몰두하면서 외환과 친해지기 시작했습니다.

언뜻 보기에 세상의 많은 것들이 혼란스러워 보입니다. 그러나 숙련된 전문가라면 다른 사람에게는 혼란스러워 보이는 패턴과 가능성이 기회로 보일수 있습니다. 통화쌍 차트도 마찬가지입니다. 이러한 혼란을 체계화하면 우리는 향후 가격 움직임을 예측할 수 있는 숨겨진 패턴을 발견할 수 있습니다.

하지만 어떻게 찾을 수 있을까요? 실제 패턴과 무작위 노이즈를 구별하는 방법은 무엇일까요? 여기서부터가 흥미로운 것입니다. 저는 Python과 MetaTrader 5를 사용해 저만의 패턴 분석 시스템을 만들기로 했습니다. 그건 외환 거래를 정복하기 위한 수학과 프로그래밍의 일종의 공생입니다.

반복되는 패턴을 찾아내고 성능을 평가하는 알고리즘을 사용하여 많은 과거 데이터를 연구하는 것이 저의 아이디어였습니다. 흥미롭게 들리나요? 그러나 실제 구현은 그렇게 간단하지 않았습니다.


환경 설정: 필요한 라이브러리 설치 및 MetaTrader 5에 연결하기

이제 첫 번째 작업은 Python을 설치하는 것입니다. 공식 웹사이트 python.org에서 다운로드할 수 있습니다. "Python을 경로에 추가" 상자를 선택해야 합니다. 

다음으로 중요한 단계는 라이브러리입니다. 몇 가지가 필요합니다. 대표적인 것이 MetaTrader 5입니다. 또한 데이터 작업을 위한 'pandas'도 있습니다. 그리고 아마도 'numpy'도 필요할 것입니다. 명령줄을 열고 입력합니다:

pip install MetaTrader5 pandas numpy matplotlib pytz

가장 먼저 해야 할 일은 MetaTrader 5 자체를 설치하는 것입니다. 브로커의 공식 웹사이트에서 다운로드하여 설치하세요. 복잡하지 않습니다.

이제 터미널로 가는 경로를 찾아야 합니다. 일반적으로 위치는 "C:\프로그램 파일\MetaTrader 5\terminal64.exe"입니다.

이제 Python을 열고 입력합니다:

import MetaTrader5 as mt5

if not mt5.initialize(path="C:/Program Files/MetaTrader 5/terminal64.exe"):
    print("MetaTrader 5 initialization failed.")
    mt5.shutdown()
else:
    print("MetaTrader 5 initialized successfully.")

실행합니다. 터미널 초기화에 성공했다는 메시지가 표시되면 모든 작업이 올바르게 완료된 것입니다.

모든 것이 제대로 작동하는지 확인하고 싶으신가요? 데이터를 가져와 보겠습니다:

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

if not mt5.initialize():
    print("Oops! Something went wrong.")
    mt5.shutdown()

eurusd_ticks = mt5.copy_ticks_from("EURUSD", datetime.now(), 10, mt5.COPY_TICKS_ALL)
ticks_frame = pd.DataFrame(eurusd_ticks)
print("Look at this beauty:")
print(ticks_frame)

mt5.shutdown()

데이터 테이블이 보이시나요, 축하합니다! 이제 여러분은 Python을 이용한 알고리즘 외환 트레이딩의 세계로 첫발을 내디뎠습니다. 보기만큼 어렵지 않습니다.


코드 구조: 기본 함수들과 각 함수의 목적

이제 코드의 구조를 분석해 보겠습니다. 이 시스템은 외환 시장에서의 패턴을 분석하는 완벽한 시스템입니다. 

시스템의 주요 부분인 find_patterns 함수부터 시작하겠습니다. 이 함수는 과거 데이터를 살펴보고 주어진 기간의 패턴을 식별합니다. 첫 번째 패턴을 찾은 후에 우리는 그 효율성을 평가해야 합니다. 이 함수는 나중에 사용하기 위해 마지막 패턴도 기억합니다.

다음 함수는 calculate_winrate_and_frequency입니다. 이 함수는 발견된 패턴을 분석하여 발생 빈도, 승률, 패턴의 정렬을 보여줍니다.

process_currency_pair 함수 또한 중요한 역할을 합니다. 이것은 상당히 중요한 핸들러로 데이터를 로드하고 데이터를 살펴보고 다양한 길이의 패턴을 검색하고 매수 및 매도에 대한 상위 300개의 패턴도 제공합니다. 코드의 시작 부분은 초기화, 매개변수 설정, 차트 주기(TF) 및 기간(제 경우에는 1990년부터 2024년까지입니다)입니다.

이제 메인 코드 실행 루프로 이동해 보겠습니다. 패턴 검색 알고리즘의 특징에는 다양한 패턴 길이가 포함되는데 짧은 패턴은 자주 나타나지만 신뢰성이 떨어지고 긴 패턴은 더 효과적이지만 너무 드물기 때문입니다. 우리는 모든 측면을 고려해야 합니다.


MetaTrader 5에서 데이터 가져오기: copy_rates_range 함수

첫 번째 함수는 터미널에서 데이터를 수신해야 합니다. 코드를 살펴보겠습니다:

import MetaTrader5 as mt5
import pandas as pd
import time
from datetime import datetime, timedelta
import pytz

# List of major currency pairs
major_pairs = ['EURUSD']

# Setting up data request parameters
timeframe = mt5.TIMEFRAME_H4
start_date = pd.Timestamp('1990-01-01')
end_date = pd.Timestamp('2024-05-31')

def process_currency_pair(symbol):
    max_retries = 5
    retries = 0
    while retries < max_retries:
        try:
            # Loading OHLC data
            rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)
            if rates is None:
                raise ValueError("No data received")
            ohlc_data = pd.DataFrame(rates)
            ohlc_data['time'] = pd.to_datetime(ohlc_data['time'], unit='s')
            break
        except Exception as e:
            print(f"Error loading data for {symbol}: {e}")
            retries += 1
            time.sleep(2)  # Wait before retrying

    if retries == max_retries:
        print(f"Failed to load data for {symbol} after {max_retries} attempts")
        return

    # Further data processing...

이 코드에서 무슨 일이 일어나고 있나요? 우리는 먼저 통화 쌍을 정의합니다. 지금은 EURUSD만 지원하지만 다른 통화도 추가할 수 있습니다. 그리고 시간 간격을 설정합니다. H4는 4시간입니다. 지금이 최적의 타이밍입니다. 

다음은 날짜입니다. 1990년부터 2024년까지. 과거 호가가 많이 필요할 것입니다. 데이터가 많을수록 분석의 정확도는 높아집니다. 이제 중요한 것은 process_currency_pair 함수입니다. 이 함수는 copy_rates_range를 사용하여 데이터를 로드합니다.

결과적으로 무엇을 얻을 수 있을까요? 과거 데이터가 포함된 데이터프레임. 시간, 시가, 종가, 고가, 저가 - 필요한 모든 것을 확인할 수 있습니다.

문제가 발생하면 오류가 식별되고 화면에 표시되고 우리는 다시 시도합니다.


시계열 처리: OHLC 데이터를 가격 변동 방향으로 변환하기

다시 주요 부분으로 돌아가 보겠습니다. 우리는 외환 시장의 혼란스러운 변동을 보다 질서 있는 것으로 바꾸고자 합니다 - 추세와 반전이라는 것으로 말입니다. 어떻게 할 수 있을까요? 가격을 방향으로 바꾸겠습니다.

다음은 코드입니다:

# Fill missing values with the mean
ohlc_data.fillna(ohlc_data.mean(), inplace=True)

# Convert price movements to directions
ohlc_data['direction'] = np.where(ohlc_data['close'].diff() > 0, 'up', 'down')

여기서 무슨 일이 일어나고 있나요? 먼저 갭을 채웁니다. 갭은 최종 결과를 크게 안 좋게 할 수 있습니다. 평균값으로 채웁니다. 

이제 가장 흥미로운 부분입니다. '방향'이라는 이름의 열을 새로 만듭니다. 가격 데이터를 트렌드를 시뮬레이션하는 데이터로 변환합니다. 이 데이터는 기본적인 방식으로 작동합니다:

  • 현재 종가가 이전 종가보다 높으면 'up'이라고 적습니다.
  • 아래에 있으면 'down'이라고 적습니다.

아주 간단한 공식이지만 꽤 효과적입니다. 이제 복잡한 숫자 대신 'up'과 'down'의 단순한 시퀀스가 생겼습니다. 이 순서는 사람이 인식하기에 훨씬 쉽습니다. 이게 왜 필요할까요? 이러한 'up'과 'down'은 패턴의 기본 구성 요소입니다. 이들로부터 시장에서 일어나는 일이 어떠한지 그 완전한 그림을 얻을 것입니다.


패턴 검색 알고리즘: find_patterns 함수

우리에게는 연속되는 'up'과 'down'이 있습니다. 이제 이 시퀀스에서 반복되는 패턴을 찾아보겠습니다.

다음은 find_patterns 함수입니다:

def find_patterns(data, pattern_length, direction):
    patterns = defaultdict(list)
    last_pattern = None
    last_winrate = None
    last_frequency = None

    for i in range(len(data) - pattern_length - 6):
        pattern = tuple(data['direction'][i:i+pattern_length])
        if data['direction'][i+pattern_length+6] == direction:
            patterns[pattern].append(True)
        else:
            patterns[pattern].append(False)

    # Check last prices for pattern match
    last_pattern_tuple = tuple(data['direction'][-pattern_length:])
    if last_pattern_tuple in patterns:
        last_winrate = np.mean(patterns[last_pattern_tuple]) * 100
        last_frequency = len(patterns[last_pattern_tuple])
        last_pattern = last_pattern_tuple

    return patterns, last_pattern, last_winrate, last_frequency

이 모든 것이 어떻게 작동하나요?

  • 'paterns' 사전을 만듭니다. 이것은 우리가 찾은 모든 패턴을 저장하는 일종의 라이브러리의 역할을 할 것입니다.
  • 그런 다음 데이터를 반복하기 시작합니다. pattern_length의 데이터 샘플(3, 4, 5 등 최대 25개까지 가능)을 가져와서 6개의 바 뒤에 어떤 일이 일어나는지 살펴봅니다.
  • 6개의 바 이후에 가격이 원하는 방향(매수 패턴의 경우 상승 또는 매도 패턴의 경우 하락)으로 이동하면 True로 설정합니다. 그렇지 않은 경우 - False 입니다.
  • 가능한 모든 데이터 샘플에 대해 이 작업을 수행합니다. 비슷한 패턴을 얻어야 합니다: "up-up-down" - True, "down-up-up" 등.
  • 이후 앞서 살펴본 패턴 중 현재 형성되고 있는 패턴이 있는지 확인합니다. 그렇다면 승률(적중 성공률)과 발생 빈도를 계산합니다.

이를 통해 단순한 'ups'과 'downs'의 시퀀스를 예측을 위한 매우 강력한 도구로 전환할 수 있습니다. 하지만 그게 다가 아닙니다. 다음으로 이러한 패턴을 정렬하고 가장 효율적인 패턴을 선택하여 분석합니다.


패턴 통계 계산하기: 승률 및 발생 빈도

이제 여러 가지 패턴이 있습니다. 이중 가장 적합한 패턴을 선택해야 합니다.

코드를 살펴보겠습니다:

def calculate_winrate_and_frequency(patterns):
    results = []
    for pattern, outcomes in patterns.items():
        winrate = np.mean(outcomes) * 100
        frequency = len(outcomes)
        results.append((pattern, winrate, frequency))
    results.sort(key=lambda x: x[1], reverse=True)
    return results

여기서는 각 패턴과 그 결과(앞서 참과 거짓이라고 했습니다)를 취한 다음 승률(생산성 백분율)을 계산합니다. 패턴이 10번 중 7번 성공하면 승률은 70%입니다. 빈도도 계산합니다 - 패턴이 발생한 횟수입니다. 잦은 빈도 일수록 통계의 신뢰도가 높아집니다. 이 모든 것을 '결과' 목록에 넣습니다. 마지막으로 정렬입니다. 최고의 패턴을 목록의 맨 위에 배치했습니다.


정렬 결과: 중요한 패턴 선택

이제 우리에게는 충분한 데이터가 확보되었습니다. 하지만 모두다 필요한 것은 아닙니다. 이를 정리해야 합니다.

filtered_buy_results = [result for result in all_buy_results if result[2] > 20]
filtered_sell_results = [result for result in all_sell_results if result[2] > 20]

filtered_buy_results.sort(key=lambda x: x[1], reverse=True)
top_300_buy_patterns = filtered_buy_results[:300]

filtered_sell_results.sort(key=lambda x: x[1], reverse=True)
top_300_sell_patterns = filtered_sell_results[:300]

비슷한 방식으로 정렬을 설정했습니다. 먼저 20회 미만 발생하는 모든 패턴을 분류합니다. 통계에서 볼 수 있듯이 희귀한 패턴은 신뢰도가 떨어집니다. 

나머지 패턴을 승률별로 정렬합니다. 가장 효율적인 항목은 목록의 맨 앞에 설정되어 있습니다. 이제 상위 300개를 선택합니다. 이것은 수천 개가 넘는 수많은 패턴 중에서 남아 있는 모든 것입니다.  


다양한 패턴 길이 작업: 3~25개

이제 거래할 때 통계적으로 일관되게 수익을 낼 수 있는 패턴 변형을 선택해야 합니다. 옵션의 길이가 다릅니다. 가격 변동은 3개 또는 25개의 가격 변동으로 구성될 수 있습니다. 가능한 모든 것을 확인해 보겠습니다:

pattern_lengths = range(3, 25)  # Pattern lengths from 3 to 25
all_buy_patterns = {}
all_sell_patterns = {}

for pattern_length in pattern_lengths:
    buy_patterns, last_buy_pattern, last_buy_winrate, last_buy_frequency = find_patterns(ohlc_data, pattern_length, 'up')
    sell_patterns, last_sell_pattern, last_sell_winrate, last_sell_frequency = find_patterns(ohlc_data, pattern_length, 'down')
    all_buy_patterns[pattern_length] = buy_patterns
    all_sell_patterns[pattern_length] = sell_patterns

3에서 25까지의 각각의 길이에 대해 패턴 검색 필터를 실행합니다. 이러한 방식을 사용하는 이유는 무엇인가요? 세 번 미만의 패턴은 신뢰하기 힘듭니다 - 앞서 언급했습니다. 25보다 긴 패턴은 너무 드뭅니다. 각 길이에 대해 매수 및 매도 패턴을 모두 찾습니다. 

그렇다면 왜 이렇게 다양한 길이가 필요할까요? 짧은 패턴은 빠르게 시장 반전을 포착할 수 있고 긴 패턴은 장기적인 추세를 보여줄 수 있습니다. 우리는 어떤 것이 더 효과적인지 미리 알 수 없습니다. 그러므로 모든 것을 테스트합니다.


매수 및 매도 패턴 분석

이제 다양한 길이의 패턴을 선택했으니 어떤 패턴이 실제로 효과가 있는지 알아볼 차례입니다. 

실제로 작동하는 코드는 다음과 같습니다:

all_buy_results = []
for pattern_length, patterns in all_buy_patterns.items():
    results = calculate_winrate_and_frequency(patterns)
    all_buy_results.extend(results)

all_sell_results = []
for pattern_length, patterns in all_sell_patterns.items():
    results = calculate_winrate_and_frequency(patterns)
    all_sell_results.extend(results)

매수, 매도 등 모든 패턴을 분석하여 승률 및 빈도 계산기를 통해 분류합니다. 

하지만 우리가 단순히 통계만 집계하는 것은 아닙니다. 매수 패턴과 매도 패턴의 차이를 찾습니다. 이것이 왜 중요할까요? 시장은 상승할 때와 하락할 때 다르게 움직일 수 있기 때문입니다. 때로는 매수 패턴이 더 효과적일 때도 있고 때로는 매도 패턴이 더 신뢰할만한 때도 있습니다.

이제 길이가 다른 패턴을 서로 비교하고 다음 단계로 넘어가겠습니다. 이제 시장 진입 시점을 결정하는 데는 짧은 패턴이, 장기 추세를 결정하는 데는 긴 패턴이 더 효과적이라는 것이 밝혀질 수 있습니다. 그 반대도 마찬가지입니다. 그렇기 때문에 모든 것을 분석하고 어떤 것도 미리 버리지 않습니다.

이 분석이 끝나면 어떤 패턴이 매수에, 어떤 패턴이 매도에 더 효과적인지 그리고 시장 상황에 따라 어떤 길이의 패턴이 가장 효과적인지 등 첫 번째 결과가 도출됩니다. 이 데이터를 통해 우리는 이미 외환 시장의 가격 분석을 수행할 수 있는 것입니다.

하지만 아무리 좋은 패턴이라도 성공을 보장하는 것은 아니라는 점을 기억하세요. 시장은 놀라움으로 가득합니다. 우리의 임무는 성공 가능성을 높이는 것이며 이를 위해 모든 측면에서 패턴을 분석하는 것입니다.


앞을 내다보세요: 최근의 패턴을 기반으로 한 예측

이제 몇 가지 예측을 해볼 차례입니다. 우리의 예측 코드를 살펴보겠습니다:

if last_buy_pattern:
    print(f"\nLast buy pattern for {symbol}: {last_buy_pattern}, Winrate: {last_buy_winrate:.2f}%, Frequency: {last_buy_frequency}")
    print(f"Forecast: Price will likely go up.")
if last_sell_pattern:
    print(f"\nLast sell pattern for {symbol}: {last_sell_pattern}, Winrate: {last_sell_winrate:.2f}%, Frequency: {last_sell_frequency}")
    print(f"Forecast: Price will likely go down.")

마지막으로 형성된 패턴을 살펴보고 미래를 예측하여 트레이딩 분석을 수행합니다.

우리는 매수 패턴과 매도 패턴의 두 가지의 시나리오를 살펴보고 있다는 점에 유의하시기 바랍니다. 왜 그럴까요? 시장은 상승세와 하락세, 매수자와 매도자 간의 영원한 대결이기 때문입니다. 우리는 어떤 상황에도 대비해야 합니다.

각 패턴에 대해 세 가지 주요 매개변수를 출력합니다: 패턴 자체, 승률, 발생 빈도. 승률은 특히 중요합니다. 매수 패턴의 승률이 70%라면 이 패턴이 나타난 이후 70%의 시간 동안 실제로 가격이 상승했다는 뜻입니다. 꽤 괜찮은 결과입니다. 하지만 90%도 보장되지 않는다는 점을 기억하세요. 외환 트레이딩 세계에는 항상 놀라움이 생깁니다.

빈도도 중요한 역할을 합니다. 자주 발생하는 패턴이 드물게 발생하는 패턴보다 더 신뢰할 수 있습니다.

흥미로운 부분은 우리의 예측입니다. "가격이 오를 것 같습니다" 또는 "가격이 내릴 것 같습니다". 이러한 예측을 통해 우리는 우리의 작업의 만족도를 높일 수 있습니다. 하지만 아무리 정확한 예측이라도 이는 확률일 뿐 보장되지 않는다는 점을 기억하세요. 외환 시장은 예측하기 매우 어렵습니다. 뉴스, 경제 이벤트, 심지어 영향력 있는 사람들의 트윗도 단 몇 초 만에 가격 움직임의 방향을 바꿀 수 있습니다.

따라서 우리의 코드는 만병통치약이 아니라 스마트한 EA인 것입니다. 우리의 코드는 다음과 같이 해석될 수 있습니다: "과거 데이터에 따르면 우리는 가격이 상승(또는 하락)할 것이라고 믿을 만한 근거가 있습니다." 시장에 진입할지 여부는 여러분의 결정에 달려 있습니다. 이러한 예측을 적용하는 것은 신중한 과정입니다. 우리는 예상되는 움직임에 대한 정보가 있지만 시장의 전반적인 상황을 고려하여 각 단계를 현명하게 수행해야 합니다.


미래: 최상의 패턴 및 예측 시각화

코드에 몇 가지 시각화를 추가해 보겠습니다:

import matplotlib.pyplot as plt

def visualize_patterns(patterns, title, filename):
    patterns = patterns[:20]  # Take top 20 for clarity
    patterns.reverse()  # Reverse the list to display it correctly on the chart

    fig, ax = plt.subplots(figsize=(12, 8))
    
    winrates = [p[1] for p in patterns]
    frequencies = [p[2] for p in patterns]
    labels = [' '.join(p[0]) for p in patterns]

    ax.barh(range(len(patterns)), winrates, align='center', color='skyblue', zorder=10)
    ax.set_yticks(range(len(patterns)))
    ax.set_yticklabels(labels)
    ax.invert_yaxis()  # Invert the Y axis to display the best patterns on top

    ax.set_xlabel('Winrate (%)')
    ax.set_title(title)

    # Add occurrence frequency
    for i, v in enumerate(winrates):
        ax.text(v + 1, i, f'Freq: {frequencies[i]}', va='center')

    plt.tight_layout()
    plt.savefig(filename)
    plt.close()

# Visualize top buy and sell patterns
visualize_patterns(top_300_buy_patterns, f'Top 20 Buy Patterns for {symbol}', 'top_buy_patterns.png')
visualize_patterns(top_300_sell_patterns, f'Top 20 Sell Patterns for {symbol}', 'top_sell_patterns.png')

# Visualize the latest pattern and forecast
def visualize_forecast(pattern, winrate, frequency, direction, symbol, filename):
    fig, ax = plt.subplots(figsize=(8, 6))
    
    ax.bar(['Winrate'], [winrate], color='green' if direction == 'up' else 'red')
    ax.set_ylim(0, 100)
    ax.set_ylabel('Winrate (%)')
    ax.set_title(f'Forecast for {symbol}: Price will likely go {direction}')

    ax.text(0, winrate + 5, f'Pattern: {" ".join(pattern)}', ha='center')
    ax.text(0, winrate - 5, f'Frequency: {frequency}', ha='center')

    plt.tight_layout()
    plt.savefig(filename)
    plt.close()

if last_buy_pattern:
    visualize_forecast(last_buy_pattern, last_buy_winrate, last_buy_frequency, 'up', symbol, 'buy_forecast.png')
if last_sell_pattern:
    visualize_forecast(last_sell_pattern, last_sell_winrate, last_sell_frequency, 'down', symbol, 'sell_forecast.png')

우리는 두 가지 함수를 만들었습니다: visualize_patterns과 visualize_forecast 첫 번째는 상위 20개 패턴, 승률 및 발생 빈도가 표시된 가로의 바 차트를 그립니다. 두 번째는 최신 패턴을 기반으로 예측을 시각적으로 표현합니다.

패턴의 경우 우리는 가로 열을 사용할 것입니다. 패턴이 길 수 있고 읽기 쉽기 때문입니다. 색상은 사람의 눈으로 인식하기 좋은 하늘색입니다.

PNG 파일로 저장합니다.


패턴 분석 시스템 테스트 및 백테스팅

패턴 분석 시스템을 만들었지만 실제로 작동하는지 어떻게 알 수 있을까요? 이를 위해서는 과거 데이터에 대해 테스트해야 합니다. 

이 작업에 필요한 코드는 다음과 같습니다:

def simulate_trade(data, direction, entry_price, take_profit, stop_loss):
    for i, row in data.iterrows():
        current_price = row['close']
        
        if direction == "BUY":
            if current_price >= entry_price + take_profit:
                return {'profit': take_profit, 'duration': i}
            elif current_price <= entry_price - stop_loss:
                return {'profit': -stop_loss, 'duration': i}
        else:  # SELL
            if current_price <= entry_price - take_profit:
                return {'profit': take_profit, 'duration': i}
            elif current_price >= entry_price + stop_loss:
                return {'profit': -stop_loss, 'duration': i}
    
    # If the loop ends without reaching TP or SL, close at the current price
    last_price = data['close'].iloc[-1]
    profit = (last_price - entry_price) if direction == "BUY" else (entry_price - last_price)
    return {'profit': profit, 'duration': len(data)}

def backtest_pattern_system(data, buy_patterns, sell_patterns):
    equity_curve = [10000]  # Initial capital $10,000
    trades = []
    
    for i in range(len(data) - max(len(p[0]) for p in buy_patterns + sell_patterns)):
        current_data = data.iloc[:i+1]
        last_pattern = tuple(current_data['direction'].iloc[-len(buy_patterns[0][0]):])
        
        matching_buy = [p for p in buy_patterns if p[0] == last_pattern]
        matching_sell = [p for p in sell_patterns if p[0] == last_pattern]
        
        if matching_buy and not matching_sell:
            entry_price = current_data['close'].iloc[-1]
            take_profit = 0.001  # 10 pips
            stop_loss = 0.0005  # 5 pips
            trade_result = simulate_trade(data.iloc[i+1:], "BUY", entry_price, take_profit, stop_loss)
            trades.append(trade_result)
            equity_curve.append(equity_curve[-1] + trade_result['profit'] * 10000)  # Multiply by 10000 to convert to USD
        elif matching_sell and not matching_buy:
            entry_price = current_data['close'].iloc[-1]
            take_profit = 0.001  # 10 pips
            stop_loss = 0.0005  # 5 pips
            trade_result = simulate_trade(data.iloc[i+1:], "SELL", entry_price, take_profit, stop_loss)
            trades.append(trade_result)
            equity_curve.append(equity_curve[-1] + trade_result['profit'] * 10000)  # Multiply by 10000 to convert to USD
        else:
            equity_curve.append(equity_curve[-1])
    
    return equity_curve, trades

# Conduct a backtest
equity_curve, trades = backtest_pattern_system(ohlc_data, top_300_buy_patterns, top_300_sell_patterns)

# Visualizing backtest results
plt.figure(figsize=(12, 6))
plt.plot(equity_curve)
plt.title('Equity Curve')
plt.xlabel('Trades')
plt.ylabel('Equity ($)')
plt.savefig('equity_curve.png')
plt.close()

# Calculating backtest statistics
total_profit = equity_curve[-1] - equity_curve[0]
win_rate = sum(1 for trade in trades if trade['profit'] > 0) / len(trades) if trades else 0
average_profit = sum(trade['profit'] for trade in trades) / len(trades) if trades else 0

print(f"\nBacktest Results:")
print(f"Total Profit: ${total_profit:.2f}")
print(f"Win Rate: {win_rate:.2%}")
print(f"Average Profit per Trade: ${average_profit*10000:.2f}")
print(f"Total Trades: {len(trades)}")

여기서 무슨 일이 일어나고 있나요? simulate_trade 함수는 단일 거래에 대한 시뮬레이터입니다. 가격을 모니터링하고 이익실현 또는 손절매에 도달하면 거래를 청산합니다. 

backtest_pattern_system은 더 중요한 함수입니다. 과거 데이터를 단계별로 매일매일 검토하여 패턴이 형성되었는지 여부를 확인합니다. 매수 패턴을 찾았나요? 그런 다음 매수합니다. 매도할 것을 찾았나요? 매도합니다.

고정 익절은 100포인트, 손절은 50포인트를 사용합니다. 한도 이상의 위험을 감수하지 않도록 너무 많지도 않고 그렇다고 너무 적지도 않게 만족스러운 수익의 한계를 설정해야 수익이 증가할 수 있습니다.

거래가 있을 때마다 우리의 예탁 자산 평가 총액 곡선을 업데이트합니다. 작업이 끝나면 총 수익이 얼마인지, 수익성이 있는 거래의 비율은 얼마인지, 거래당 평균 수익은 얼마인지 등의 결과를 얻을 수 있습니다. 물론 우리는 결과를 시각화 할 것입니다.

MQL5 언어를 사용하여 패턴 검색을 구현해 보겠습니다. 다음은 코드입니다:

//+------------------------------------------------------------------+
//|                                       PatternProbabilityIndicator|
//|                                                 Copyright 2024   |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Your Name Here"
#property link      "https://www.mql5.com"
#property version   "1.06"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2

//--- plot BuyProbability
#property indicator_label1  "BuyProbability"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//--- plot SellProbability
#property indicator_label2  "SellProbability"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

//--- input parameters
input int      InpPatternLength = 5;    // Pattern Length (3-10)
input int      InpLookback     = 1000;  // Lookback Period (100-5000)
input int      InpForecastHorizon = 6;  // Forecast Horizon (1-20)

//--- indicator buffers
double         BuyProbabilityBuffer[];
double         SellProbabilityBuffer[];

//--- global variables
int            g_pattern_length;
int            g_lookback;
int            g_forecast_horizon;
string         g_patterns[];
int            g_pattern_count;
int            g_pattern_occurrences[];
int            g_pattern_successes[];
int            g_total_bars;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- validate inputs
   if(InpPatternLength < 3 || InpPatternLength > 10)
   {
      Print("Invalid Pattern Length. Must be between 3 and 10.");
      return INIT_PARAMETERS_INCORRECT;
   }
   
   if(InpLookback < 100 || InpLookback > 5000)
   {
      Print("Invalid Lookback Period. Must be between 100 and 5000.");
      return INIT_PARAMETERS_INCORRECT;
   }

   if(InpForecastHorizon < 1 || InpForecastHorizon > 20)
   {
      Print("Invalid Forecast Horizon. Must be between 1 and 20.");
      return INIT_PARAMETERS_INCORRECT;
   }

   //--- indicator buffers mapping
   SetIndexBuffer(0, BuyProbabilityBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, SellProbabilityBuffer, INDICATOR_DATA);
   
   //--- set accuracy
   IndicatorSetInteger(INDICATOR_DIGITS, 2);
   
   //--- set global variables
   g_pattern_length = InpPatternLength;
   g_lookback = InpLookback;
   g_forecast_horizon = InpForecastHorizon;
   
   //--- generate all possible patterns
   if(!GeneratePatterns())
   {
      Print("Failed to generate patterns.");
      return INIT_FAILED;
   }
   
   g_total_bars = iBars(_Symbol, PERIOD_CURRENT);
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   //--- check for rates total
   if(rates_total <= g_lookback + g_pattern_length + g_forecast_horizon)
   {
      Print("Not enough data for calculation.");
      return 0;
   }

   int start = (prev_calculated > g_lookback + g_pattern_length + g_forecast_horizon) ? 
               prev_calculated - 1 : g_lookback + g_pattern_length + g_forecast_horizon;
   
   if(ArraySize(g_pattern_occurrences) != g_pattern_count)
   {
      ArrayResize(g_pattern_occurrences, g_pattern_count);
      ArrayResize(g_pattern_successes, g_pattern_count);
   }
   
   ArrayInitialize(g_pattern_occurrences, 0);
   ArrayInitialize(g_pattern_successes, 0);
   
   // Pre-calculate patterns for efficiency
   string patterns[];
   ArrayResize(patterns, rates_total);
   for(int i = g_pattern_length; i < rates_total; i++)
   {
      patterns[i] = "";
      for(int j = 0; j < g_pattern_length; j++)
      {
         patterns[i] += (close[i-j] > close[i-j-1]) ? "U" : "D";
      }
   }
   
   // Main calculation loop
   for(int i = start; i < rates_total; i++)
   {
      string current_pattern = patterns[i];
      
      if(StringLen(current_pattern) != g_pattern_length) continue;
      
      double buy_probability = CalculateProbability(current_pattern, true, close, patterns, i);
      double sell_probability = CalculateProbability(current_pattern, false, close, patterns, i);
      
      BuyProbabilityBuffer[i] = buy_probability;
      SellProbabilityBuffer[i] = sell_probability;
   }
   
   // Update Comment with pattern statistics if total bars changed
   if(g_total_bars != iBars(_Symbol, PERIOD_CURRENT))
   {
      g_total_bars = iBars(_Symbol, PERIOD_CURRENT);
      UpdatePatternStatistics();
   }
   
   return(rates_total);
}

//+------------------------------------------------------------------+
//| Generate all possible patterns                                   |
//+------------------------------------------------------------------+
bool GeneratePatterns()
{
   g_pattern_count = (int)MathPow(2, g_pattern_length);
   if(!ArrayResize(g_patterns, g_pattern_count))
   {
      Print("Failed to resize g_patterns array.");
      return false;
   }
   
   for(int i = 0; i < g_pattern_count; i++)
   {
      string pattern = "";
      for(int j = 0; j < g_pattern_length; j++)
      {
         pattern += ((i >> j) & 1) ? "U" : "D";
      }
      g_patterns[i] = pattern;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| Calculate probability for a given pattern                        |
//+------------------------------------------------------------------+
double CalculateProbability(const string &pattern, bool is_buy, const double &close[], const string &patterns[], int current_index)
{
   if(StringLen(pattern) != g_pattern_length || current_index < g_lookback)
   {
      return 50.0; // Return neutral probability on error
   }

   int pattern_index = ArraySearch(g_patterns, pattern);
   if(pattern_index == -1)
   {
      return 50.0;
   }

   int total_occurrences = 0;
   int successful_predictions = 0;
   
   for(int i = g_lookback; i > g_pattern_length + g_forecast_horizon; i--)
   {
      int historical_index = current_index - i;
      if(historical_index < 0 || historical_index + g_pattern_length + g_forecast_horizon >= ArraySize(close))
      {
         continue;
      }

      if(patterns[historical_index] == pattern)
      {
         total_occurrences++;
         g_pattern_occurrences[pattern_index]++;
         if(is_buy && close[historical_index + g_pattern_length + g_forecast_horizon] > close[historical_index + g_pattern_length])
         {
            successful_predictions++;
            g_pattern_successes[pattern_index]++;
         }
         else if(!is_buy && close[historical_index + g_pattern_length + g_forecast_horizon] < close[historical_index + g_pattern_length])
         {
            successful_predictions++;
            g_pattern_successes[pattern_index]++;
         }
      }
   }
   
   return (total_occurrences > 0) ? (double)successful_predictions / total_occurrences * 100 : 50;
}

//+------------------------------------------------------------------+
//| Update pattern statistics and display in Comment                 |
//+------------------------------------------------------------------+
void UpdatePatternStatistics()
{
   string comment = "Pattern Statistics:\n";
   comment += "Pattern Length: " + IntegerToString(g_pattern_length) + "\n";
   comment += "Lookback Period: " + IntegerToString(g_lookback) + "\n";
   comment += "Forecast Horizon: " + IntegerToString(g_forecast_horizon) + "\n\n";
   comment += "Top 5 Patterns:\n";
   
   int sorted_indices[];
   ArrayResize(sorted_indices, g_pattern_count);
   for(int i = 0; i < g_pattern_count; i++) sorted_indices[i] = i;
   
   // Use quick sort for better performance
   ArraySort(sorted_indices);
   
   for(int i = 0; i < 5 && i < g_pattern_count; i++)
   {
      int idx = sorted_indices[g_pattern_count - 1 - i];  // Reverse order for descending sort
      double win_rate = g_pattern_occurrences[idx] > 0 ? 
                        (double)g_pattern_successes[idx] / g_pattern_occurrences[idx] * 100 : 0;
      
      comment += g_patterns[idx] + ": " +
                 "Occurrences: " + IntegerToString(g_pattern_occurrences[idx]) + ", " +
                 "Win Rate: " + DoubleToString(win_rate, 2) + "%\n";
   }
   
   Comment(comment);
}

//+------------------------------------------------------------------+
//| Custom function to search for a string in an array               |
//+------------------------------------------------------------------+
int ArraySearch(const string &arr[], string value)
{
   for(int i = 0; i < ArraySize(arr); i++)
   {
      if(arr[i] == value) return i;
   }
   return -1;
}

차트에 나타나는 모습은 다음과 같습니다:


패턴 감지 및 트레이딩을 위한 EA 만들기

다음으로 저는 Python에서 테스트가 성공적이었기 때문에 MetaTrader 5 테스터에서 개발 사항을 확인했습니다. 아래 코드도 문서에 첨부되어 있습니다. 이 코드는 외환 시장에서 패턴 분석의 개념을 실제로 구현한 것입니다. 이는 과거 가격 패턴이 미래의 시장 움직임에 대해 통계적으로 유의미한 정보를 제공할 수 있다는 아이디어를 구현한 것입니다.

EA 핵심 구성 요소:

  • 패턴 생성: EA는 가격 움직임(상승 또는 하락)의 이진 표현을 사용하여 주어진 패턴 길이에 대해 가능한 모든 조합을 생성합니다.
  • 통계 분석: EA는 회고적 분석을 수행하여 각 패턴의 발생 빈도와 예측 효율성을 평가합니다.
  • 동적 적응: EA는 변화하는 시장 상황에 적응하기 위해 패턴 통계를 지속적으로 업데이트합니다.
  • 거래 결정: 확인된 가장 효과적인 매매 패턴을 기반으로 EA는 포지션을 개시, 청산 또는 보유합니다.
  • 매개변수화: EA에서 패턴의 길이, 분석 기간, 예측 기간, 고려할 최소 패턴 발생 횟수 등의 주요 매개변수를 사용자가 지정할 수 있습니다.

우리는 총 4가지 버전의 EA를 만들었습니다: 첫 번째는 이 글의 개념을 기반으로 한 것으로 패턴에 따라 거래를 개시하고 반대 방향의 새로운 더 나은 패턴이 감지되면 청산합니다. 두 번째는 동일하지만 다중 통화입니다: 세계은행 통계에 따라 가장 유동성이 높은 10개의 외환 쌍으로 작동합니다. 세 번째는 동일하지만 가격이 예측 호가보다 많은 수의 바를 통과하면 거래를 청산합니다. 마지막은 익절 및 손절로 청산하는 것입니다.

다음은 첫 번째 EA의 코드입니다. 나머지는 첨부 파일에 있습니다:

//+------------------------------------------------------------------+
//|                                  PatternProbabilityExpertAdvisor |
//|                                Copyright 2024, Evgeniy Koshtenko |
//|                          https://www.mql5.com/en/users/koshtenko |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Evgeniy Koshtenko"
#property link      "https://www.mql5.com/en/users/koshtenko"
#property version   "1.00"

#include <Trade\Trade.mqh>            // Include the CTrade trading class

//--- input parameters
input int      InpPatternLength = 5;    // Pattern Length (3-10)
input int      InpLookback     = 1000;  // Lookback Period (100-5000)
input int      InpForecastHorizon = 6;  // Forecast Horizon (1-20)
input double   InpLotSize = 0.1;        // Lot Size
input int      InpMinOccurrences = 30;  // Minimum Pattern Occurrences

//--- global variables
int            g_pattern_length;
int            g_lookback;
int            g_forecast_horizon;
string         g_patterns[];
int            g_pattern_count;
int            g_pattern_occurrences[];
int            g_pattern_successes[];
int            g_total_bars;
string         g_best_buy_pattern = "";
string         g_best_sell_pattern = "";

CTrade trade;                         // Use the CTrade trading class
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- validate inputs
   if(InpPatternLength < 3 || InpPatternLength > 10)
   {
      Print("Invalid Pattern Length. Must be between 3 and 10.");
      return INIT_PARAMETERS_INCORRECT;
   }
   
   if(InpLookback < 100 || InpLookback > 5000)
   {
      Print("Invalid Lookback Period. Must be between 100 and 5000.");
      return INIT_PARAMETERS_INCORRECT;
   }

   if(InpForecastHorizon < 1 || InpForecastHorizon > 20)
   {
      Print("Invalid Forecast Horizon. Must be between 1 and 20.");
      return INIT_PARAMETERS_INCORRECT;
   }

   //--- set global variables
   g_pattern_length = InpPatternLength;
   g_lookback = InpLookback;
   g_forecast_horizon = InpForecastHorizon;
   
   //--- generate all possible patterns
   if(!GeneratePatterns())
   {
      Print("Failed to generate patterns.");
      return INIT_FAILED;
   }
   
   g_total_bars = iBars(_Symbol, PERIOD_CURRENT);
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   if(!IsNewBar()) return;
   
   UpdatePatternStatistics();
   
   string current_pattern = GetCurrentPattern();
   
   if(current_pattern == g_best_buy_pattern)
   {
      if(PositionSelect(_Symbol) && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
      {
         trade.PositionClose(_Symbol);
      }
      if(!PositionSelect(_Symbol))
      {
         trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "Buy Pattern: " + current_pattern);
      }
   }
   else if(current_pattern == g_best_sell_pattern)
   {
      if(PositionSelect(_Symbol) && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
      {
         trade.PositionClose(_Symbol);
      }
      if(!PositionSelect(_Symbol))
      {
         trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "Sell Pattern: " + current_pattern);
      }
   }
}

//+------------------------------------------------------------------+
//| Generate all possible patterns                                   |
//+------------------------------------------------------------------+
bool GeneratePatterns()
{
   g_pattern_count = (int)MathPow(2, g_pattern_length);
   if(!ArrayResize(g_patterns, g_pattern_count))
   {
      Print("Failed to resize g_patterns array.");
      return false;
   }
   
   for(int i = 0; i < g_pattern_count; i++)
   {
      string pattern = "";
      for(int j = 0; j < g_pattern_length; j++)
      {
         pattern += ((i >> j) & 1) ? "U" : "D";
      }
      g_patterns[i] = pattern;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| Update pattern statistics and find best patterns                 |
//+------------------------------------------------------------------+
void UpdatePatternStatistics()
{
   if(ArraySize(g_pattern_occurrences) != g_pattern_count)
   {
      ArrayResize(g_pattern_occurrences, g_pattern_count);
      ArrayResize(g_pattern_successes, g_pattern_count);
   }
   
   ArrayInitialize(g_pattern_occurrences, 0);
   ArrayInitialize(g_pattern_successes, 0);
   
   int total_bars = iBars(_Symbol, PERIOD_CURRENT);
   int start = total_bars - g_lookback;
   if(start < g_pattern_length + g_forecast_horizon) start = g_pattern_length + g_forecast_horizon;
   
   double close[];
   ArraySetAsSeries(close, true);
   CopyClose(_Symbol, PERIOD_CURRENT, 0, total_bars, close);
   
   string patterns[];
   ArrayResize(patterns, total_bars);
   ArraySetAsSeries(patterns, true);
   
   for(int i = 0; i < total_bars - g_pattern_length; i++)
   {
      patterns[i] = "";
      for(int j = 0; j < g_pattern_length; j++)
      {
         patterns[i] += (close[i+j] > close[i+j+1]) ? "U" : "D";
      }
   }
   
   for(int i = start; i >= g_pattern_length + g_forecast_horizon; i--)
   {
      string current_pattern = patterns[i];
      int pattern_index = ArraySearch(g_patterns, current_pattern);
      
      if(pattern_index != -1)
      {
         g_pattern_occurrences[pattern_index]++;
         if(close[i-g_forecast_horizon] > close[i])
         {
            g_pattern_successes[pattern_index]++;
         }
      }
   }
   
   double best_buy_win_rate = 0;
   double best_sell_win_rate = 0;
   
   for(int i = 0; i < g_pattern_count; i++)
   {
      if(g_pattern_occurrences[i] >= InpMinOccurrences)
      {
         double win_rate = (double)g_pattern_successes[i] / g_pattern_occurrences[i];
         if(win_rate > best_buy_win_rate)
         {
            best_buy_win_rate = win_rate;
            g_best_buy_pattern = g_patterns[i];
         }
         if((1 - win_rate) > best_sell_win_rate)
         {
            best_sell_win_rate = 1 - win_rate;
            g_best_sell_pattern = g_patterns[i];
         }
      }
   }
   
   Print("Best Buy Pattern: ", g_best_buy_pattern, " (Win Rate: ", DoubleToString(best_buy_win_rate * 100, 2), "%)");
   Print("Best Sell Pattern: ", g_best_sell_pattern, " (Win Rate: ", DoubleToString(best_sell_win_rate * 100, 2), "%)");
}

//+------------------------------------------------------------------+
//| Get current price pattern                                        |
//+------------------------------------------------------------------+
string GetCurrentPattern()
{
   double close[];
   ArraySetAsSeries(close, true);
   CopyClose(_Symbol, PERIOD_CURRENT, 0, g_pattern_length + 1, close);
   
   string pattern = "";
   for(int i = 0; i < g_pattern_length; i++)
   {
      pattern += (close[i] > close[i+1]) ? "U" : "D";
   }
   
   return pattern;
}

//+------------------------------------------------------------------+
//| Custom function to search for a string in an array               |
//+------------------------------------------------------------------+
int ArraySearch(const string &arr[], string value)
{
   for(int i = 0; i < ArraySize(arr); i++)
   {
      if(arr[i] == value) return i;
   }
   return -1;
}

//+------------------------------------------------------------------+
//| Check if it's a new bar                                          |
//+------------------------------------------------------------------+
bool IsNewBar()
{
   static datetime last_time = 0;
   datetime current_time = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(current_time != last_time)
   {
      last_time = current_time;
      return true;
   }
   return false;
}

테스트 결과는 EURUSD에서 다음과 같습니다:

자세히 하면:

나쁘지 않고 그래픽도 아름답습니다. 다른 EA 버전은 0에서 멈추거나 긴 손해 상태가 됩니다. 가장 좋은 옵션도 제 기준에는 맞지 않습니다. 저는 수익률이 2 이상이고 샤프 비율이 1 이상인 EA를 선호합니다. Python 테스터에서는 스프레드와 스왑 뿐만 아니라 거래 수수료도 모두 고려해야 한다는 생각이 들었습니다.


잠재적인 개선 사항: 차트 주기 확장 및 지표 추가하기

계속해 보겠습니다. 이 시스템은 확실히 긍정적인 결과를 보여주지만 어떻게 개선될 수 있으며 현실적일수 있을까요?

이제 4시간이라는 차트 주기를 살펴보겠습니다. 좀 더 자세히 살펴보겠습니다. 우리는 일별, 주별, 월별 차트를 추가해야 합니다. 이러한 접근 방식을 통해 더 많은 글로벌적인 트렌드와 더 큰 규모의 패턴을 파악할 수 있습니다. 이 모든 시간을 포함하도록 코드를 확장해 보겠습니다:

timeframes = [mt5.TIMEFRAME_H4, mt5.TIMEFRAME_D1, mt5.TIMEFRAME_W1, mt5.TIMEFRAME_MN1]
for tf in timeframes:
    ohlc_data = get_ohlc_data(symbol, tf, start_date, end_date)
    patterns = find_patterns(ohlc_data)

데이터가 많을수록 노이즈도 많아집니다. 보다 명확한 데이터를 얻으려면 이러한 노이즈를 분류하는 방법을 알아야 합니다.

분석된 기능의 범위를 확장해 보겠습니다. 트레이딩 세계에서 이것은 기술적 지표를 추가하는 것입니다. RSI, MACD, 볼린저 밴드는 이때 가장 자주 사용되는 도구입니다.

def add_indicators(data):
    data['RSI'] = ta.RSI(data['close'])
    data['MACD'] = ta.MACD(data['close']).macd()
    data['BB_upper'], data['BB_middle'], data['BB_lower'] = ta.BBANDS(data['close'])
    return data

ohlc_data = add_indicators(ohlc_data)

지표는 우리의 패턴 신호를 확인하는 데 도움이 될 수 있습니다. 또는 우리는 지표에서 패턴을 추가로 검색할 수도 있습니다.


결론

이상 우리는 패턴을 찾고 분석하는 작업을 마쳤습니다. 우리는 시장의 혼란 속에서 패턴을 찾는 시스템을 만들었습니다. 결과를 시각화하고, 백테스트를 수행하고, 향후 개선 사항을 계획하는 방법을 살펴봤습니다. 하지만 가장 중요한 것은 우리가 분석적인 트레이더처럼 사고하는 법을 배웠다는 점입니다. 우리는 단순히 대중을 따라가는 것이 아니라 우리만의 길, 우리만의 패턴, 우리만의 가능성을 찾습니다.

시장은 살아있는 사람들의 행동의 산물이라는 점을 명심하세요. 시장은 성장하고 변화합니다. 그리고 우리의 임무는 그것과 함께 변화하는 것입니다. 오늘의 패턴이 내일은 작동하지 않을 수도 있지만 그렇다고 해서 절망할 필요는 없습니다. 이것은 배우고, 적응하고, 성장할 수 있는 기회입니다. 이 시스템을 시작점으로 사용하세요. 실험하고, 개선하고, 나만의 것을 만들어 보세요. 언젠가는 성공적인 트레이딩의 문을 여는 바로 그 패턴을 발견하게 될지도 모릅니다!

여러분의 이 흥미진진한 여정에 행운을 빕니다! 여러분의 패턴이 항상 수익성 있게 하고 손실은 성공으로 가는 과정의 교훈으로 삼으세요. 외환의 세계에서 곧 뵙겠습니다!

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/15965

파일 첨부됨 |
PredictPattern.py (9.23 KB)
AutoPattern.mq5 (18.98 KB)
PatternEA.mq5 (16.12 KB)
PatternEAMult.mq5 (9.59 KB)
최근 코멘트 | 토론으로 가기 (1)
linfo2
linfo2 | 9 5월 2025 에서 02:09
감사합니다 예브니 , 파이썬으로 아이디어를 평가하는 훌륭한 템플릿입니다.
새로운 기능: MQL5의 커스텀 인디케이터 새로운 기능: MQL5의 커스텀 인디케이터
MetaTrader5와 MQL5의 새로운 기능 전체를 나열하지는 않겠습니다. 종류도 많은 데다가, 별도의 설명이 필요한 기능들도 있거든요. 객체 지향 프로그래밍을 이용한 코드 작성법 또한 다음에 알아보도록 하겠습니다. 다른 기능들과 함께 설명하기에는 조금 어려운 이야기일 수 있으니까요. 이 글에서는 인디케이터와 인디케이터의 구조, 드로잉 타입과 프로그래밍 디테일을 MQL4와 비교해 볼게요. 초보자 분들께 많은 도움이 되면 좋겠고 기존에 사용하시던 개발자 분들도 뭔가 새로운 걸 얻어 가실 수 있길 바랍니다.
MetaTrader 5를 이용한 Python 고빈도 차익 거래 시스템 MetaTrader 5를 이용한 Python 고빈도 차익 거래 시스템
이 글에서 우리는 브로커의 시각에서 합법적인 차익거래 시스템을 만들고 외환 시장에서 수천 개의 합성 가격을 생성하고 이를 분석하고 성공적으로 수익을 창출하는 거래를 만들어 보겠습니다.
새 MetaTrader 와 MQL5를 소개해드립니다 새 MetaTrader 와 MQL5를 소개해드립니다
본 문서는 MetaTrader5의 간략 리뷰입니다. 짧은 시간 내에 시스템의 모든 세부 사항을 안내해드리기는 어렵습니다 - 테스트는 2009.09.09에 시작되었습니다. 이는 상징적인 일자로, 전 이것이 행운의 숫자가 될거라 믿어 의심치않습니다. 제가 새 MetaTrader 5 터미널과 MQL5 베타버전을 받은지 며칠이 지났습니다. 아직 모든 기능을 사용해본 것은 아니지만, 벌써부터 감명깊네요.
MQL5의 테이블 모델에 기반한 테이블 및 헤더 클래스: MVC 개념 적용하기 MQL5의 테이블 모델에 기반한 테이블 및 헤더 클래스: MVC 개념 적용하기
이 글의 두 번째 파트에서는 MVC(모델-뷰-컨트롤러) 아키텍처 패러다임을 사용하여 MQL5에서 테이블 모델을 구현하는 방법에 대해 알아봅니다. 이 문서에서는 이전에 만든 테이블 모델을 기반으로 테이블 클래스와 테이블 헤더를 개발하는 방법에 대해 알아봅니다. 개발된 클래스는 다음 글에서 설명할 뷰 및 컨트롤러 컴포넌트를 추가 구현하는 기반이 될 것입니다.