Back to Skills
    🦞

    riskofficer

    Manage investment portfolios, calculate risk metrics

    By @mib424242
    View on GitHub
    SKILL.md
    ---
    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`:
    ```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:
    
    ```bash
    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:
    
    ```bash
    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":
    
    ```bash
    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:**
    ```json
    {
      "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:
    
    ```bash
    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:
    
    ```bash
    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:
    
    ```bash
    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:
    ```bash
    curl -s "https://api.riskofficer.tech/api/v1/portfolio/snapshot/{snapshot_id}" \
      -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}"
    ```
    
    2. Then create new snapshot with updated positions (use same name):
    ```bash
    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:
    
    ```bash
    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):
    
    ```bash
    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:
    
    ```bash
    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:
    
    ```bash
    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:
    
    ```bash
    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:
    ```bash
    curl -s "https://api.riskofficer.tech/api/v1/risk/stress-test/crises" \
      -H "Authorization: Bearer ${RISK_OFFICER_TOKEN}"
    ```
    
    Then run stress test:
    ```bash
    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:
    
    ```bash
    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.
    
    ```bash
    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 fi
    
    ... (truncated)