Investobot spec ยท refresh to reread Markdown
mtime: 1780191376

# Investment Analyst Agent Spec

Status: Draft Feature owner: TBD Last updated: 2026-05-31

# Goal

Build a separate-process investment analyst agent that converts trusted upstream AI research into a final investment thesis, corresponding portfolio decision, and human-approved broker execution plan.

For v1, the primary upstream research source is the Capitalist Exploits McIntosh/MacIntyre newsletter folder at analyses/mcintosh/. The source model must allow additional newsletter folders later, but v1 implementation should stay focused on McIntosh. The agent must read numbered McIntosh issues, bias toward the most recent issue by largest issue number, understand the continuing thesis across recent issues, then perform deeper fundamental and technical analysis on candidate securities before producing a portfolio decision for a starting budget of USD 10,000.

The intended product is a self-running AI fund workflow. The investment analyst agent makes the final portfolio decision and, after explicit human approval, submits the approved trades through its own Interactive Brokers account. The human user reviews the decision record and approves or rejects execution; the system must not place trades, modify accounts, or imply guaranteed outcomes without that approval gate.

The strategy is investment-oriented, not trading-oriented. Assets selected for purchase must be suitable for a minimum expected holding period of at least 6 months. The agent may position cash in preparation for major market moves, but should not decide on short-term trading, high-turnover tactics, or entry/exit activity driven only by technical signals.

# Non-Goals

approval. The agent may submit trades only after the user approves the exact proposed portfolio/order set.

Anthropic, or any other financial/AI API.

workflows in v1.

enabled later. Runs should be manually triggered or explicitly invoked by an authenticated service endpoint.

read the latest completed decision and display it, but the analyst run itself must execute in a separate process/service.

Interactive Brokers credentials, raw filings, full upstream API responses, or full model transcripts.

abstraction, but implement McIntosh only for v1.

# Operating Model

The feature should run as a separate process, invoked from the command line:

uv run investobot-analyst run --source mcintosh --budget-usd 10000

The production deployment target is Fly.io. The analyst should run as a second service/process in the same Fly app, on its own machine, separate from the Flask web server machine. The Flask web app should display the latest completed decision, but the analyst run itself should not execute inside the web server process.

In addition to the CLI, expose an authenticated service endpoint for triggering an analysis run:

POST /run-analysis
Content-Type: application/json

Example request body:

{
  "source": "mcintosh",
  "budget_usd": 10000,
  "issue_numbers": [328, 327, 326]
}

The endpoint should create a PortfolioStrategy row with status analysis_in_progress, enqueue or start a bounded run, and return a run identifier. It must not block an HTTP request until a full analysis completes.

# Web App Integration

The main Flask app should read the latest analyst strategy from the database and show it as the current agent instruction set for human review and approval.

The web app must make the portfolio strategy status visually obvious:

The UI should show enough metadata for the user to understand what is happening:

The preferred presentation is a rich, interactive document embedded in the main web app. It should contain:

Use a trusted, conservative data-science stack. For dataframe work, use Polars. Do not add pandas for v1 unless a specific library integration requires it. Additional visualization or notebook dependencies require explicit approval, exact pins, a lockfile update, and the full check suite.

Reporting decision for v1: use marimo and export the strategy report as WebAssembly HTML. The report should be a rich document with charts, tables, and interactive controls, not a Markdown-only artifact.

Marimo notes:

artifacts.

storage, then serve them through the Flask web app or short-lived signed URLs.

HTTP.

before depending on them for the primary report.

Streamlit is not the v1 reporting target. It can remain a future alternative if marimo WebAssembly export proves incompatible with required charts or private deployment constraints.

Proposed package layout:

src/investobot/analyst/
  __init__.py
  cli.py
  config.py
  mcintosh.py
  fiscal_client.py
  broker.py
  ibkr_client.py
  eligibility.py
  fundamentals.py
  technicals.py
  filings.py
  portfolio.py
  report.py
  service.py

Proposed artifact object-key layout:

analyst-runs/<run-id>/
  report.py
  report.html
  assets/
  portfolio.json
  candidates.json
  methodology.json
  sources.json
  decision.json

Fly.io machines should be treated as ephemeral. The feature must not depend on local persistent volumes for analyst artifacts. Local disk may be used only as a temporary workspace during a run; durable artifacts must be uploaded to Tigris object storage before a strategy is submitted.

