feat(live): add broker-agnostic PreTradeAdvisoryInterface (#317)#328
Merged
Conversation
…KUDS#317) Add broker-agnostic advisory interface for external risk assessment services to provide observational pre-trade opinions. Purely additive — never blocks or alters order execution. - Verdict enum: APPROVE, APPROVE_WITH_CONCERNS, REJECT, REVIEW_UNAVAILABLE - AdvisoryContext: frozen dataclass with 8 normalized fields - AdvisoryResult: frozen dataclass with auto-filled UTC timestamp - PreTradeAdvisoryInterface: abstract base with provider_id + review() - AdvisoryOrchestrator: sequential provider execution, fail-open aggregation - AggregatedVerdict: worst-case verdict with flattened concerns Signed-off-by: shadowinlife <shadowinlife@gmail.com>
Configurable in-memory advisory provider for test suites. Supports verdict injection, optional delay, forced failure, and call-history recording — everything needed to exercise the orchestrator and gate integration without any network dependency. Signed-off-by: shadowinlife <shadowinlife@gmail.com>
…l) (HKUDS#317) Add advisory review hook to LiveOrderGuardTool._allow() behind an opt-in environment variable (VIBE_TRADING_ENABLE_ADVISORY). Advisory verdicts are embedded in gate_decision['advisory'] for audit visibility but never block or alter order execution. - _advisory_review() builds AdvisoryContext from intent/positions/balance - Fail-open: any exception → REVIEW_UNAVAILABLE, order proceeds - Default off: env var unset = no advisory field in gate_decision - _allow() accepts positions/balance kwargs threaded from execute() Signed-off-by: shadowinlife <shadowinlife@gmail.com>
) 9 test cases covering: - Verdict enum members (4 values matching invinoveritas contract) - AdvisoryContext frozen dataclass - AdvisoryResult auto-timestamp - MockAdvisory default approve, configurable verdict, raise_on_review - AdvisoryOrchestrator fail-open on provider exception - Gate advisory disabled by default (no advisory in gate_decision) - Gate advisory enabled with mock provider (verdict embedded in audit) All tests pass, ruff clean, no network dependencies. Signed-off-by: shadowinlife <shadowinlife@gmail.com>
7ac4a85 to
131ce50
Compare
…ception sanitization Address review findings from PR HKUDS#328: - Add module-level provider registry (register/clear/get_advisory_providers) replacing the hardcoded AdvisoryOrchestrator([]) dead-code path - Add public aliases in enforcement.py for 3 private helpers used by advisory - Replace monkeypatch __init__ in test HKUDS#9 with registry-based injection - Sanitize exception messages: str(exc) → type(exc).__name__ in audit dicts - Add exc_info=True to logger.warning for full traceback in logs only - Add logger.info when advisory enabled but no providers registered - Rename account_drawdown → utilization_ratio (semantic accuracy) Co-Authored-By: OpenCode <noreply@opencode.ai> AI-Model: qwen3.7-max Co-Authored-By: opencode <noreply@ai-tool.com> Co-Authored-By: Claude Code <noreply@anthropic.com> AI-Contributed/Feature: 85/85 AI-Contributed/UT: 57/57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PreTradeAdvisoryInterfacefor external risk-assessment services to provide observational pre-trade opinions (e.g. invinoveritas/review)MockAdvisorywith configurable verdict, delay, exception injection, and call-history recording for test suitesLiveOrderGuardTool._allow()behind opt-in env varVIBE_TRADING_ENABLE_ADVISORY— purely observational, never blocks order executionWhy
The existing mandate gate answers "is this allowed?" (structural compliance: universe, notional, leverage, daily cap, kill switch). Issue #317 identified a missing layer above the mandate: a capital-scale-aware verdict on whether this specific order, right now is sound given the current account state — answering "is this wise?".
Per the community discussion, @warren618 requested:
This PR delivers exactly that first step. The
InvinoveritasAdvisoryHTTP implementation is a follow-up PR by @babyblueviper1.Closes #317
Changes
agent/src/live/advisory/__init__.py(new, 242 LOC):Verdictenum (APPROVE / APPROVE_WITH_CONCERNS / REJECT / REVIEW_UNAVAILABLE),AdvisoryContextfrozen dataclass (8 fields: symbol, side, notional_usd, account_equity, account_drawdown, open_position_count, total_exposure_usd, funding_usd),AdvisoryResultfrozen dataclass with auto-filled UTC timestamp,PreTradeAdvisoryInterfaceABC (provider_id + review()),AdvisoryOrchestrator(sequential execution, fail-open exception handling, worst-case aggregation),AggregatedVerdictwithall_concernsproperty.agent/src/live/advisory/mock.py(new, 88 LOC):MockAdvisoryimplementingPreTradeAdvisoryInterface— configurable verdict, optional delay, forced exception, call-history recording. No network dependencies.agent/src/live/order_guard.py(modified, +113/-4): Add_advisory_review()method toLiveOrderGuardToolthat buildsAdvisoryContextfrom intent/positions/balance/mandate, calls the orchestrator, and returns a verdict dict. Wire into_allow()before broker forward. Env-varVIBE_TRADING_ENABLE_ADVISORYgates activation (default off). Advisory verdict embedded ingate_decision["advisory"]for audit visibility. Fail-open: any exception → REVIEW_UNAVAILABLE, order proceeds unchanged._allow()signature extended withpositions/balancekwargs threaded fromexecute().agent/tests/test_advisory.py(new, 343 LOC): 9 unit tests following existing_MockAdapter/live_runtimefixture patterns fromtest_mandate_enforcement.pyandtest_killswitch_blocks_orders.py.Test Plan
Automated tests
conda run -n legonanobot python -m pytest tests/test_advisory.py -v→ 9 passed in 5.61s)conda run -n legonanobot python -m pytest tests/test_mandate_enforcement.py tests/test_killswitch_blocks_orders.py -q→ 23 passed, zero regressions)ruff check agent/src/live/advisory/ agent/tests/test_advisory.py→ 0 errors)Unit test breakdown (9 tests, no network required)
test_verdict_enum_memberstest_advisory_context_frozentest_advisory_result_auto_timestamptest_mock_advisory_default_approvetest_mock_advisory_configurable_verdicttest_mock_advisory_raise_on_reviewtest_orchestrator_catches_exceptiontest_gate_advisory_disabled_by_defaulttest_gate_advisory_enabled_with_mock_providerDesign decisions
REVIEW_UNAVAILABLE. An advisory failure must never affect execution.VIBE_TRADING_ENABLE_ADVISORYenvironment variable. When unset or falsy, the advisory layer is a complete no-op (zero overhead).gate_decision["advisory"]field. No newLiveActionKindor audit schema changes.AdvisoryContextdecouples providers from broker-specific payload shapes. The gate builds the context from the samepositions/balancereads thatcheck_mandatealready consumes — no additional broker calls.invinoveritas contract mapping
For the future
InvinoveritasAdvisoryimplementer (@babyblueviper1):Input:
AdvisoryContext→/reviewrequestsymbolcoinsidedirectionnotional_usdsize_usdleverage(needs mandate or order-level data)entry_price(needs live quote)confidence/regime/reasoning(signal-level, not order-level)Output:
/reviewresponse →AdvisoryResultverdictverdict(direct:approve→APPROVE, etc.)confidenceconfidencesummarysummaryissues[]concerns(tuple)proof.iddetail["proof_id"]Known limitations
sdk_order_gate.py(Tiger/Alpaca/OKX/Binance/Futu/Dhan/Shoonya connectors) has its own_allow()that does not receive advisory integration. MCP/Robinhood path first, SDK connectors in a follow-up.account_drawdownapproximation — usesmandate.hard_caps.account_funding_usdas a proxy for peak equity. TheInvinoveritasAdvisoryimplementer should pass actual peak equity from the broker's account read when available.Future recommendations
InvinoveritasAdvisory— HTTP client forPOST https://api.babyblueviper.com/reviewwith timeout (8s), 402 handling, signed proof verification (@babyblueviper1)LocalRulesAdvisory— pure-local rule engine (drawdown guard, concentration check). Zero network, zero cost.MCPAdvisory— generic adapter using existingMCPServerAdapterinfrastructuresdk_order_gate.pyfor direct-SDK connectorsVIBE_TRADING_ADVISORY_BLOCKING=truewith distinctadvisory_holddecision typeChecklist
check_mandate(),BreachEvent, or mandate enforcement logicVIBE_TRADING_ENABLE_ADVISORYenv varSigned-off-by:DCO trailer