界面已本地化;技能介绍与说明文档按所选语言由 AI 自动翻译,代码、命令、路径和专业术语保留原文,以原作者文档为准。
探索技能
开发与工程 · SKILL.md

riskofficer

riskofficer

【开发与工程】Manage investment portfolios, calculate risk metrics (VaR, Monte Carlo, Stress Tests), and optimize allocations using Risk Parity or Calmar Ratio

独立 GitHub 快照 · 自动分类 · 以原作者文档为准

技能说明

GitHub

name: riskofficer description: Manage investment portfolios, calculate risk metrics (VaR, Monte Carlo, Stress Tests), and optimize allocations using Risk Parity or Calmar Ratio metadata: {"openclaw":{"requires":{"env":["RISK_OFFICER_TOKEN"]},"primaryEnv":"RISK_OFFICER_TOKEN","emoji":"📊","homepage":"https://riskofficer.tech"}}

RiskOfficer Portfolio Management

This skill connects to RiskOfficer API to manage investment portfolios and calculate risks.

Setup

  1. Open RiskOfficer app → Settings → API Keys
  2. Create new token for "OpenClaw"
  3. Set environment variable: RISK_OFFICER_TOKEN=ro_pat_...

Or configure in ~/.openclaw/openclaw.json:

{
  "skills": {
    "entries": {
      "riskofficer": {
        "enabled": true,
        "apiKey": "ro_pat_..."
      }
    }
  }
}

API Base URL

https://api.riskofficer.tech/api/v1

All requests require header: Authorization: Bearer ${RISK_OFFICER_TOKEN}


Available Commands

Portfolio Management

List Portfolios

When user asks to see their portfolios, list portfolios, or show portfolio overview:

curl -s "https://api.riskofficer.tech/api/v1/portfolios/list" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}"

Response contains array of portfolios with: id, name, total_value, currency, positions_count, broker, sandbox.

Get Portfolio Details

When user asks about a specific portfolio or wants to see positions:

curl -s "https://api.riskofficer.tech/api/v1/portfolio/snapshot/{snapshot_id}" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}"

Response contains: name, total_value, currency, positions (array with ticker, quantity, current_price, value, weight).

Get Aggregated Portfolio

When user asks for total/combined portfolio, overall position, or "show everything together":

curl -s "https://api.riskofficer.tech/api/v1/portfolio/aggregated?type=all" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}"

Query params:

  • type=production — manual + broker (sandbox=false)
  • type=sandbox — broker (sandbox=true) only
  • type=all — everything (default)

Response:

  • portfolio.positions — all positions merged across portfolios
  • portfolio.total_value — total value in base currency
  • portfolio.currency — base currency (RUB or USD)
  • portfolio.sources_count — number of portfolios aggregated

Example response:

{
  "portfolio": {
    "positions": [
      {"ticker": "SBER", "quantity": 150, "value": 42795, "sources": ["Т-Банк", "Manual"]},
      {"ticker": "AAPL", "quantity": 10, "value": 189500, "original_currency": "USD"}
    ],
    "total_value": 1500000,
    "currency": "RUB",
    "sources_count": 3
  },
  "snapshot_id": "uuid-of-aggregated"
}

Currency conversion: Positions in different currencies are automatically converted to base currency using current exchange rates (CBR for RUB).

Change Base Currency (Aggregated Portfolio)

When user wants to see aggregated portfolio in different currency:

curl -s -X PATCH "https://api.riskofficer.tech/api/v1/portfolio/{aggregated_snapshot_id}/settings" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"base_currency": "USD"}'

Supported currencies: RUB, USD

After changing, aggregated portfolio recalculates automatically.

User prompt examples:

  • "Покажи всё в долларах" → change base_currency to USD
  • "Переведи портфель в рубли" → change base_currency to RUB

Include/Exclude from Aggregated

When user wants to exclude a portfolio from total calculation:

curl -s -X PATCH "https://api.riskofficer.tech/api/v1/portfolio/{snapshot_id}/settings" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"include_in_aggregated": false}'

Use cases:

  • "Не учитывай песочницу в общем портфеле" → exclude sandbox
  • "Убери демо-портфель из расчёта" → exclude manual portfolio

Create Manual Portfolio

When user wants to create a new portfolio with specific positions:

curl -s -X POST "https://api.riskofficer.tech/api/v1/portfolio/manual" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Portfolio Name",
    "positions": [
      {"ticker": "SBER", "quantity": 100},
      {"ticker": "GAZP", "quantity": 50}
    ]
  }'