# Artifact Storage

Use Tigris object storage for generated analyst artifacts in Fly.io.

Tigris is S3-compatible object storage built into Fly.io infrastructure. A Fly Tigris bucket can be created with fly storage create. Fly may expose AWS-compatible secret names by default, but Investobot should normalize those into Tigris-specific app configuration so the application interface does not reference AWS.

Required behavior:

analyst-runs/<run-id>/.

artifacts before moving a strategy to strategy_submitted_awaiting_human_approval.

strategy and execution instructions.

Tigris, or generate short-lived signed URLs if direct browser access is needed.

transcripts, or account identifiers in artifacts.

strategy_submitted_awaiting_human_approval and surface a sanitized failure state.

Proposed environment values:

Deployment note: if Fly provisions AWS-compatible variable names for Tigris, map them to the TIGRIS_* names in Fly secrets or release configuration. Do not read AWS-named variables directly in application code.

Dependency note: Python S3 client support will require explicit dependency approval before implementation. Prefer a narrow existing-compatible option, pin it exactly, update uv.lock, and run the full check suite.

# Database Model

The analyst workflow needs durable database objects so the web app can show the current state even when the analyst service is not running. Use the existing Piccolo/SQLite setup.

# PortfolioStrategy

Tracks the overarching analysis and final strategy recommendation.

Proposed fields:

human approval and later agent submission.

source.

used. For McIntosh, these are McIntosh issue numbers.

required.

portfolio/order set for broker execution.

begins.

or all submitted orders reach a terminal tracked state.

marked implemented.

Only one strategy should normally be active at a time. If a new analysis starts while a prior strategy is awaiting approval, approved for execution, or in broker execution, the prior strategy must first be set to cancelled unless it has already submitted live broker orders. Once live orders exist, supersession must use an explicit reconciliation flow rather than silently cancelling the strategy. The UI must make cancellation/supersession visible so the human does not approve or execute stale instructions.

# PortfolioStrategyHolding

Tracks the final holdings attached to a strategy in a UI-friendly form.

Proposed fields:

# PortfolioStrategySource

Tracks source analysis references used by a strategy.

Proposed fields:

# Status Transitions

Allowed transitions:

analysis_in_progress
  -> strategy_submitted_awaiting_human_approval
  -> approved_for_broker_execution
  -> broker_execution_in_progress
  -> implemented

strategy_submitted_awaiting_human_approval
  -> cancelled

approved_for_broker_execution
  -> cancelled

broker_execution_in_progress
  -> implemented

analysis_in_progress
  -> cancelled

Failure behavior:

failure message associated with the in-progress strategy.

strategy, holdings, proposed execution instructions, and source references were generated.

approves the exact proposed portfolio/order set in the web app.

approved_for_broker_execution and the order set still matches the approved decision record.

complete and order/account state has been reconciled.

or a clearly recorded supersession flow before a replacement analysis begins.

# Inputs

# Newsletter Analyses

Newsletter inputs live under source-specific folders in analyses/, for example:

v1 implements McIntosh ingestion only, but the source-discovery shape should not hard-code an assumption that McIntosh will be the only newsletter forever. Future sources should be added as explicit source adapters with their own parsers, naming rules, and source-reference metadata.

# McIntosh Analyses

Input path: analyses/mcintosh/

Issue files are named like issue-328.md. The agent must:

  1. Discover all issue files with a strict filename parser.
  2. Sort by parsed issue number descending.
  3. Accept an optional explicit issue-number list for targeted strategy updates.
  4. Treat the largest selected issue number as the primary source.
  5. If no explicit issue list is provided, read enough recent history to identify

recurring themes and changes in conviction. For v1, default to the latest 3 issues.

  1. Extract:

The agent should effectively trust McIntosh's direction, but not blindly copy the final security list. It must validate purchaseability, account eligibility, valuation, balance sheet quality, liquidity, and current technical setup.

For traceability, every McIntosh issue used in the run must be recorded with its issue number, file path, and role in the final decision. The final report should include references to these source reports.

Current observed v1 trend from issues 326-328:

equipment/services, drilling support, LNG infrastructure, and broad hard asset/commodity exposure.

non-Middle-East production, oil and gas services capacity constraints, LNG disruption, and large-cap energy anchors.

