Interactive Brokers Client Services
Jun 17 12:40 AM
Message Center Notification
Interactive Brokers ticket response regarding Kanex account messaging.
Joaquín Medina
Jun 17 12:34 AM
RE: Re:[## 708 ##] RE: RMA for EXT-4KHD70M
AVIT Vision requests credit memo for destroyed equipment per RMA 708.
Microsoft Outlook
Jun 16 10:36 PM
Undeliverable: FW: TIER 3 Trending Stocks Part 2 (1 New Red Candle): $DELL, $OSCR, $ONDS, $ZETA, $LMND and $TMC (June 17, 2026-daily)
Microsoft Outlook: Undeliverable: FW: TIER 3 Trending Stocks Part 2 (1 New Red
Interactive Brokers Client Services
Jun 16 9:59 PM
Security Notice for User k******8: Verify Log In
Fake Interactive Brokers security alert requesting account verification.
Microsoft Outlook
Jun 16 9:38 PM
Undeliverable: FW: $QCOM (June 17, 2026-daily)
Microsoft Outlook: Undeliverable: FW: $QCOM (June 17, 2026-daily)
TDhruv Sharma
Jun 16 9:05 PM
[Teams oneOnOne] (Teams DM)
Dhruv Sharma: [Teams oneOnOne] (Teams DM)
Fabiola Hernandez
Jun 16 6:55 PM
Tomorrow is Spirit Day – Souvenir T-Shirt Day
Spirit Day reminder: kids invited to wear souvenir t-shirt tomorrow at camp.
Derrick
Jun 16 6:19 PM
Request for More Info: EXT-USBC2XI-100M - Dual-Host USB 3.2 Gen 1 Extender over CAT6a — USB-C & USB-B Inputs, 100m/328ft
Product inquiry for USB extender; requesting pricing, specs, availability.
Sydnee Agent (AI)
Jun 16 6:00 PM
[Spread Cal] 2026-06-17 — per-stock max_spread_bps results
Sydnee Agent (AI): [Spread Cal] 2026-06-17 — per-stock max_spread_bps results
Sydnee Agent (AI)
Jun 16 5:40 PM
[Calibration Daily] 2026-06-17
Sydnee Agent (AI): [Calibration Daily] 2026-06-17
Sydnee Agent (AI)
Jun 16 5:30 PM
Sydnee algo daily — dev $-2,699 · prod $+0 · 6d window
Sydnee Agent (AI): Sydnee algo daily — dev $-2,699 · prod $+0 · 6d window
Find My
Jun 16 5:24 PM
A sound was played on Kelvin’s iPad mini.
Find My: A sound was played on Kelvin’s iPad mini.
Sammy Cemo, Matt Pourcho and Anthony DeLorenzo
Jun 16 4:04 PM
New Pricing | Fortune 40 Credit | STNL Industrial | 11 Yr. WALT | SoCal
⚠ PHISHING: employee impersonation: display name matches 'anthony' but sender is cbre.com
Microsoft Store
Jun 16 3:23 PM
Meet the new Surface lineup—with limited-time offers
Microsoft Store: Meet the new Surface lineup—with limited-time offers
[email protected]
Jun 16 2:34 PM
New Funding Program
Funding broker offering working capital solutions and consolidation services.
Sammy Cemo and Anthony DeLorenzo
Jun 16 2:03 PM
Single-Story Owner-User Offering in Newport Beach
⚠ PHISHING: employee impersonation: display name matches 'anthony' but sender is cbre.com
Aarti Gupta
Jun 16 1:53 PM
Re: CR-TOUCH6R 6" Widescreen Touch Panel with Knob, RS232/RS485 & PoE
Aarti Gupta: Re: CR-TOUCH6R 6" Widescreen Touch Panel with Knob, RS232/RS
[email protected]
Jun 16 1:43 PM
Action required: Your June booking bonuses expire soon
⚠ PHISHING: phishing subject pattern: 'Action required' from external sender vacationoffer.com
TAnthony Patino
Jun 16 1:30 PM
[Teams oneOnOne] (Teams DM)
LK account overdrawn; bank fee applied, needs replenishment before QB charge.
TAnthony Patino
Jun 16 1:27 PM
[Teams oneOnOne] (Teams DM)
Anthony Patino sent a brief Teams DM referencing 'LK'.
TAnthony Patino
Jun 16 1:27 PM
[Teams oneOnOne] (Teams DM)
Anthony Patino reports insufficient funds issue.
Amazon Payments
Jun 16 12:44 PM
Action requise sur le compte Amazon Payments
Fake Amazon Payments suspension notice in French requesting account verification.
Nick Daniel
Jun 16 11:04 AM
Quick question
TriNet HR solution sales inquiry.
Aarti Gupta
Jun 16 10:04 AM
Re: Order help #1227
Aarti Gupta: Re: Order help #1227
Aarti Gupta
Jun 16 10:03 AM
Re: Order help #1227
Aarti Gupta: Re: Order help #1227
Aarti Gupta
Jun 16 9:59 AM
EXT-USBCPD4K-70M 18Gbps USB-C 4K60 HDBaseT 3.0 Extender with 100W PD (70m)
Aarti Gupta: EXT-USBCPD4K-70M 18Gbps USB-C 4K60 HDBaseT 3.0 Extender with
Sean McGinley
Jun 16 9:58 AM
Order help #1227
Customer inquiry about HDMI extender transmitter compatibility for order #1227.
IBKR FYI
Jun 16 9:51 AM
FYI: Upcoming Exchange Holidays
Exchange holiday notice: MIAX, NASDAQ, NYSE closed June 19.
IBKR FYI
Jun 16 9:27 AM
FYI: Upcoming Exchange Holidays
NASDAQ/MIAX exchange holiday June 19, 2026 — no trading.
Hims
Jun 16 9:02 AM
Kelvin - action required on your account
⚠ PHISHING: phishing subject pattern: 'action required' from external sender icloud.com
[email protected]
Jun 16 9:02 AM
You have a new estimate
Suspicious medical estimate link from Providence—verify legitimacy before clicking.
Mail Delivery System
Jun 16 8:20 AM
Mail delivery failed [Invoice #36077 PO: SH260526762J]
Mail delivery failed for invoice #36077 to Mike Vanderkamp; recipient address rejected.
Mail Delivery System
Jun 16 8:19 AM
Mail delivery failed [Invoice #36078 PO: SH260430677J]
Mail delivery failed: [email protected] → [email protected] for invoice #36078.
Mail Delivery System
Jun 16 8:19 AM
Mail delivery failed [Invoice #36079 PO: SH2606101007J]
Mail delivery failure: invoice to [email protected] rejected by recipient server.
TDhruv Sharma
Jun 16 8:16 AM
[Teams oneOnOne] (Teams DM)
Dhruv Sharma: [Teams oneOnOne] (Teams DM)
Zoho Campaigns
Jun 16 7:55 AM
Campaign "NEW USB-C + USB-B Dual-Host Extender · 100m" has been successfully Sent - Zoho Campaigns
Zoho Campaigns notification: USB-C/USB-B extender product campaign sent successfully.
Ali Pacheco
Jun 16 7:53 AM
Request for More Info: EXT-USBC2XI-100M - Dual-Host USB 3.2 Gen 1 Extender over CAT6a — USB-C & USB-B Inputs, 100m/328ft
Product inquiry for USB 3.2 extender from external vendor.
KanexPro
Jun 16 7:51 AM
NEW USB-C + USB-BDual-Host Extender · 100m
KanexPro: NEW USB-C + USB-BDual-Host Extender · 100m
KanexPro
Jun 16 7:50 AM
NEW USB-C + USB-BDual-Host Extender · 100m
KanexPro: NEW USB-C + USB-BDual-Host Extender · 100m
KanexPro
Jun 16 7:50 AM
NEW USB-C + USB-BDual-Host Extender · 100m
KanexPro: NEW USB-C + USB-BDual-Host Extender · 100m
KanexPro
Jun 16 7:49 AM
NEW USB-C + USB-BDual-Host Extender · 100m
KanexPro: NEW USB-C + USB-BDual-Host Extender · 100m
TDhruv Sharma
Jun 16 7:33 AM
[Teams oneOnOne] (Teams DM)
Dhruv Sharma: [Teams oneOnOne] (Teams DM)
TDhruv Sharma
Jun 16 7:32 AM
[Teams oneOnOne] (Teams DM)
Dhruv Sharma: [Teams oneOnOne] (Teams DM)
Bank of America
Jun 16 6:16 AM
Your available account balance is low
Bank of America: Your available account balance is low
Let's Talk Supply Chain
Jun 16 6:01 AM
Are you ready?
Suspicious meeting confirmation from unknown sender with obfuscation.
Interactive Brokers Client Services
Jun 16 5:18 AM
Message Center Notification
Interactive Brokers ticket response regarding Kanex account notification.
Benjamin & Williams
Jun 16 5:03 AM
Commercial Claim Discovery Documents Our file:D-8222 Debtor: VICTORIA ROPA ELEGANTE
Fake debt collector demanding payment on unknown commercial claim within 24h.
IBKR FYI
Jun 16 5:01 AM
FYI: Option Expiration Notification
Oracle options expiring 18JUN2026 — immediate action required.
IBKR FYI
Jun 16 5:00 AM
FYI: Option Expiration Notification
MSTR call option expiring 18JUN2026; action needed if extending position.
Sydnee Agent (AI)
Jun 16 4:25 AM
Sydnee nightly — exit_flow audit 2026-06-16 — 0P0 8P1 6R
Sydnee Agent (AI): Sydnee nightly — exit_flow audit 2026-06-16 — 0P0 8P1 6R
Sydnee nightly — exit_flow audit 2026-06-16 — 0P0 8P1 6R
AI verdict
employee
high
· confidence: high
· by internal-exempt
“Sydnee Agent (AI): Sydnee nightly — exit_flow audit 2026-06-16 — 0P0 8P1 6R”
Reasoning: @sydnee.ai is a protected domain — hard exemption
Sydnee nightly — exit_flow audit — 2026-06-16
P0 findings: 0 P1 findings: 8 Risks: 6
- Area: Exit Flow (Tuesday theme)
- Branch: `dev` (a808030)
- Files scanned: `bot.py` (~20 285 lines), `docs/strategy_decisions.md`, prior exit-flow audits (2026-06-09, 2026-06-10), `git log --oneline -30`
- Bugs found (P0 / P1): 0 / 8 (1 new, 7 carried)
- Risks noted: 6 (1 new, 5 carried)
Cross-check: `git log --oneline -30` shows all commits since the 2026-06-10 exit-flow audit are tape entry/sizing/UI changes (TAPE_RTH_DELAY_MIN, RSI exhaustion guard, TAPE_PCT_NLV, OBV-5m gate tighten, tape_metadata JSONB merge). No commit touches `_evaluate_symbol` or the core exit-flow path. All 7 P1s from 2026-06-10 are confirmed unfixed. Line numbers shifted ~+110 from tape additions. 1 new P1 found: tape `_tape_entry_ms` not persisted to DB.
---
Full report (dev branch): https://github.com/kanex1/sydnee.signals/blob/dev/docs/audit_2026-06-16_exit_flow.md
Reply FROM [email protected] to [email protected] to request fixes, e.g.:
"code_task on sydnee-signals-dev: apply fix for the P0 about RVOL threshold in bot.py"
Sydnee Agent will propose + you APPROVE (or plain 'approve') + auto-push to dev.
--- Full audit below (first 12 KB) ---
# Nightly Audit 2026-06-16 — EXIT FLOW
## Summary
- Area: Exit Flow (Tuesday theme)
- Branch: `dev` (a808030)
- Files scanned: `bot.py` (~20 285 lines), `docs/strategy_decisions.md`, prior exit-flow audits (2026-06-09, 2026-06-10), `git log --oneline -30`
- Bugs found (P0 / P1): 0 / 8 (1 new, 7 carried)
- Risks noted: 6 (1 new, 5 carried)
Cross-check: `git log --oneline -30` shows all commits since the 2026-06-10 exit-flow audit are tape entry/sizing/UI changes (TAPE_RTH_DELAY_MIN, RSI exhaustion guard, TAPE_PCT_NLV, OBV-5m gate tighten, tape_metadata JSONB merge). No commit touches `_evaluate_symbol` or the core exit-flow path. All 7 P1s from 2026-06-10 are confirmed unfixed. Line numbers shifted ~+110 from tape additions. 1 new P1 found: tape `_tape_entry_ms` not persisted to DB.
---
## Findings
### BUG [P1] (new): Tape `_tape_entry_ms` not persisted — 60min hard cap resets on restart
**File:** `bot.py:18705`
**Evidence:**
```python
# bot.py:18705 — entry_ms default falls back to now_ms:
entry_ms = int(getattr(my_pos, "_tape_entry_ms", now_ms))
# ↑ _tape_entry_ms is never written to DB; on restart it is always None
# → entry_ms = now_ms → 60min cap counted from restart, not from original entry
# bot.py:18706 — hard cap check:
if now_ms - entry_ms > 60 * 60 * 1000:
_tape_close_with_snapshot(my_pos, "Tape-4of4 60min cap")
return
# bot.py:18904 — written at entry time (in-memory only):
t._tape_entry_ms = now_ms
# Not in DB restore block (bot.py:1119–1175) — survives no restarts.
```
After any container restart, all Tape-4of4 positions have `_tape_entry_ms = None`. The default `now_ms` resets the clock to restart time. A tape position open for 55 min before restart gets a fresh 60min window — it can run 55 + 60 = 115 min total, well past the scalping cap. The RTH-close flat at 15:50 ET provides an absolute ceiling but the 60min intent is violated.
**Impact:** TAPE trades run past their 60min hard cap after container restarts (which occur on most deploys). Adversely-positioned scalps can stay open 90–120 min with only the -1% catastrophic stop as protection.
**Fix:** Lazy-restore `_tape_entry_ms` from the persisted `entry_time` field (already in DB) when the in-memory attribute is missing:
```python
# bot.py:18705 — replace the one-liner with:
if getattr(my_pos, "_tape_entry_ms", None) is None:
try:
_et = datetime.fromisoformat(str(my_pos.entry_time).replace("Z", "+00:00"))
my_pos._tape_entry_ms = int(_et.timestamp() * 1000)
except Exception:
my_pos._tape_entry_ms = now_ms
entry_ms = int(my_pos._tape_entry_ms)
```
---
### BUG [P1] (carried from 2026-06-09): TRIM1 — `_trimmed` / `_rsi_force_trim` flags never set for 1-share parents
**File:** `bot.py:5510–5571` (EXIT 4 TRIM1 block)
**Evidence:**
```python
# bot.py:5510-5522:
if not hit and not is_trimmed and _trim_hit:
if True:
trim = True
...
trim_shares = t.shares if _flat_full_close else t.shares // 2 # 0 for 1-share
if trim_shares > 0:
t._trimmed = True # ← inside guard — never set for 1-share
t._rsi_force_trim = False # ← inside guard — never cleared for 1-share
self._update_trade_field(t.trade_id,
trimmed_1=True, force_trim_pending=False, ...)
# no else branch
```
For 1-share non-FLAT_EXIT: `trim_shares = 0`, `_trimmed` never set, `_rsi_force_trim` never cleared. `trim = True` fires every bar → EXIT 5 (BE) and FIX 1 (trailing stop) both blocked by `not trim` guard. `force_trim_pending=True` persists in DB through restarts.
**Fix:** Add after `if trim_shares > 0:` block:
```python
else:
t._trimmed = True
t._rsi_force_trim = False
self._update_trade_field(t.trade_id,
trimmed_1=True, force_trim_pending=False,
force_trim_reason=None, extreme_since_trim=live_price)
t._extreme_since_trim = live_price
```
---
### BUG [P1] (carried from 2026-05-26): TRIM2 — `_trimmed_2` / `_rsi_force_trim2` flags never set for 1-share runners
**File:** `bot.py:5452–5486` (EXIT 3b SECOND TRIM block)
**Evidence:**
```python
# bot.py:5452-5460:
if not hit and is_trimmed and not is_trimmed_2 and (_trim2_hit or _force_trim2):
trim = True
trim2_shares = t.shares // 2 # 0 for 1-share runner
if trim2_shares > 0:
t._trimmed_2 = True # ← inside guard — never set for 1-share
t._rsi_force_trim2 = False # ← inside guard — never cleared for 1-share
# no else branch
```
`_rsi_force_trim2 = True` and `force_trim2_pending=True` in DB stay set. TRIM2 re-fires every bar, `trim = True`, silencing the hold log and suppressing trailing stop.
**Fix:** Add `else: t._trimmed_2 = True; t._rsi_force_trim2 = False; self._update_trade_field(t.trade_id, trimmed_2=True, force_trim2_pending=False, force_trim_reason=None)`.
---
### BUG [P1] (carried from 2026-06-10): TRIM3 — `_trimmed_3` flag never set for 1-share runners
**File:** `bot.py:5407–5437` (EXIT 3 TRIM3 block)
**Evidence:**
```python
# bot.py:5409-5416:
if not hit and is_trimmed_2 and not is_trimmed_3 and t.target_price:
if _tgt_hit:
trim = True
trim3_shares = t.shares // 2 # 0 for 1-share (common: 3sh entry → 2sh → 1sh)
if trim3_shares > 0:
t._trimmed_3 = True # ← inside guard — never set for 1-share
self._update_trade_field(t.trade_id, trimmed_3=True)
new_stop3 = ... # ← stop advance never happens
# no else branch
```
1-share runner at target: `_trimmed_3` stays False, TRIM3 fires every bar while `live_price >= target`. Stop stuck at TRIM2 level (entry + 0.25×ATR) instead of advancing to TRIM3 level (entry + 0.5×ATR). FIX 1 trailing stop suppressed by `trim = True`.
**Fix:** Add after `if trim3_shares > 0:` block:
```python
else:
t._trimmed_3 = True
self._update_trade_field(t.trade_id, trimmed_3=True)
new_stop3 = t.entry_price + 0.5 * _daily_atr * t.direction if _daily_atr > 0 else t.stop_price
cur_stop3 = float(t.stop_price or 0)
if t.direction == 1:
new_stop3 = max(new_stop3, cur_stop3)
elif cur_stop3 > 0:
new_stop3 = min(new_stop3, cur_stop3)
self._update_trade_field(t.trade_id, stop_price=new_stop3)
self._log_activity(sym, f"TRIM3 1-share runner @${live_price:.2f} — lock stop → ${new_stop3:.2f}", "trade")
```
---
### BUG [P1] (carried from 2026-06-02): OBV Rule F permanently disabled after in-loss profit-guard skip
**File:** `bot.py:5036–5058` (EXIT 0, inner profit-guard block)
**Evidence:**
```python
# bot.py:5036-5058 — profit-guard / loss path:
if _profit_guard and profit_per_share < 0:
_dir_lbl_ex = "LONG" if t.direction == 1 else "SHORT"
if _loss_exit:
hit = True # OBV_LOSS_EXIT spot-exit
...
elif not getattr(t, '_obv_be_applied', False):
self._log_activity(sym, f"BE-STOP (OBV rule F) skipped ...", "info")
t._obv_be_applied = True # ← always set, even on profit-guard skip
t._obv_be_applied_at = ...
self._update_trade_field(t.trade_id, obv_be_applied=True, ...)
```
When `OBV_BE_STOP=true`, `RSI_TRIM_PROFIT_GUARD=true`, `OBV_LOSS_EXIT=false` (or unset): the skip path sets `_obv_be_applied = True` without moving the stop. The outer guard `not getattr(t, "_obv_be_applied", False)` (line 4990) then permanently blocks Rule F for this trade — even after it recovers to profit and OBV turns adverse again. Stop stays at the wide original level instead of tightening to BE.
**Impact:** Applicable to prod if `OBV_LOSS_EXIT` is not set there. A trade that was underwater when OBV first turned against it keeps its full −1R risk after recovery; the BE-stop protection that Rule F would have provided never fires.
**Fix:** Remove `t._obv_be_applied = True` from the in-loss / non-loss-exit branch. To prevent log spam, add a rate-limit check instead:
```python
elif not getattr(t, '_obv_be_applied', False):
_last_skip = getattr(t, '_obv_skip_log_at', 0)
if time.time() - _last_skip > 300: # log at most once per 5 min
self._log_activity(sym, f"BE-STOP (OBV rule F) skipped ...", "info")
t._obv_skip_log_at = time.time()
# Do NOT set t._obv_be_applied here — rule stays live for recovery path
```
---
### BUG [P1] (carried from 2026-06-02): `profit_per_share` uses stale 5m bar close — `_in_profit` gate misroutes RSI/BXt exits intrabar
**File:** `bot.py:4970` (`profit_per_share`); `bot.py:5237` (`_in_profit`)
**Evidence:**
```python
# bot.py:4969-4970:
live_price = float(ss.last_price) if ss.last_price else close_price
profit_per_share = (close_price - t.entry_price) * t.direction # ← stale 5m close, not live
# bot.py:5237:
_in_profit = profit_per_share > 0 # ← gates trim-ladder vs full-close routing
```
When RSI/BXt reversal fires and intrabar price moved below entry since the last 5m close, `_in_profit = True` (stale) routes to trim ladder. Trim child fills at live (lower) price; BE stop is then placed above current price → immediate stop-out on the runner.
**Fix:** `_in_profit = (live_price - float(t.entry_price)) * t.direction > 0` at lines 5237 and 5386 (BXt reversal uses the same variable in scope).
---
### BUG [P1] (carried from 2026-06-02): `_check_stops` defined but never called — intrabar wick through stop missed
**File:** `bot.py:6723` (definition); zero callers
**Evidence:**
```python
# bot.py:6723 — defined, never wired:
def _check_stops(self, trade: Trade, bar) -> None:
if trade.stop_price:
if trade.direction == 1 and bar.low <= trade.stop_price: # correct bar-low check
...
# bot.py:5107-5110 — live exit only checks close_price:
if t.direction == 1 and close_price <= t.stop_price:
_stop_hit_pending = True
```
An intrabar wick trading through the stop but closing above it is never caught. `_check_stops` was written to fix this using bar high/low but was never called. Mitigated only when `WS_TICK_EXITS=true` (default off on dev/prod).
**Fix:** Wire `_check_stops` into `_evaluate_symbol` near the `_stop_hit_pending` block using `signal_df["low"].iloc[-1]` / `signal_df["high"].iloc[-1]` already in scope, before the `_stop_hit_pending` deferred evaluation.
---
### BUG [P1] (carried from 2026-05-31): `TV_BXT_TRAIL_AGAINST` default mismatch — live `1.0` vs history replay `1.5`
**File:** `bot.py:3838` (live); `bot.py:14949` (history replay)
**Evidence:**
```python
# bot.py:3838 — _obv_trail_stop_hit (live):
agnst = float(os.environ.get("TV_BXT_TRAIL_AGAINST", "1.0"))
# bot.py:14949 — /api/trade_obv_trail dashboard endpoint:
agnst = float(os.environ.get("TV_BXT_TRAIL_AGAINST", "1.5"))
```
When `TV_BXT_TRAIL_AGAINST` env var is unset: live trail = 1.0×ATR, dashboard shows 1.5×ATR. Stop-analysis reports are wrong by 50% on unset-env sessions.
**Fix:** Change `bot.py:14949` default `"1.5"` → `"1.0"`. One-line fix.
---
## Risks
### RISK: `_stop_hit_pending` deferred evaluation overwrites earlier `hit=True` exit reason
**File:** `bot.py:5223–5225`
```python
if _stop_hit_pending: # ← no `not hit` guard
hit, reason = True, f"Stop-loss ${t.stop_price:.2f} · ..." # ← overwrites OBV/HOLD_CAP reason
```
If `OBV_LOSS_EXIT` or `HOLD_CAP_MIN` already set `hit=True`, and the same bar's 5m close also hits the stop, the reason is overwritten to "Stop-loss" in the DB. Trade closes correctly; P&L attribution is wrong. **Fix:** `if _stop_hit_pending and not hit:` at line 5223.
---
### RISK (carried): Tick exit path (`_tick_exit_step`) has no TRIM2 level check
**File:** `bot.py:_tick_exit_step` (~line 19402)
When `WS_TICK_EXITS=true`: after tick T1 (50% trim), the remaining 50% can be closed by tick TARGET before bar close — compressing the 4-level ladder into 2 levels. Behavioral divergence vs default `WS_TICK_EXITS=false`. `WS_TICK_EXITS` is off by default; low live impact.
---
### RISK (carried): 5m-naked OBV trail stop unavailable for first 75 min after pre-market open
**File:** `bot.py:379