← Back to Skills
Automation

riskofficer

mib424242 By mib424242 👁 3 views ▲ 0 votes

Manage investment portfolios, calculate risk metrics

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`:
```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)
automation

Comments

Sign in to leave a comment

Loading comments...