mirror of
https://github.com/Xe138/AI-Trader.git
synced 2026-04-08 11:47:24 -04:00
fix: calculate final holdings from actions instead of querying database
**Problem:**
Final positions showed empty holdings despite executing 15+ trades.
The issue persisted even after fixing the get_current_position_from_db query.
**Root Cause:**
At end of trading day, base_agent.py line 672 called
_get_current_portfolio_state() which queried the database for current
position. On the FIRST trading day, this query returns empty holdings
because there's no previous day's record.
**Why the Previous Fix Wasn't Enough:**
The previous fix (date < instead of date <=) correctly retrieves
STARTING position for subsequent days, but didn't address END-OF-DAY
position calculation, which needs to account for trades executed
during the current session.
**Solution:**
Added new method _calculate_final_position_from_actions() that:
1. Gets starting holdings from previous day (via get_starting_holdings)
2. Gets all actions from actions table for current trading day
3. Applies each buy/sell to calculate final state:
- Buy: holdings[symbol] += qty, cash -= qty * price
- Sell: holdings[symbol] -= qty, cash += qty * price
4. Returns accurate final holdings and cash
**Impact:**
- First trading day: Correctly saves all executed trades as final holdings
- Subsequent days: Final position reflects all trades from that day
- Holdings now persist correctly across all trading days
**Tests:**
- test_calculate_final_position_first_day_with_trades: 15 trades on first day
- test_calculate_final_position_with_previous_holdings: Multi-day scenario
- test_calculate_final_position_no_trades: No-trade edge case
All tests pass ✅
This commit is contained in:
@@ -319,6 +319,60 @@ class BaseAgent:
|
||||
print(f"⚠️ Could not get position from database: {e}")
|
||||
return {}, self.initial_cash
|
||||
|
||||
def _calculate_final_position_from_actions(
|
||||
self,
|
||||
trading_day_id: int,
|
||||
starting_cash: float
|
||||
) -> tuple[Dict[str, int], float]:
|
||||
"""
|
||||
Calculate final holdings and cash from starting position + actions.
|
||||
|
||||
This is the correct way to get end-of-day position: start with the
|
||||
starting position and apply all trades from the actions table.
|
||||
|
||||
Args:
|
||||
trading_day_id: The trading day ID
|
||||
starting_cash: Cash at start of day
|
||||
|
||||
Returns:
|
||||
(holdings_dict, final_cash) where holdings_dict maps symbol -> quantity
|
||||
"""
|
||||
from api.database import Database
|
||||
|
||||
db = Database()
|
||||
|
||||
# 1. Get starting holdings (from previous day's ending)
|
||||
starting_holdings_list = db.get_starting_holdings(trading_day_id)
|
||||
holdings = {h["symbol"]: h["quantity"] for h in starting_holdings_list}
|
||||
|
||||
# 2. Initialize cash
|
||||
cash = starting_cash
|
||||
|
||||
# 3. Get all actions for this trading day
|
||||
actions = db.get_actions(trading_day_id)
|
||||
|
||||
# 4. Apply each action to calculate final state
|
||||
for action in actions:
|
||||
symbol = action["symbol"]
|
||||
quantity = action["quantity"]
|
||||
price = action["price"]
|
||||
action_type = action["action_type"]
|
||||
|
||||
if action_type == "buy":
|
||||
# Add to holdings
|
||||
holdings[symbol] = holdings.get(symbol, 0) + quantity
|
||||
# Deduct from cash
|
||||
cash -= quantity * price
|
||||
|
||||
elif action_type == "sell":
|
||||
# Remove from holdings
|
||||
holdings[symbol] = holdings.get(symbol, 0) - quantity
|
||||
# Add to cash
|
||||
cash += quantity * price
|
||||
|
||||
# 5. Return final state
|
||||
return holdings, cash
|
||||
|
||||
def _calculate_portfolio_value(
|
||||
self,
|
||||
holdings: Dict[str, int],
|
||||
@@ -614,8 +668,14 @@ Summary:"""
|
||||
summarizer = ReasoningSummarizer(model=self.model)
|
||||
summary = await summarizer.generate_summary(self.conversation_history)
|
||||
|
||||
# 8. Get current portfolio state from database
|
||||
current_holdings, current_cash = self._get_current_portfolio_state(today_date, job_id)
|
||||
# 8. Calculate final portfolio state from starting position + actions
|
||||
# NOTE: We must calculate from actions, not query database, because:
|
||||
# - On first day, database query returns empty (no previous day)
|
||||
# - This method applies all trades to get accurate final state
|
||||
current_holdings, current_cash = self._calculate_final_position_from_actions(
|
||||
trading_day_id=trading_day_id,
|
||||
starting_cash=starting_cash
|
||||
)
|
||||
|
||||
# 9. Save final holdings to database
|
||||
for symbol, quantity in current_holdings.items():
|
||||
|
||||
Reference in New Issue
Block a user