Skip to content

Commit d5eec48

Browse files
yingwangclaude
andcommitted
fix(splits): credit phantom-share proceeds for split-affected sells
When a split-affected position (BKNG 1:25) is sold post-split, Alpaca records the trade at qty×post_split_price (pre-split qty count), missing the (ratio-1)×qty×price cash that should have come in. Previously the equity adjustment only accounted for currently-held split positions. Once sold, the adjustment dropped to 0 and displayed equity fell by ~$12k for the BKNG case despite no real change in account value. This commit adds a sold_credit pass over filled SELL orders post-split-date, so equity stays consistent before and after the position is liquidated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 774a856 commit d5eec48

2 files changed

Lines changed: 42 additions & 9 deletions

File tree

generate_site.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,17 +242,32 @@ def _fetch_trades_from_alpaca(api_key, secret_key):
242242
logger.info("Fetched %d orders across %d rebalance dates from Alpaca",
243243
len(filled_orders), len(rebalances))
244244

245-
# Adjust account equity and portfolio history for split corrections
246-
equity_adjustment = 0
245+
# Adjust account equity and portfolio history for split corrections.
246+
# See generate_site_lgbm.py for held vs sold breakdown.
247+
held_adjustment = 0
247248
for p in api.list_positions():
248249
split = STOCK_SPLITS.get(p.symbol)
249250
if split:
250-
equity_adjustment += float(p.qty) * (split["ratio"] - 1) * float(p.current_price)
251+
held_adjustment += float(p.qty) * (split["ratio"] - 1) * float(p.current_price)
251252

253+
sold_credit = 0
254+
for o in filled_orders:
255+
split = STOCK_SPLITS.get(o.symbol)
256+
if not split or o.side != "sell":
257+
continue
258+
fill_dt = str(o.filled_at)[:10] if o.filled_at else str(o.submitted_at)[:10]
259+
if fill_dt >= split["date"]:
260+
ratio = split["ratio"]
261+
qty = float(o.filled_qty)
262+
price = float(o.filled_avg_price) if o.filled_avg_price else 0
263+
sold_credit += (ratio - 1) * qty * price
264+
265+
equity_adjustment = held_adjustment + sold_credit
252266
if equity_adjustment:
253267
total_mv = sum(p["market_value"] for p in positions)
254-
account_info["equity"] = round(account_info["cash"] + total_mv, 2)
255-
logger.info("Adjusted account equity by +$%.2f for stock splits", equity_adjustment)
268+
account_info["equity"] = round(account_info["cash"] + sold_credit + total_mv, 2)
269+
logger.info("Adjusted account equity by +$%.2f for stock splits (held=%.2f, sold=%.2f)",
270+
equity_adjustment, held_adjustment, sold_credit)
256271
cutoff_date = None
257272
for sym, split in STOCK_SPLITS.items():
258273
split_date = split["date"]

generate_site_lgbm.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,17 +257,35 @@ def _fetch_trades_from_alpaca(api_key, secret_key):
257257
# Adjust account equity and portfolio history for split corrections.
258258
# Alpaca paper trading doesn't process corporate actions, so equity
259259
# and history include wrong market values for split-affected stocks.
260-
equity_adjustment = 0
260+
# Two cases: (a) currently held — positions[] already split-adjusted,
261+
# but Alpaca cash is correct so equity needs +(ratio-1)*qty*price added;
262+
# (b) sold post-split — Alpaca booked proceeds at qty×price (pre-split
263+
# qty), missing (ratio-1)×qty×price of cash that should have come in.
264+
held_adjustment = 0
261265
for p in api.list_positions():
262266
split = STOCK_SPLITS.get(p.symbol)
263267
if split:
264268
ratio = split["ratio"]
265-
equity_adjustment += float(p.qty) * (ratio - 1) * float(p.current_price)
269+
held_adjustment += float(p.qty) * (ratio - 1) * float(p.current_price)
266270

271+
sold_credit = 0
272+
for o in filled_orders:
273+
split = STOCK_SPLITS.get(o.symbol)
274+
if not split or o.side != "sell":
275+
continue
276+
fill_dt = str(o.filled_at)[:10] if o.filled_at else str(o.submitted_at)[:10]
277+
if fill_dt >= split["date"]:
278+
ratio = split["ratio"]
279+
qty = float(o.filled_qty)
280+
price = float(o.filled_avg_price) if o.filled_avg_price else 0
281+
sold_credit += (ratio - 1) * qty * price
282+
283+
equity_adjustment = held_adjustment + sold_credit
267284
if equity_adjustment:
268285
total_mv = sum(p["market_value"] for p in positions)
269-
account_info["equity"] = round(account_info["cash"] + total_mv, 2)
270-
logger.info("Adjusted account equity by +$%.2f for stock splits", equity_adjustment)
286+
account_info["equity"] = round(account_info["cash"] + sold_credit + total_mv, 2)
287+
logger.info("Adjusted account equity by +$%.2f for stock splits (held=%.2f, sold=%.2f)",
288+
equity_adjustment, held_adjustment, sold_credit)
271289
# Find correction start date = max(split_date, buy_date) per symbol
272290
cutoff_date = None
273291
for sym, split in STOCK_SPLITS.items():

0 commit comments

Comments
 (0)