Upload 4 files
Browse files
.gitattributes
CHANGED
|
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
no-HH-LL/market_representation_M3_high_res.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
with-HH-LL/market_representation_M3_high_res.png filter=lfs diff=lfs merge=lfs -text
|
no-HH-LL/market_representation_M3_high_res.png
ADDED
|
Git LFS Details
|
no-HH-LL/no_highest-high_no_lowest-low.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# !pip install pandas numpy matplotlib
|
| 2 |
+
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import numpy as np
|
| 5 |
+
import matplotlib.pyplot as plt
|
| 6 |
+
from matplotlib.collections import LineCollection, PolyCollection
|
| 7 |
+
import time
|
| 8 |
+
|
| 9 |
+
class MarketRepresentation:
|
| 10 |
+
def __init__(self, gap, premarket, daytime, nighttime):
|
| 11 |
+
self.current_time_gap = gap
|
| 12 |
+
self.current_premarket_session = premarket
|
| 13 |
+
self.current_daytime_session = daytime
|
| 14 |
+
self.current_nighttime_session = nighttime
|
| 15 |
+
|
| 16 |
+
def to_dataframe(self):
|
| 17 |
+
"""Reconstruct the active time session."""
|
| 18 |
+
return pd.concat([
|
| 19 |
+
self.current_premarket_session,
|
| 20 |
+
self.current_daytime_session,
|
| 21 |
+
self.current_nighttime_session
|
| 22 |
+
])
|
| 23 |
+
|
| 24 |
+
def run_market_representation_pipeline(csv_path):
|
| 25 |
+
print("="*55)
|
| 26 |
+
print("1. LOADING DATA")
|
| 27 |
+
print("="*55)
|
| 28 |
+
start_time = time.time()
|
| 29 |
+
|
| 30 |
+
df = pd.read_csv(csv_path)
|
| 31 |
+
# Optimized datetime parsing
|
| 32 |
+
df['datetime'] = pd.to_datetime(df['datetime'], format='%Y-%m-%d %H:%M:%S')
|
| 33 |
+
df = df.sort_values('datetime').reset_index(drop=True)
|
| 34 |
+
|
| 35 |
+
print(f"[✔] Data loaded in {time.time() - start_time:.2f} seconds.")
|
| 36 |
+
print(f"[i] Total records: {len(df):,}")
|
| 37 |
+
|
| 38 |
+
print("\n" + "="*55)
|
| 39 |
+
print("2. EXTRACTING COMPLETE DAILY REPRESENTATIONS")
|
| 40 |
+
print("="*55)
|
| 41 |
+
extract_start = time.time()
|
| 42 |
+
|
| 43 |
+
# 45 mins threshold robustly captures the standard 1-hour daily break and 48-hour weekend gap
|
| 44 |
+
gap_threshold = pd.Timedelta(minutes=45)
|
| 45 |
+
|
| 46 |
+
df['time_diff'] = df['datetime'].diff()
|
| 47 |
+
is_gap = df['time_diff'] > gap_threshold
|
| 48 |
+
df['session_id'] = is_gap.cumsum()
|
| 49 |
+
|
| 50 |
+
representations = []
|
| 51 |
+
|
| 52 |
+
grouped = df.groupby('session_id')
|
| 53 |
+
for sid, group in grouped:
|
| 54 |
+
# 1. current_time_gap
|
| 55 |
+
current_time_gap = group['time_diff'].iloc[0] if sid > 0 else pd.Timedelta(seconds=0)
|
| 56 |
+
|
| 57 |
+
# Determine reference date for 0:00 UTC
|
| 58 |
+
zero_hour_rows = group[group['datetime'].dt.hour == 0]
|
| 59 |
+
if len(zero_hour_rows) > 0:
|
| 60 |
+
ref_date = zero_hour_rows['datetime'].dt.floor('D').iloc[0]
|
| 61 |
+
else:
|
| 62 |
+
# Fallback for sessions that skip exactly 0:00 but span across it
|
| 63 |
+
ref_date = group['datetime'].iloc[len(group)//2].floor('D')
|
| 64 |
+
|
| 65 |
+
group = group.copy()
|
| 66 |
+
|
| 67 |
+
# Relative time in hours (0:00 UTC is exactly 0.0)
|
| 68 |
+
group['rel_time_hours'] = (group['datetime'] - ref_date).dt.total_seconds() / 3600.0
|
| 69 |
+
|
| 70 |
+
# Align all candle_open at 0:00 UTC to Y=0
|
| 71 |
+
idx_closest_to_zero = group['rel_time_hours'].abs().idxmin()
|
| 72 |
+
ref_price = group.loc[idx_closest_to_zero, 'open']
|
| 73 |
+
|
| 74 |
+
group['open'] -= ref_price
|
| 75 |
+
group['high'] -= ref_price
|
| 76 |
+
group['low'] -= ref_price
|
| 77 |
+
group['close'] -= ref_price
|
| 78 |
+
|
| 79 |
+
# Slicing sessions based on precise UTC constraints
|
| 80 |
+
# 2. current_premarket_session
|
| 81 |
+
current_premarket_session = group[group['rel_time_hours'] < 0.0]
|
| 82 |
+
# 3. current_daytime_session
|
| 83 |
+
current_daytime_session = group[(group['rel_time_hours'] >= 0.0) & (group['rel_time_hours'] < 12.0)]
|
| 84 |
+
# 4. current_nighttime_session
|
| 85 |
+
current_nighttime_session = group[group['rel_time_hours'] >= 12.0]
|
| 86 |
+
|
| 87 |
+
# Keep only structurally complete representations
|
| 88 |
+
if not current_premarket_session.empty and not current_daytime_session.empty and not current_nighttime_session.empty:
|
| 89 |
+
rep = MarketRepresentation(
|
| 90 |
+
current_time_gap,
|
| 91 |
+
current_premarket_session,
|
| 92 |
+
current_daytime_session,
|
| 93 |
+
current_nighttime_session
|
| 94 |
+
)
|
| 95 |
+
representations.append(rep)
|
| 96 |
+
|
| 97 |
+
print(f"[✔] Extraction complete in {time.time() - extract_start:.2f} seconds.")
|
| 98 |
+
print(f"[i] Found {len(representations):,} complete daily market representations.")
|
| 99 |
+
|
| 100 |
+
if len(representations) == 0:
|
| 101 |
+
print("[!] No complete sessions found. Exiting.")
|
| 102 |
+
return
|
| 103 |
+
|
| 104 |
+
print("\n" + "="*55)
|
| 105 |
+
print("3. GENERATING VECTORIZED OVERLAY PLOT")
|
| 106 |
+
print("="*55)
|
| 107 |
+
plot_start = time.time()
|
| 108 |
+
|
| 109 |
+
# Fast merge of validated data structures
|
| 110 |
+
valid_sessions_dfs = [rep.to_dataframe() for rep in representations]
|
| 111 |
+
df_aligned = pd.concat(valid_sessions_dfs, ignore_index=True)
|
| 112 |
+
|
| 113 |
+
up = df_aligned['close'] >= df_aligned['open']
|
| 114 |
+
down = ~up
|
| 115 |
+
|
| 116 |
+
# Render width parameters UPDATED FOR M3 (3 minutes)
|
| 117 |
+
width = (3.0 / 60.0) * 0.8 # 80% width of a 3-minute period mapped in hours
|
| 118 |
+
w = width / 2
|
| 119 |
+
|
| 120 |
+
x = df_aligned['rel_time_hours'].values
|
| 121 |
+
o = df_aligned['open'].values
|
| 122 |
+
c = df_aligned['close'].values
|
| 123 |
+
h = df_aligned['high'].values
|
| 124 |
+
l = df_aligned['low'].values
|
| 125 |
+
|
| 126 |
+
x_up, o_up, c_up, h_up, l_up = x[up], o[up], c[up], h[up], l[up]
|
| 127 |
+
x_down, o_down, c_down, h_down, l_down = x[down], o[down], c[down], h[down], l[down]
|
| 128 |
+
|
| 129 |
+
# Memory-efficient vectorized polygon coordinate mapping
|
| 130 |
+
wicks_up = np.empty((len(x_up), 2, 2))
|
| 131 |
+
wicks_up[:, 0, 0] = x_up; wicks_up[:, 0, 1] = l_up
|
| 132 |
+
wicks_up[:, 1, 0] = x_up; wicks_up[:, 1, 1] = h_up
|
| 133 |
+
|
| 134 |
+
wicks_down = np.empty((len(x_down), 2, 2))
|
| 135 |
+
wicks_down[:, 0, 0] = x_down; wicks_down[:, 0, 1] = l_down
|
| 136 |
+
wicks_down[:, 1, 0] = x_down; wicks_down[:, 1, 1] = h_down
|
| 137 |
+
|
| 138 |
+
boxes_up = np.empty((len(x_up), 4, 2))
|
| 139 |
+
boxes_up[:, 0, 0] = x_up - w; boxes_up[:, 0, 1] = o_up
|
| 140 |
+
boxes_up[:, 1, 0] = x_up + w; boxes_up[:, 1, 1] = o_up
|
| 141 |
+
boxes_up[:, 2, 0] = x_up + w; boxes_up[:, 2, 1] = c_up
|
| 142 |
+
boxes_up[:, 3, 0] = x_up - w; boxes_up[:, 3, 1] = c_up
|
| 143 |
+
|
| 144 |
+
boxes_down = np.empty((len(x_down), 4, 2))
|
| 145 |
+
boxes_down[:, 0, 0] = x_down - w; boxes_down[:, 0, 1] = o_down
|
| 146 |
+
boxes_down[:, 1, 0] = x_down + w; boxes_down[:, 1, 1] = o_down
|
| 147 |
+
boxes_down[:, 2, 0] = x_down + w; boxes_down[:, 2, 1] = c_down
|
| 148 |
+
boxes_down[:, 3, 0] = x_down - w; boxes_down[:, 3, 1] = c_down
|
| 149 |
+
|
| 150 |
+
# White-themed rendering (Massive Canvas for 1200 DPI Detail)
|
| 151 |
+
plt.style.use('default')
|
| 152 |
+
fig, ax = plt.subplots(figsize=(30, 15), facecolor='white')
|
| 153 |
+
ax.set_facecolor('white')
|
| 154 |
+
|
| 155 |
+
up_color = '#26a69a'
|
| 156 |
+
down_color = '#ef5350'
|
| 157 |
+
alpha_val = 0.1 # Exactly 10% opacity
|
| 158 |
+
|
| 159 |
+
# Hardware-accelerated GPU/CPU rendering using Collections
|
| 160 |
+
lc_up = LineCollection(wicks_up, colors=up_color, linewidths=0.5, alpha=alpha_val)
|
| 161 |
+
lc_down = LineCollection(wicks_down, colors=down_color, linewidths=0.5, alpha=alpha_val)
|
| 162 |
+
pc_up = PolyCollection(boxes_up, facecolors=up_color, edgecolors='none', alpha=alpha_val)
|
| 163 |
+
pc_down = PolyCollection(boxes_down, facecolors=down_color, edgecolors='none', alpha=alpha_val)
|
| 164 |
+
|
| 165 |
+
ax.add_collection(lc_up)
|
| 166 |
+
ax.add_collection(lc_down)
|
| 167 |
+
ax.add_collection(pc_up)
|
| 168 |
+
ax.add_collection(pc_down)
|
| 169 |
+
|
| 170 |
+
ax.autoscale_view()
|
| 171 |
+
|
| 172 |
+
# Annotations & Formatting
|
| 173 |
+
ax.set_title("Complete Daily Market Representation Overlay (M3 Timeframe)\nAligned at 0:00 UTC", fontsize=20, fontweight='bold', pad=15)
|
| 174 |
+
ax.set_xlabel("Hours Relative to 0:00 UTC", fontsize=16, labelpad=10)
|
| 175 |
+
ax.set_ylabel("Price Deviation from 0:00 UTC Open", fontsize=16, labelpad=10)
|
| 176 |
+
|
| 177 |
+
min_x, max_x = ax.get_xlim()
|
| 178 |
+
ax.set_xticks(np.arange(np.floor(min_x), np.ceil(max_x)+1, 2))
|
| 179 |
+
|
| 180 |
+
# Segment shading
|
| 181 |
+
ax.axvspan(min_x, 0, color='#e3f2fd', alpha=0.3, label='Premarket (< 0:00)')
|
| 182 |
+
ax.axvspan(0, 12, color='#fff3e0', alpha=0.3, label='Daytime (0:00 - 12:00)')
|
| 183 |
+
ax.axvspan(12, max_x, color='#f3e5f5', alpha=0.3, label='Nighttime (> 12:00)')
|
| 184 |
+
|
| 185 |
+
ax.grid(True, linestyle='--', alpha=0.5)
|
| 186 |
+
ax.axvline(0, color='black', linestyle='-', linewidth=2.0, alpha=0.8, label='0:00 UTC Alignment')
|
| 187 |
+
ax.axhline(0, color='black', linestyle='-', linewidth=2.0, alpha=0.8)
|
| 188 |
+
|
| 189 |
+
ax.legend(loc='upper left', fontsize=14)
|
| 190 |
+
plt.tight_layout()
|
| 191 |
+
|
| 192 |
+
# High Resolution Export Setup
|
| 193 |
+
export_path = 'market_representation_M3_high_res.png'
|
| 194 |
+
print(f"[✔] Exporting Extreme-Res image (1200 DPI) to {export_path}...")
|
| 195 |
+
plt.savefig(export_path, dpi=1200, bbox_inches='tight')
|
| 196 |
+
print(f"[✔] Plot generated and saved in {time.time() - plot_start:.2f} seconds.")
|
| 197 |
+
print("="*55)
|
| 198 |
+
plt.show()
|
| 199 |
+
|
| 200 |
+
# Execution Call
|
| 201 |
+
# Upload your M3 file to Colab and update the path accordingly.
|
| 202 |
+
csv_path = '/content/XAUUSDc_M3_2014-01-14_to_2026-04-29_1060828records_88092KB.csv'
|
| 203 |
+
run_market_representation_pipeline(csv_path)
|
with-HH-LL/market_representation_M3_high_res.png
ADDED
|
Git LFS Details
|
with-HH-LL/wtih_highest-high_no_lowest-low.py
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# !pip install pandas numpy matplotlib
|
| 2 |
+
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import numpy as np
|
| 5 |
+
import matplotlib.pyplot as plt
|
| 6 |
+
from matplotlib.collections import LineCollection, PolyCollection
|
| 7 |
+
import time
|
| 8 |
+
|
| 9 |
+
class MarketRepresentation:
|
| 10 |
+
def __init__(self, gap, premarket, daytime, nighttime, extremes=None):
|
| 11 |
+
self.current_time_gap = gap
|
| 12 |
+
self.current_premarket_session = premarket
|
| 13 |
+
self.current_daytime_session = daytime
|
| 14 |
+
self.current_nighttime_session = nighttime
|
| 15 |
+
self.extremes = extremes
|
| 16 |
+
|
| 17 |
+
def to_dataframe(self):
|
| 18 |
+
"""Reconstruct the active time session."""
|
| 19 |
+
return pd.concat([
|
| 20 |
+
self.current_premarket_session,
|
| 21 |
+
self.current_daytime_session,
|
| 22 |
+
self.current_nighttime_session
|
| 23 |
+
])
|
| 24 |
+
|
| 25 |
+
def run_market_representation_pipeline(csv_path):
|
| 26 |
+
print("="*55)
|
| 27 |
+
print("1. LOADING DATA")
|
| 28 |
+
print("="*55)
|
| 29 |
+
start_time = time.time()
|
| 30 |
+
|
| 31 |
+
df = pd.read_csv(csv_path)
|
| 32 |
+
# Optimized datetime parsing
|
| 33 |
+
df['datetime'] = pd.to_datetime(df['datetime'], format='%Y-%m-%d %H:%M:%S')
|
| 34 |
+
df = df.sort_values('datetime').reset_index(drop=True)
|
| 35 |
+
|
| 36 |
+
print(f"[✔] Data loaded in {time.time() - start_time:.2f} seconds.")
|
| 37 |
+
print(f"[i] Total records: {len(df):,}")
|
| 38 |
+
|
| 39 |
+
print("\n" + "="*55)
|
| 40 |
+
print("2. EXTRACTING COMPLETE DAILY REPRESENTATIONS")
|
| 41 |
+
print("="*55)
|
| 42 |
+
extract_start = time.time()
|
| 43 |
+
|
| 44 |
+
# 45 mins threshold robustly captures the standard 1-hour daily break and 48-hour weekend gap
|
| 45 |
+
gap_threshold = pd.Timedelta(minutes=45)
|
| 46 |
+
|
| 47 |
+
df['time_diff'] = df['datetime'].diff()
|
| 48 |
+
is_gap = df['time_diff'] > gap_threshold
|
| 49 |
+
df['session_id'] = is_gap.cumsum()
|
| 50 |
+
|
| 51 |
+
representations = []
|
| 52 |
+
|
| 53 |
+
grouped = df.groupby('session_id')
|
| 54 |
+
for sid, group in grouped:
|
| 55 |
+
# 1. current_time_gap
|
| 56 |
+
current_time_gap = group['time_diff'].iloc[0] if sid > 0 else pd.Timedelta(seconds=0)
|
| 57 |
+
|
| 58 |
+
# Determine reference date for 0:00 UTC
|
| 59 |
+
zero_hour_rows = group[group['datetime'].dt.hour == 0]
|
| 60 |
+
if len(zero_hour_rows) > 0:
|
| 61 |
+
ref_date = zero_hour_rows['datetime'].dt.floor('D').iloc[0]
|
| 62 |
+
else:
|
| 63 |
+
# Fallback for sessions that skip exactly 0:00 but span across it
|
| 64 |
+
ref_date = group['datetime'].iloc[len(group)//2].floor('D')
|
| 65 |
+
|
| 66 |
+
group = group.copy()
|
| 67 |
+
|
| 68 |
+
# Relative time in hours (0:00 UTC is exactly 0.0)
|
| 69 |
+
group['rel_time_hours'] = (group['datetime'] - ref_date).dt.total_seconds() / 3600.0
|
| 70 |
+
|
| 71 |
+
# Align all candle_open at 0:00 UTC to Y=0
|
| 72 |
+
idx_closest_to_zero = group['rel_time_hours'].abs().idxmin()
|
| 73 |
+
ref_price = group.loc[idx_closest_to_zero, 'open']
|
| 74 |
+
|
| 75 |
+
group['open'] -= ref_price
|
| 76 |
+
group['high'] -= ref_price
|
| 77 |
+
group['low'] -= ref_price
|
| 78 |
+
group['close'] -= ref_price
|
| 79 |
+
|
| 80 |
+
# Slicing sessions based on precise UTC constraints
|
| 81 |
+
# 2. current_premarket_session
|
| 82 |
+
current_premarket_session = group[group['rel_time_hours'] < 0.0]
|
| 83 |
+
# 3. current_daytime_session
|
| 84 |
+
current_daytime_session = group[(group['rel_time_hours'] >= 0.0) & (group['rel_time_hours'] < 12.0)]
|
| 85 |
+
# 4. current_nighttime_session
|
| 86 |
+
current_nighttime_session = group[group['rel_time_hours'] >= 12.0]
|
| 87 |
+
|
| 88 |
+
# Keep only structurally complete representations
|
| 89 |
+
if not current_premarket_session.empty and not current_daytime_session.empty and not current_nighttime_session.empty:
|
| 90 |
+
|
| 91 |
+
# Find extreme points
|
| 92 |
+
pre_hh_idx = current_premarket_session['high'].idxmax()
|
| 93 |
+
pre_ll_idx = current_premarket_session['low'].idxmin()
|
| 94 |
+
|
| 95 |
+
day_hh_idx = current_daytime_session['high'].idxmax()
|
| 96 |
+
day_ll_idx = current_daytime_session['low'].idxmin()
|
| 97 |
+
|
| 98 |
+
night_hh_idx = current_nighttime_session['high'].idxmax()
|
| 99 |
+
night_ll_idx = current_nighttime_session['low'].idxmin()
|
| 100 |
+
|
| 101 |
+
extremes = {
|
| 102 |
+
'pre_hh': (current_premarket_session.loc[pre_hh_idx, 'rel_time_hours'], current_premarket_session.loc[pre_hh_idx, 'high']),
|
| 103 |
+
'pre_ll': (current_premarket_session.loc[pre_ll_idx, 'rel_time_hours'], current_premarket_session.loc[pre_ll_idx, 'low']),
|
| 104 |
+
'day_hh': (current_daytime_session.loc[day_hh_idx, 'rel_time_hours'], current_daytime_session.loc[day_hh_idx, 'high']),
|
| 105 |
+
'day_ll': (current_daytime_session.loc[day_ll_idx, 'rel_time_hours'], current_daytime_session.loc[day_ll_idx, 'low']),
|
| 106 |
+
'night_hh': (current_nighttime_session.loc[night_hh_idx, 'rel_time_hours'], current_nighttime_session.loc[night_hh_idx, 'high']),
|
| 107 |
+
'night_ll': (current_nighttime_session.loc[night_ll_idx, 'rel_time_hours'], current_nighttime_session.loc[night_ll_idx, 'low'])
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
rep = MarketRepresentation(
|
| 111 |
+
current_time_gap,
|
| 112 |
+
current_premarket_session,
|
| 113 |
+
current_daytime_session,
|
| 114 |
+
current_nighttime_session,
|
| 115 |
+
extremes
|
| 116 |
+
)
|
| 117 |
+
representations.append(rep)
|
| 118 |
+
|
| 119 |
+
print(f"[✔] Extraction complete in {time.time() - extract_start:.2f} seconds.")
|
| 120 |
+
print(f"[i] Found {len(representations):,} complete daily market representations.")
|
| 121 |
+
|
| 122 |
+
if len(representations) == 0:
|
| 123 |
+
print("[!] No complete sessions found. Exiting.")
|
| 124 |
+
return
|
| 125 |
+
|
| 126 |
+
print("\n" + "="*55)
|
| 127 |
+
print("3. GENERATING VECTORIZED OVERLAY PLOT")
|
| 128 |
+
print("="*55)
|
| 129 |
+
plot_start = time.time()
|
| 130 |
+
|
| 131 |
+
# Fast merge of validated data structures
|
| 132 |
+
valid_sessions_dfs = [rep.to_dataframe() for rep in representations]
|
| 133 |
+
df_aligned = pd.concat(valid_sessions_dfs, ignore_index=True)
|
| 134 |
+
|
| 135 |
+
up = df_aligned['close'] >= df_aligned['open']
|
| 136 |
+
down = ~up
|
| 137 |
+
|
| 138 |
+
# Render width parameters UPDATED FOR M3 (3 minutes)
|
| 139 |
+
width = (3.0 / 60.0) * 0.8 # 80% width of a 3-minute period mapped in hours
|
| 140 |
+
w = width / 2
|
| 141 |
+
|
| 142 |
+
x = df_aligned['rel_time_hours'].values
|
| 143 |
+
o = df_aligned['open'].values
|
| 144 |
+
c = df_aligned['close'].values
|
| 145 |
+
h = df_aligned['high'].values
|
| 146 |
+
l = df_aligned['low'].values
|
| 147 |
+
|
| 148 |
+
x_up, o_up, c_up, h_up, l_up = x[up], o[up], c[up], h[up], l[up]
|
| 149 |
+
x_down, o_down, c_down, h_down, l_down = x[down], o[down], c[down], h[down], l[down]
|
| 150 |
+
|
| 151 |
+
# Memory-efficient vectorized polygon coordinate mapping
|
| 152 |
+
wicks_up = np.empty((len(x_up), 2, 2))
|
| 153 |
+
wicks_up[:, 0, 0] = x_up; wicks_up[:, 0, 1] = l_up
|
| 154 |
+
wicks_up[:, 1, 0] = x_up; wicks_up[:, 1, 1] = h_up
|
| 155 |
+
|
| 156 |
+
wicks_down = np.empty((len(x_down), 2, 2))
|
| 157 |
+
wicks_down[:, 0, 0] = x_down; wicks_down[:, 0, 1] = l_down
|
| 158 |
+
wicks_down[:, 1, 0] = x_down; wicks_down[:, 1, 1] = h_down
|
| 159 |
+
|
| 160 |
+
boxes_up = np.empty((len(x_up), 4, 2))
|
| 161 |
+
boxes_up[:, 0, 0] = x_up - w; boxes_up[:, 0, 1] = o_up
|
| 162 |
+
boxes_up[:, 1, 0] = x_up + w; boxes_up[:, 1, 1] = o_up
|
| 163 |
+
boxes_up[:, 2, 0] = x_up + w; boxes_up[:, 2, 1] = c_up
|
| 164 |
+
boxes_up[:, 3, 0] = x_up - w; boxes_up[:, 3, 1] = c_up
|
| 165 |
+
|
| 166 |
+
boxes_down = np.empty((len(x_down), 4, 2))
|
| 167 |
+
boxes_down[:, 0, 0] = x_down - w; boxes_down[:, 0, 1] = o_down
|
| 168 |
+
boxes_down[:, 1, 0] = x_down + w; boxes_down[:, 1, 1] = o_down
|
| 169 |
+
boxes_down[:, 2, 0] = x_down + w; boxes_down[:, 2, 1] = c_down
|
| 170 |
+
boxes_down[:, 3, 0] = x_down - w; boxes_down[:, 3, 1] = c_down
|
| 171 |
+
|
| 172 |
+
# White-themed rendering (Massive Canvas for 1200 DPI Detail)
|
| 173 |
+
plt.style.use('default')
|
| 174 |
+
fig, ax = plt.subplots(figsize=(30, 15), facecolor='white')
|
| 175 |
+
ax.set_facecolor('white')
|
| 176 |
+
|
| 177 |
+
up_color = '#26a69a'
|
| 178 |
+
down_color = '#ef5350'
|
| 179 |
+
alpha_val = 0.1 # Exactly 10% opacity
|
| 180 |
+
|
| 181 |
+
# Hardware-accelerated GPU/CPU rendering using Collections
|
| 182 |
+
lc_up = LineCollection(wicks_up, colors=up_color, linewidths=0.5, alpha=alpha_val)
|
| 183 |
+
lc_down = LineCollection(wicks_down, colors=down_color, linewidths=0.5, alpha=alpha_val)
|
| 184 |
+
pc_up = PolyCollection(boxes_up, facecolors=up_color, edgecolors='none', alpha=alpha_val)
|
| 185 |
+
pc_down = PolyCollection(boxes_down, facecolors=down_color, edgecolors='none', alpha=alpha_val)
|
| 186 |
+
|
| 187 |
+
ax.add_collection(lc_up)
|
| 188 |
+
ax.add_collection(lc_down)
|
| 189 |
+
ax.add_collection(pc_up)
|
| 190 |
+
ax.add_collection(pc_down)
|
| 191 |
+
|
| 192 |
+
# Plot extreme points
|
| 193 |
+
hh_x, hh_y = [], []
|
| 194 |
+
ll_x, ll_y = [], []
|
| 195 |
+
|
| 196 |
+
for rep in representations:
|
| 197 |
+
if rep.extremes:
|
| 198 |
+
ext = rep.extremes
|
| 199 |
+
hh_x.extend([ext['pre_hh'][0], ext['day_hh'][0], ext['night_hh'][0]])
|
| 200 |
+
hh_y.extend([ext['pre_hh'][1], ext['day_hh'][1], ext['night_hh'][1]])
|
| 201 |
+
|
| 202 |
+
ll_x.extend([ext['pre_ll'][0], ext['day_ll'][0], ext['night_ll'][0]])
|
| 203 |
+
ll_y.extend([ext['pre_ll'][1], ext['day_ll'][1], ext['night_ll'][1]])
|
| 204 |
+
|
| 205 |
+
ax.scatter(hh_x, hh_y, color='blue', s=15, alpha=0.6, label='Highest Highs', zorder=5)
|
| 206 |
+
ax.scatter(ll_x, ll_y, color='red', s=15, alpha=0.6, label='Lowest Lows', zorder=5)
|
| 207 |
+
|
| 208 |
+
ax.autoscale_view()
|
| 209 |
+
|
| 210 |
+
# Annotations & Formatting
|
| 211 |
+
ax.set_title("Complete Daily Market Representation Overlay (M3 Timeframe)\nAligned at 0:00 UTC", fontsize=20, fontweight='bold', pad=15)
|
| 212 |
+
ax.set_xlabel("Hours Relative to 0:00 UTC", fontsize=16, labelpad=10)
|
| 213 |
+
ax.set_ylabel("Price Deviation from 0:00 UTC Open", fontsize=16, labelpad=10)
|
| 214 |
+
|
| 215 |
+
min_x, max_x = ax.get_xlim()
|
| 216 |
+
ax.set_xticks(np.arange(np.floor(min_x), np.ceil(max_x)+1, 2))
|
| 217 |
+
|
| 218 |
+
# Segment shading
|
| 219 |
+
ax.axvspan(min_x, 0, color='#e3f2fd', alpha=0.3, label='Premarket (< 0:00)')
|
| 220 |
+
ax.axvspan(0, 12, color='#fff3e0', alpha=0.3, label='Daytime (0:00 - 12:00)')
|
| 221 |
+
ax.axvspan(12, max_x, color='#f3e5f5', alpha=0.3, label='Nighttime (> 12:00)')
|
| 222 |
+
|
| 223 |
+
ax.grid(True, linestyle='--', alpha=0.5)
|
| 224 |
+
ax.axvline(0, color='black', linestyle='-', linewidth=2.0, alpha=0.8, label='0:00 UTC Alignment')
|
| 225 |
+
ax.axhline(0, color='black', linestyle='-', linewidth=2.0, alpha=0.8)
|
| 226 |
+
|
| 227 |
+
ax.legend(loc='upper left', fontsize=14)
|
| 228 |
+
plt.tight_layout()
|
| 229 |
+
|
| 230 |
+
# High Resolution Export Setup
|
| 231 |
+
export_path = 'market_representation_M3_high_res.png'
|
| 232 |
+
print(f"[✔] Exporting Extreme-Res image (1200 DPI) to {export_path}...")
|
| 233 |
+
plt.savefig(export_path, dpi=1200, bbox_inches='tight')
|
| 234 |
+
print(f"[✔] Plot generated and saved in {time.time() - plot_start:.2f} seconds.")
|
| 235 |
+
print("="*55)
|
| 236 |
+
plt.show()
|
| 237 |
+
|
| 238 |
+
# Execution Call
|
| 239 |
+
# Upload your M3 file to Colab and update the path accordingly.
|
| 240 |
+
csv_path = '/content/XAUUSDc_M3_2014-01-14_to_2026-04-29_1060828records_88092KB.csv'
|
| 241 |
+
run_market_representation_pipeline(csv_path)
|