-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpre_open_scan.py
More file actions
148 lines (124 loc) · 5.89 KB
/
pre_open_scan.py
File metadata and controls
148 lines (124 loc) · 5.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
"""Kaz pre-open scan — runs at 6:15am PDT (9:15am ET), 15 minutes before market open.
Goals:
1. Show last known prices vs entry for all positions — set expectations before open
2. Warn if any position is at or past stop threshold based on prior close
3. Flag upcoming macro events (CPI, FOMC, earnings windows) for the next 3 days
4. Remind about any planned trades (from playbook)
5. Post a brief pre-market briefing to fleet chat
Does NOT auto-execute trades — that's for market_scan.py after open.
Extended hours prices not available on paper/IEX plan, so uses prior close.
"""
import os
import subprocess
import sys
from datetime import datetime, timezone, timedelta
sys.path.insert(0, "/Users/services/drebin/tools")
sys.path.insert(0, "/Users/services/kaz")
from alpaca_client import get_positions, get_clock, get_account
CHAT_CHANNEL = "#kaz"
STOP_LOSS_PCT = 0.03 # -3%
# ── Macro event calendar ─────────────────────────────────────────────────────
# Add events here as they become known. Format: (date_str, label, impact)
# impact: "HIGH" | "MED" — HIGH events warrant extra caution
MACRO_EVENTS = [
("2026-04-09", "March CPI eve — CPI contracts resolve tomorrow", "HIGH"),
("2026-04-10", "March CPI release — all Kalshi CPI contracts resolve","HIGH"),
("2026-04-29", "FOMC meeting begins", "MED"),
("2026-04-30", "FOMC rate decision", "HIGH"),
# Add earnings dates for held symbols as they become known:
# ("2026-05-06", "HIMS earnings", "HIGH"),
]
def post_chat(msg):
try:
env = {**os.environ, "CHAT_AGENT": "kaz"}
subprocess.run(
["node", "/Users/services/drebin/tools/chat-helper.js", "post", CHAT_CHANNEL, msg],
env=env, capture_output=True, timeout=15,
)
except Exception as e:
print(f"[chat post failed: {e}]")
def upcoming_events(days_ahead=3):
"""Return macro events within the next N days."""
today = datetime.now(timezone.utc).date()
results = []
for date_str, label, impact in MACRO_EVENTS:
event_date = datetime.strptime(date_str, "%Y-%m-%d").date()
delta = (event_date - today).days
if 0 <= delta <= days_ahead:
results.append((delta, date_str, label, impact))
return sorted(results)
def scan():
try:
clock = get_clock()
except Exception as e:
post_chat(f"⚠️ pre-open-scan: clock fetch failed — {e}")
return
# Only run on market days — skip weekends/holidays
if clock.get("is_open"):
print("Market already open — pre-open scan running late, proceeding anyway.")
lines = ["**Pre-open briefing:**"]
# ── Upcoming macro events ────────────────────────────────────────────────
events = upcoming_events(days_ahead=3)
if events:
lines.append("**Upcoming events:**")
for delta, date_str, label, impact in events:
prefix = "🔴" if impact == "HIGH" else "🟡"
when = "TODAY" if delta == 0 else f"in {delta}d ({date_str})"
lines.append(f" {prefix} {when}: {label}")
# ── Position snapshot from last close ────────────────────────────────────
try:
positions = get_positions()
account = get_account()
daily_pnl = float(account.get("equity", 0)) - float(account.get("last_equity", 0))
except Exception as e:
post_chat(f"⚠️ pre-open-scan: positions fetch failed — {e}")
return
if not positions:
lines.append("No open positions. All cash.")
else:
lines.append("**Positions at prior close:**")
at_risk = []
for p in positions:
sym = p["symbol"]
qty = float(p["qty"])
avg = float(p["avg_entry_price"])
close = float(p.get("lastday_price", p.get("current_price", avg)))
pl_pct = (close - avg) / avg
pl_abs = (close - avg) * qty
flag = " ⚠️" if pl_pct <= -STOP_LOSS_PCT else ""
lines.append(
f" {sym}: close ${close:.2f} vs entry ${avg:.2f} "
f"({pl_pct:+.1%}, ${pl_abs:+.2f}){flag}"
)
if pl_pct <= -STOP_LOSS_PCT:
at_risk.append(sym)
if at_risk:
lines.append(
f"**At/past stop threshold at close:** {', '.join(at_risk)} — "
f"plan exit at open (use: python3 alpaca_client.py sell-position SYMBOL)"
)
# ── Planned trades reminder ───────────────────────────────────────────────
# Read from playbook — extract the Monday plan section if present
try:
with open("/Users/services/kaz/playbook.md") as f:
playbook = f.read()
# Find pending action lines
import re
pending = re.findall(r"^- ((?:SELL|BUY|EVALUATE|CONSIDER)[^\n]+)", playbook, re.MULTILINE)
if pending:
lines.append("**Planned trades (from playbook):**")
for action in pending[:5]:
lines.append(f" • {action}")
except Exception:
pass
# ── Next open/close ───────────────────────────────────────────────────────
lines.append(f"Market opens: {clock.get('next_open', '?')}")
msg = "\n".join(lines)
print(msg)
post_chat(msg)
if __name__ == "__main__":
try:
scan()
except Exception as e:
post_chat(f"⚠️ pre-open-scan FAILED: {e}")
sys.exit(1)