and crowded financial assets.

# Starting Budget

Default budget: USD 10,000.

The budget is a hard sizing input. The portfolio engine must generate whole share quantities where possible and leave residual cash explicitly stated.

The output should be phrased as an approval-ready execution plan: what the agent will submit to Interactive Brokers after human approval, not merely a list of ideas to consider.

# Secrets

Required v1 secrets:

pydantic-ai Claude model configuration.

IBKR integration mode.

Secrets must be read through centralized environment access. They must never be printed, logged, written to generated reports, included in exceptions, or stored in local runtime artifacts.

The existing src/investobot/env.py pattern should be extended rather than adding ad hoc os.getenv reads across the codebase.

Non-secret artifact storage configuration, such as TIGRIS_BUCKET_NAME, TIGRIS_ENDPOINT_URL, and TIGRIS_REGION, should also be centralized in src/investobot/env.py.

# External Data

# Fiscal.ai

Use Fiscal.ai as the primary financial data provider.

Fiscal.ai confirmed that ETF securities such as XLE are not currently included in the API. For v1, ETF securities are therefore unsupported by the investment analyst agent. Fiscal-backed analysis must operate on individual companies only. McIntosh-mentioned ETFs may still be used as thematic context or benchmark references, but they must not be selected for the final portfolio until a separate ETF-capable data source is approved and implemented.

Relevant documented endpoints include:

availability.

balance sheet, and cash flow statement data.

line items.

point-in-time valuation and profitability ratios.

filing review.

API keys should be sent through the X-Api-Key header when supported, not as a query parameter, to reduce accidental leakage through URLs.

# Interactive Brokers Purchaseability and Account Eligibility

Candidate securities must pass an Interactive Brokers suitability gate before they can appear in the final portfolio.

For v1, define this as a conservative two-step gate:

  1. Purchaseability check:

Brokers in the agent's account.

identifiers suitable for deterministic IBKR order submission.

manually settled securities are excluded unless explicitly enabled later.

  1. Account eligibility check:

type, permissions, country, currency, and regulatory constraints.

traded on a designated stock exchange or otherwise meet qualified investment rules for that account type.

of Canada / Department of Finance guidance, not inferred only from ticker format.

designated stock exchanges list to determine whether the listing venue is designated, and cross-check CRA qualified-investment guidance for the account-level rule when relevant.

contract lookup and a non-transmitting order preview/what-if path where available.

the final portfolio and place it in a watchlist/rejected section.

Important v1 implication: several McIntosh-mentioned securities may be useful for thematic context but should not automatically enter the portfolio because foreign exchange access, IBKR account permissions, liquidity, or account eligibility may be uncertain.

Examples likely to need rejection or manual confirmation:

The final report must state which McIntosh ideas were excluded and why.

# Broker Execution via Interactive Brokers

Interactive Brokers is the v1 execution broker for the agent's own trading account. The broker integration must be treated as an execution subsystem, not as an analysis shortcut.

Required execution flow:

  1. The analyst run produces a proposed portfolio and exact proposed order set.
  2. The web app displays the proposed orders, sizing, prices used, risks, source

references, and any assumptions that affect execution.

  1. The human explicitly approves or rejects the exact order set.
  2. After approval, the agent may submit only the approved orders through

Interactive Brokers.

  1. The system records submitted order ids, broker status, fills, partial fills,

rejects, cancellation results, and final reconciled positions.

  1. The strategy becomes implemented only after broker/account state is

reconciled against the approved decision record.

Execution guardrails:

approved_for_broker_execution.

paths for development and tests where available.

type; do not use market orders unless the user explicitly approves that policy.

or account funding through the agent.

store raw credentials, session cookies, complete account identifiers, or raw broker response bodies in reports or durable artifacts.

surface a visible reconciliation state instead of silently treating the strategy as implemented.

# Candidate Universe

Candidate generation should merge:

  1. Direct McIntosh tickers from recent issues.
  2. Liquid, Interactive-Brokers-accessible proxies for sectors McIntosh favors.
  3. Large-cap anchors that express the same thesis with lower operational and

liquidity risk.

Initial v1 universe should prioritize:

purchaseable, account eligible, and express the same thesis. If no suitable Canadian-listed equivalent exists, use the US-listed entity.