IMPORTANT RULE - Single Currency: All assets in a portfolio must be in the same currency.

  • RUB assets: SBER, GAZP, LKOH, YNDX, etc.
  • USD assets: AAPL, MSFT, GOOGL, etc. Cannot mix! If user tries to mix currencies, explain and suggest creating separate portfolios.

Update Portfolio (Add/Remove Positions)

When user wants to modify an existing portfolio:

  1. First get current portfolio to find the name:
curl -s "https://api.riskofficer.tech/api/v1/portfolio/snapshot/{snapshot_id}" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}"
  1. Then create new snapshot with updated positions (use same name):
curl -s -X POST "https://api.riskofficer.tech/api/v1/portfolio/manual" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "<same name from step 1>",
    "positions": [<updated list of all positions>]
  }'

IMPORTANT: Always show user what will change and ask for confirmation before updating.


Broker Integration

List Connected Brokers

When user asks about connected brokers or broker status:

curl -s "https://api.riskofficer.tech/api/v1/brokers/connections" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}"

Refresh Portfolio from Tinkoff

When user wants to sync/update portfolio from Tinkoff (broker must be connected via app):

curl -s -X POST "https://api.riskofficer.tech/api/v1/portfolio/proxy/broker/tinkoff/portfolio" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"sandbox": false}'

If response is 400 with missing_api_key, broker is not connected. Explain how to connect:

  1. Get API token from https://www.tbank.ru/invest/settings/api/
  2. Open RiskOfficer app → Settings → Brokers → Connect Tinkoff
  3. Paste token and connect

Risk Calculations

Calculate VaR (FREE)

When user asks to calculate risks, VaR, or risk metrics:

curl -s -X POST "https://api.riskofficer.tech/api/v1/risk/calculate-var" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "portfolio_snapshot_id": "{snapshot_id}",
    "method": "historical",
    "confidence": 0.95,
    "horizon_days": 1
  }'

Methods: historical, parametric, garch

This returns calculation_id. Poll for result:

curl -s "https://api.riskofficer.tech/api/v1/risk/calculation/{calculation_id}" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}"

Wait until status is done, then present results.

Run Monte Carlo (QUANT - currently free for all users)

When user asks for Monte Carlo simulation:

curl -s -X POST "https://api.riskofficer.tech/api/v1/risk/monte-carlo" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "portfolio_snapshot_id": "{snapshot_id}",
    "simulations": 1000,
    "horizon_days": 365,
    "model": "gbm"
  }'

Poll: GET /api/v1/risk/monte-carlo/{simulation_id}

Run Stress Test (QUANT - currently free for all users)

When user asks for stress test:

First, get available crises:

curl -s "https://api.riskofficer.tech/api/v1/risk/stress-test/crises" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}"

Then run stress test:

curl -s -X POST "https://api.riskofficer.tech/api/v1/risk/stress-test" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "portfolio_snapshot_id": "{snapshot_id}",
    "crisis": "covid_19"
  }'

Poll: GET /api/v1/risk/stress-test/{stress_test_id}


Portfolio Optimization (QUANT - currently free for all users)

Risk Parity Optimization

When user asks to optimize portfolio or balance risks:

curl -s -X POST "https://api.riskofficer.tech/api/v1/portfolio/{snapshot_id}/optimize" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "optimization_mode": "preserve_directions",
    "constraints": {
      "max_weight": 0.30,
      "min_weight": 0.02
    }
  }'

Modes:

  • long_only: All weights ≥ 0
  • preserve_directions: Keep long/short as-is
  • unconstrained: Any direction allowed

Poll: GET /api/v1/portfolio/optimizations/{optimization_id} Result: GET /api/v1/portfolio/optimizations/{optimization_id}/result

Calmar Ratio Optimization

When user asks for Calmar optimization, maximize Calmar Ratio (CAGR / |Max Drawdown|). Requires 200+ trading days of price history per ticker (backend requests 252 days). If user has short history, suggest Risk Parity instead.

curl -s -X POST "https://api.riskofficer.tech/api/v1/portfolio/{snapshot_id}/optimize-calmar" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "optimization_mode": "long_only",
    "constraints": {
      "max_weight": 0.50,
      "min_weight": 0.05,
      "min_expected_return": 0.0,
      "max_drawdown_limit": 0.15,
      "min_calmar_target": 0.5
    }
  }'

