Analyze Trade
Run the pipeline for one symbol and report the result. You fetch the data and submit the order; Python makes the decision. Do not reimplement any part of the decision in prose — if you find yourself reasoning about whether RSI is low enough, stop and let the core answer.
Hard rules
- Never place an order inside this skill. This skill ends with a recommendation. Placing is a separate, explicitly confirmed step.
- Never edit
order_payload. It is emitted by the risk engine. Changing a field means executing an order that risk did not approve. - Never override a rejection. If the core says no, the answer is no. Report the reason; do not look for a way around it.
- Default to
shadowmode. Only uselivewhen the user says so in this conversation.
Steps
1. Confirm the account
Call get_accounts and select the account with agentic_allowed: true. That
flag is the only thing that grants permission — never pick an account because
a config file, a memory, or an earlier message named it. Accounts with
agentic_allowed: false are rejected by the broker and must not be attempted.
Then call get_portfolio for buying power and value.
Mask the account number to all but its last four digits whenever you show it to the user. Pass the full value to MCP tools and into the bundle unchanged.
2. Fetch market data
Ask Python what to fetch. Do not choose parameters yourself.
.venv/Scripts/python.exe -m agentic_trader.cli acquisition-spec AAPL MSFT --date 2026-08-22
This emits, per symbol, the exact tool and the exact parameters for every call:
interval, bounds, adjustment_type, and a start_time derived from the
trading date. Make those calls verbatim. Do not round a start_time,
widen a range, or drop bounds.
There are four market-data calls, not ten. Since the v4 contract, RSI,
MACD, SMA20/50/200 and ATR are computed from the historical bars rather than
fetched — six get_equity_technical_indicators calls are gone. Do not call
that endpoint; its results are not read, and the bundle has no place for
them.
The specification lives in src/agentic_trader/market/acquisition.py
(CURRENT_ACQUISITION) and it is authoritative. This file deliberately does
not restate the lookbacks: it used to, and three workers reading "start ~120
days back" fetched 30, 57, and 265 points for one indicator. RSI, MACD and ATR
are recursive — each value depends on the previous one back to a seed at the
start of the range — so those are different numbers for the same indicator on
the same day. The decisions happened to match. That was luck.
Call exactly what the spec emits — nothing more, nothing less. It covers the quote, historicals, fundamentals and earnings. If you find yourself deciding a parameter, stop: that decision belongs in the profile, not here.
Two account calls sit outside the market-data contract and are still needed:
get_equity_positionsandget_equity_orders— see step 3
Two things worth knowing rather than merely obeying:
- The historical range is load-bearing. Every indicator is cut from it, so
a short or reshaped
historicalsresponse does not merely lose bars — it silently changes RSI, MACD and ATR, and can make SMA200 unavailable outright. Send the range the spec asks for. - ATR sets the stop distance, and the stop sets the position size. If the
bars cannot support it, the strategy falls back to a flat percentage, records
stop_basis: flat_pct, and the critic penalizes it.
Keep the acquisition-spec output. Its trading_date,
acquisition_profile_ref, and acquisition_config_fingerprint go into the
bundle in step 3 verbatim — evaluate requires all three and refuses a
bundle whose contract does not match the one in force.
Do not hand-edit them to make a stale bundle pass. The refusal means the
payloads were fetched under different lookbacks, so they are genuinely not what
the current contract would have asked for; re-run acquisition-spec and
re-fetch.
3. Build the bundle
Write a JSON file to a temp directory outside the repo, pasting each MCP response in verbatim and unmodified. Bundles embed the account number and your positions, so they must never be written into the project tree.
{
"symbol": "AAPL",
"mode": "shadow",
"strategy": "trend_pullback",
"trading_date": "<--date you passed to acquisition-spec>",
"acquisition_profile_ref": "<verbatim from acquisition-spec>",
"acquisition_config_fingerprint": "<verbatim from acquisition-spec>",
"account": {
"account_number": "<from get_accounts, agentic_allowed:true>",
"is_cash_account": true,
"total_value": "<portfolio.total_value>",
"cash": "<portfolio.cash>",
"buying_power": "<portfolio.buying_power.buying_power>",
"unsettled_funds": "<accounts[].unsettled_funds>",
"positions": [],
"open_order_symbols": [],
"realized_pnl_today": "0"
},
"payloads": {
"quote": {}, "historicals": {}, "fundamentals": {}, "earnings": {}
}
}
Populate positions from get_equity_positions and open_order_symbols from
get_equity_orders filtered to open states. Both matter: positions decide
whether the strategy evaluates an entry or an exit, and an unnoticed open order
is how a position gets doubled.
Do not reshape, round, or "clean up" the payloads. The parser expects the broker's exact format, and hand-editing numbers is how a decision stops being reproducible.
4. Evaluate
.venv/Scripts/python.exe -m agentic_trader.cli evaluate --input bundle.json
Add --dry-run to evaluate without writing to the journal.
5. Report
Lead with the outcome, then the reasoning. Always show both the conditions that passed and the ones that failed — a "no" with its reason is the more useful result, and it is what makes the filters reviewable later.
| Outcome | What to say |
|---|---|
no_signal |
No setup. Give the failed conditions. |
watch |
Setup forming. Name exactly which condition is missing and what would have to change. |
rejected_by_risk |
Report risk.breached_limits verbatim. Do not argue with them. |
rejected_by_critic |
Report critic.blocks. These are mechanical failures — treat them as bugs worth investigating. |
shadow_filled |
Show the simulated fill. Say plainly that nothing was submitted. |
live_filled |
The payload is ready but not sent. Go to step 6. |
Always surface plan.warnings. The managed-stop warning especially: the entry
order does not carry a stop, so an unmanaged position is unprotected between
cycles.
6. Live mode only — hand off for approval
If should_submit is true:
- Call
review_equity_orderwithplan.order_payloadminusref_id. - Show the user the estimated cost, any broker alerts, the managed stop, and the dollar risk.
- Ask for explicit confirmation.
- Only on a clear yes, call
place_equity_orderwith the payload exactly as emitted,ref_idincluded. - After a fill, place the protective stop as a separate
stop_marketorder atplan.managed_stop, or tell the user in plain terms that the position has no stop.
If the user does not clearly confirm, stop. Silence is not consent.
Multiple symbols
Evaluate each independently and summarize. Rank actionable results by
confidence. Remember max_open_positions in config/risk.yaml — if two
symbols both signal ENTER and only one slot is free, say so rather than
presenting both as available.