{
  "schemaVersion": "1.0",
  "item": {
    "slug": "riskofficer",
    "name": "Riskofficer Openclaw Skill",
    "source": "tencent",
    "type": "skill",
    "category": "效率提升",
    "sourceUrl": "https://clawhub.ai/mib424242/riskofficer",
    "canonicalUrl": "https://clawhub.ai/mib424242/riskofficer",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/riskofficer",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=riskofficer",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "README.md",
      "SKILL.md",
      "clawhub.json",
      "references/academic-references.md",
      "references/methodology-aggregation.md",
      "references/methodology-auto-portfolio.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T16:55:25.780Z",
      "expiresAt": "2026-05-07T16:55:25.780Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
        "contentDisposition": "attachment; filename=\"network-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/riskofficer"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/riskofficer",
    "agentPageUrl": "https://openagent3.xyz/skills/riskofficer/agent",
    "manifestUrl": "https://openagent3.xyz/skills/riskofficer/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/riskofficer/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "RiskOfficer Portfolio Management",
        "body": "Connects to the RiskOfficer API to manage investment portfolios and calculate financial risk metrics.\n\nRequired: One environment variable — RISK_OFFICER_TOKEN (create in RiskOfficer app → Settings → API Keys). No other env vars or binaries are required.\n\nSource: Official skill repository: github.com/mib424242/riskofficer-openclaw-skill. Product: riskofficer.tech. The token is issued only by the RiskOfficer app; this skill does not collect or store credentials."
      },
      {
        "title": "Credentials and token handling",
        "body": "This skill does not store or log your token. The token is sent only in the HTTP Authorization header to api.riskofficer.tech; it is not written to disk, logged, or sent elsewhere. Where you store the token (environment variable or ~/.openclaw/openclaw.json) is entirely under your control.\nPrefer setting RISK_OFFICER_TOKEN as an environment variable for the session rather than saving it in openclaw.json. If you use openclaw.json, restrict file permissions and be aware which agents or users can read that file.\nRiskOfficer currently issues account-level tokens (no scoped tokens). Create a token named for this skill (e.g. \"OpenClaw\") and revoke it in the RiskOfficer app if you stop using the skill.\nToken scope: The token allows the skill to access your RiskOfficer data (portfolios, risk calculations, broker-synced positions for read-only analysis). Revoke or rotate the token if you need to revoke access.\nVerify links: Confirm that github.com/mib424242/riskofficer-openclaw-skill and riskofficer.tech match the publisher you trust before installing or providing a token."
      },
      {
        "title": "Scope: analysis and research only (virtual portfolios)",
        "body": "All portfolio data and operations in this skill take place inside RiskOfficer’s own environment. Portfolios you create, edit, or optimize here are virtual — they are used for analysis and research only. The agent can:\n\nRead your portfolios (including those synced from brokers) to show positions, history, and risk metrics\nCreate and change virtual/manual portfolios and run optimizations inside RiskOfficer\nRun calculations (VaR, Monte Carlo, stress tests) on these portfolios\n\nNothing in this skill places or executes real orders in your broker account. Broker sync is read-only for analysis; any rebalancing or trading in the real account is done by you in your broker’s app or in RiskOfficer’s own flows, not by the assistant. The token is used only to access RiskOfficer’s API for this analytical and research use."
      },
      {
        "title": "Setup",
        "body": "Open RiskOfficer app → Settings → API Keys\nCreate a new token named \"OpenClaw\"\nSet environment variable: RISK_OFFICER_TOKEN=ro_pat_...\n\nOr configure in ~/.openclaw/openclaw.json:\n\n{\n  \"skills\": {\n    \"entries\": {\n      \"riskofficer\": {\n        \"enabled\": true,\n        \"apiKey\": \"ro_pat_...\"\n      }\n    }\n  }\n}"
      },
      {
        "title": "API Base URL",
        "body": "https://api.riskofficer.tech/api/v1\n\nAll requests require: Authorization: Bearer ${RISK_OFFICER_TOKEN}"
      },
      {
        "title": "Currency policy",
        "body": "Supported currencies: RUB and USD only. No EUR/CNY/other in API contracts for base or analysis currency.\nFX source: All exchange rates are CBR (Central Bank of Russia) via Data Service (MOEX/CBR). There is no alternative provider.\nSingle currency per portfolio: Each portfolio must contain assets in one currency (all MOEX or all US). Mixed-currency portfolios are not supported; create separate portfolios.\nAggregated view: User chooses base_currency (RUB or USD); sub-portfolios in the other currency are converted using CBR rates."
      },
      {
        "title": "Ticker Search",
        "body": "Search Tickers\n\nUse this before creating or editing any portfolio to validate ticker symbols and get their currency/exchange info. Also use when the user mentions a company name instead of a ticker.\n\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/search?q=Apple&limit=10&locale=en\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nQuery params:\n\nq (optional): search query — by ticker, name, or full name (case-insensitive). Omit to get popular tickers sorted by popularity.\nlimit (optional, default 20, max 50): number of results\ninclude_prices (optional, default false): include current_price, price_change_percent, price_change_absolute, price_date\nlocale (optional, default ru): en for English names, ru for Russian names\nexchange (optional): filter by exchange — MOEX, NYSE, NASDAQ, CRYPTO\n\nResponse: tickers array, each with: ticker, name, full_name, instrument_type, currency, exchange, popularity_score, isin.\n\nInstrument types: share, bond, etf, futures, futures_continuous (e.g. BR, SI on MOEX), currency, crypto\n\nKey rules:\n\nAlways use ticker search to resolve company names → ticker symbols (e.g. \"Apple\" → \"AAPL\", \"Sberbank\" → \"SBER\")\nUse currency field from the result to check same-currency constraint before adding to a portfolio\nMOEX futures: searching \"BR\" or \"SI\" returns the continuous contract, not individual contracts (BRF6, SIM5)\nUse include_prices=true to show current price when user asks \"how much is X worth?\"\n\n# Search by company name (English)\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/search?q=Gazprom&locale=en&limit=5\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n# Search by Russian name\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/search?q=%D0%93%D0%B0%D0%B7%D0%BF%D1%80%D0%BE%D0%BC&locale=ru&limit=5\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n# Get current price for a ticker\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/search?q=AAPL&include_prices=true\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n# Get popular tickers (no query param)\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/search?limit=10&include_prices=true\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n# Filter by exchange\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/search?q=SBER&exchange=MOEX\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nGet Historical Ticker Prices\n\nWhen the user asks about price history, chart data, or trends for specific assets:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/historical?tickers=SBER,GAZP,AAPL&days=30\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nQuery params: tickers (required, comma-separated, max 50), days (optional, default 7, max 252 trading days).\n\nResponse: data object keyed by ticker symbol, each with:\n\nprices: array of {date, close} objects\ncurrent_price, price_change_percent, price_change_absolute"
      },
      {
        "title": "Portfolio Management",
        "body": "List Portfolios\n\nWhen the user asks to see their portfolios or wants an overview:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/portfolios/list\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nQuery params: portfolio_type (optional): \"production\" (manual + live brokers), \"sandbox\" (broker sandbox only), \"all\" (default).\n\nResponse: array of portfolios with snapshot_id, name, total_value, currency, positions_count, broker, sandbox, active_snapshot_id (UUID or null — if set, risk calculations use this historical snapshot instead of the latest).\n\nGet Portfolio Details\n\nWhen the user wants to see positions in a specific portfolio:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/portfolio/snapshot/{snapshot_id}\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nResponse: name, total_value, currency, positions (array with ticker, quantity, current_price, value, weight, avg_price).\n\nGet Portfolio History\n\nWhen the user asks how their portfolio changed over time or wants to browse past snapshots:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/portfolio/history?days=30\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nQuery params: days (optional, default 30, range 1–365).\n\nResponse: snapshots array with snapshot_id, timestamp, total_value, positions_count, sync_source, type (aggregated/manual/broker), name, broker, sandbox.\n\nGet Snapshot Diff (compare two portfolio versions)\n\nWhen the user wants to compare two portfolio states (e.g. before/after rebalancing, or two dates):\n\ncurl -s \"https://api.riskofficer.tech/api/v1/portfolio/snapshot/{snapshot_id}/diff?compare_to={other_snapshot_id}\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nResponse: added/removed/modified positions, total_value_delta. Both snapshots must belong to the user.\n\nGet Aggregated Portfolio\n\nWhen the user asks for their total or combined portfolio across all accounts:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/portfolio/aggregated?type=all\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nQuery params:\n\ntype=production — manual + broker live accounts\ntype=sandbox — broker sandbox only\ntype=all — everything (default)\n\nResponse:\n\nportfolio.positions — all positions merged across portfolios\nportfolio.total_value — total in base currency\nportfolio.currency — base currency (RUB or USD)\nportfolio.sources_count — number of portfolios aggregated\nexchange_rates (optional): USD_RUB, EUR_RUB, CNY_RUB, rate_date, rate_source (always \"CBR\"), base_currency, fx_quality (\"live\" = from CBR/cache, \"default\" = static fallback used when CBR unavailable, \"none\" = no conversion)\nwarnings — e.g. mixed-currency positions, FX fallback used\ndata_quality (optional): fx_coverage (share of FX lookups with live rate), fx_live_count, fx_default_count, fx_unavailable_count, portfolios_included, portfolios_excluded — for observability of aggregation and FX usage\n\nExample response:\n\n{\n  \"portfolio\": {\n    \"positions\": [\n      {\"ticker\": \"SBER\", \"quantity\": 150, \"value\": 42795, \"sources\": [\"T-Bank\", \"Manual\"]},\n      {\"ticker\": \"AAPL\", \"quantity\": 10, \"value\": 189500, \"original_currency\": \"USD\"}\n    ],\n    \"total_value\": 1500000,\n    \"currency\": \"RUB\",\n    \"sources_count\": 3\n  },\n  \"snapshot_id\": \"uuid-of-aggregated\"\n}\n\nPositions in different currencies are automatically converted using CBR (Central Bank of Russia) rates. Only RUB and USD are supported; each sub-portfolio must have assets in a single currency.\n\nChange Base Currency (Aggregated Portfolio)\n\nWhen the user wants to see the aggregated portfolio in a different currency:\n\ncurl -s -X PATCH \"https://api.riskofficer.tech/api/v1/portfolio/{aggregated_snapshot_id}/settings\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"base_currency\": \"USD\"}'\n\nSupported currencies: RUB, USD. The aggregated portfolio recalculates automatically after change.\n\nUser prompt examples:\n\n\"Show everything in dollars\" / \"Покажи всё в долларах\" → base_currency: \"USD\"\n\"Switch to rubles\" / \"Переведи в рубли\" → base_currency: \"RUB\"\n\nInclude/Exclude Portfolio from Aggregated\n\nWhen the user wants to exclude a specific portfolio from total calculations:\n\ncurl -s -X PATCH \"https://api.riskofficer.tech/api/v1/portfolio/{snapshot_id}/settings\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"include_in_aggregated\": false}'\n\nUser prompt examples:\n\n\"Exclude sandbox from total\" / \"Не учитывай песочницу в общем портфеле\"\n\"Remove demo portfolio from calculations\" / \"Убери демо-портфель из расчёта\"\n\nCreate Manual Portfolio\n\nWhen the user wants to create a new portfolio with specific positions:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/manual\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"My Portfolio\",\n    \"positions\": [\n      {\"ticker\": \"SBER\", \"quantity\": 100},\n      {\"ticker\": \"GAZP\", \"quantity\": 50, \"avg_price\": 148.0},\n      {\"ticker\": \"YNDX\", \"quantity\": -20}\n    ]\n  }'\n\nFields:\n\nticker (required): ticker symbol. Always use /tickers/search first to validate and check currency.\nquantity (required): number of shares. Negative = short position (e.g. -20 = short 20 shares).\navg_price (optional): average purchase/entry price for P&L tracking. If omitted on new portfolio → uses current market price. If omitted on edit → inherits from previous snapshot.\n\nQuery params: locale (optional, default ru) — affects asset name resolution.\n\nCurrency policy (platform-wide): Only RUB and USD are supported. All FX rates come from CBR (Central Bank of Russia) via Data Service (MOEX/CBR). There is no choice of FX provider — it is always CBR.\n\nIMPORTANT — Single Currency Rule:\nAll assets in one portfolio must be in the same currency.\n\nRUB assets (MOEX): SBER, GAZP, LKOH, YNDX, etc.\nUSD assets (NYSE/NASDAQ): AAPL, MSFT, GOOGL, TSLA, etc.\nCannot mix currencies in a single portfolio! Suggest creating separate portfolios.\n\nShort positions:\n\nUse negative quantity for shorts (e.g. {\"ticker\": \"GAZP\", \"quantity\": -50})\nLong + short in the same portfolio is supported (long-short portfolio)\nWhen optimizing a long-short portfolio, use optimization_mode: \"preserve_directions\" to keep shorts\n\nUpdate Portfolio (Add/Remove Positions)\n\nWhen the user wants to modify an existing portfolio:\n\nGet current positions:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/portfolio/snapshot/{snapshot_id}\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nRepost with the same name and updated full positions list:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/manual\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"<same name>\", \"positions\": [<complete updated list>]}'\n\nIMPORTANT: Always show the user what will change and ask for confirmation before updating. avg_price is preserved from previous snapshot unless explicitly specified.\n\nDelete Manual Portfolio\n\nWhen the user wants to delete/remove a manual portfolio entirely:\n\ncurl -s -X DELETE \"https://api.riskofficer.tech/api/v1/portfolio/manual/My%20Portfolio\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nPortfolio name must be URL-encoded\nArchives all snapshots for that portfolio — irreversible\nALWAYS confirm with the user before deleting — cannot be undone\nResponse: archived_snapshots count, portfolio_name, message\n\nDelete Broker Portfolio Snapshots\n\nWhen the user wants to clear broker portfolio history without disconnecting the broker:\n\ncurl -s -X DELETE \"https://api.riskofficer.tech/api/v1/portfolio/broker/tinkoff?sandbox=false\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nsandbox=true for sandbox portfolio, sandbox=false for live/production\nArchives snapshots only; broker connection stays active\nNext sync will create a new snapshot\nALWAYS confirm before deleting"
      },
      {
        "title": "Broker Integration",
        "body": "List Connected Brokers\n\nWhen the user asks about their broker connections:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/brokers/connections\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nList Available Broker Providers\n\nWhen the user asks what brokers are supported:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/brokers/available\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nSync Portfolio from Broker\n\nWhen the user wants to refresh/update their portfolio from a connected broker:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/proxy/broker/{broker}/portfolio\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"sandbox\": false}'\n\n{broker}: tinkoff or alfa\nsandbox: false for live account, true for Tinkoff sandbox\n\nIf response is 400 with missing_api_key, the broker is not connected. Guide the user:\n\nGet API token from https://www.tbank.ru/invest/settings/api/\nOpen RiskOfficer app → Settings → Brokers → Connect Tinkoff\nPaste token and connect\n\nDisconnect Broker\n\nWhen the user wants to remove a broker connection:\n\ncurl -s -X DELETE \"https://api.riskofficer.tech/api/v1/brokers/connections/tinkoff?sandbox=false\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nsandbox=false for live connection, sandbox=true for sandbox\nRemoves the connection and its saved API key; portfolio snapshot history is preserved\nTo also delete snapshot history, first use DELETE /portfolio/broker/{broker}?sandbox=false\nALWAYS confirm before disconnecting — reconnection requires the mobile app\n\nDifference between the two delete endpoints:\n\nActionDELETE /portfolio/broker/{id}DELETE /brokers/connections/{id}Deletes snapshots✅ Yes (archives history)❌ No (history kept)Deletes connection❌ No✅ YesCan sync again without re-connecting✅ Yes❌ No"
      },
      {
        "title": "Active Snapshot Selection",
        "body": "By default, all risk calculations use the latest snapshot. You can pin a historical snapshot to run calculations on a past portfolio state — useful for backtesting risk or comparing \"before vs after\" rebalancing.\n\nSet Active Snapshot\n\nWhen the user wants to run risk calculations on a historical version of their portfolio:\n\ncurl -s -X PATCH \"https://api.riskofficer.tech/api/v1/portfolio/active-snapshot\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"portfolio_key\": \"manual:My Portfolio\", \"snapshot_id\": \"{historical_snapshot_id}\"}'\n\nportfolio_key format:\n\nPortfolio typeFormatExampleAggregatedaggregated\"aggregated\"Manualmanual:{name}\"manual:My Portfolio\"Broker livebroker:{broker_id}:false\"broker:tinkoff:false\"Broker sandboxbroker:{broker_id}:true\"broker:tinkoff:true\"\n\nWorkflow:\n\nGET /portfolio/history?days=90 → pick snapshot by date\nPATCH /portfolio/active-snapshot with chosen snapshot_id + portfolio_key\nRun VaR / Monte Carlo — uses selected historical snapshot\nReset when done (see below)\n\nIn /portfolios/list: active_snapshot_id field shows the pinned snapshot (null = using latest).\n\nReset Active Snapshot to Latest\n\ncurl -s -X DELETE \"https://api.riskofficer.tech/api/v1/portfolio/active-snapshot?portfolio_key=manual:My%20Portfolio\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nUser prompt examples:\n\n\"Calculate risk for my portfolio as it was a month ago\" / \"Посчитай риски как было месяц назад\" → set active snapshot\n\"Go back to current portfolio\" / \"Сбрось на текущий портфель\" → DELETE active-snapshot"
      },
      {
        "title": "Risk Calculations",
        "body": "Calculate VaR (FREE)\n\nWhen the user asks to calculate risk, VaR, or risk metrics:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/risk/calculate-var\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portfolio_snapshot_id\": \"{snapshot_id}\",\n    \"method\": \"historical\",\n    \"confidence\": 0.95,\n    \"horizon_days\": 1,\n    \"force_recalc\": false\n  }'\n\nParameters:\n\nmethod: \"historical\" (default, recommended), \"parametric\", or \"garch\"\nconfidence: confidence level, default 0.95 (range 0.01–0.99)\nhorizon_days: forecast horizon, default 1 (range 1–30 days)\nforce_recalc (optional, default false): set true to bypass cache and force a fresh calculation (use when user says \"recalculate\" or \"refresh\")\n\nResponse:\n\nIf reused_existing: true and status: \"done\" → result is already in response (var_95, cvar_95, sharpe_ratio), no polling needed\nOtherwise → returns calculation_id, poll for result:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/risk/calculation/{calculation_id}\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nWait until status: \"done\", then present results.\n\nGet VaR / Risk Calculation History\n\nWhen the user asks for past risk calculations:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/risk/history?limit=50\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nQuery params: limit (optional, default 50, max 100).\n\nResponse: calculations array with calculation_id, portfolio_snapshot_id, status, method, var_95, cvar_95, sharpe_ratio, created_at, completed_at.\n\nRun Monte Carlo (QUANT — currently free for all users)\n\nWhen the user asks for a Monte Carlo simulation:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/risk/monte-carlo\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portfolio_snapshot_id\": \"{snapshot_id}\",\n    \"simulations\": 1000,\n    \"horizon_days\": 365,\n    \"model\": \"gbm\",\n    \"force_recalc\": false\n  }'\n\nParameters:\n\nsimulations: number of paths, default 1000 (range 100–10000)\nhorizon_days: forecast horizon, default 365 (range 1–365)\nmodel: \"gbm\" (Geometric Brownian Motion — only this is implemented) or \"garch\" (accepted but not yet implemented; will behave as GBM)\nconfidence_levels (optional): array of percentiles, default [0.05, 0.50, 0.95]\nforce_recalc (optional, default false): bypass cache\n\nPoll: GET /api/v1/risk/monte-carlo/{simulation_id}\n\nRun Stress Test (QUANT — currently free for all users)\n\nWhen the user asks for a stress test against historical crises:\n\nFirst, get available crises:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/risk/stress-test/crises\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nThen run:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/risk/stress-test\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portfolio_snapshot_id\": \"{snapshot_id}\",\n    \"crisis\": \"covid_19\",\n    \"force_recalc\": false\n  }'\n\nParameters:\n\ncrisis: crisis scenario ID from /stress-test/crises (e.g. covid_19, 2008_crisis)\nforce_recalc (optional, default false): bypass cache\n\nPoll: GET /api/v1/risk/stress-test/{stress_test_id}"
      },
      {
        "title": "Portfolio Optimization (QUANT — currently free for all users)",
        "body": "Risk Parity Optimization\n\nWhen the user asks to optimize their portfolio or balance risks:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/{snapshot_id}/optimize\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"optimization_mode\": \"preserve_directions\",\n    \"constraints\": {\n      \"max_weight\": 0.30,\n      \"min_weight\": 0.02\n    },\n    \"options\": {\n      \"risk_measure\": \"variance\",\n      \"turnover_limit\": 0.3,\n      \"turnover_penalty\": 0.1\n    }\n  }'\n\noptimization_mode:\n\n\"long_only\": all weights ≥ 0 (shorts are flipped to long before optimization)\n\"preserve_directions\": keeps long/short directions as-is (default)\n\"unconstrained\": weights can change sign freely\n\noptions.risk_measure (optional, default \"variance\"):\n\n\"variance\": classic ERC (Maillard, Roncalli, Teïletche 2010)\n\"cvar\": CVaR Risk Budgeting via skfolio (convex optimization, CLARABEL solver). Better for fat-tailed distributions\n\nTurnover constraints (optional, require current_weights in portfolio):\n\nturnover_limit: hard constraint — sum(|w_new - w_old|) <= limit. Optimizer stays within budget\nturnover_penalty: soft L1 penalty in objective — trades off improvement vs turnover cost\n\nPoll: GET /api/v1/portfolio/optimizations/{optimization_id}\nResult: GET /api/v1/portfolio/optimizations/{optimization_id}/result\n\nIMPORTANT: For optimization, use active_snapshot_id || snapshot_id from the portfolio list entry (respects the user's selected historical snapshot if set).\n\nCalmar Ratio Optimization\n\nWhen the user asks to maximize Calmar Ratio (CAGR / |Max Drawdown|):\n\nRequires 200+ trading days of price history per ticker (backend requests 252 days). If the portfolio has short history, suggest Risk Parity instead.\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/{snapshot_id}/optimize-calmar\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"optimization_mode\": \"long_only\",\n    \"constraints\": {\n      \"max_weight\": 0.50,\n      \"min_weight\": 0.05,\n      \"min_expected_return\": 0.0,\n      \"max_drawdown_limit\": 0.15,\n      \"min_calmar_target\": 0.5\n    },\n    \"options\": {\n      \"turnover_limit\": 0.3,\n      \"turnover_penalty\": 0.1\n    }\n  }'\n\nPoll: GET /api/v1/portfolio/optimizations/{optimization_id} (check optimization_type === \"calmar_ratio\").\nResult: GET /api/v1/portfolio/optimizations/{optimization_id}/result — includes current_metrics and optimized_metrics (CAGR, max drawdown, Calmar ratio, recovery time in days).\nApply: same as Risk Parity → POST /api/v1/portfolio/optimizations/{optimization_id}/apply.\n\nError INSUFFICIENT_HISTORY: not enough price history → explain the 200+ days requirement and suggest Risk Parity as alternative.\n\nApply Optimization\n\nIMPORTANT: Always show the full rebalancing plan and ask for explicit user confirmation before applying!\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/optimizations/{optimization_id}/apply\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nResponse: new_snapshot_id. Can only be applied once per optimization.\n\nBlack-Litterman Optimization (QUANT)\n\nWhen the user has views (expected returns with confidence) on specific assets and wants an optimal portfolio:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/optimize-bl\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"tickers\": [\"SBER\", \"GAZP\", \"LKOH\", \"ROSN\"],\n    \"views\": [\n      {\"ticker\": \"SBER\", \"expected_return\": 0.15, \"confidence\": 0.7},\n      {\"ticker\": \"ROSN\", \"expected_return\": -0.05, \"confidence\": 0.5}\n    ],\n    \"constraints\": { ... },\n    \"options\": { \"risk_free_rate\": 0.16, \"tau\": 0.05 },\n    \"portfolio_snapshot_id\": null,\n    \"currency\": \"RUB\"\n  }'\n\nParameters:\n\ntickers (required, min 2): ticker symbols for universe\nviews (required, min 1): investor views — expected_return is annual (0.15 = 15%), confidence 0.01–1.0 (Idzorek method scales Omega)\nconstraints.weight_bound_lower: min weight per asset (negative allows shorts, default -0.15)\nconstraints.weight_bound_upper: max weight per asset (default 0.25)\nconstraints.max_gross_exposure: max sum(|w_i|) (default 2.0)\nconstraints.target_net_exposure: exact sum(w_i) target (e.g. 1.0 for fully invested). Mutually exclusive with max_net_exposure\nconstraints.max_net_exposure: max |sum(w_i)|\nconstraints.turnover_limit / constraints.turnover_penalty: same as Risk Parity (see above). To enforce turnover vs current portfolio, pass portfolio_snapshot_id (see below).\noptions.risk_free_rate: annual risk-free rate (default 0.16 for MOEX)\noptions.tau: uncertainty scaling (default 0.05)\nportfolio_snapshot_id (optional): snapshot UUID of the portfolio to compute current weights from. When set, turnover constraints are applied relative to this portfolio. Omit for greenfield optimization.\ncurrency (optional, default \"RUB\"): market data universe — \"RUB\" or \"USD\". Use USD for US-listed tickers.\n\nResponse: optimization_id, status: \"pending\". Poll via GET /portfolio/optimizations/{id}, result via GET /portfolio/optimizations/{id}/result.\n\nResult contains: target_portfolio (tickers with weights and directions), metrics (expected return, volatility, Sharpe, net/gross exposure, portfolio_type), bl_posterior_returns.\n\nApply: POST /portfolio/optimizations/{id}/apply — creates a new manual portfolio \"BL Optimized\".\n\nUser prompt examples:\n\n\"I think Sber will return 15% with high confidence, optimize my portfolio\" → BL with view\n\"Build a long-short portfolio: long on banks, short on oil\" → BL with positive/negative views\n\"Optimize with Black-Litterman\" → ask user for views (expected returns + confidence per ticker)"
      },
      {
        "title": "Pre-Trade Check (FREE)",
        "body": "Run Pre-Trade Risk Check\n\nWhen the user or AI agent wants to validate a target portfolio before execution. VaR is computed using historical method (empirical distribution from market data).\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/risk/pre-trade-check\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"target_portfolio\": {\"SBER\": 0.15, \"GAZP\": 0.10, \"LKOH\": 0.12, \"ROSN\": -0.08},\n    \"amount\": 1000000,\n    \"currency\": \"RUB\",\n    \"constraints\": {\n      \"max_var_pct\": 5.0,\n      \"weight_bound_upper\": 0.25,\n      \"weight_bound_lower\": -0.15,\n      \"max_gross_exposure\": 2.0,\n      \"max_net_exposure\": 0.5,\n      \"max_sector_concentration\": 0.35,\n      \"sector_limits\": {\"Energy\": 0.30, \"Finance\": 0.25}\n    }\n  }'\n\nParameters:\n\ntarget_portfolio (required): dict of {ticker: weight} — negative weight = short\namount (required): portfolio notional value for VaR calculation\ncurrency (optional, default \"RUB\"): currency of the portfolio — \"RUB\" or \"USD\". Determines which market data universe is used for historical VaR.\nconstraints (optional):\n\nmax_var_pct: maximum allowed VaR as % (e.g. 5.0 = 5%)\nweight_bound_upper / weight_bound_lower: per-position weight limits\nmax_gross_exposure / max_net_exposure: exposure limits\nmax_sector_concentration: global maximum sector weight (e.g. 0.35 = no single sector above 35%). Sector data is fetched from Data Service. If Data Service is unavailable, a warning is returned instead of an error.\nsector_limits: per-sector max weight as {sector_name: max_weight} (e.g. {\"Energy\": 0.30}). Case-insensitive matching. Can be used together with max_sector_concentration.\n\nResponse (synchronous, no polling):\n\n{\n  \"verdict\": \"pass\",\n  \"num_positions\": 4,\n  \"max_position_weight\": 0.15,\n  \"var_95_pct\": 3.21,\n  \"currency\": \"RUB\",\n  \"exposure_metrics\": {\n    \"net_exposure\": 0.29,\n    \"gross_exposure\": 0.45,\n    \"long_exposure\": 0.37,\n    \"short_exposure\": 0.08\n  },\n  \"constraint_violations\": [],\n  \"sector_exposures\": {\n    \"Energy\": 0.488889,\n    \"Finance\": 0.333333,\n    \"Other\": 0.177778\n  },\n  \"result_hash\": \"0x...\",\n  \"data_quality\": {\n    \"tickers_requested\": 4,\n    \"tickers_with_data\": 4,\n    \"tickers_missing\": [],\n    \"total_dates\": 252,\n    \"dates_dropped\": 0\n  }\n}\n\nsector_exposures (optional, present when max_sector_concentration or sector_limits is set): maps sector name to its share of gross exposure. Formula: sector_exposure[s] = sum(abs(w[t]) for t in sector) / gross_exposure. For long/short portfolios, abs(weight) is used so both long and short contribute proportionally.\n\ndata_quality (optional): tickers_requested, tickers_with_data, tickers_missing, total_dates, dates_dropped — reflects alignment of historical data used for VaR (inner-join by date; no zero-fill). May also include tickers_without_sector if sector metadata was unavailable for some tickers.\n\nverdict: \"pass\" (all OK), \"fail\" (hard constraint violations), \"warning\" (soft issues)\n\nUser prompt examples:\n\n\"Check if this portfolio is safe\" / \"Проверь портфель перед торговлей\"\n\"Run pre-trade check for: SBER 15%, GAZP 10%, ROSN -8%\"\nAfter BL optimization: \"Check the result before applying\""
      },
      {
        "title": "Batch Portfolio Creation (FREE)",
        "body": "Create Multiple Portfolios\n\nWhen the user or platform wants to create several portfolios at once:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/manual/batch\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portfolios\": [\n      {\n        \"name\": \"Pod Alpha\",\n        \"positions\": [{\"ticker\": \"SBER\", \"quantity\": 100}, {\"ticker\": \"GAZP\", \"quantity\": -50}],\n        \"currency\": \"RUB\"\n      },\n      {\n        \"name\": \"Pod Beta\",\n        \"positions\": [{\"ticker\": \"LKOH\", \"quantity\": 30}],\n        \"currency\": \"RUB\"\n      }\n    ]\n  }'\n\nResponse: array of {name, snapshot_id, status} per portfolio. Partial success is possible — one portfolio can fail without affecting others."
      },
      {
        "title": "Cross-Portfolio Correlation (QUANT)",
        "body": "Compute PnL Correlation Between Portfolios\n\nWhen the user asks about diversification across portfolios/pods or re-correlation risk:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/correlation\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portfolio_ids\": null,\n    \"window_days\": 60,\n    \"include_crisis_regime\": true,\n    \"analysis_currency\": \"RUB\"\n  }'\n\nParameters:\n\nportfolio_ids (optional): list of snapshot UUIDs. null = all user's portfolios (latest snapshot per portfolio)\nwindow_days (optional, default 60, range 20–252): rolling window for PnL correlation\ninclude_crisis_regime (optional, default false): compare normal vs crisis correlations (crisis = days with aggregate PnL < μ-2σ)\nanalysis_currency (optional, default \"RUB\"): currency to normalize all PnL series into before correlation — \"RUB\" or \"USD\". Use when portfolios are in different currencies; rates are CBR historical.\n\nResponse: async — poll via GET /portfolio/optimizations/{id}, result via GET /portfolio/optimizations/{id}/result.\n\nResult contains:\n\n{\n  \"result_data\": {\n    \"portfolio_names\": [\"Pod Alpha\", \"Pod Beta\", \"T-Bank\"],\n    \"correlation_matrix\": [[1.0, 0.35, 0.12], [0.35, 1.0, 0.48], [0.12, 0.48, 1.0]],\n    \"pairs\": [ ... ],\n    \"avg_pairwise_correlation\": 0.317,\n    \"analysis_currency\": \"RUB\",\n    \"fx_source\": \"CBR\",\n    \"fx_coverage\": 1.0,\n    \"fx_conversions\": { \"Pod Alpha\": \"USD->RUB\", \"Pod Beta\": \"RUB\" },\n    \"crisis_regime\": {\n      \"available\": true,\n      \"avg_normal_correlation\": 0.25,\n      \"avg_crisis_correlation\": 0.72,\n      \"re_correlation_delta\": 0.47,\n      \"n_crisis_days\": 8,\n      \"n_normal_days\": 52\n    }\n  }\n}\n\nKey metric: re_correlation_delta — how much correlations increase during stress. > 0.2 = significant re-correlation risk (pods lose diversification under stress).\n\nUser prompt examples:\n\n\"How correlated are my portfolios?\" / \"Какая корреляция между портфелями?\"\n\"Check re-correlation risk\" / \"Center Book analysis\"\n\"Compare normal vs crisis correlations\""
      },
      {
        "title": "Per-Portfolio Risk Settings (FREE)",
        "body": "Set Individual VaR Threshold\n\nWhen the user wants a different risk alert threshold for a specific portfolio:\n\ncurl -s -X PATCH \"https://api.riskofficer.tech/api/v1/portfolio/{snapshot_id}/settings\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"risk_threshold_var\": 5.0}'\n\nParameters:\n\nrisk_threshold_var: VaR threshold in percent (5.0 = 5%). When VaR exceeds this, a push notification / alert is triggered for this specific portfolio\n\nUser prompt examples:\n\n\"Set VaR alert at 3% for my conservative portfolio\"\n\"Raise the risk threshold to 7% for the aggressive pod\""
      },
      {
        "title": "Feature Flags",
        "body": "Get Feature Flags\n\nCheck which features are enabled:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/feature-flags\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nResponse:\n\n{\n  \"websocket_enabled\": true,\n  \"subscription_required_for_quant\": false,\n  \"pre_trade_check_enabled\": true,\n  \"black_litterman_enabled\": true,\n  \"cross_correlation_enabled\": true,\n  \"cvar_risk_parity_enabled\": true\n}"
      },
      {
        "title": "Subscription Status",
        "body": "Note: Quant subscription is currently FREE for all users. All features work without payment.\n\ncurl -s \"https://api.riskofficer.tech/api/v1/subscription/status\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nCurrently all users return has_subscription: true."
      },
      {
        "title": "Async Operations",
        "body": "VaR, Monte Carlo, Stress Test, and Optimization are asynchronous.\n\nPolling pattern:\n\nPOST endpoint → get calculation_id / simulation_id / optimization_id\nPoll GET endpoint every 2–3 seconds\nCheck status:\n\npending or processing → keep polling\ndone → present results\nfailed → show error\n\nTypical times:\n\nOperationTypical timeVaR3–10 secondsMonte Carlo10–30 secondsStress Test5–15 secondsOptimization10–30 seconds\n\nUser communication:\n\nShow \"Calculating...\" immediately after starting\nIf polling takes > 10 seconds: \"Still calculating, please wait...\"\nAlways show the final result or error"
      },
      {
        "title": "Important Rules",
        "body": "Virtual / analytical scope: Portfolios and all operations (create, optimize, delete, sync) exist only inside RiskOfficer. This skill is for analysis and research; it does not place or execute real broker orders.\n\n\nSingle Currency Rule (manual/broker portfolios): Each portfolio must contain same-currency assets. Cannot mix SBER (RUB) with AAPL (USD). Aggregated portfolio is the exception — it auto-converts using CBR rates.\n\n\nShort Positions: Negative quantity creates a short. For long-short portfolios, use optimization_mode: \"preserve_directions\" to keep short positions when optimizing.\n\n\nAlways search tickers first: Before creating or editing portfolios, use /tickers/search to validate ticker symbols and check their currency.\n\n\nConfirmations: Always show what will change and ask for confirmation before: updating/deleting portfolios, applying optimizations, disconnecting brokers. These actions can be irreversible.\n\n\nAsync: VaR, Monte Carlo, Stress Test, and Optimization are async. Poll for results.\n\n\nSubscription: Monte Carlo, Stress Test, and Optimization are Quant features (currently free). VaR is always free.\n\n\nBroker Integration: Users must connect brokers via the RiskOfficer mobile app first. Cannot connect via chat (security).\n\n\nError Handling:\n\n401 Unauthorized → Token invalid or expired; user needs to recreate it\n403 subscription_required → Need Quant subscription (currently free for all)\n400 missing_api_key → Broker not connected via app\n400 currency_mismatch → Mixed currencies in a single portfolio\n400 INSUFFICIENT_HISTORY → Not enough price history for Calmar (200+ trading days needed); suggest Risk Parity\n400 MVO infeasible → BL constraints too tight; suggest relaxing weight bounds or exposure limits\n404 Not Found → Portfolio or snapshot not found (may have been deleted)\n429 Too Many Requests → Optimization rate limit reached (per-tier: free 30/h, quant 100/h, pro 1000/h)\n\n\n\nActive Snapshot: active_snapshot_id from /portfolios/list takes priority over snapshot_id when running calculations. Use active_snapshot_id || snapshot_id for optimization calls.\n\n\nresult_hash (ERC-8004): Optimize and VaR responses include result_hash — a keccak256 hash for deterministic verification. Informational; safe to ignore unless building on-chain verification.\n\n\nPre-trade check before apply: For AI agents building autonomous trade pipelines, always run POST /risk/pre-trade-check on the BL optimization result before calling apply. This catches constraint violations the optimizer may not enforce (sector limits, VaR limits). Use currency: \"RUB\" or \"USD\" to match the portfolio (default RUB).\n\n\nCurrency in APIs: Pre-trade accepts optional currency (RUB/USD). BL accepts optional currency (market data universe) and portfolio_snapshot_id (for turnover vs current portfolio). Correlation accepts optional analysis_currency (RUB/USD) — PnL is normalized to this currency using CBR historical rates.\n\nMethodology and references: This skill includes a references/ folder with implementation notes and academic sources. When users ask how VaR, Risk Parity, Calmar, Black-Litterman, pre-trade check, correlation, or aggregated portfolio are implemented, you can cite the matching file: methodology-var.md, methodology-risk-parity.md, methodology-calmar.md, methodology-black-litterman.md, methodology-pre-trade.md, methodology-correlation.md, methodology-aggregation.md, methodology-hrp.md, methodology-monte-carlo.md, methodology-stress-test.md, methodology-metrics.md, methodology-auto-portfolio.md. For a consolidated list of papers and libraries, see references/academic-references.md."
      },
      {
        "title": "User wants to see their portfolios",
        "body": "\"Show my portfolios\" / \"Покажи мои портфели\"\n→ GET /portfolios/list\n→ Format nicely: name, total value, positions count, currency, last updated"
      },
      {
        "title": "User wants the combined total across all accounts",
        "body": "\"Show total portfolio\" / \"Total across all accounts\" / \"Сколько у меня всего?\"\n→ GET /portfolio/aggregated?type=all\n→ Show total value, merged positions, number of sources\n→ Note positions converted from other currencies"
      },
      {
        "title": "User wants to change display currency",
        "body": "\"Show everything in dollars\" / \"Покажи в долларах\"\n→ PATCH /portfolio/{aggregated_id}/settings with {\"base_currency\": \"USD\"}\n→ GET /portfolio/aggregated again\n→ Show portfolio in new currency"
      },
      {
        "title": "User asks about a company by name (not ticker)",
        "body": "\"Add Sberbank to my portfolio\" / \"What's the ticker for Gazprom?\" / \"Добавь Газпром\"\n→ GET /tickers/search?q=Sberbank&locale=en\n→ Found: ticker SBER, currency RUB, exchange MOEX\n→ Confirm with user, then proceed to create/update portfolio"
      },
      {
        "title": "User asks for a current price",
        "body": "\"How much is Tesla?\" / \"Сколько стоит Газпром?\"\n→ GET /tickers/search?q=TSLA&include_prices=true\n→ Show current_price, price_change_percent, exchange"
      },
      {
        "title": "User wants to create a long-short portfolio",
        "body": "\"Create portfolio: long SBER 100 shares, short YNDX 50 shares\"\n→ GET /tickers/search for both tickers → confirm both are RUB/MOEX\n→ POST /portfolio/manual with [{\"ticker\":\"SBER\",\"quantity\":100},{\"ticker\":\"YNDX\",\"quantity\":-50}]\n→ Show created portfolio with positions"
      },
      {
        "title": "User wants to analyze portfolio risk",
        "body": "\"What are the risks of my portfolio?\" / \"Analyze the risk\"\n→ GET /portfolios/list → find portfolio\n→ POST /risk/calculate-var with method: \"historical\"\n→ Poll until done\n→ Present VaR, CVaR, volatility, risk contributions per ticker\n→ Offer optimization if risks are concentrated"
      },
      {
        "title": "User wants Calmar optimization",
        "body": "\"Optimize by Calmar ratio\" / \"Maximize return per drawdown\" / \"Оптимизируй по Калмару\"\n→ Get snapshot_id from portfolios list\n→ POST /portfolio/{snapshot_id}/optimize-calmar\n→ If INSUFFICIENT_HISTORY: explain 200+ trading days needed, suggest Risk Parity\n→ Poll until done\n→ Show current_metrics vs optimized_metrics (Calmar ratio, CAGR, max drawdown)\n→ Show rebalancing plan and ask for confirmation before apply"
      },
      {
        "title": "User wants Monte Carlo simulation",
        "body": "\"Run Monte Carlo for 1 year\" / \"Запусти Монте-Карло\"\n→ POST /risk/monte-carlo with simulations: 1000, horizon_days: 365, model: \"gbm\"\n→ Poll until done\n→ Present percentile projections (5th, 50th, 95th)"
      },
      {
        "title": "User wants a stress test",
        "body": "\"Run stress test\" / \"How would my portfolio survive 2008 crisis?\"\n→ GET /risk/stress-test/crises → show available scenarios\n→ User picks crisis (or default to most relevant)\n→ POST /risk/stress-test\n→ Poll, then present results"
      },
      {
        "title": "User wants to calculate risk for a historical portfolio",
        "body": "\"Calculate risk for my portfolio as it was last month\" / \"Посчитай риски как было месяц назад\"\n→ GET /portfolio/history?days=45 → find snapshot from ~30 days ago\n→ PATCH /portfolio/active-snapshot with that snapshot_id and portfolio_key\n→ POST /risk/calculate-var → poll → present results\n→ Offer to reset: DELETE /portfolio/active-snapshot"
      },
      {
        "title": "User tries to mix currencies",
        "body": "\"Add Apple to my RUB portfolio\"\n→ GET /tickers/search?q=AAPL → currency: USD, exchange: NASDAQ\n→ Portfolio is RUB → cannot mix\n→ Explain the single-currency rule, suggest creating a separate USD portfolio"
      },
      {
        "title": "User wants to compare two portfolio snapshots",
        "body": "\"What changed in my portfolio?\" / \"Compare to last week\" / \"Что изменилось в портфеле?\"\n→ GET /portfolio/history → get two snapshot IDs\n→ GET /portfolio/snapshot/{id}/diff?compare_to={other_id}\n→ Present added/removed/modified positions, total value delta"
      },
      {
        "title": "User wants to delete a portfolio",
        "body": "\"Delete my test portfolio\" / \"Удали портфель 'Тест'\"\n→ Confirm: \"This will permanently delete all N snapshots for 'Test'. Cannot be undone. Continue?\"\n→ On confirmation: DELETE /portfolio/manual/Test\n→ Report archived_snapshots count"
      },
      {
        "title": "User wants to disconnect a broker",
        "body": "\"Disconnect Tinkoff\" / \"Отключи Тинькофф\"\n→ Confirm: \"This will remove the Tinkoff connection. Portfolio history will be kept. Continue?\"\n→ On confirmation: DELETE /brokers/connections/tinkoff?sandbox=false\n→ Inform that reconnection requires the mobile app"
      },
      {
        "title": "User wants Black-Litterman optimization",
        "body": "\"I think Sber will return 15% and Gazprom 10%\" / \"Оптимизируй по Блэку-Литтерману\"\n→ Collect views: ask for tickers, expected returns, and confidence\n→ POST /portfolio/optimize-bl with views + constraints\n→ Poll until done\n→ Show target portfolio (tickers, weights, directions), metrics (Sharpe, expected return, volatility)\n→ Ask confirmation before POST /portfolio/optimizations/{id}/apply"
      },
      {
        "title": "User wants to check a portfolio before trading",
        "body": "\"Check this before I trade\" / \"Проверь портфель перед исполнением\"\n→ POST /risk/pre-trade-check with target weights + amount + constraints\n→ Show verdict (pass/fail/warning), VaR, exposure metrics, violations\n→ If \"fail\" → explain which constraints are violated"
      },
      {
        "title": "User wants to create multiple portfolios at once",
        "body": "\"Create 3 pod portfolios\" / \"Создай несколько портфелей\"\n→ POST /portfolio/manual/batch with array of portfolios\n→ Show per-portfolio status (created/error)"
      },
      {
        "title": "User asks about cross-portfolio correlation",
        "body": "\"How diversified are my pods?\" / \"Какая корреляция между портфелями?\"\n→ POST /portfolio/correlation with include_crisis_regime: true\n→ Poll until done\n→ Show avg pairwise correlation, matrix, pairs detail\n→ If crisis data available: show normal vs crisis correlation and re-correlation delta\n→ If re_correlation_delta > 0.2: warn about re-correlation risk"
      },
      {
        "title": "User wants CVaR Risk Parity",
        "body": "\"Optimize with CVaR\" / \"Оптимизируй по CVaR\"\n→ POST /portfolio/{snapshot_id}/optimize with options: { risk_measure: \"cvar\" }\n→ Explain: CVaR is better for fat-tailed distributions, accounts for tail risk beyond VaR"
      }
    ],
    "body": "RiskOfficer Portfolio Management\n\nConnects to the RiskOfficer API to manage investment portfolios and calculate financial risk metrics.\n\nRequired: One environment variable — RISK_OFFICER_TOKEN (create in RiskOfficer app → Settings → API Keys). No other env vars or binaries are required.\n\nSource: Official skill repository: github.com/mib424242/riskofficer-openclaw-skill. Product: riskofficer.tech. The token is issued only by the RiskOfficer app; this skill does not collect or store credentials.\n\nCredentials and token handling\nThis skill does not store or log your token. The token is sent only in the HTTP Authorization header to api.riskofficer.tech; it is not written to disk, logged, or sent elsewhere. Where you store the token (environment variable or ~/.openclaw/openclaw.json) is entirely under your control.\nPrefer setting RISK_OFFICER_TOKEN as an environment variable for the session rather than saving it in openclaw.json. If you use openclaw.json, restrict file permissions and be aware which agents or users can read that file.\nRiskOfficer currently issues account-level tokens (no scoped tokens). Create a token named for this skill (e.g. \"OpenClaw\") and revoke it in the RiskOfficer app if you stop using the skill.\nToken scope: The token allows the skill to access your RiskOfficer data (portfolios, risk calculations, broker-synced positions for read-only analysis). Revoke or rotate the token if you need to revoke access.\nVerify links: Confirm that github.com/mib424242/riskofficer-openclaw-skill and riskofficer.tech match the publisher you trust before installing or providing a token.\nScope: analysis and research only (virtual portfolios)\n\nAll portfolio data and operations in this skill take place inside RiskOfficer’s own environment. Portfolios you create, edit, or optimize here are virtual — they are used for analysis and research only. The agent can:\n\nRead your portfolios (including those synced from brokers) to show positions, history, and risk metrics\nCreate and change virtual/manual portfolios and run optimizations inside RiskOfficer\nRun calculations (VaR, Monte Carlo, stress tests) on these portfolios\n\nNothing in this skill places or executes real orders in your broker account. Broker sync is read-only for analysis; any rebalancing or trading in the real account is done by you in your broker’s app or in RiskOfficer’s own flows, not by the assistant. The token is used only to access RiskOfficer’s API for this analytical and research use.\n\nSetup\nOpen RiskOfficer app → Settings → API Keys\nCreate a new token named \"OpenClaw\"\nSet environment variable: RISK_OFFICER_TOKEN=ro_pat_...\n\nOr configure in ~/.openclaw/openclaw.json:\n\n{\n  \"skills\": {\n    \"entries\": {\n      \"riskofficer\": {\n        \"enabled\": true,\n        \"apiKey\": \"ro_pat_...\"\n      }\n    }\n  }\n}\n\nAPI Base URL\nhttps://api.riskofficer.tech/api/v1\n\n\nAll requests require: Authorization: Bearer ${RISK_OFFICER_TOKEN}\n\nCurrency policy\nSupported currencies: RUB and USD only. No EUR/CNY/other in API contracts for base or analysis currency.\nFX source: All exchange rates are CBR (Central Bank of Russia) via Data Service (MOEX/CBR). There is no alternative provider.\nSingle currency per portfolio: Each portfolio must contain assets in one currency (all MOEX or all US). Mixed-currency portfolios are not supported; create separate portfolios.\nAggregated view: User chooses base_currency (RUB or USD); sub-portfolios in the other currency are converted using CBR rates.\nAvailable Commands\nTicker Search\nSearch Tickers\n\nUse this before creating or editing any portfolio to validate ticker symbols and get their currency/exchange info. Also use when the user mentions a company name instead of a ticker.\n\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/search?q=Apple&limit=10&locale=en\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nQuery params:\n\nq (optional): search query — by ticker, name, or full name (case-insensitive). Omit to get popular tickers sorted by popularity.\nlimit (optional, default 20, max 50): number of results\ninclude_prices (optional, default false): include current_price, price_change_percent, price_change_absolute, price_date\nlocale (optional, default ru): en for English names, ru for Russian names\nexchange (optional): filter by exchange — MOEX, NYSE, NASDAQ, CRYPTO\n\nResponse: tickers array, each with: ticker, name, full_name, instrument_type, currency, exchange, popularity_score, isin.\n\nInstrument types: share, bond, etf, futures, futures_continuous (e.g. BR, SI on MOEX), currency, crypto\n\nKey rules:\n\nAlways use ticker search to resolve company names → ticker symbols (e.g. \"Apple\" → \"AAPL\", \"Sberbank\" → \"SBER\")\nUse currency field from the result to check same-currency constraint before adding to a portfolio\nMOEX futures: searching \"BR\" or \"SI\" returns the continuous contract, not individual contracts (BRF6, SIM5)\nUse include_prices=true to show current price when user asks \"how much is X worth?\"\n# Search by company name (English)\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/search?q=Gazprom&locale=en&limit=5\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n# Search by Russian name\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/search?q=%D0%93%D0%B0%D0%B7%D0%BF%D1%80%D0%BE%D0%BC&locale=ru&limit=5\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n# Get current price for a ticker\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/search?q=AAPL&include_prices=true\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n# Get popular tickers (no query param)\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/search?limit=10&include_prices=true\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n# Filter by exchange\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/search?q=SBER&exchange=MOEX\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nGet Historical Ticker Prices\n\nWhen the user asks about price history, chart data, or trends for specific assets:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/tickers/historical?tickers=SBER,GAZP,AAPL&days=30\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nQuery params: tickers (required, comma-separated, max 50), days (optional, default 7, max 252 trading days).\n\nResponse: data object keyed by ticker symbol, each with:\n\nprices: array of {date, close} objects\ncurrent_price, price_change_percent, price_change_absolute\nPortfolio Management\nList Portfolios\n\nWhen the user asks to see their portfolios or wants an overview:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/portfolios/list\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nQuery params: portfolio_type (optional): \"production\" (manual + live brokers), \"sandbox\" (broker sandbox only), \"all\" (default).\n\nResponse: array of portfolios with snapshot_id, name, total_value, currency, positions_count, broker, sandbox, active_snapshot_id (UUID or null — if set, risk calculations use this historical snapshot instead of the latest).\n\nGet Portfolio Details\n\nWhen the user wants to see positions in a specific portfolio:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/portfolio/snapshot/{snapshot_id}\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nResponse: name, total_value, currency, positions (array with ticker, quantity, current_price, value, weight, avg_price).\n\nGet Portfolio History\n\nWhen the user asks how their portfolio changed over time or wants to browse past snapshots:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/portfolio/history?days=30\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nQuery params: days (optional, default 30, range 1–365).\n\nResponse: snapshots array with snapshot_id, timestamp, total_value, positions_count, sync_source, type (aggregated/manual/broker), name, broker, sandbox.\n\nGet Snapshot Diff (compare two portfolio versions)\n\nWhen the user wants to compare two portfolio states (e.g. before/after rebalancing, or two dates):\n\ncurl -s \"https://api.riskofficer.tech/api/v1/portfolio/snapshot/{snapshot_id}/diff?compare_to={other_snapshot_id}\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nResponse: added/removed/modified positions, total_value_delta. Both snapshots must belong to the user.\n\nGet Aggregated Portfolio\n\nWhen the user asks for their total or combined portfolio across all accounts:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/portfolio/aggregated?type=all\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nQuery params:\n\ntype=production — manual + broker live accounts\ntype=sandbox — broker sandbox only\ntype=all — everything (default)\n\nResponse:\n\nportfolio.positions — all positions merged across portfolios\nportfolio.total_value — total in base currency\nportfolio.currency — base currency (RUB or USD)\nportfolio.sources_count — number of portfolios aggregated\nexchange_rates (optional): USD_RUB, EUR_RUB, CNY_RUB, rate_date, rate_source (always \"CBR\"), base_currency, fx_quality (\"live\" = from CBR/cache, \"default\" = static fallback used when CBR unavailable, \"none\" = no conversion)\nwarnings — e.g. mixed-currency positions, FX fallback used\ndata_quality (optional): fx_coverage (share of FX lookups with live rate), fx_live_count, fx_default_count, fx_unavailable_count, portfolios_included, portfolios_excluded — for observability of aggregation and FX usage\n\nExample response:\n\n{\n  \"portfolio\": {\n    \"positions\": [\n      {\"ticker\": \"SBER\", \"quantity\": 150, \"value\": 42795, \"sources\": [\"T-Bank\", \"Manual\"]},\n      {\"ticker\": \"AAPL\", \"quantity\": 10, \"value\": 189500, \"original_currency\": \"USD\"}\n    ],\n    \"total_value\": 1500000,\n    \"currency\": \"RUB\",\n    \"sources_count\": 3\n  },\n  \"snapshot_id\": \"uuid-of-aggregated\"\n}\n\n\nPositions in different currencies are automatically converted using CBR (Central Bank of Russia) rates. Only RUB and USD are supported; each sub-portfolio must have assets in a single currency.\n\nChange Base Currency (Aggregated Portfolio)\n\nWhen the user wants to see the aggregated portfolio in a different currency:\n\ncurl -s -X PATCH \"https://api.riskofficer.tech/api/v1/portfolio/{aggregated_snapshot_id}/settings\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"base_currency\": \"USD\"}'\n\n\nSupported currencies: RUB, USD. The aggregated portfolio recalculates automatically after change.\n\nUser prompt examples:\n\n\"Show everything in dollars\" / \"Покажи всё в долларах\" → base_currency: \"USD\"\n\"Switch to rubles\" / \"Переведи в рубли\" → base_currency: \"RUB\"\nInclude/Exclude Portfolio from Aggregated\n\nWhen the user wants to exclude a specific portfolio from total calculations:\n\ncurl -s -X PATCH \"https://api.riskofficer.tech/api/v1/portfolio/{snapshot_id}/settings\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"include_in_aggregated\": false}'\n\n\nUser prompt examples:\n\n\"Exclude sandbox from total\" / \"Не учитывай песочницу в общем портфеле\"\n\"Remove demo portfolio from calculations\" / \"Убери демо-портфель из расчёта\"\nCreate Manual Portfolio\n\nWhen the user wants to create a new portfolio with specific positions:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/manual\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"My Portfolio\",\n    \"positions\": [\n      {\"ticker\": \"SBER\", \"quantity\": 100},\n      {\"ticker\": \"GAZP\", \"quantity\": 50, \"avg_price\": 148.0},\n      {\"ticker\": \"YNDX\", \"quantity\": -20}\n    ]\n  }'\n\n\nFields:\n\nticker (required): ticker symbol. Always use /tickers/search first to validate and check currency.\nquantity (required): number of shares. Negative = short position (e.g. -20 = short 20 shares).\navg_price (optional): average purchase/entry price for P&L tracking. If omitted on new portfolio → uses current market price. If omitted on edit → inherits from previous snapshot.\n\nQuery params: locale (optional, default ru) — affects asset name resolution.\n\nCurrency policy (platform-wide): Only RUB and USD are supported. All FX rates come from CBR (Central Bank of Russia) via Data Service (MOEX/CBR). There is no choice of FX provider — it is always CBR.\n\nIMPORTANT — Single Currency Rule: All assets in one portfolio must be in the same currency.\n\nRUB assets (MOEX): SBER, GAZP, LKOH, YNDX, etc.\nUSD assets (NYSE/NASDAQ): AAPL, MSFT, GOOGL, TSLA, etc. Cannot mix currencies in a single portfolio! Suggest creating separate portfolios.\n\nShort positions:\n\nUse negative quantity for shorts (e.g. {\"ticker\": \"GAZP\", \"quantity\": -50})\nLong + short in the same portfolio is supported (long-short portfolio)\nWhen optimizing a long-short portfolio, use optimization_mode: \"preserve_directions\" to keep shorts\nUpdate Portfolio (Add/Remove Positions)\n\nWhen the user wants to modify an existing portfolio:\n\nGet current positions:\ncurl -s \"https://api.riskofficer.tech/api/v1/portfolio/snapshot/{snapshot_id}\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nRepost with the same name and updated full positions list:\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/manual\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"<same name>\", \"positions\": [<complete updated list>]}'\n\n\nIMPORTANT: Always show the user what will change and ask for confirmation before updating. avg_price is preserved from previous snapshot unless explicitly specified.\n\nDelete Manual Portfolio\n\nWhen the user wants to delete/remove a manual portfolio entirely:\n\ncurl -s -X DELETE \"https://api.riskofficer.tech/api/v1/portfolio/manual/My%20Portfolio\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nPortfolio name must be URL-encoded\nArchives all snapshots for that portfolio — irreversible\nALWAYS confirm with the user before deleting — cannot be undone\nResponse: archived_snapshots count, portfolio_name, message\nDelete Broker Portfolio Snapshots\n\nWhen the user wants to clear broker portfolio history without disconnecting the broker:\n\ncurl -s -X DELETE \"https://api.riskofficer.tech/api/v1/portfolio/broker/tinkoff?sandbox=false\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nsandbox=true for sandbox portfolio, sandbox=false for live/production\nArchives snapshots only; broker connection stays active\nNext sync will create a new snapshot\nALWAYS confirm before deleting\nBroker Integration\nList Connected Brokers\n\nWhen the user asks about their broker connections:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/brokers/connections\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nList Available Broker Providers\n\nWhen the user asks what brokers are supported:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/brokers/available\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nSync Portfolio from Broker\n\nWhen the user wants to refresh/update their portfolio from a connected broker:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/proxy/broker/{broker}/portfolio\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"sandbox\": false}'\n\n{broker}: tinkoff or alfa\nsandbox: false for live account, true for Tinkoff sandbox\n\nIf response is 400 with missing_api_key, the broker is not connected. Guide the user:\n\nGet API token from https://www.tbank.ru/invest/settings/api/\nOpen RiskOfficer app → Settings → Brokers → Connect Tinkoff\nPaste token and connect\nDisconnect Broker\n\nWhen the user wants to remove a broker connection:\n\ncurl -s -X DELETE \"https://api.riskofficer.tech/api/v1/brokers/connections/tinkoff?sandbox=false\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\nsandbox=false for live connection, sandbox=true for sandbox\nRemoves the connection and its saved API key; portfolio snapshot history is preserved\nTo also delete snapshot history, first use DELETE /portfolio/broker/{broker}?sandbox=false\nALWAYS confirm before disconnecting — reconnection requires the mobile app\n\nDifference between the two delete endpoints:\n\nAction\tDELETE /portfolio/broker/{id}\tDELETE /brokers/connections/{id}\nDeletes snapshots\t✅ Yes (archives history)\t❌ No (history kept)\nDeletes connection\t❌ No\t✅ Yes\nCan sync again without re-connecting\t✅ Yes\t❌ No\nActive Snapshot Selection\n\nBy default, all risk calculations use the latest snapshot. You can pin a historical snapshot to run calculations on a past portfolio state — useful for backtesting risk or comparing \"before vs after\" rebalancing.\n\nSet Active Snapshot\n\nWhen the user wants to run risk calculations on a historical version of their portfolio:\n\ncurl -s -X PATCH \"https://api.riskofficer.tech/api/v1/portfolio/active-snapshot\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"portfolio_key\": \"manual:My Portfolio\", \"snapshot_id\": \"{historical_snapshot_id}\"}'\n\n\nportfolio_key format:\n\nPortfolio type\tFormat\tExample\nAggregated\taggregated\t\"aggregated\"\nManual\tmanual:{name}\t\"manual:My Portfolio\"\nBroker live\tbroker:{broker_id}:false\t\"broker:tinkoff:false\"\nBroker sandbox\tbroker:{broker_id}:true\t\"broker:tinkoff:true\"\n\nWorkflow:\n\nGET /portfolio/history?days=90 → pick snapshot by date\nPATCH /portfolio/active-snapshot with chosen snapshot_id + portfolio_key\nRun VaR / Monte Carlo — uses selected historical snapshot\nReset when done (see below)\n\nIn /portfolios/list: active_snapshot_id field shows the pinned snapshot (null = using latest).\n\nReset Active Snapshot to Latest\ncurl -s -X DELETE \"https://api.riskofficer.tech/api/v1/portfolio/active-snapshot?portfolio_key=manual:My%20Portfolio\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nUser prompt examples:\n\n\"Calculate risk for my portfolio as it was a month ago\" / \"Посчитай риски как было месяц назад\" → set active snapshot\n\"Go back to current portfolio\" / \"Сбрось на текущий портфель\" → DELETE active-snapshot\nRisk Calculations\nCalculate VaR (FREE)\n\nWhen the user asks to calculate risk, VaR, or risk metrics:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/risk/calculate-var\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portfolio_snapshot_id\": \"{snapshot_id}\",\n    \"method\": \"historical\",\n    \"confidence\": 0.95,\n    \"horizon_days\": 1,\n    \"force_recalc\": false\n  }'\n\n\nParameters:\n\nmethod: \"historical\" (default, recommended), \"parametric\", or \"garch\"\nconfidence: confidence level, default 0.95 (range 0.01–0.99)\nhorizon_days: forecast horizon, default 1 (range 1–30 days)\nforce_recalc (optional, default false): set true to bypass cache and force a fresh calculation (use when user says \"recalculate\" or \"refresh\")\n\nResponse:\n\nIf reused_existing: true and status: \"done\" → result is already in response (var_95, cvar_95, sharpe_ratio), no polling needed\nOtherwise → returns calculation_id, poll for result:\ncurl -s \"https://api.riskofficer.tech/api/v1/risk/calculation/{calculation_id}\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nWait until status: \"done\", then present results.\n\nGet VaR / Risk Calculation History\n\nWhen the user asks for past risk calculations:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/risk/history?limit=50\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nQuery params: limit (optional, default 50, max 100).\n\nResponse: calculations array with calculation_id, portfolio_snapshot_id, status, method, var_95, cvar_95, sharpe_ratio, created_at, completed_at.\n\nRun Monte Carlo (QUANT — currently free for all users)\n\nWhen the user asks for a Monte Carlo simulation:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/risk/monte-carlo\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portfolio_snapshot_id\": \"{snapshot_id}\",\n    \"simulations\": 1000,\n    \"horizon_days\": 365,\n    \"model\": \"gbm\",\n    \"force_recalc\": false\n  }'\n\n\nParameters:\n\nsimulations: number of paths, default 1000 (range 100–10000)\nhorizon_days: forecast horizon, default 365 (range 1–365)\nmodel: \"gbm\" (Geometric Brownian Motion — only this is implemented) or \"garch\" (accepted but not yet implemented; will behave as GBM)\nconfidence_levels (optional): array of percentiles, default [0.05, 0.50, 0.95]\nforce_recalc (optional, default false): bypass cache\n\nPoll: GET /api/v1/risk/monte-carlo/{simulation_id}\n\nRun Stress Test (QUANT — currently free for all users)\n\nWhen the user asks for a stress test against historical crises:\n\nFirst, get available crises:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/risk/stress-test/crises\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nThen run:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/risk/stress-test\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portfolio_snapshot_id\": \"{snapshot_id}\",\n    \"crisis\": \"covid_19\",\n    \"force_recalc\": false\n  }'\n\n\nParameters:\n\ncrisis: crisis scenario ID from /stress-test/crises (e.g. covid_19, 2008_crisis)\nforce_recalc (optional, default false): bypass cache\n\nPoll: GET /api/v1/risk/stress-test/{stress_test_id}\n\nPortfolio Optimization (QUANT — currently free for all users)\nRisk Parity Optimization\n\nWhen the user asks to optimize their portfolio or balance risks:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/{snapshot_id}/optimize\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"optimization_mode\": \"preserve_directions\",\n    \"constraints\": {\n      \"max_weight\": 0.30,\n      \"min_weight\": 0.02\n    },\n    \"options\": {\n      \"risk_measure\": \"variance\",\n      \"turnover_limit\": 0.3,\n      \"turnover_penalty\": 0.1\n    }\n  }'\n\n\noptimization_mode:\n\n\"long_only\": all weights ≥ 0 (shorts are flipped to long before optimization)\n\"preserve_directions\": keeps long/short directions as-is (default)\n\"unconstrained\": weights can change sign freely\n\noptions.risk_measure (optional, default \"variance\"):\n\n\"variance\": classic ERC (Maillard, Roncalli, Teïletche 2010)\n\"cvar\": CVaR Risk Budgeting via skfolio (convex optimization, CLARABEL solver). Better for fat-tailed distributions\n\nTurnover constraints (optional, require current_weights in portfolio):\n\nturnover_limit: hard constraint — sum(|w_new - w_old|) <= limit. Optimizer stays within budget\nturnover_penalty: soft L1 penalty in objective — trades off improvement vs turnover cost\n\nPoll: GET /api/v1/portfolio/optimizations/{optimization_id} Result: GET /api/v1/portfolio/optimizations/{optimization_id}/result\n\nIMPORTANT: For optimization, use active_snapshot_id || snapshot_id from the portfolio list entry (respects the user's selected historical snapshot if set).\n\nCalmar Ratio Optimization\n\nWhen the user asks to maximize Calmar Ratio (CAGR / |Max Drawdown|):\n\nRequires 200+ trading days of price history per ticker (backend requests 252 days). If the portfolio has short history, suggest Risk Parity instead.\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/{snapshot_id}/optimize-calmar\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"optimization_mode\": \"long_only\",\n    \"constraints\": {\n      \"max_weight\": 0.50,\n      \"min_weight\": 0.05,\n      \"min_expected_return\": 0.0,\n      \"max_drawdown_limit\": 0.15,\n      \"min_calmar_target\": 0.5\n    },\n    \"options\": {\n      \"turnover_limit\": 0.3,\n      \"turnover_penalty\": 0.1\n    }\n  }'\n\n\nPoll: GET /api/v1/portfolio/optimizations/{optimization_id} (check optimization_type === \"calmar_ratio\"). Result: GET /api/v1/portfolio/optimizations/{optimization_id}/result — includes current_metrics and optimized_metrics (CAGR, max drawdown, Calmar ratio, recovery time in days). Apply: same as Risk Parity → POST /api/v1/portfolio/optimizations/{optimization_id}/apply.\n\nError INSUFFICIENT_HISTORY: not enough price history → explain the 200+ days requirement and suggest Risk Parity as alternative.\n\nApply Optimization\n\nIMPORTANT: Always show the full rebalancing plan and ask for explicit user confirmation before applying!\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/optimizations/{optimization_id}/apply\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nResponse: new_snapshot_id. Can only be applied once per optimization.\n\nBlack-Litterman Optimization (QUANT)\n\nWhen the user has views (expected returns with confidence) on specific assets and wants an optimal portfolio:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/optimize-bl\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"tickers\": [\"SBER\", \"GAZP\", \"LKOH\", \"ROSN\"],\n    \"views\": [\n      {\"ticker\": \"SBER\", \"expected_return\": 0.15, \"confidence\": 0.7},\n      {\"ticker\": \"ROSN\", \"expected_return\": -0.05, \"confidence\": 0.5}\n    ],\n    \"constraints\": { ... },\n    \"options\": { \"risk_free_rate\": 0.16, \"tau\": 0.05 },\n    \"portfolio_snapshot_id\": null,\n    \"currency\": \"RUB\"\n  }'\n\n\nParameters:\n\ntickers (required, min 2): ticker symbols for universe\nviews (required, min 1): investor views — expected_return is annual (0.15 = 15%), confidence 0.01–1.0 (Idzorek method scales Omega)\nconstraints.weight_bound_lower: min weight per asset (negative allows shorts, default -0.15)\nconstraints.weight_bound_upper: max weight per asset (default 0.25)\nconstraints.max_gross_exposure: max sum(|w_i|) (default 2.0)\nconstraints.target_net_exposure: exact sum(w_i) target (e.g. 1.0 for fully invested). Mutually exclusive with max_net_exposure\nconstraints.max_net_exposure: max |sum(w_i)|\nconstraints.turnover_limit / constraints.turnover_penalty: same as Risk Parity (see above). To enforce turnover vs current portfolio, pass portfolio_snapshot_id (see below).\noptions.risk_free_rate: annual risk-free rate (default 0.16 for MOEX)\noptions.tau: uncertainty scaling (default 0.05)\nportfolio_snapshot_id (optional): snapshot UUID of the portfolio to compute current weights from. When set, turnover constraints are applied relative to this portfolio. Omit for greenfield optimization.\ncurrency (optional, default \"RUB\"): market data universe — \"RUB\" or \"USD\". Use USD for US-listed tickers.\n\nResponse: optimization_id, status: \"pending\". Poll via GET /portfolio/optimizations/{id}, result via GET /portfolio/optimizations/{id}/result.\n\nResult contains: target_portfolio (tickers with weights and directions), metrics (expected return, volatility, Sharpe, net/gross exposure, portfolio_type), bl_posterior_returns.\n\nApply: POST /portfolio/optimizations/{id}/apply — creates a new manual portfolio \"BL Optimized\".\n\nUser prompt examples:\n\n\"I think Sber will return 15% with high confidence, optimize my portfolio\" → BL with view\n\"Build a long-short portfolio: long on banks, short on oil\" → BL with positive/negative views\n\"Optimize with Black-Litterman\" → ask user for views (expected returns + confidence per ticker)\nPre-Trade Check (FREE)\nRun Pre-Trade Risk Check\n\nWhen the user or AI agent wants to validate a target portfolio before execution. VaR is computed using historical method (empirical distribution from market data).\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/risk/pre-trade-check\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"target_portfolio\": {\"SBER\": 0.15, \"GAZP\": 0.10, \"LKOH\": 0.12, \"ROSN\": -0.08},\n    \"amount\": 1000000,\n    \"currency\": \"RUB\",\n    \"constraints\": {\n      \"max_var_pct\": 5.0,\n      \"weight_bound_upper\": 0.25,\n      \"weight_bound_lower\": -0.15,\n      \"max_gross_exposure\": 2.0,\n      \"max_net_exposure\": 0.5,\n      \"max_sector_concentration\": 0.35,\n      \"sector_limits\": {\"Energy\": 0.30, \"Finance\": 0.25}\n    }\n  }'\n\n\nParameters:\n\ntarget_portfolio (required): dict of {ticker: weight} — negative weight = short\namount (required): portfolio notional value for VaR calculation\ncurrency (optional, default \"RUB\"): currency of the portfolio — \"RUB\" or \"USD\". Determines which market data universe is used for historical VaR.\nconstraints (optional):\nmax_var_pct: maximum allowed VaR as % (e.g. 5.0 = 5%)\nweight_bound_upper / weight_bound_lower: per-position weight limits\nmax_gross_exposure / max_net_exposure: exposure limits\nmax_sector_concentration: global maximum sector weight (e.g. 0.35 = no single sector above 35%). Sector data is fetched from Data Service. If Data Service is unavailable, a warning is returned instead of an error.\nsector_limits: per-sector max weight as {sector_name: max_weight} (e.g. {\"Energy\": 0.30}). Case-insensitive matching. Can be used together with max_sector_concentration.\n\nResponse (synchronous, no polling):\n\n{\n  \"verdict\": \"pass\",\n  \"num_positions\": 4,\n  \"max_position_weight\": 0.15,\n  \"var_95_pct\": 3.21,\n  \"currency\": \"RUB\",\n  \"exposure_metrics\": {\n    \"net_exposure\": 0.29,\n    \"gross_exposure\": 0.45,\n    \"long_exposure\": 0.37,\n    \"short_exposure\": 0.08\n  },\n  \"constraint_violations\": [],\n  \"sector_exposures\": {\n    \"Energy\": 0.488889,\n    \"Finance\": 0.333333,\n    \"Other\": 0.177778\n  },\n  \"result_hash\": \"0x...\",\n  \"data_quality\": {\n    \"tickers_requested\": 4,\n    \"tickers_with_data\": 4,\n    \"tickers_missing\": [],\n    \"total_dates\": 252,\n    \"dates_dropped\": 0\n  }\n}\n\n\nsector_exposures (optional, present when max_sector_concentration or sector_limits is set): maps sector name to its share of gross exposure. Formula: sector_exposure[s] = sum(abs(w[t]) for t in sector) / gross_exposure. For long/short portfolios, abs(weight) is used so both long and short contribute proportionally.\n\ndata_quality (optional): tickers_requested, tickers_with_data, tickers_missing, total_dates, dates_dropped — reflects alignment of historical data used for VaR (inner-join by date; no zero-fill). May also include tickers_without_sector if sector metadata was unavailable for some tickers.\n\nverdict: \"pass\" (all OK), \"fail\" (hard constraint violations), \"warning\" (soft issues)\n\nUser prompt examples:\n\n\"Check if this portfolio is safe\" / \"Проверь портфель перед торговлей\"\n\"Run pre-trade check for: SBER 15%, GAZP 10%, ROSN -8%\"\nAfter BL optimization: \"Check the result before applying\"\nBatch Portfolio Creation (FREE)\nCreate Multiple Portfolios\n\nWhen the user or platform wants to create several portfolios at once:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/manual/batch\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portfolios\": [\n      {\n        \"name\": \"Pod Alpha\",\n        \"positions\": [{\"ticker\": \"SBER\", \"quantity\": 100}, {\"ticker\": \"GAZP\", \"quantity\": -50}],\n        \"currency\": \"RUB\"\n      },\n      {\n        \"name\": \"Pod Beta\",\n        \"positions\": [{\"ticker\": \"LKOH\", \"quantity\": 30}],\n        \"currency\": \"RUB\"\n      }\n    ]\n  }'\n\n\nResponse: array of {name, snapshot_id, status} per portfolio. Partial success is possible — one portfolio can fail without affecting others.\n\nCross-Portfolio Correlation (QUANT)\nCompute PnL Correlation Between Portfolios\n\nWhen the user asks about diversification across portfolios/pods or re-correlation risk:\n\ncurl -s -X POST \"https://api.riskofficer.tech/api/v1/portfolio/correlation\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portfolio_ids\": null,\n    \"window_days\": 60,\n    \"include_crisis_regime\": true,\n    \"analysis_currency\": \"RUB\"\n  }'\n\n\nParameters:\n\nportfolio_ids (optional): list of snapshot UUIDs. null = all user's portfolios (latest snapshot per portfolio)\nwindow_days (optional, default 60, range 20–252): rolling window for PnL correlation\ninclude_crisis_regime (optional, default false): compare normal vs crisis correlations (crisis = days with aggregate PnL < μ-2σ)\nanalysis_currency (optional, default \"RUB\"): currency to normalize all PnL series into before correlation — \"RUB\" or \"USD\". Use when portfolios are in different currencies; rates are CBR historical.\n\nResponse: async — poll via GET /portfolio/optimizations/{id}, result via GET /portfolio/optimizations/{id}/result.\n\nResult contains:\n\n{\n  \"result_data\": {\n    \"portfolio_names\": [\"Pod Alpha\", \"Pod Beta\", \"T-Bank\"],\n    \"correlation_matrix\": [[1.0, 0.35, 0.12], [0.35, 1.0, 0.48], [0.12, 0.48, 1.0]],\n    \"pairs\": [ ... ],\n    \"avg_pairwise_correlation\": 0.317,\n    \"analysis_currency\": \"RUB\",\n    \"fx_source\": \"CBR\",\n    \"fx_coverage\": 1.0,\n    \"fx_conversions\": { \"Pod Alpha\": \"USD->RUB\", \"Pod Beta\": \"RUB\" },\n    \"crisis_regime\": {\n      \"available\": true,\n      \"avg_normal_correlation\": 0.25,\n      \"avg_crisis_correlation\": 0.72,\n      \"re_correlation_delta\": 0.47,\n      \"n_crisis_days\": 8,\n      \"n_normal_days\": 52\n    }\n  }\n}\n\n\nKey metric: re_correlation_delta — how much correlations increase during stress. > 0.2 = significant re-correlation risk (pods lose diversification under stress).\n\nUser prompt examples:\n\n\"How correlated are my portfolios?\" / \"Какая корреляция между портфелями?\"\n\"Check re-correlation risk\" / \"Center Book analysis\"\n\"Compare normal vs crisis correlations\"\nPer-Portfolio Risk Settings (FREE)\nSet Individual VaR Threshold\n\nWhen the user wants a different risk alert threshold for a specific portfolio:\n\ncurl -s -X PATCH \"https://api.riskofficer.tech/api/v1/portfolio/{snapshot_id}/settings\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"risk_threshold_var\": 5.0}'\n\n\nParameters:\n\nrisk_threshold_var: VaR threshold in percent (5.0 = 5%). When VaR exceeds this, a push notification / alert is triggered for this specific portfolio\n\nUser prompt examples:\n\n\"Set VaR alert at 3% for my conservative portfolio\"\n\"Raise the risk threshold to 7% for the aggressive pod\"\nFeature Flags\nGet Feature Flags\n\nCheck which features are enabled:\n\ncurl -s \"https://api.riskofficer.tech/api/v1/feature-flags\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nResponse:\n\n{\n  \"websocket_enabled\": true,\n  \"subscription_required_for_quant\": false,\n  \"pre_trade_check_enabled\": true,\n  \"black_litterman_enabled\": true,\n  \"cross_correlation_enabled\": true,\n  \"cvar_risk_parity_enabled\": true\n}\n\nSubscription Status\n\nNote: Quant subscription is currently FREE for all users. All features work without payment.\n\ncurl -s \"https://api.riskofficer.tech/api/v1/subscription/status\" \\\n  -H \"Authorization: Bearer ${RISK_OFFICER_TOKEN}\"\n\n\nCurrently all users return has_subscription: true.\n\nAsync Operations\n\nVaR, Monte Carlo, Stress Test, and Optimization are asynchronous.\n\nPolling pattern:\n\nPOST endpoint → get calculation_id / simulation_id / optimization_id\nPoll GET endpoint every 2–3 seconds\nCheck status:\npending or processing → keep polling\ndone → present results\nfailed → show error\n\nTypical times:\n\nOperation\tTypical time\nVaR\t3–10 seconds\nMonte Carlo\t10–30 seconds\nStress Test\t5–15 seconds\nOptimization\t10–30 seconds\n\nUser communication:\n\nShow \"Calculating...\" immediately after starting\nIf polling takes > 10 seconds: \"Still calculating, please wait...\"\nAlways show the final result or error\nImportant Rules\n\nVirtual / analytical scope: Portfolios and all operations (create, optimize, delete, sync) exist only inside RiskOfficer. This skill is for analysis and research; it does not place or execute real broker orders.\n\nSingle Currency Rule (manual/broker portfolios): Each portfolio must contain same-currency assets. Cannot mix SBER (RUB) with AAPL (USD). Aggregated portfolio is the exception — it auto-converts using CBR rates.\n\nShort Positions: Negative quantity creates a short. For long-short portfolios, use optimization_mode: \"preserve_directions\" to keep short positions when optimizing.\n\nAlways search tickers first: Before creating or editing portfolios, use /tickers/search to validate ticker symbols and check their currency.\n\nConfirmations: Always show what will change and ask for confirmation before: updating/deleting portfolios, applying optimizations, disconnecting brokers. These actions can be irreversible.\n\nAsync: VaR, Monte Carlo, Stress Test, and Optimization are async. Poll for results.\n\nSubscription: Monte Carlo, Stress Test, and Optimization are Quant features (currently free). VaR is always free.\n\nBroker Integration: Users must connect brokers via the RiskOfficer mobile app first. Cannot connect via chat (security).\n\nError Handling:\n\n401 Unauthorized → Token invalid or expired; user needs to recreate it\n403 subscription_required → Need Quant subscription (currently free for all)\n400 missing_api_key → Broker not connected via app\n400 currency_mismatch → Mixed currencies in a single portfolio\n400 INSUFFICIENT_HISTORY → Not enough price history for Calmar (200+ trading days needed); suggest Risk Parity\n400 MVO infeasible → BL constraints too tight; suggest relaxing weight bounds or exposure limits\n404 Not Found → Portfolio or snapshot not found (may have been deleted)\n429 Too Many Requests → Optimization rate limit reached (per-tier: free 30/h, quant 100/h, pro 1000/h)\n\nActive Snapshot: active_snapshot_id from /portfolios/list takes priority over snapshot_id when running calculations. Use active_snapshot_id || snapshot_id for optimization calls.\n\nresult_hash (ERC-8004): Optimize and VaR responses include result_hash — a keccak256 hash for deterministic verification. Informational; safe to ignore unless building on-chain verification.\n\nPre-trade check before apply: For AI agents building autonomous trade pipelines, always run POST /risk/pre-trade-check on the BL optimization result before calling apply. This catches constraint violations the optimizer may not enforce (sector limits, VaR limits). Use currency: \"RUB\" or \"USD\" to match the portfolio (default RUB).\n\nCurrency in APIs: Pre-trade accepts optional currency (RUB/USD). BL accepts optional currency (market data universe) and portfolio_snapshot_id (for turnover vs current portfolio). Correlation accepts optional analysis_currency (RUB/USD) — PnL is normalized to this currency using CBR historical rates.\n\nMethodology and references: This skill includes a references/ folder with implementation notes and academic sources. When users ask how VaR, Risk Parity, Calmar, Black-Litterman, pre-trade check, correlation, or aggregated portfolio are implemented, you can cite the matching file: methodology-var.md, methodology-risk-parity.md, methodology-calmar.md, methodology-black-litterman.md, methodology-pre-trade.md, methodology-correlation.md, methodology-aggregation.md, methodology-hrp.md, methodology-monte-carlo.md, methodology-stress-test.md, methodology-metrics.md, methodology-auto-portfolio.md. For a consolidated list of papers and libraries, see references/academic-references.md.\n\nExample Conversations\nUser wants to see their portfolios\n\n\"Show my portfolios\" / \"Покажи мои портфели\" → GET /portfolios/list → Format nicely: name, total value, positions count, currency, last updated\n\nUser wants the combined total across all accounts\n\n\"Show total portfolio\" / \"Total across all accounts\" / \"Сколько у меня всего?\" → GET /portfolio/aggregated?type=all → Show total value, merged positions, number of sources → Note positions converted from other currencies\n\nUser wants to change display currency\n\n\"Show everything in dollars\" / \"Покажи в долларах\" → PATCH /portfolio/{aggregated_id}/settings with {\"base_currency\": \"USD\"} → GET /portfolio/aggregated again → Show portfolio in new currency\n\nUser asks about a company by name (not ticker)\n\n\"Add Sberbank to my portfolio\" / \"What's the ticker for Gazprom?\" / \"Добавь Газпром\" → GET /tickers/search?q=Sberbank&locale=en → Found: ticker SBER, currency RUB, exchange MOEX → Confirm with user, then proceed to create/update portfolio\n\nUser asks for a current price\n\n\"How much is Tesla?\" / \"Сколько стоит Газпром?\" → GET /tickers/search?q=TSLA&include_prices=true → Show current_price, price_change_percent, exchange\n\nUser wants to create a long-short portfolio\n\n\"Create portfolio: long SBER 100 shares, short YNDX 50 shares\" → GET /tickers/search for both tickers → confirm both are RUB/MOEX → POST /portfolio/manual with [{\"ticker\":\"SBER\",\"quantity\":100},{\"ticker\":\"YNDX\",\"quantity\":-50}] → Show created portfolio with positions\n\nUser wants to analyze portfolio risk\n\n\"What are the risks of my portfolio?\" / \"Analyze the risk\" → GET /portfolios/list → find portfolio → POST /risk/calculate-var with method: \"historical\" → Poll until done → Present VaR, CVaR, volatility, risk contributions per ticker → Offer optimization if risks are concentrated\n\nUser wants Calmar optimization\n\n\"Optimize by Calmar ratio\" / \"Maximize return per drawdown\" / \"Оптимизируй по Калмару\" → Get snapshot_id from portfolios list → POST /portfolio/{snapshot_id}/optimize-calmar → If INSUFFICIENT_HISTORY: explain 200+ trading days needed, suggest Risk Parity → Poll until done → Show current_metrics vs optimized_metrics (Calmar ratio, CAGR, max drawdown) → Show rebalancing plan and ask for confirmation before apply\n\nUser wants Monte Carlo simulation\n\n\"Run Monte Carlo for 1 year\" / \"Запусти Монте-Карло\" → POST /risk/monte-carlo with simulations: 1000, horizon_days: 365, model: \"gbm\" → Poll until done → Present percentile projections (5th, 50th, 95th)\n\nUser wants a stress test\n\n\"Run stress test\" / \"How would my portfolio survive 2008 crisis?\" → GET /risk/stress-test/crises → show available scenarios → User picks crisis (or default to most relevant) → POST /risk/stress-test → Poll, then present results\n\nUser wants to calculate risk for a historical portfolio\n\n\"Calculate risk for my portfolio as it was last month\" / \"Посчитай риски как было месяц назад\" → GET /portfolio/history?days=45 → find snapshot from ~30 days ago → PATCH /portfolio/active-snapshot with that snapshot_id and portfolio_key → POST /risk/calculate-var → poll → present results → Offer to reset: DELETE /portfolio/active-snapshot\n\nUser tries to mix currencies\n\n\"Add Apple to my RUB portfolio\" → GET /tickers/search?q=AAPL → currency: USD, exchange: NASDAQ → Portfolio is RUB → cannot mix → Explain the single-currency rule, suggest creating a separate USD portfolio\n\nUser wants to compare two portfolio snapshots\n\n\"What changed in my portfolio?\" / \"Compare to last week\" / \"Что изменилось в портфеле?\" → GET /portfolio/history → get two snapshot IDs → GET /portfolio/snapshot/{id}/diff?compare_to={other_id} → Present added/removed/modified positions, total value delta\n\nUser wants to delete a portfolio\n\n\"Delete my test portfolio\" / \"Удали портфель 'Тест'\" → Confirm: \"This will permanently delete all N snapshots for 'Test'. Cannot be undone. Continue?\" → On confirmation: DELETE /portfolio/manual/Test → Report archived_snapshots count\n\nUser wants to disconnect a broker\n\n\"Disconnect Tinkoff\" / \"Отключи Тинькофф\" → Confirm: \"This will remove the Tinkoff connection. Portfolio history will be kept. Continue?\" → On confirmation: DELETE /brokers/connections/tinkoff?sandbox=false → Inform that reconnection requires the mobile app\n\nUser wants Black-Litterman optimization\n\n\"I think Sber will return 15% and Gazprom 10%\" / \"Оптимизируй по Блэку-Литтерману\" → Collect views: ask for tickers, expected returns, and confidence → POST /portfolio/optimize-bl with views + constraints → Poll until done → Show target portfolio (tickers, weights, directions), metrics (Sharpe, expected return, volatility) → Ask confirmation before POST /portfolio/optimizations/{id}/apply\n\nUser wants to check a portfolio before trading\n\n\"Check this before I trade\" / \"Проверь портфель перед исполнением\" → POST /risk/pre-trade-check with target weights + amount + constraints → Show verdict (pass/fail/warning), VaR, exposure metrics, violations → If \"fail\" → explain which constraints are violated\n\nUser wants to create multiple portfolios at once\n\n\"Create 3 pod portfolios\" / \"Создай несколько портфелей\" → POST /portfolio/manual/batch with array of portfolios → Show per-portfolio status (created/error)\n\nUser asks about cross-portfolio correlation\n\n\"How diversified are my pods?\" / \"Какая корреляция между портфелями?\" → POST /portfolio/correlation with include_crisis_regime: true → Poll until done → Show avg pairwise correlation, matrix, pairs detail → If crisis data available: show normal vs crisis correlation and re-correlation delta → If re_correlation_delta > 0.2: warn about re-correlation risk\n\nUser wants CVaR Risk Parity\n\n\"Optimize with CVaR\" / \"Оптимизируй по CVaR\" → POST /portfolio/{snapshot_id}/optimize with options: { risk_measure: \"cvar\" } → Explain: CVaR is better for fat-tailed distributions, accounts for tail risk beyond VaR"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/mib424242/riskofficer",
    "publisherUrl": "https://clawhub.ai/mib424242/riskofficer",
    "owner": "mib424242",
    "version": "4.3.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/riskofficer",
    "downloadUrl": "https://openagent3.xyz/downloads/riskofficer",
    "agentUrl": "https://openagent3.xyz/skills/riskofficer/agent",
    "manifestUrl": "https://openagent3.xyz/skills/riskofficer/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/riskofficer/agent.md"
  }
}