Manage investment portfolios, calculate risk metrics (VaR, Monte Carlo, Stress Tests), and optimize allocations using Risk Parity or Calmar Ratio
Scanned 5/30/2026
Install via CLI
openskills install Demerzels-lab/elsamultiskillagent---
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 (requires 200+ days history):
```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"
}'
```
#### Apply Optimization
**IMPORTANT:** Always show rebalancing plan and ask for explicit user confirmation first!
```bash
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:
```bash
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:**
| Operation | Typical Time |
|-----------|--------------|
| VaR | 3-10 seconds |
| Monte Carlo | 10-30 seconds |
| Stress Test | 5-15 seconds |
| Optimization | 10-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 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
No comments yet. Be the first to comment!