Candidate examples for investigation, not automatic inclusion:

v1 because Fiscal.ai does not currently support ETF securities.

The agent may include a McIntosh-mentioned foreign listing only if the Interactive Brokers purchaseability and account-eligibility gates pass with high confidence.

# Investment Policy

The analyst should behave like a long-horizon capital allocator, not a trader.

Required policy:

trigger fires.

poor or when McIntosh's thesis implies waiting for a dislocation.

but should not produce standalone short-term trades.

do not clear the fundamental, eligibility, liquidity, and thesis-fit gates.

future runs can distinguish genuine long-term thesis breaks from ordinary volatility.

secondary to the core investment thesis. Preferred hedges are cash reserves, reduced gross exposure, defensive eligible holdings, or a small allocation to a liquid non-leveraged hedge instrument that is suitable for a 6-month minimum holding period. The hedge rationale, sizing, expected behavior, and risk must be documented.

Disallowed in v1:

# Fundamental Methodology

Fundamental analysis has priority over technical analysis.

Each final portfolio holding should receive a score from 0-100 composed of:

Required checks:

trends across annual and recent quarterly periods.

capex super-cycle thesis.

ETF analysis is out of scope for v1 because Fiscal.ai does not currently support ETF securities such as XLE. If ETF support is added in a later version through an approved ETF-capable data provider, ETF analysis should focus on:

proxy.

# Filing Analysis Methodology

Use pydantic-ai with Claude Opus 4.7 to analyze raw company filings. The initial model id should be claude-opus-4-7, verified against Anthropic's current model documentation during implementation.

The filing-analysis step should:

  1. Retrieve recent annual/interim filings through Fiscal.ai where available.
  2. Provide the model only the relevant filing text or extracted sections needed

for the analysis.

  1. Ask for structured output matching a local Pydantic model, such as:
  1. Store only summarized, non-secret structured outputs.

Raw filing text may be cached only if explicitly approved later. The v1 default should avoid storing full filing bodies.

Dependency note: adding pydantic-ai and Anthropic provider dependencies will require explicit dependency approval before implementation, exact pins in pyproject.toml, uv lock, and the full check suite.

Cost-control note: the implementation should record token usage and estimated cost per run when provider metadata is available. A hard per-run budget cap is not specified in this draft.

Dependency note: Polars is part of the intended v1 data-science stack. It still must be added with an exact pin, lockfile update, and the full check suite.

# Technical Methodology

Technical analysis is a secondary timing and risk-control input.

Each candidate should receive a 0-100 technical score composed of:

Minimum indicators:

comparable company peer basket. ETF-relative strength, including against XLE, is deferred until an ETF-capable data source is implemented.

Technical analysis should not override a failing fundamental or eligibility screen. It can affect sizing, entry timing, watchlist placement, and the amount held in cash. A poor technical setup may defer deployment, but it should not turn the system into a short-term trading strategy.

# Portfolio Construction

The portfolio should be long-only in v1.

Default constraints:

wait for a major expected market move or when too few eligible candidates pass the investment gates.

securities.

The portfolio should generally express the latest McIntosh thesis using liquid, eligible instruments:

thesis-aligned opportunities.

For each proposed holding, output:

Rejected candidates should include:

# Output Report

The primary report output is a marimo WebAssembly HTML document:

analyst-runs/<run-id>/report.html

The generated marimo notebook source should also be retained:

analyst-runs/<run-id>/report.py

Required sections:

  1. Executive summary.
  2. Upstream McIntosh thesis summary.
  3. Candidate universe and exclusions.
  4. Fundamental methodology.
  5. Technical methodology.
  6. Security-level analysis.
  7. Final USD 10,000 portfolio decision.
  8. Residual cash.
  9. Risks and invalidation triggers.
  10. Human approval instructions and proposed broker order set.
  11. Source reports, data sources, and timestamps.
  12. Execution-boundary notes.

The report should avoid raw API payloads and model transcripts. It should be safe to serve from the Flask app to the authenticated user, but it should still be treated as sensitive because it contains financial strategy and execution instructions.

Machine-readable outputs:

Database persistence is authoritative for web-app status display. JSON/report artifacts in Tigris support auditability and richer rendering, but the web app should not need to parse the marimo report to know whether a strategy is in progress, awaiting human approval, approved for broker execution, in broker execution, or implemented.

