mirror of
https://github.com/777r1NTR/FX-QUANT-SCAN.git
synced 2026-07-27 17:37:46 +00:00
168 lines
7.5 KiB
Python
168 lines
7.5 KiB
Python
import yfinance as yf
|
|
import pandas as pd
|
|
from datetime import datetime, timedelta, timezone
|
|
from pathlib import Path
|
|
from collections import defaultdict
|
|
import pytz
|
|
import smtplib
|
|
import os
|
|
import json
|
|
|
|
from pathlib import Path
|
|
BASE_DIR = Path(__file__).resolve().parents[1] # adjust as needed
|
|
DATA_DIR = BASE_DIR / "data"
|
|
REPORTS_DIR = BASE_DIR / "reports"
|
|
REPORTS_DIR.mkdir(exist_ok=True)
|
|
|
|
# === CONFIG ===
|
|
ZONE_STATE_FILE = REPORTS_DIR / "last_known_zone.json"
|
|
KEY_LEVELS_FILE = DATA_DIR / "Key_levels_1D.xlsx"
|
|
TICKER_LIST = [
|
|
'GBPNZD=X', 'EURCHF=X', 'NZDCAD=X', 'USDZAR=X', 'CADCHF=X',
|
|
'GBPJPY=X', 'AUDNZD=X', 'GBPCHF=X', 'USDCAD=X', 'CADJPY=X',
|
|
'AUDJPY=X', 'EURUSD=X', 'EURGBP=X', 'USDNOK=X', 'NOKSEK=X'
|
|
]
|
|
PIP_RANGE = 0.001
|
|
LOOKBACK_HOURS = 24
|
|
since = datetime.now(timezone.utc) - timedelta(hours=LOOKBACK_HOURS)
|
|
|
|
ZONE_DEFINITIONS = [
|
|
("Premium+", float("inf"), "Purple upper"),
|
|
("Premium", "Purple upper", "Red Upper"),
|
|
("Plus+", "Red Upper", "Yellow Upper"),
|
|
("Fair", "Yellow Upper", "Green"),
|
|
("Budget", "Green", "Yellow Lower"),
|
|
("Discount", "Yellow Lower", "Red Lower"),
|
|
("Clearance", "Red Lower", "Purple lower"),
|
|
("Reset", "Purple lower", float("-inf"))
|
|
]
|
|
|
|
if os.path.exists(ZONE_STATE_FILE):
|
|
with open(ZONE_STATE_FILE, "r") as f:
|
|
last_known_zone = json.load(f)
|
|
else:
|
|
last_known_zone = {}
|
|
|
|
def load_key_levels(filepath):
|
|
df = pd.read_excel(filepath)
|
|
df.set_index("Ticker", inplace=True)
|
|
return df
|
|
|
|
def compute_current_zone(price, zone_definitions, level_dict):
|
|
levels = {}
|
|
for _, upper_bound, lower_bound in zone_definitions:
|
|
if isinstance(upper_bound, str):
|
|
levels[upper_bound] = float(level_dict.get(upper_bound, float("inf")))
|
|
if isinstance(lower_bound, str):
|
|
levels[lower_bound] = float(level_dict.get(lower_bound, float("-inf")))
|
|
|
|
levels["Purple upper"] = float(level_dict.get("Purple upper", float("inf")))
|
|
levels["Purple lower"] = float(level_dict.get("Purple lower", float("-inf")))
|
|
|
|
for zone_name, upper_bound, lower_bound in zone_definitions:
|
|
upper_value = levels.get(upper_bound, float("inf")) if isinstance(upper_bound, str) else upper_bound
|
|
lower_value = levels.get(lower_bound, float("-inf")) if isinstance(lower_bound, str) else lower_bound
|
|
if lower_value < price <= upper_value:
|
|
return zone_name
|
|
return "Unknown"
|
|
|
|
def generate_current_zone_snapshot():
|
|
key_levels_df = load_key_levels(KEY_LEVELS_FILE)
|
|
current_zone_results = []
|
|
|
|
for ticker in TICKER_LIST:
|
|
print(f"[→] Checking {ticker} current zone...")
|
|
try:
|
|
data = yf.download(ticker, period="1d", interval="1h", progress=False)
|
|
if data.empty:
|
|
print(f"[⚠️] No data for {ticker}")
|
|
continue
|
|
|
|
latest_close = data["Close"].iloc[-1].item()
|
|
short = ticker.split("=")[0] + "=X" if "=X" in ticker else ticker
|
|
levels_series = key_levels_df.loc[short].dropna()
|
|
level_dict = {k: float(v) for k, v in levels_series.items()}
|
|
zone = compute_current_zone(latest_close, ZONE_DEFINITIONS, level_dict)
|
|
|
|
history_row = {
|
|
"Date": pd.Timestamp.utcnow().strftime("%Y-%m-%d"),
|
|
"Ticker": ticker,
|
|
"Zone": zone
|
|
}
|
|
history_file = "reports/zone_history.csv"
|
|
pd.DataFrame([history_row]).to_csv(history_file, mode="a", index=False, header=not os.path.exists(history_file))
|
|
|
|
previous_zone = last_known_zone.get(ticker, None)
|
|
if previous_zone != zone and previous_zone is not None:
|
|
transition_row = {
|
|
"Date": pd.Timestamp.utcnow().strftime("%Y-%m-%d %H:%M:%S"),
|
|
"Ticker": ticker,
|
|
"From Zone": previous_zone,
|
|
"To Zone": zone
|
|
}
|
|
transition_file = "reports/zone_transition_log.csv"
|
|
pd.DataFrame([transition_row]).to_csv(transition_file, mode="a", index=False, header=not os.path.exists(transition_file))
|
|
last_known_zone[ticker] = zone
|
|
|
|
print(f"[✓] {ticker} → Zone: {zone} (Price: {latest_close:.4f})")
|
|
current_zone_results.append({
|
|
"Ticker": ticker,
|
|
"Current Zone": zone,
|
|
"Current Price": latest_close
|
|
})
|
|
except Exception as e:
|
|
print(f"[❌] Failed for {ticker}: {e}")
|
|
|
|
with open(ZONE_STATE_FILE, "w") as f:
|
|
json.dump(last_known_zone, f)
|
|
|
|
df_current_zones = pd.DataFrame(current_zone_results).sort_values(by="Current Zone")
|
|
print("[✅] Current Zone Snapshot:\n")
|
|
print(df_current_zones.to_string(index=False))
|
|
|
|
os.makedirs("reports", exist_ok=True)
|
|
outpath = "reports/current_zone_snapshot.xlsx"
|
|
df_current_zones.to_excel(outpath, index=False)
|
|
print(f"[💾] Exported current zone snapshot to: {outpath}")
|
|
return df_current_zones
|
|
|
|
def export_current_zone_heatmap(df_current_zones, output_path="reports/current_zone_snapshot_heatmap.xlsx"):
|
|
if df_current_zones.empty:
|
|
print("[⚠️] No current zones to export.")
|
|
return
|
|
|
|
print("[🎨] Exporting color heatmap version...")
|
|
with pd.ExcelWriter(output_path, engine="xlsxwriter") as writer:
|
|
df_current_zones.to_excel(writer, sheet_name="Current Zones", index=False)
|
|
workbook = writer.book
|
|
worksheet = writer.sheets["Current Zones"]
|
|
|
|
format_premium_plus = workbook.add_format({"bg_color": "#DA70D6"}) # Purple-Gold
|
|
format_premium = workbook.add_format({"bg_color": "#FFD700", "bold": True}) # Gold
|
|
format_plus = workbook.add_format({"bg_color": "#FFA500"}) # Orange
|
|
format_fair = workbook.add_format({"bg_color": "#90EE90"}) # LightGreen
|
|
format_budget = workbook.add_format({"bg_color": "#ADD8E6"}) # LightBlue
|
|
format_discount = workbook.add_format({"bg_color": "#FF9999"}) # LightRed
|
|
format_clearance = workbook.add_format({"bg_color": "#FF5555"}) # Deep Red
|
|
format_reset = workbook.add_format({"bg_color": "#A9A9A9"}) # Gray
|
|
|
|
|
|
zone_col = df_current_zones.columns.get_loc("Current Zone")
|
|
zone_range = f"${chr(65 + zone_col)}2:${chr(65 + zone_col)}{len(df_current_zones)+1}"
|
|
|
|
worksheet.conditional_format(zone_range, {"type": "text", "criteria": "containing", "value": "Premium+", "format": format_premium_plus})
|
|
worksheet.conditional_format(zone_range, {"type": "text", "criteria": "containing", "value": "Premium", "format": format_premium})
|
|
worksheet.conditional_format(zone_range, {"type": "text", "criteria": "containing", "value": "Plus+", "format": format_plus})
|
|
worksheet.conditional_format(zone_range, {"type": "text", "criteria": "containing", "value": "Fair", "format": format_fair})
|
|
worksheet.conditional_format(zone_range, {"type": "text", "criteria": "containing", "value": "Budget", "format": format_budget})
|
|
worksheet.conditional_format(zone_range, {"type": "text", "criteria": "containing", "value": "Discount", "format": format_discount})
|
|
worksheet.conditional_format(zone_range, {"type": "text", "criteria": "containing", "value": "Clearance", "format": format_clearance})
|
|
worksheet.conditional_format(zone_range, {"type": "text", "criteria": "containing", "value": "Reset", "format": format_reset})
|
|
|
|
print(f"[💾] Exported color heatmap to: {output_path}")
|
|
|
|
if __name__ == "__main__":
|
|
df_current_zones = generate_current_zone_snapshot()
|
|
export_current_zone_heatmap(df_current_zones)
|
|
|
|
|