루틴 없는 알고리즘 트레이딩: MetaTrader 5에서 SQLite를 사용하여 트레이딩 분석을 빠르게 하는 방법
알고리즘 트레이딩에서 피드백 문제
알고리즘 트레이딩을 위해서는 경쟁력 있는 전략을 개발하는 것뿐만 아니라 결과에 대한 지속적인 모니터링도 필요로 합니다. 결과를 통한 개선이 없으면 트레이딩 시스템은 통제 불능의 상태가 될 수 있습니다. 전략이 실시간으로 어떻게 작동하는지 직접 확인하기 전까지 여러분은 시스템을 관리하는 것이 아니라 그저 추측에 의존하는 것일 뿐입니다.
현대 주식 시장과 외환 시장에서 거래 빈도는 기하급수적으로 증가했습니다. 데이터는 여러분이 결론을 도출하기 전에 훨씬 빠르게 뒤로 밀려납니다. 데이터가 현재를 반영하지 못하는 데이터가 되어버리면 그 데이터를 바탕으로 내린 결론 또한 그렇게 됩니다. 게다가 반복적이고 지루한 수동 작업은 필연적으로 지연과 오류를 초래합니다. 이러한 현상은 여러분이 피곤하거나 집중력이 떨어지거나 서두를 때 나타납니다.
바로 이 점에서 MetaTrader 5가 특히 유용해집니다. SQLite(로컬 데이터베이스)에 대한 지원은 획기적인 변화입니다. 데이터베이스는 더 이상 단순한 외부 저장소가 아니라 거래 과정의 필수적인 부분입니다. 쿼리는 거의 즉시 실행되고 통계는 자동으로 업데이트되며 거래 분석은 몇 시간이 아닌 몇 초 만에 완료됩니다. 분석 결과는 거래 과정에서 즉 거래 터미널 외부가 아닌 내부에서 바로 확인할 수 있습니다.
이 글에서는 알고리즘 트레이더가 가지고 있어야 할 "최소한의 필수 도구 세트"에 대해 살펴봅니다: 여기에는 거래 일지, 데이터베이스의 구조, 안전하고 빠른 데이터 기록, 분석적인 SQL 쿼리 그리고 MetaTrader 5의 대화형 대시보드에 주요 통계를 표시하는 방법이 포함됩니다. 이를 통해 지루하고 반복적인 작업을 없애고 트레이더로 하여금 창의적인 측면에 집중할 수 있도록 합니다: 즉 거래 시스템을 분석하고 개선하는 것 말입니다.
알고리즘 트레이더에게 로컬 데이터베이스가 필요한 이유는 무엇일까요?
거래 전략을 개발하고 테스트하는 과정에서 엄청난 양의 데이터가 생성됩니다. 최적화 결과, 거래 내역, 지표 신호, 거시 경제 이벤트 등 - 이 모든 것은 저장만 한다고 해서 끝날 일이 아닙니다. 이들에 대한 구조화 및 신속한 접근도 필요 합니다. 로컬 데이터베이스는 진중한 알고리즘 트레이더가 직면하는 이러한 문제들을 해결해 줍니다.
테스트 및 최적화 결과 저장매개변수를 열거형으로 만들어 전략을 최적화하면 수백, 아니 수천 가지 매개변수의 변형을 만들 수 있습니다. 각각의 패스는 일련의 측정 지표, 매개변수의 값 및 결과로 구성됩니다. 어떻게 하면 이들을 서로 비교할 수 있을까요? 시간 경과에 따른 전략의 변화 과정을 어떻게 추적할 수 있을까요? 데이터베이스를 사용하면 최적화의 전체 이력을 저장하고 간단한 표부터 복잡한 상관관계에 이르기까지 이 이력의 모든 맥락에서 분석할 수 있습니다.
자동 거래 일지스프레드시트에 매번 거래 내역을 기록하는 대신 터미널에서 EA를 실행하여 각 거래에 대한 모든 속성(심볼, 매직 넘버, 볼륨, 가격, 시간, 결과, 설명)을 자동으로 기록하세요. 데이터는 실시간으로 데이터베이스에 입력되고 트레이더는 별도의 작업을 할 필요 없이 즉시 분석할 수 있습니다.
거래 신호 분석거래 신호를 저장하고 분석하면 일반 로그를 사용할 때는 얻을 수 없었던 새로운 가능성을 포착할 수 있습니다. 신호의 효과는 어느 정도일까요? 어떤 지표들이 서로 상관관계가 있을까요? 진입과 청산은 어떠한 조건에서 가장 좋을까요? 모든 트레이더는 이러한 질문에 대한 답을 알고 싶어합니다. SQL 쿼리는 추가적인 코드를 작성할 필요 없이 강력한 분석 도구를 제공합니다.
SQLite를 시작하려면 다음 세 가지 함수만 있으면 됩니다.
- DatabaseOpen — 데이터베이스를 엽니다.
- DatabaseExecute — 데이터베이스에 대한 쿼리를 실행합니다.
- DatabaseClose — 데이터베이스를 종료합니다.
이것만으로도 알고리즘 트레이더가 직면하는 문제의 90%를 해결하기에 충분합니다.
데이터베이스를 시작하고 종료하는 예시:
int db = DatabaseOpen("trading.sqlite", DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE); if(db == INVALID_HANDLE) { Print("Error opening Database: ", GetLastError()); return; } // ... working with the database ... DatabaseClose(db); // close the database
가장 중요한 것은: SQLite는 MetaTrader 5 시스템에 기본적으로 내장되어 있으며 터미널 코어와 직접 연동됩니다. Excel 및 기타 스프레드시트 프로그램은 MQL5 EA가 OLE 자동화와 같은 구식 방식을 통해 또는 외부 CSV 파일을 통해 액세스하는 외부 프로그램입니다. MetaTrader 5에서 SQLite가 가지는 이점은 속도, 편의성 및 안정성 측면에서 엄청납니다.
테이블 레이아웃: 거래 일지 아키텍처
데이터베이스 설계는 "무엇을 정확히 저장해야 하는가?" 라는 질문에서부터 시작됩니다. 거래 일지의 경우 답은 명확합니다: 거래 내역, 신호 및 이벤트입니다. 그런데 왜 하필 세 개의 표인 것일까요? 왜 하나의 "크고 보편적인" 표가 아닌 걸까요? 거래, 신호, 이벤트로 구분하는 것은 단순한 변덕이 아니라 필수적인 구분입니다. 각 엔티티는 별도로 저장되며 쿼리 시 필요할 때 다른 엔티티와 연결됩니다. 80% 이상의 신뢰도를 가진 신호 이후의 모든 거래 내역을 알고 싶으신가요? SQLite는 거래 터미널을 벗어나지 않고도 이 작업을 쉽고 깔끔하게 처리합니다.
DEALS 테이블 - 거래 내역이 표에는 각각의 거래에 대한 모든 정보가 포함되어 있습니다. ID, 티켓 수, 심볼, "매직 넘버"(전략 ID), 볼륨, 시가 및 종가, 시간, 재무적인 결과 및 선택적 코멘트. 이것은 거래 일지의 "골격"입니다 - 모든 분석의 기초가 됩니다.
SIGNAL 테이블 - 신호 이력해당 표에는 지표 및 거래 시스템에서 나오는 신호가 저장됩니다: 신호에는 종목 코드, 차트 주기, 신호 유형(매수/매도), 가격, 발생 시간 및 추가 매개변수가 포함됩니다. 또한 신호의 품질과 실제 거래와의 상관관계를 분석할 수 있습니다 - 이는 전략이 효과적인지 이해하는 데 핵심적인 요소입니다.
EBVENT 테이블 - 뉴스이 표에는 주요 경제 사건에 대한 정보(사건명, 통화, 중요도, 예측치, 실제값, 발표 시점)가 포함되어 있습니다. 이를 통해 거래 결과와 뉴스 사이의 연관성을 찾을 수 있습니다 - 결국 시장은 진공 상태에서 존재하는 것이 아닙니다.
아래 그림은 ER 다이어그램(엔티티-관계 다이어그램)을 보여줍니다 - ER 다이어그램은 데이터베이스 구조와 테이블 간의 관계를 시각적으로 나타낸 것입니다.
그림 1: 트레이드 저널 테이블의 ER 다이어그램
테이블 생성 코드:
// Deal history table - DEALS: string createDeals = "CREATE TABLE IF NOT EXISTS DEALS (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "deal_ticket INTEGER UNIQUE, " "order_ticket INTEGER, " "symbol TEXT NOT NULL, " "type INTEGER, " // 0=BUY, 1=SELL "direction INTEGER, " // 0=IN, 1=OUT, 2=IN/OUT "volume REAL, " "price_open REAL, " "price_close REAL, " "profit REAL, " "swap REAL, " "commission REAL, " "sl REAL, " "tp REAL, " "magic INTEGER, " "comment TEXT, " "time INTEGER, " // Unix timestamp "time_msc INTEGER, " "reason INTEGER" ");"; // Trading signals table - SIGNALS: string createSignals = "CREATE TABLE IF NOT EXISTS SIGNALS (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "symbol TEXT NOT NULL, " "signal_type TEXT, " // 'BUY', 'SELL', 'CLOSE' "price REAL, " "stop_loss REAL, " "take_profit REAL, " "strength REAL, " // 0.0 - 1.0 "source TEXT, " // Strategy name "notes TEXT, " "time INTEGER" ");"; // Events table - EVENTS: string createEvents = "CREATE TABLE IF NOT EXISTS EVENTS (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "event_type TEXT, " // 'NEWS', 'ERROR', 'NOTE', etc. "symbol TEXT, " "description TEXT, " "importance INTEGER, " // 1=Low, 2=Medium, 3=High "time INTEGER," "actual REAL, " "previous REAL, " "forecast REAL " ");"; // Indices to speed up search queries: string createIndexes[] = { "CREATE INDEX IF NOT EXISTS idx_deals_symbol ON DEALS(symbol);", "CREATE INDEX IF NOT EXISTS idx_deals_magic ON DEALS(magic);", "CREATE INDEX IF NOT EXISTS idx_deals_time ON DEALS(time);", "CREATE INDEX IF NOT EXISTS idx_signals_symbol ON SIGNALS(symbol);", "CREATE INDEX IF NOT EXISTS idx_signals_time ON SIGNALS(time);" }; // Create tables in the database: if(!DatabaseExecute(database, createDeals)) { Print("Error creating DEALS table: ", GetLastError()); return(false); } if(!DatabaseExecute(database, createSignals)) { Print("Error creating SIGNALS table: ", GetLastError()); return(false); } if(!DatabaseExecute(database, createEvents)) { Print("Error creating EVENTS table: ", GetLastError()); return(false); } // Create indices: for(int i = 0; i < ArraySize(createIndexes); i++) { DatabaseExecute(database, createIndexes[i]); }
인덱스에 주의하세요: idx_deals_symbol, idx_deals_magic, idx_deals_time, idx_signals_symbol 및 idx_signals_time. 이러한 기능은 DEALS 테이블의 심볼, 매직, 시간 필드와 SIGNALS 테이블의 심볼, 시간 필드에 대한 쿼리 속도를 향상시킵니다. 인덱스는 성능이라는 측면에서 모든 분석 쿼리의 효과를 발휘하게 하는 투자와 같은 것입니다.
데이터 삽입 및 트랜잭션: 속도가 중요합니다
MQL5는 테이블에 데이터를 삽입하는 두 가지 방법을 제공합니다. SQL 쿼리를 직접 실행하는 방법과 준비된 문을 사용하는 방법입니다. 어떤 것을 선택해야 할까요? 답은 명확합니다. 보안 및 성능 측면 모두에서 준비된 문이 더 바람직합니다.
DatabaseExecute를 통한 직접 삽입:
string sql = StringFormat( "INSERT INTO DEALS (ticket, symbol, magic, volume, price, time, profit) " "VALUES (%d, '%s', %d, %.2f, %.5f, %I64d, %.2f)", ticket, symbol, magic, volume, price, TimeCurrent(), profit); DatabaseExecute(db, sql);
DatabasePrepare를 통한 매개변수화된 쿼리:
// Create a parameterized query: int request = DatabasePrepare(db, "INSERT INTO DEALS (ticket, symbol, magic, volume, price, time, profit) " "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"); // Set the value of the first query parameter - in the DatabaseBind function, the indexing of fields in the entry starts from zero: DatabaseBind(request, 0, ticket); DatabaseTransactionBegin(db); // Set the values of the remaining parameters before adding the entry: DatabaseBind(request, 1, symbol); DatabaseBind(request, 2, magic); DatabaseBind(request, 3, volume); DatabaseBind(request, 4, price); DatabaseBind(request, 5, TimeCurrent()); DatabaseBind(request, 6, profit); // Execute a request for inserting the entry: DatabaseRead(request); DatabaseFinalize(request); DatabaseTransactionCommit(db);
트랜잭션을 사용하는 것은 선택 사항이 아니라 필수 사항입니다. 대량의 삽입 및 업데이트 작업의 경우, 트랜잭션을 사용하면 성능이 수백 배 또는 수천 배까지 향상됩니다. 왜 일까요? 트랜잭션이 없으면 각 작업은 디스크에 개별적으로 기록됩니다. 트랜잭션은 모든 작업이 그룹화되어 하나의 블록으로 기록됩니다.
우리의 테스트 결과는 매우 인상적입니다: MQL5에서 SQLite의 성능은 네이티브 C++ 코드(LLVM 9.0)와 유사합니다. 대부분의 테스트에서 차이는 5% 미만이며 일부 시나리오에서는 MQL5가 C++보다 우수한 성능을 보이기도 합니다. 아래 다이어그램은 트랜잭션이 없는 1,000개 레코드와 트랜잭션이 있는 25,000개 레코드의 삽입 시간을 비교한 것으로, 레코드당 3,500배 이상의 차이가 납니다!
그림 2: 삽입 속도 비교: 거래 유무에 따른 비교
대량 삽입을 위한 트랜잭션 사용 예시:
DatabaseTransactionBegin(db); for(int i = 0; i < ArraySize(deals); i++) { InsertDeal(db, deals[i]); // inserting the next trade - an element of the array of deals[] structures } DatabaseTransactionCommit(db);
모든 테스트는 MQL5 포럼 섹션에서 더 알아볼 수 있습니다. MQL5의 SQLite: 새로운 기능 및 성능 테스트.
SQL 쿼리: 프로그래밍 없이 분석하기
SQL의 주요 장점 중 하나는 단일 쿼리로 복잡한 분석을 할수 있다는 점입니다. MetaTrader 5에서는 이 기능을 바로 사용할 수 있습니다. 주기도 없고, 조건도 없고, 시간 변수도 없습니다. 하나의 쿼리로 충분합니다. 몇 가지 일반적인 시나리오를 살펴보겠습니다.
거래 심볼 통계어떤 투자 상품이 수익을 창출하고 어떤 상품이 조용히 예수금을 갉아먹을까요? 이 질문을 실행하는 SQL 쿼리는 모든 트레이더가 갖춰야 할 가장 기본적인 자료입니다. 쿼리는 각 심볼에 대한 요약 정보를 보여줍니다:거래 횟수, 총 수익, 평균 수익
string sql = "SELECT symbol, COUNT(*) as deals, " "SUM(profit) as total_profit, " "AVG(profit) as avg_profit " "FROM DEALS GROUP BY symbol " "ORDER BY total_profit DESC"; DatabasePrint(db, sql);
쿼리 요소 파싱:
| 쿼리 요소 | 설명 |
|---|---|
| SELECT symbol | 심볼명으로 열을 선택합니다 |
| COUNT(*) | 특정 심볼의 거래 횟수를 보여줍니다 |
| SUM(profit) | 모든 심볼의 거래 수익을 합산합니다 |
| GROUP BY symbol | 심볼별로 결과를 그룹화합니다 |
| ORDER BY total_profit DESC | 총이익 내림차순으로 정렬 |
전략 분석 (매직 넘버)
매직 넘버는 각 거래의 고유한 EA ID입니다. 하나의 계정에 여러 전략이 효과가 있는 경우 이 쿼리를 통해 어떤 전략이 더 효율적인지 확인할 수 있습니다. 특별한 기능은 조건부 CASE 표현식을 사용하여 수익성 있는 거래를 계산한다는 점입니다.
string sql = "SELECT magic, COUNT(*) as trades, " "SUM(CASE WHEN profit > 0 THEN 1 ELSE 0 END) as wins, " "SUM(profit) as net_profit " "FROM DEALS GROUP BY magic"; DatabasePrint(db, sql);
쿼리 요소 파싱:
| 쿼리 요소 | 설명 |
|---|---|
| SELECT magic | 매직 넘버가 있는 열을 선택 |
| COUNT(*) | 선택한 전략의 총 거래 횟수 |
| CASE WHEN profit > 0 | 조건: 이익이 양수인 경우 |
| THEN 1 ELSE 0 END | 수익성이 있는 거래에는 1을, 손실이 있는 거래에는 0을 반환 |
| SUM(wins) | 수익성 있는 거래(wins)의 횟수를 알기 위해 단위를 모두 합산 |
수익성이 있는 거래(wins) 횟수를 전체 거래 횟수로 나누면 수익성 있는 거래의 비율을 얻을 수 있습니다 - 이는 전략의 품질을 평가하는 핵심 지표입니다. 총 이익이 마이너스이면서 비율은 높았나요? 이는 위험 관리 문제의 징후입니다 - 손실 거래의 규모가 수익 거래의 규모보다 크다는 뜻입니다.
시간대별 거래 분포
이 전략은 언제 가장 효과적일까? 유럽장 중인가? 아니면 미국장인가? 아니면 "조용한 시간"이었나? strftime 함수는 거래 진입 시간에서 시(형식 00--23)를 추출하여 가장 수익성이 높은 시간 간격과 가장 수익성이 낮은 시간 간격을 파악할 수 있도록 합니다.
string sql = "SELECT strftime('%H', time, 'unixepoch') as hour, " "COUNT(*) as trades, SUM(profit) as profit " "FROM DEALS GROUP BY hour"; DatabasePrint(db, sql);
중요: MQL5 및 SQLite에서 시간은 Unix 타임스탬프(1970년 1월 1일 이후 경과된 초) 형식으로 저장됩니다. 따라서 'unixepoch' 수정자가 추가되었습니다. 마찬가지로 요일별 분포(strftime('%w', ...)), 월별 분포(strftime('%m', ...)) 또는 이들을 조합한 것을 분석할 수 있습니다.
쿼리 요소 파싱:
| 쿼리 요소 | 설명 |
|---|---|
| strftime('%H', ...) | 거래소 개장 시간(00-23)에서 시를 추출 |
| time | 거래 진입 시간을 나타내는 열 |
| GROUP BY hour | 진입 시별 그룹 거래 |
통계 대시보드: 터미널 내 시각화
통계도 좋지만 명료한 것이 더 중요합니다. 이 데이터베이스는 터미널 내의 그래픽 대시보드와 통합되어 데이터를 이해하기 쉬운 시각적 정보로 변환합니다. 대화형 통계 대시보드는 실시간으로 작동하여 트레이더에게 필요한 정보를 정확하게 보여줍니다.
아래 대시보드에는 지정된 기간 동안의 총 결과(SUMMARY), 종목별 결과(BY SYMBOL) 및 최적 거래 시간(BEST HOURS)이 표시됩니다. 구조체에 데이터를 읽어 들이려면 DatabaseReadBind 함수를 사용하면 됩니다:
그림 3. 터미널에서 볼 수 있는 대화형 통계 대시보드
struct DealStats { string symbol; int count; double total_profit; double avg_profit; }; int request = DatabasePrepare(db, "SELECT symbol, COUNT(*) as count, SUM(profit) as total_profit, " "AVG(profit) as avg_profit FROM DEALS GROUP BY symbol"); DealStats stats; while(DatabaseReadBind(request, stats)) { // Output to the table on the dashboard: AddRowToTable(stats); } DatabaseFinalize(request);
DatabaseReadBind 함수는 구조체 필드를 이름으로 쿼리 열과 자동으로 매칭합니다. SQL 쿼리 열 이름에 해당하는 필드를 가진 구조체를 선언하는 것으로 충분합니다.
대시보드 관리[Refresh] 버튼을 누르면 전체 통계가 다시 계산되어 업데이트됩니다. [Export] 버튼을 누르면 데이터베이스 테이블에서 계산된 데이터(전체 작업 결과, 거래, 이벤트 및 신호)가 CSV 파일로 출력됩니다. Export 예시:
1,XAGUSD,196,322.25,1.64,14,181
2,AUDUSD,196,-12.0,-0.06,82,99
3,EURUSD,58,-38.5,-0.66,23,35
4,GBPUSD,57,-43.2,-0.75,17,40
빠른 제어를 위한 단축키:
- 'A' 또는 'a' — 분석 업데이트,
- 'E' 또는 'e' — 데이터를 CSV 파일로 출력,
- 'R' 또는 'r' — 거래 내역 불러오기,
- 'P' 또는 'p' — 대시보드를 활성화/비활성화.
EA 설정:
- Database file name — 데이터베이스 파일의 이름 (기본값: "TradingJournal.db"),
- Auto-import trade history — 거래 내역 자동 불러오기(기본값: true),
- History depth in days — 기록 깊이(기본값: 30일),
- Export path — CSV 파일 경로(기본값: "MQL5/파일"),
- Export to CSV format — CSV 파일로 내보내기(기본값: true),
- Show statistics dashboard on chart — 대시보드 표시(기본값: true).
MetaEditor: 데이터베이스 도구
MetaTrader 5에 포함된 MetaEditor에는 SQLite 작업을 위한 내장 도구가 있습니다: 이 도구를 사용하면 테이블을 열고 편집하고 SQL 쿼리를 실행하고 변경 사항을 되돌릴 수 있습니다. SQLite 데이터베이스는 사용자의 컴퓨터에 단일 파일로 저장되므로 MetaEditor 탐색기를 통해 언제든지 액세스 가능합니다 - 테이블의 구조와 내용을 검사하고 디버그 쿼리를 실행할 수 있습니다.
이는 특히 데이터베이스를 사용하는 MQL5 애플리케이션을 디버깅할 때 매우 편리합니다. 실시간으로 어떤 데이터가 표시되는지 확인하고 EA의 로직을 조정해 보세요.
이 글에서 소개하는 TradingJournalSQLite-EA 테스트 EA가 생성한 샘플 데이터베이스를 사용하여 이 과정이 얼마나 쉽게 수행될 수 있는지 살펴보겠습니다. 데이터베이스와 그 안의 테이블들을 열고 몇 가지 SQL 쿼리를 실행한 다음 닫습니다. 이 모든 것을 말로 오래 설명할 수도 있지만 직접 보는 것이 훨씬 더 좋습니다...
1. 가장 간단한 것부터 시작하세요 - 데이터베이스를 열고 원하는 테이블을 선택하세요.
그림 4. 테이블 열기/선택/닫기
DEALS 테이블은 해당 테이블 이름을 마우스 왼쪽 버튼으로 두 번 클릭하여 선택할 수 있습니다. 이는 탐색기에서 SQL 쿼리를 실행하는 것과 같습니다.
SELECT * FROM 'DEALS';2. 시간 형식을 설정합니다:
그림 5. 시간 형식
3. 핵심 질문: 무엇이 얼마의 수익을 올리는 걸까요? 밀리초 단위로 답을 알아보세요:SELECT symbol, COUNT(*) as count, SUM(profit) as total_profit, AVG(profit) as avg_profit FROM DEALS GROUP BY symbol;
긴 라인을 싫어하는 분들을 위해:
SELECT symbol, COUNT(*) as count, SUM(profit) as total_profit, AVG(profit) as avg_profit FROM DEALS GROUP BY symbol;
그림 6. 무엇이 얼마를 벌어들이는 걸까요?
4. 수익이 높은 순서대로 정렬:SELECT symbol, COUNT(*) as count, SUM(profit) as total_profit, AVG(profit) as avg_profit FROM DEALS GROUP BY symbol ORDER BY total_profit DESC;

그림 7. 수익이 높은 순서대로 내림차순으로 정렬
SELECT SUM(CASE WHEN direction IN (1, 2, 3) THEN profit ELSE 0 END) as net_profit FROM DEALS;

그림 8. 토탈
복잡한 쿼리를 디버깅하고 싶으신가요? 문제없습니다:
SELECT COUNT(*) as total_deals, SUM(CASE WHEN direction IN (1, 2, 3) THEN 1 ELSE 0 END) as closed_trades, SUM(CASE WHEN (profit + swap + commission) > 0 AND direction IN (1, 2, 3) THEN 1 ELSE 0 END) as total_wins, SUM(CASE WHEN (profit + swap + commission) < 0 AND direction IN (1, 2, 3) THEN 1 ELSE 0 END) as total_losses, ROUND(100.0 * SUM(CASE WHEN (profit + swap + commission) > 0 AND direction IN (1, 2, 3) THEN 1 ELSE 0 END) / NULLIF(SUM(CASE WHEN direction IN (1, 2, 3) THEN 1 ELSE 0 END), 0), 2) as overall_win_rate, SUM(CASE WHEN direction IN (1, 2, 3) THEN profit ELSE 0 END) as net_profit, SUM(CASE WHEN direction IN (1, 2, 3) THEN swap ELSE 0 END) as total_swap, SUM(CASE WHEN direction IN (1, 2, 3) THEN commission ELSE 0 END) as total_commission, SUM(CASE WHEN direction IN (1, 2, 3) THEN profit + swap + commission ELSE 0 END) as net_result, ROUND(AVG(CASE WHEN direction IN (1, 2, 3) THEN profit + swap + commission ELSE NULL END), 2) as avg_profit_per_trade, SUM(CASE WHEN (profit + swap + commission) > 0 AND direction IN (1, 2, 3) THEN (profit + swap + commission) ELSE 0 END) as gross_profit, SUM(CASE WHEN (profit + swap + commission) < 0 AND direction IN (1, 2, 3) THEN ABS(profit + swap + commission) ELSE 0 END) as gross_loss, CASE WHEN SUM(CASE WHEN (profit + swap + commission) < 0 AND direction IN (1, 2, 3) THEN ABS(profit + swap + commission) ELSE 0 END) > 0 THEN ROUND(SUM(CASE WHEN (profit + swap + commission) > 0 AND direction IN (1, 2, 3) THEN (profit + swap + commission) ELSE 0 END) / SUM(CASE WHEN (profit + swap + commission) < 0 AND direction IN (1, 2, 3) THEN ABS(profit + swap + commission) ELSE 0 END), 2) ELSE 0 END as profit_factor, COUNT(DISTINCT symbol) as symbols_traded, COUNT(DISTINCT magic) as strategies_used FROM DEALS;
그림 9. 복잡한 SQL 쿼리
해당 SQL 쿼리는 첨부된 TradingJournalSQLite-EA 테스트 EA에서 변경 없이 가져온 것입니다.
결론: MetaTrader 5의 거래 및 분석 시스템
거래 일지는 단순히 내보내기를 하고, 다른 별도의 프로그램에서 열고, 수동으로 변환해야 하는 정적인 파일이 아닙니다. 거래 일지는 MetaTrader 5의 거래 및 분석 시스템의 일부입니다 - 정확하고 신뢰할 수 있으며 실시간으로 변화에 민감하게 반응합니다. 이는 기본적으로 제공되는 기능입니다 - SQLite는 MetaTrader 5 터미널의 핵심 기능에 통합되어 있습니다.
이를 통해 우리는 MetaTrader 5를 독립적인 거래 및 분석 시스템으로 활용하고 SQL 쿼리의 모든 기능을 사용할 수 있으며 새로운 아이디어에 맞춰 확장 및 수정할 수도 있습니다. 이는 트레이더에게 다음과 같은 이점을 제공합니다:
- 데이터는 자동으로 저장되며 바로 분석에 사용할 수 있습니다;
- 분석용 SQL 쿼리는 EA 코드를 복잡하게 만들지 않고도 빠르고 유연하게 변경될 수 있습니다;
- 분석 대시보드는 거래가 이루어지는 터미널의 차트에서 바로 이용할 수 있습니다;
- 이 시스템은 별도의 애플리케이션에 의존하지 않고도 새로운 아이디어, 측정 기준 및 분석 관점을 반영하도록 쉽게 확장될 수 있습니다;
그렇기 때문에 MetaTrader 5에 내장된 SQLite의 지원은 단순히 편리한 데이터 저장 솔루션을 넘어 단일 환경에서 단일 언어와 네이티브 SQL 지원을 사용하여 거래와 분석이 이루어지는 완벽한 거래 및 분석 인프라를 위한 견고한 기반이 됩니다.
MQL5에서 SQLite를 심층적으로 학습하기 위한 추천 자료:
- 데이터베이스 작업
- MQL5의 SQLite: 새로운 기능 및 성능 테스트
- SQLite: MQL5에서 SQL 데이터베이스를 네이티브로 처리하는 방법
- MQL5 쿡북 — 거시경제 이벤트 데이터베이스
- MQL5의 SQLite 기능: 종목별 및 매직 넘버별 거래 통계를 보여주는 대시보드의 예
이 글에 첨부된 파일 목록:
| 파일 이름 | 설명 |
|---|---|
| TradingJournalSQLite-EA.mq5 | 거래 통계 대시보드를 생성하는 테스트용 EA 코드가 포함된 파일 |
MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/22009
경고: 이 자료들에 대한 모든 권한은 MetaQuotes(MetaQuotes Ltd.)에 있습니다. 이 자료들의 전부 또는 일부에 대한 복제 및 재출력은 금지됩니다.
새로운 기능: MQL5의 커스텀 인디케이터
외환 거래에서 포트폴리오 최적화: VaR와 마코위츠 이론의 결합
새 MetaTrader 와 MQL5를 소개해드립니다
3차원 반전 패턴에 기반한 알고리즘 트레이딩