# Security and Privacy Requirements

callback URLs, or raw upstream response bodies in logs or reports.

phrase, content type, and response length.

financial intent and portfolio recommendations.

calls from browser code.

the exact approved order set.

# Error Handling

The agent should fail closed when eligibility, pricing, or data freshness is uncertain.

Examples:

the key or request URL query.

portfolio unless there is another reliable data source approved later.

confidence low, and do not overweight the candidate.

store only sanitized error metadata, and do not expose local temporary paths.

as analysis_in_progress with updated timestamp and sanitized error metadata if failure can be detected.

broker execution: reject by default unless the request explicitly cancels/supersedes the awaiting strategy first. Persist cancelled_at and make the cancelled strategy visible in the strategy history.

explicit reconciliation of submitted orders, fills, partial fills, and account state before a replacement strategy can become executable.

# Testing Strategy

Unit tests:

redacts storage credentials from errors.

API calls.

on local durable storage.

order set, and holding horizon.

creating a replacement in-progress strategy.

Integration tests:

without starting an analyst run in the Flask process.

strategy_submitted_awaiting_human_approval only after a complete decision is persisted.

strategy_submitted_awaiting_human_approval to approved_for_broker_execution.

broker_execution_in_progress to implemented using fixture-backed broker responses.

approved-but-unsubmitted strategy as cancelled before creating the new analysis_in_progress strategy.

uploaded to Tigris with its required assets.

without relying on local files.

No live Fiscal.ai, Anthropic, Questrade, or Interactive Brokers calls should run in the default test suite.

# Acceptance Criteria

v1 is complete when:

and return a run identifier.

analysis_in_progress.

holdings, source references, artifact object keys, and status strategy_submitted_awaiting_human_approval.

and reconcile fixture-backed order/fill state before marking the strategy implemented.

strategy as cancelled.

running analysis inside the web process.

highest-priority source, and incorporates recent context.

the final report and sources.json.

strategy updates.

credentials/configuration through secure environment configuration.

proxies.

eligibility gates or is excluded.

months for every purchase.

for a major market move.

are produced.

with charts, tables, and source references.

artifacts are uploaded to Tigris under the run prefix.

assets/ over HTTP for the authenticated user.

set are produced before any broker submission is possible.

analyst-runs/<run-id>/.

uv run ruff format --check .
uv run ruff check .
uv run pyright
uv run pytest -q

# Resolved Decisions

claude-opus-4-7 after verifying current Anthropic docs during implementation.

exchanges list and CRA qualified-investment guidance as primary rule sources when the agent account type requires it. Use Interactive Brokers contract lookup and non-transmitting order preview/what-if capability for broker purchaseability where available.

are liquid, eligible, and thesis-equivalent. Otherwise use the US-listed security.

constraints.

lists for targeted strategy updates, and default to the latest 3 McIntosh issues when no list is provided. Future newsletters should be added as explicit source adapters reading from source-specific analyses/<source>/ folders.

strategy is marked cancelled, unless live broker orders have already been submitted; then explicit order/fill reconciliation is required.

persistent volumes or local durable artifact paths.

# Phased Implementation Plan

# Phase 1: Data Model and Status UI Foundation

Implement Piccolo tables and migrations for PortfolioStrategy, PortfolioStrategyHolding, and PortfolioStrategySource. Add status transition helpers for analysis_in_progress, strategy_submitted_awaiting_human_approval, approved_for_broker_execution, broker_execution_in_progress, implemented, and cancelled. Add a minimal Flask view that shows the active strategy status and latest persisted metadata.

Next phase gate:

# Phase 2: Analyst Service Shell

Add the analyst CLI and separate service endpoint skeleton. The endpoint should create an analysis_in_progress strategy row, return a run id, reject ambiguous concurrent runs, and support explicit cancellation/supersession before a new run. Add centralized Tigris configuration fields to environment loading, but do not perform live object-storage writes yet.

Next phase gate:

leaking credentials.

# Phase 3: McIntosh Ingestion and Source Tracking

Implement issue discovery, strict issue-number parsing, default latest-3 selection, explicit issue-number selection, source summarization, and PortfolioStrategySource persistence.

Next phase gate:

# Phase 4: Fiscal.ai Client, Eligibility Gates, and Data Fixtures

