| """
|
| Script 1 β Raw Microsecond Bid-Ask Unit Data Visualization
|
| Fetches XAUUSDc data from MetaTrader 5 for February 12, 2026 (full day),
|
| and produces a 4-panel figure:
|
| Top-left: Bid Y-distribution histogram (blue, 0.01-unit bins)
|
| Top-right: Bid line chart with dot markers (blue)
|
| Bottom-left: Ask Y-distribution histogram (red, 0.01-unit bins)
|
| Bottom-right:Ask line chart with dot markers (red)
|
|
|
| 0.01 unit = $0.01 XAU price change.
|
| The 'c' suffix in XAUUSDc is an Exness broker account-type indicator
|
| (standard cent live account), not related to XAU pricing.
|
| """
|
|
|
| import MetaTrader5 as mt5
|
| import pandas as pd
|
| import numpy as np
|
| import matplotlib
|
| matplotlib.use('Agg')
|
| import matplotlib.pyplot as plt
|
| import matplotlib.dates as mdates
|
| from datetime import datetime, timezone
|
|
|
|
|
|
|
|
|
| if not mt5.initialize():
|
| print(f"MT5 initialize() failed, error code = {mt5.last_error()}")
|
| quit()
|
|
|
|
|
|
|
|
|
| utc_from = datetime(2026, 2, 12, 0, 0, 0, tzinfo=timezone.utc)
|
| utc_to = datetime(2026, 2, 12, 23, 59, 59, tzinfo=timezone.utc)
|
|
|
| SYMBOL = "XAUUSDc"
|
| UNIT_SIZE = 0.01
|
|
|
|
|
|
|
|
|
| ticks = mt5.copy_ticks_range(SYMBOL, utc_from, utc_to, mt5.COPY_TICKS_ALL)
|
|
|
| if ticks is None or len(ticks) == 0:
|
| print(f"No data retrieved for {SYMBOL}. Error: {mt5.last_error()}")
|
| mt5.shutdown()
|
| quit()
|
|
|
| df = pd.DataFrame(ticks)
|
|
|
| df['datetime'] = pd.to_datetime(df['time_msc'], unit='ms', utc=True)
|
|
|
| print(f"Fetched {len(df):,} data points for {SYMBOL}")
|
| print(f"Time range: {df['datetime'].iloc[0]} β {df['datetime'].iloc[-1]}")
|
| print(f"Bid range : {df['bid'].min():.2f} β {df['bid'].max():.2f}")
|
| print(f"Ask range : {df['ask'].min():.2f} β {df['ask'].max():.2f}")
|
|
|
| mt5.shutdown()
|
|
|
|
|
|
|
|
|
| csv_path = "raw_ticks_XAUUSDc_20260212.csv"
|
| df[['datetime', 'bid', 'ask', 'last', 'volume', 'flags']].to_csv(csv_path, index=False)
|
| print(f"Saved CSV β {csv_path} ({len(df):,} rows)")
|
|
|
|
|
|
|
|
|
| overall_min = min(df['bid'].min(), df['ask'].min())
|
| overall_max = max(df['bid'].max(), df['ask'].max())
|
|
|
| bin_lo = np.floor(overall_min / UNIT_SIZE) * UNIT_SIZE - UNIT_SIZE
|
| bin_hi = np.ceil(overall_max / UNIT_SIZE) * UNIT_SIZE + UNIT_SIZE
|
| bins = np.arange(bin_lo, bin_hi + UNIT_SIZE, UNIT_SIZE)
|
| bins = np.round(bins, 2)
|
|
|
|
|
|
|
|
|
| bid_times = mdates.date2num(df['datetime'].values)
|
| ask_times = bid_times
|
|
|
| print("Plotting...")
|
|
|
|
|
|
|
|
|
| fig, axes = plt.subplots(
|
| 2, 2,
|
| figsize=(20, 12),
|
| gridspec_kw={'width_ratios': [1, 4]},
|
| sharey='row',
|
| )
|
| fig.suptitle(
|
| f'{SYMBOL} β Raw Microsecond Unit Data | {utc_from.strftime("%Y-%m-%d")}',
|
| fontsize=16, fontweight='bold',
|
| )
|
|
|
|
|
| BID_COLOR = '#0000FF'
|
| ASK_COLOR = '#FF0000'
|
|
|
|
|
| ax_hist_bid = axes[0, 0]
|
| ax_line_bid = axes[0, 1]
|
|
|
|
|
| ax_hist_bid.hist(
|
| df['bid'].values, bins=bins, orientation='horizontal',
|
| color=BID_COLOR, alpha=1.0, edgecolor='white', linewidth=0.3,
|
| )
|
| ax_hist_bid.set_xlabel('Count', fontsize=10)
|
| ax_hist_bid.set_ylabel('Bid Price', fontsize=10)
|
| ax_hist_bid.set_title('Bid Y-Distribution (0.01-unit bins)', fontsize=12)
|
|
|
|
|
|
|
| ax_line_bid.plot(
|
| bid_times, df['bid'].values,
|
| color=BID_COLOR, linewidth=0.5, alpha=1.0,
|
| rasterized=True,
|
| )
|
| ax_line_bid.xaxis_date()
|
| ax_line_bid.set_title('Bid Price (Time Series)', fontsize=12)
|
| ax_line_bid.set_xlabel('Time (UTC)', fontsize=10)
|
| ax_line_bid.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
|
| ax_line_bid.xaxis.set_major_locator(mdates.HourLocator(interval=2))
|
| plt.setp(ax_line_bid.xaxis.get_majorticklabels(), rotation=45, ha='right')
|
| ax_line_bid.grid(True, alpha=0.3)
|
|
|
|
|
| ax_hist_ask = axes[1, 0]
|
| ax_line_ask = axes[1, 1]
|
|
|
|
|
| ax_hist_ask.hist(
|
| df['ask'].values, bins=bins, orientation='horizontal',
|
| color=ASK_COLOR, alpha=1.0, edgecolor='white', linewidth=0.3,
|
| )
|
| ax_hist_ask.set_xlabel('Count', fontsize=10)
|
| ax_hist_ask.set_ylabel('Ask Price', fontsize=10)
|
| ax_hist_ask.set_title('Ask Y-Distribution (0.01-unit bins)', fontsize=12)
|
|
|
|
|
|
|
| ax_line_ask.plot(
|
| ask_times, df['ask'].values,
|
| color=ASK_COLOR, linewidth=0.5, alpha=1.0,
|
| rasterized=True,
|
| )
|
| ax_line_ask.xaxis_date()
|
| ax_line_ask.set_title('Ask Price (Time Series)', fontsize=12)
|
| ax_line_ask.set_xlabel('Time (UTC)', fontsize=10)
|
| ax_line_ask.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
|
| ax_line_ask.xaxis.set_major_locator(mdates.HourLocator(interval=2))
|
| plt.setp(ax_line_ask.xaxis.get_majorticklabels(), rotation=45, ha='right')
|
| ax_line_ask.grid(True, alpha=0.3)
|
|
|
|
|
| plt.tight_layout(rect=[0, 0, 1, 0.95])
|
|
|
| output_path = "raw_ticks_4panel.png"
|
| fig.savefig(output_path, dpi=150, bbox_inches='tight')
|
| print(f"Saved β {output_path}")
|
|
|