Inspiration

I run options-income and swing trades across three brokers (Longbridge, Tiger, MooMoo). Every morning starts with the same 40-minute grind before any decision: reconcile positions across accounts, check what options expire this week, re-read the technicals on each name, size income trades, and try to recall what I was thinking yesterday. It's repetitive, error-prone, and the "yesterday's intent" part lives only in my head. That's exactly the kind of event-driven, multi-step routine an agent should own.

What it does

Deskpilot is a Taskmaster agent: it runs my entire pre-market routine autonomously and hands back one prioritized daily plan. On a single trigger it recalls yesterday's intent, reviews the live book for risk (near-term expiries, concentration, P/L, assignment-cash gaps), reads the technicals on each flagged name, sizes any options-income setups off the implied expected move, carries its theses across days, writes the plan back to memory, and delivers the finished brief to Telegram so it lands on my phone. A Cloud Scheduler job runs the whole thing automatically before every US market open — so the plan is waiting without a single tap. It is read-only — it never places, modifies, or cancels an order.

How I built it

A Google ADK multi-agent system on Gemini 3.5. An Orchestrator LlmAgent calls three specialists as tools (ADK AgentTool) — RiskOfficer, MarketAnalyst, OptionsStrategist — so it keeps control for the whole run and finishes by persisting and delivering the plan. Deterministic Python does all the numbers (FIFO P/L, FX→SGD, SMA/RSI/ATR, ATM-straddle expected move via yfinance); Gemini does only prioritization and synthesis, never arithmetic.

It's served on Cloud Run (ADK FastAPI app), with a Firestore memory bank (local-JSON fallback for offline demos) and the Gemini key in Secret Manager. It reads my positions from a published Google Sheet (CSV, no credentials) — so anyone, including a reviewer with their own sheet, can point Deskpilot at their data with one env var. The finished plan is pushed to Telegram via a small outbound-only notifier, and a Cloud Scheduler trigger runs a Cloud Run Job on a weekday US-pre-market schedule for a truly hands-off morning brief.

It's built to run unattended: every market and notify tool returns an error dict instead of raising, memory falls back to local JSON when Firestore is unavailable, and the runner auto-retries transient Gemini 503s (high-demand spikes) with exponential backoff — so a busy moment never crashes a run.

Challenges I ran into

  • AgentTool vs. sub-agent transfer: ADK's transfer_to_agent hands control away and never returns, so the routine stalled after the first specialist and never saved a plan. Re-modeling the specialists as AgentTools kept the orchestrator in control for the whole run.
  • Tool outputs are part of the model request: a run 400'd because a market tool returned NaN (a holiday row from yfinance); NaN/inf aren't valid JSON. I coerce them to null at the tool boundary now, with a regression test.
  • Provider load is a real failure mode: under demand, a multi-call run (orchestrator + each specialist) can draw a transient 503. I added retry-with-backoff at the run boundary so a spike doesn't reach the user.
  • A PowerShell deploy gotcha: an unquoted --set-env-vars A=1,B=2 gets comma-split by the shell and collapses into one var — surfaced as a runtime "unexpected model name format." Quoting the value fixes it.

What I learned

Keeping all arithmetic deterministic and letting Gemini do only judgment is what makes the output trustworthy and the tests fast and network-free — and it's the split that makes the agent demoable. Persistent memory is what turns a chatbot into an operator that knows my book over time. And "unattended" is a real engineering bar: retry-with-backoff and error-dict tools are what let the routine finish, on a schedule, without a human watching.

What's next

Backtest the flagged setups against outcomes to tune the specialists, add intraday risk alerts (not just the morning brief), and support multiple portfolios and users.

Prior work disclosed (New Projects Only)

Deskpilot is a new project built in the submission window. It reads positions from a published Google Sheet (CSV) and has no runtime dependency on any of the author's other systems — a reviewer can point it at their own sheet with one env var. The author populates their own sheet from a pre-existing project, broker-portfolio-sync (disclosed as prior work, not part of this repo), but that is an optional data-prep convenience, not a component of Deskpilot. All agent, tool, memory, serving, deploy, and test code in this repo was written for this submission.

Built With

  • cloud-scheduler
  • fastapi
  • firestore
  • gemini
  • google-adk
  • google-cloud-run
  • python
  • secret-manager
  • telegram
  • vertex-ai
  • yfinance
Share this project:

Updates

Submission history