Implement the Fiscal.ai client with sanitized errors and secure API-key header handling. Add Interactive Brokers purchaseability and account-eligibility gates using IBKR contract metadata/order preview plus Government of Canada/CRA rule sources where account type requires them. Build sanitized fixtures so the default test suite never makes live Fiscal.ai, Anthropic, Questrade, or Interactive Brokers calls. Do not add v1 ETF support; Fiscal.ai does not currently include ETF securities such as XLE, so ETFs should be treated as unsupported candidates unless a separate approved ETF-capable provider is introduced in a future phase.

Next phase gate:

# Phase 5: Fundamental, Technical, and Portfolio Engine

Implement Polars-based analysis, fundamental scoring, technical scoring, Canadian-listed-equivalent preference, unsupported-ETF rejection, minimal hedging constraints, cash positioning, whole-share sizing, and final strategy persistence.

Next phase gate:

periods, hedging cap, and rejected candidates.

# Phase 6: Filing Analysis with pydantic-ai and Claude Opus 4.7

Add structured filing-analysis models and pydantic-ai integration with Claude Opus 4.7. Store only summarized structured outputs, token usage, estimated cost when available, confidence flags, and missing-data notes.

Next phase gate:

Anthropic provider package.

# Phase 7: Local Marimo Analysis Notebook

Introduce marimo as a local analyst-review surface before adding durable artifact storage or browser-served reports. Generate a local marimo notebook from the completed strategy inputs and outputs so the agent can conduct an analysis and the human can run the notebook locally for assessment.

This phase is intentionally local-only:

The notebook should be generated into a local ignored working area, such as data/analyst-runs/<run-id>/analysis.py, and should be reproducible from persisted database state plus locally available source files. It should include the McIntosh thesis context, candidate table, Fiscal-backed fundamental and technical score outputs, rejected candidate notes, proposed holdings, residual cash, and proposed Interactive Brokers order instructions for human approval.

Next phase gate:

package.

during notebook rendering/generation.

review workflow before starting Phase 8.

# Phase 8: Tigris Artifact Storage

Implement an artifact storage layer for Tigris using S3-compatible APIs. Upload JSON artifacts, generated notebook source, exported report HTML, and report assets under analyst-runs/<run-id>/. Persist object keys on PortfolioStrategy. Add authenticated Flask routes or signed-URL support for retrieving report artifacts without relying on local files.

Next phase gate:

storage client.

redacted from errors.

# Phase 9: Marimo WebAssembly Report

Generate a marimo report.py from persisted structured artifacts, export it to report.html with marimo export html-wasm, include required assets/, upload the report artifacts to Tigris, and serve the report through the Flask app for the authenticated user.

Next phase gate:

packages.

and tables.

# Phase 10: Interactive Brokers Execution with Human Approval

Implement the broker execution subsystem for Interactive Brokers. The first implementation should support fixture-backed contract lookup, order preview or what-if validation where available, persisted human approval events, exact order set hashing/comparison, order submission after approval, submitted-order tracking, fill/reject/partial-fill reconciliation, and visible execution status in the Flask app.

This phase must not broaden the trading policy. It should preserve the v1 long-only, no-margin, no-options, no-shorting constraints unless the spec is explicitly changed later.

Next phase gate:

dependency or integration mode.

changed order sets.

# Phase 11: Fly.io Deployment Shape and Hardening

Wire the analyst service as a separate Fly process/machine from the Flask web server. Add operational safeguards for bounded runs, sanitized errors, visible failure state, cancellation/supersession, Tigris artifact object keys, active strategy display, human approval, and broker execution reconciliation.

Next phase gate:

secret handling are reviewed.

source references, Tigris-backed JSON artifacts, marimo report, proposed broker order set, human approval record, and reconciled broker-execution result.

# Standard Checks

Every phase gate requires:

uv run ruff format --check .
uv run ruff check .
uv run pyright
uv run pytest -q

# References

https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-6

https://docs.marimo.io/guides/exporting/webassembly_html/

https://docs.streamlit.io/deploy/streamlit-community-cloud/share-your-app/embed-your-app

https://www.canada.ca/en/revenue-agency/services/tax/individuals/topics/tax-free-savings-account/types-investments.html

https://www.canada.ca/en/department-finance/services/designated-stock-exchanges.html