| import pandas as pd
|
| from bs4 import BeautifulSoup
|
| import re
|
| from datetime import datetime
|
|
|
| class MT5ReportParser:
|
| def __init__(self, html_file_path):
|
| """Initialize parser with HTML file path"""
|
| self.html_file_path = html_file_path
|
| self.soup = None
|
| self.deals_df = None
|
| self.statistics = {}
|
|
|
| def load_html(self):
|
| """Load and parse HTML file"""
|
| with open(self.html_file_path, 'r', encoding='utf-8') as f:
|
| content = f.read()
|
| self.soup = BeautifulSoup(content, 'html.parser')
|
|
|
| def extract_deals_table(self):
|
| """Extract the Deals table from HTML"""
|
|
|
| tables = self.soup.find_all('table')
|
|
|
| for table in tables:
|
| headers = [th.get_text(strip=True) for th in table.find_all('th')]
|
|
|
|
|
| if 'Deal' in headers or 'Time' in headers:
|
|
|
| cols = headers
|
|
|
|
|
| rows = []
|
| for tr in table.find_all('tr')[1:]:
|
| cells = tr.find_all('td')
|
| if cells:
|
| row = [td.get_text(strip=True) for td in cells]
|
| rows.append(row)
|
|
|
|
|
| if rows:
|
| self.deals_df = pd.DataFrame(rows, columns=cols)
|
| self._clean_deals_data()
|
| break
|
|
|
| def _clean_deals_data(self):
|
| """Clean and convert data types in deals DataFrame"""
|
| if self.deals_df is None:
|
| return
|
|
|
|
|
| numeric_cols = ['Volume', 'Price', 'Commission', 'Swap', 'Profit', 'Balance']
|
| for col in numeric_cols:
|
| if col in self.deals_df.columns:
|
| self.deals_df[col] = pd.to_numeric(
|
| self.deals_df[col].str.replace(r'[^\d.-]', '', regex=True),
|
| errors='coerce'
|
| )
|
|
|
|
|
| if 'Time' in self.deals_df.columns:
|
| try:
|
| self.deals_df['Time'] = pd.to_datetime(self.deals_df['Time'])
|
| except:
|
| pass
|
|
|
| def extract_statistics(self):
|
| """Extract all statistics from the report"""
|
|
|
| all_tables = self.soup.find_all('table')
|
|
|
| for table in all_tables:
|
| rows = table.find_all('tr')
|
| for row in rows:
|
| cells = row.find_all('td')
|
| if len(cells) == 2:
|
| key = cells[0].get_text(strip=True)
|
| value = cells[1].get_text(strip=True)
|
|
|
|
|
| key = key.replace(':', '').strip()
|
|
|
| if key and value:
|
|
|
| try:
|
| value = float(value.replace(' ', '').replace(',', ''))
|
| except:
|
| pass
|
|
|
| self.statistics[key] = value
|
|
|
|
|
| divs = self.soup.find_all('div')
|
| for div in divs:
|
| text = div.get_text(strip=True)
|
| if ':' in text:
|
| parts = text.split(':')
|
| if len(parts) == 2:
|
| key = parts[0].strip()
|
| value = parts[1].strip()
|
| if key and value and key not in self.statistics:
|
| try:
|
| value = float(value.replace(' ', '').replace(',', ''))
|
| except:
|
| pass
|
| self.statistics[key] = value
|
|
|
| def get_common_statistics(self):
|
| """Extract commonly used MT5 statistics"""
|
| common_stats = {
|
| 'Initial Deposit': None,
|
| 'Total Net Profit': None,
|
| 'Gross Profit': None,
|
| 'Gross Loss': None,
|
| 'Profit Factor': None,
|
| 'Expected Payoff': None,
|
| 'Absolute Drawdown': None,
|
| 'Maximal Drawdown': None,
|
| 'Relative Drawdown': None,
|
| 'Total Trades': None,
|
| 'Short Positions': None,
|
| 'Long Positions': None,
|
| 'Profit Trades': None,
|
| 'Loss Trades': None,
|
| 'Largest Profit': None,
|
| 'Largest Loss': None,
|
| 'Average Profit': None,
|
| 'Average Loss': None,
|
| 'Maximum Consecutive Wins': None,
|
| 'Maximum Consecutive Losses': None,
|
| 'Maximal Consecutive Profit': None,
|
| 'Maximal Consecutive Loss': None,
|
| 'Average Consecutive Wins': None,
|
| 'Average Consecutive Losses': None,
|
| }
|
|
|
|
|
| for key in self.statistics:
|
| for common_key in common_stats.keys():
|
| if common_key.lower() in key.lower():
|
| common_stats[common_key] = self.statistics[key]
|
|
|
| return common_stats
|
|
|
| def parse(self):
|
| """Main method to parse the entire report"""
|
| self.load_html()
|
| self.extract_deals_table()
|
| self.extract_statistics()
|
|
|
| def save_deals_to_csv(self, output_path='deals.csv'):
|
| """Save deals table to CSV"""
|
| if self.deals_df is not None:
|
| self.deals_df.to_csv(output_path, index=False)
|
| print(f"Deals saved to {output_path}")
|
| else:
|
| print("No deals table found")
|
|
|
| def save_statistics_to_csv(self, output_path='statistics.csv'):
|
| """Save statistics to CSV"""
|
| if self.statistics:
|
| stats_df = pd.DataFrame(list(self.statistics.items()),
|
| columns=['Statistic', 'Value'])
|
| stats_df.to_csv(output_path, index=False)
|
| print(f"Statistics saved to {output_path}")
|
| else:
|
| print("No statistics found")
|
|
|
| def print_summary(self):
|
| """Print a summary of the parsed data"""
|
| print("=" * 60)
|
| print("MT5 TRADE REPORT SUMMARY")
|
| print("=" * 60)
|
|
|
| if self.deals_df is not None:
|
| print(f"\nDeals Table: {len(self.deals_df)} rows")
|
| print(f"Columns: {', '.join(self.deals_df.columns.tolist())}")
|
| print("\nFirst 5 deals:")
|
| print(self.deals_df.head())
|
| else:
|
| print("\nNo deals table found")
|
|
|
| print(f"\n\nStatistics Found: {len(self.statistics)}")
|
| common_stats = self.get_common_statistics()
|
| print("\nCommon Statistics:")
|
| for key, value in common_stats.items():
|
| if value is not None:
|
| print(f" {key}: {value}")
|
|
|
| print("\n" + "=" * 60)
|
|
|
|
|
|
|
| if __name__ == "__main__":
|
|
|
| html_file = "mt5_report.html"
|
|
|
|
|
| parser = MT5ReportParser(html_file)
|
|
|
|
|
| parser.parse()
|
|
|
|
|
| parser.print_summary()
|
|
|
|
|
| parser.save_deals_to_csv("deals.csv")
|
| parser.save_statistics_to_csv("statistics.csv")
|
|
|
|
|
| deals_df = parser.deals_df
|
| statistics = parser.statistics
|
| common_stats = parser.get_common_statistics()
|
|
|
|
|
| if deals_df is not None and 'Profit' in deals_df.columns:
|
| total_profit = deals_df['Profit'].sum()
|
| print(f"\nTotal Profit from Deals: {total_profit}") |