Poll: GET /api/v1/portfolio/optimizations/{optimization_id} (check optimization_type === "calmar_ratio").
Result: GET /api/v1/portfolio/optimizations/{optimization_id}/result — includes current_metrics, optimized_metrics (cagr, max_drawdown, calmar_ratio, recovery_time_days).
Apply: same as Risk Parity — POST /api/v1/portfolio/optimizations/{optimization_id}/apply.

Apply Optimization

IMPORTANT: Always show rebalancing plan and ask for explicit user confirmation first!

curl -s -X POST "https://api.riskofficer.tech/api/v1/portfolio/optimizations/{optimization_id}/apply" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}"

Subscription Status

Note: Quant subscription is currently FREE for all users. All features work without payment.

Check Subscription

When you need to check if user has Quant subscription:

curl -s "https://api.riskofficer.tech/api/v1/subscription/status" \
  -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}"

Currently all users have has_subscription: true (free tier enabled).


Async Operations

VaR, Monte Carlo, Stress Test, and Optimization are asynchronous.

Polling pattern:

  1. Call POST endpoint → get calculation_id / simulation_id / optimization_id
  2. Poll GET endpoint every 2-3 seconds
  3. Check status field:
    • pending or processing → keep polling
    • done → present results
    • failed → show error message

Typical times:

OperationTypical Time
VaR3-10 seconds
Monte Carlo10-30 seconds
Stress Test5-15 seconds
Optimization10-30 seconds

User communication:

  • Show "Calculating..." message immediately after starting
  • If polling takes >10 seconds, update: "Still calculating... please wait"
  • Always show result or error when complete

Important Rules

  1. Single Currency Rule (Manual/Broker portfolios): Each individual portfolio must have same-currency assets. Cannot mix SBER (RUB) with AAPL (USD) in one manual portfolio. Suggest separate portfolios.

  2. Aggregated Portfolio: The aggregated portfolio CAN contain assets in different currencies - they are automatically converted to base currency (RUB or USD) using CBR rates.

  3. Subscription: Monte Carlo, Stress Test, and Optimization are Quant features (currently free for all users). VaR is always FREE.

  4. Broker Integration: User must connect broker in RiskOfficer app first. Cannot connect via chat (security).

  5. Confirmations: Before applying optimizations or making significant portfolio changes, always show what will change and ask for confirmation.

  6. Async Operations: VaR, Monte Carlo, Stress Test, and Optimization are async. Poll for results.

  7. Error Handling:

    • 401 Unauthorized → Token invalid or expired, user needs to recreate
    • 403 subscription_required → Need Quant subscription (currently free for all)
    • 400 missing_api_key → Broker not connected
    • 400 currency_mismatch → Mixed currencies

Example Conversations

User wants portfolio overview

User: "Show my portfolios" → Call GET /portfolios/list → Format nicely with values, positions count, last updated

User wants combined/total portfolio

User: "Покажи всё вместе" / "Total portfolio" / "Сколько у меня всего?" → Call GET /portfolio/aggregated?type=all → Show total value, all positions merged, sources count → Note which positions were converted from other currencies

User wants to change display currency

User: "Покажи в долларах" / "Switch to USD" → Call PATCH /portfolio/{aggregated_id}/settings with {"base_currency": "USD"} → Call GET /portfolio/aggregated again → Show portfolio in new currency

User wants to analyze risks

User: "What are the risks of my main portfolio?" → Call GET /portfolios/list to find the portfolio → Call POST /risk/calculate-var → Poll until done → Present VaR, CVaR, volatility, risk contributions → Offer optimization if risks are unbalanced

User wants Calmar optimization

User: "Оптимизируй портфель по Калмару" / "Optimize using Calmar Ratio" / "Maximize return per drawdown" → Call GET /portfolios/list or aggregated to get snapshot_id → Call POST /portfolio/{snapshot_id}/optimize-calmar with optimization_mode and optional constraints → If 400 INSUFFICIENT_HISTORY: explain need 200+ trading days of history, suggest Risk Parity as alternative → Poll GET /optimizations/{id} until status is done → Call GET /optimizations/{id}/result — show current_metrics vs optimized_metrics (Calmar ratio, CAGR, max drawdown) → Show rebalancing plan and ask for confirmation before apply

User tries to mix currencies

User: "Add Apple to my portfolio" → Check portfolio currency (RUB) vs AAPL currency (USD) → Explain cannot mix, suggest creating separate USD portfolio

User requests Monte Carlo or Stress Test

User: "Run Monte Carlo" → Call POST /risk/monte-carlo with portfolio snapshot → Poll until done → Present simulation results with percentiles and projections

我的收藏