$ diff -u ../02_Memories/trading_agent/agent.py trading_agent/agent.py
--- ../02_Memories/trading_agent/agent.py 2026-05-18 04:50:36.609821500 +0000
+++ trading_agent/agent.py 2026-06-24 00:33:46.870713112 +0000
@@ -1,7 +1,7 @@
import os
import sys
import time
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, timezone
from typing import Dict, Any, Optional
from dotenv import load_dotenv
@@ -13,8 +13,11 @@
from google.adk.agents import Agent
from google.adk.agents.callback_context import CallbackContext
+from google.adk.memory.memory_entry import MemoryEntry
from google.adk.tools.load_memory_tool import LoadMemoryTool
from google.adk.tools.preload_memory_tool import PreloadMemoryTool
+from google.adk.tools.tool_context import ToolContext
+from google.genai import types
from vertexai import agent_engines
# Load environment variables
@@ -106,14 +109,23 @@
quote = stock_client.get_stock_latest_quote(request_params)
return float(quote[symbol].ask_price)
-def place_trade_order(symbol: str, side: str, amount_usd: Optional[float] = None, qty: Optional[float] = None) -> str:
- """Places a market order (fractional shares supported for buys)."""
+def place_trade_order(symbol: str, side: str, amount_usd: Optional[float] = None, qty: Optional[float] = None, reason: str = "", tool_context: ToolContext = None) -> str:
+ """Places a market order (fractional shares supported for buys).
+
+ Args:
+ symbol: The ticker to trade.
+ side: 'buy' or 'sell'.
+ amount_usd: Dollar amount for a notional buy.
+ qty: Share quantity (required for sells).
+ reason: Short rationale for the trade (ticker, sentiment score, news summary).
+ Saved to memory so future sessions can recall why this trade was made.
+ """
side = side.lower()
if side not in ['buy', 'sell']:
return "Error: Side must be 'buy' or 'sell'."
order_side = OrderSide.BUY if side == 'buy' else OrderSide.SELL
-
+
try:
if amount_usd is not None and side == 'buy':
order_request = MarketOrderRequest(
@@ -134,16 +146,56 @@
time.sleep(1)
order = trading_client.submit_order(order_data=order_request)
+
+ # Record the confirmed fill in session state so save_to_memory can
+ # persist a clean, structured trade record at the end of the cycle.
+ if tool_context is not None:
+ trades = tool_context.state.get("trades_this_session", [])
+ trades.append({
+ "symbol": symbol,
+ "side": side,
+ "amount_usd": amount_usd,
+ "qty": qty,
+ "reason": reason,
+ })
+ tool_context.state["trades_this_session"] = trades
+
return f"Success: {side.upper()} order placed for {symbol}. Order ID: {order.id}"
except Exception as e:
return f"Failed to place {side} order for {symbol}: {str(e)}"
async def save_to_memory(callback_context: CallbackContext):
+ # Full transcript: general cross-session recall (news context, reasoning).
await callback_context.add_events_to_memory(
events=callback_context.session.events,
custom_metadata={"force_flush": True},
)
+ # Structured trade record: a clean, guaranteed memory of exactly what was
+ # traded and why, built from the orders captured in place_trade_order.
+ trades = callback_context.state.get("trades_this_session", [])
+ if not trades:
+ return
+
+ lines = []
+ for t in trades:
+ size = f"${t['amount_usd']}" if t.get("amount_usd") else f"{t['qty']} shares"
+ rationale = f" — {t['reason']}" if t.get("reason") else ""
+ lines.append(f"{t['side'].upper()} {t['symbol']} {size}{rationale}")
+ text = "Trades executed this session:\n" + "\n".join(lines)
+ print(text, flush=True)
+
+ await callback_context.add_memory(
+ memories=[MemoryEntry(
+ content=types.Content(role="model", parts=[types.Part(text=text)]),
+ author="TradingAgent",
+ timestamp=datetime.now(timezone.utc).isoformat(),
+ )],
+ # Set a TTL of ~1 month (30 days) so the trading history can be recalled
+ # for a month while avoiding too much clutter.
+ custom_metadata={"type": "trade", "force_flush": True, "ttl": "2592000s"},
+ )
+
# Define the Agent
root_agent = Agent(
name="TradingAgent",
@@ -172,6 +224,11 @@
- Rank by positivity.
- Create market BUY trades for EXACTLY $1000 of EACH of **up to 5** stocks with highest scores.
- Stop if available cash is less than $1000.
+
+ IMPORTANT: Every time you call `place_trade_order` (buy or sell), pass the `reason`
+ argument with a concise rationale: the ticker, its sentiment score, and a one-line
+ news summary. This is saved to memory so future sessions can recall why each trade
+ was made.
6. FINAL REPORT:
Produce a comprehensive summary: