Skill: elmoyeldo/ctrader-commander (version 1.0.0)

--- name: ctrader-commander description: Place and manage cTrader orders (market, limit, stop), check open positions, fetch live quotes and OHLC candles, and query account balance and equity via a local HTTP proxy. No credentials or token required at call time. homepage: https://github.com/LogicalSapien/ctrader-openapi-proxy metadata: {"openclaw": {"emoji": "\ud83d\udcc8", "requires": {"bins": ["curl"]}, "homepage": "https://github.com/LogicalSapien/ctrader-openapi-proxy"}} --- # cTrader Commander Use when the user wants to place trades, check positions or balance, get live prices, fetch candles, or manage orders on a cTrader account. All calls go to `http://localhost:9009` — credentials live in `.env` on the server, never passed by callers. > **Proxy repo:** https://github.com/LogicalSapien/ctrader-openapi-proxy > Clone it, add your `.env`, and run `make run` to start the proxy before using this skill. Full reference: `{baseDir}/endpoints.md` ## Check proxy is running ```bash curl -s "http://localhost:9009/get-data?command=ProtoOAVersionReq" ``` If it fails, start the proxy: `cd ~/ctrader-openapi-proxy && make run` ## Find symbol IDs (do this first) Symbol IDs are broker-specific — look them up before placing orders or fetching data: ```bash curl -s "http://localhost:9009/get-data?command=ProtoOASymbolsListReq" ``` Returns `symbol[]` with `symbolId` and `symbolName`. Note the ID for your instrument. ## Place a market order ```bash curl -s -X POST http://localhost:9009/api/market-order \ -H "Content-Type: application/json" \ -d '{"symbolId": 158, "orderType": "MARKET", "tradeSide": "BUY", "volume": 1000}' ``` Volume is in **units**: `1000` = 0.01 lot · `10000` = 0.1 lot · `100000` = 1 lot. Add `"relativeStopLoss": 200, "relativeTakeProfit": 350` (pips, market orders only). ## Place a limit or stop order ```bash curl -s -X POST http://localhost:9009/api/market-order \ -H "Content-Type: application/json" \ -d '{"symbolId": 158, "orderType": "LIMIT", "tradeSide": "BUY", "volume": 1000, "price": 0.62500}' ``` `orderType`: `MARKET` · `LIMIT` · `STOP` — `tradeSide`: `BUY` · `SELL` ## Get OHLC candles ```bash NOW_MS=$(python3 -c "import time; print(int(time.time()*1000))") FROM_MS=$(python3 -c "import time; print(int(time.time()*1000) - 3600000)") curl -s -X POST http://localhost:9009/api/trendbars \ -H "Content-Type: application/json" \ -d "{\"fromTimestamp\": $FROM_MS, \"toTimestamp\": $NOW_MS, \"period\": \"M5\", \"symbolId\": 158}" ``` Periods: `M1 M2 M3 M4 M5 M10 M15 M30 H1 H4 H12 D1 W1 MN1` ## Get live quote (tick data) ```bash curl -s -X POST http://localhost:9009/api/live-quote \ -H "Content-Type: application/json" \ -d '{"symbolId": 158, "quoteType": "BID", "timeDeltaInSeconds": 60}' ``` `quoteType`: `BID` or `ASK` ## Open positions and pending orders ```bash curl -s "http://localhost:9009/get-data?command=ProtoOAReconcileReq" ``` ## Close a position ```bash curl -s "http://localhost:9009/get-data?command=ClosePosition%20123456%201000" # ClosePosition <positionId> <volumeInUnits> ``` ## Cancel a pending order ```bash curl -s "http://localhost:9009/get-data?command=CancelOrder%20789" ``` ## Account info (balance, equity, leverage) ```bash curl -s "http://localhost:9009/get-data?command=ProtoOATraderReq" ``` A local HTTP proxy (`localhost:9009`) that wraps the cTrader OpenAPI Protobuf connection and exposes it as a REST API. No credentials are passed at call time — they are loaded from `.env` on the server. Full endpoint reference: `{baseDir}/endpoints.md` Python usage examples: `{baseDir}/examples.md` --- ## Prerequisites The proxy must be running before any call. If unsure, check: ```bash curl -s "http://localhost:9009/get-data?command=ProtoOAVersionReq" ``` If it returns JSON, the proxy is up. If it fails, start it: ```bash cd ~/ctrader-openapi-proxy && make run ``` --- ## IMPORTANT: Symbol IDs are broker-specific **Always look up the symbol ID before placing orders or fetching candle/tick data.** Symbol IDs differ between brokers and between demo and live accounts. ```bash curl -s "http://localhost:9009/get-data?command=ProtoOASymbolsListReq" ``` Response contains `symbol[]` with `symbolId` and `symbolName`. Find your instrument and note its `symbolId`. --- ## Endpoints ### Get OHLC Candles ``` POST /api/trendbars ``` ```json { "fromTimestamp": 1700000000000, "toTimestamp": 1700086400000, "period": "M5", "symbolId": 158 } ``` `period` options: `M1 M2 M3 M4 M5 M10 M15 M30 H1 H4 H12 D1 W1 MN1` For current time in ms (macOS): ```bash NOW_MS=$(python3 -c "import time; print(int(time.time()*1000))") FROM_MS=$(python3 -c "import time; print(int(time.time()*1000) - 3600000)") ``` --- ### Get Live Quote / Tick Data ``` POST /api/live-quote ``` ```json { "symbolId": 158, "quoteType": "BID", "timeDeltaInSeconds": 60 } ``` `quoteType`: `"BID"` or `"ASK"` --- ### Place a Market / Limit / Stop Order ``` POST /api/market-order ``` ```json { "symbolId": 158, "orderType": "MARKET", "tradeSide": "BUY", "volume": 1000, "comment": "my trade", "relativeStopLoss": 200, "relativeTakeProfit": 350 } ``` **orderType** values: `"MARKET"` `"LIMIT"` `"STOP"` **tradeSide** values: `"BUY"` `"SELL"` For `LIMIT` and `STOP` orders, include `"price": 0.62500`. `relativeStopLoss` / `relativeTakeProfit` are in **pips** and apply to `MARKET` orders only. **Volume units (NOT lots):** | `volume` | Lots | Notes | |---|---|---| | `1000` | 0.01 | Micro lot — typical minimum | | `10000` | 0.1 | Mini lot | | `100000` | 1 | Standard lot | --- ### Get Open Positions and Pending Orders ``` GET /get-data?command=ProtoOAReconcileReq ``` Returns `position[]` and `order[]`. Each position has `positionId`, `symbolId`, `tradeSide`, `volume`, `price`. --- ### Close an Open Position ``` GET /get-data?command=ClosePosition <positionId> <volumeInUnits> ``` Example — close position 123456 with 1000 units (0.01 lot): ```bash curl -s "http://localhost:9009/get-data?command=ClosePosition%20123456%201000" ``` --- ### Cancel a Pending Order ``` GET /get-data?command=CancelOrder <orderId> ``` ```bash curl -s "http://localhost:9009/get-data?command=CancelOrder%20789" ``` --- ### Set Active Account *(optional)* Account is auto-authorised from `.env` on startup. Only call this to switch accounts at runtime: ```bash curl -s -X POST http://localhost:9009/api/set-account ``` To switch to a different account pass `{"accountId": 12345678}` as JSON body. --- ### Generic Command Passthrough Any cTrader API command can be called via: ``` GET /get-data?command=COMMAND_NAME arg1 arg2 ``` No token required — credentials are read from `.env` on the server. Full list of supported commands: `{baseDir}/endpoints.md` --- ## Workflow: first trade 1. Look up your symbol ID: ```bash curl -s "http://localhost:9009/get-data?command=ProtoOASymbolsListReq" | python3 -c " import sys, json data = json.load(sys.stdin) [print(s['symbolId'], s['symbolName']) for s in data.get('symbol', []) if 'EURUSD' in s['symbolName']] " ``` 2. Check your account details: ```bash curl -s "http://localhost:9009/get-data?command=ProtoOATraderReq" ``` 3. Place a market buy: ```bash curl -s -X POST http://localhost:9009/api/market-order \ -H "Content-Type: application/json" \ -d '{"symbolId": 1, "orderType": "MARKET", "tradeSide": "BUY", "volume": 1000}' ``` 4. Check open positions: ```bash curl -s "http://localhost:9009/get-data?command=ProtoOAReconcileReq" ```

path: endpoints.md
content: # cTrader Proxy — Full Endpoint Reference

Base URL: `http://localhost:9009`  
No token or API key is needed by callers. All credentials live in `.env` on the server.

---

## REST Endpoints

| Method | Path | Description |
|---|---|---|
| `POST` | `/api/set-account` | Switch active account (optional — auto-set from `.env` on startup) |
| `POST` | `/api/trendbars` | OHLC candle data |
| `POST` | `/api/live-quote` | Recent tick/quote data |
| `POST` | `/api/market-order` | Place market, limit, or stop order |
| `GET` | `/get-data?command=...` | Generic passthrough for any cTrader command |

---

## POST /api/set-account

Sending no body (or `{}`) uses `CTRADER_ACCOUNTID` from `.env`.

```
POST /api/set-account
Content-Type: application/json

{}
```

To switch to a different account:
```json
{ "accountId": 12345678 }
```

**Normal response when already authorised (not an error):**
```json
{
  "ctidTraderAccountId": "12345678",
  "errorCode": "ALREADY_LOGGED_IN",
  "description": "Trading account is already authorized in this channel"
}
```

---

## POST /api/trendbars

```
POST /api/trendbars
Content-Type: application/json

{
  "fromTimestamp": 1700000000000,
  "toTimestamp":   1700086400000,
  "period":        "M5",
  "symbolId":      158
}
```

- Timestamps in **milliseconds** since Unix epoch.
- `period` values: `M1 M2 M3 M4 M5 M10 M15 M30 H1 H4 H12 D1 W1 MN1`
- `symbolId` is broker-specific — use `ProtoOASymbolsListReq` to look it up.

---

## POST /api/live-quote

```
POST /api/live-quote
Content-Type: application/json

{
  "symbolId":           158,
  "quoteType":          "BID",
  "timeDeltaInSeconds": 60
}
```

- `quoteType`: `"BID"` or `"ASK"`
- Returns tick data for the last N seconds.

---

## POST /api/market-order

```
POST /api/market-order
Content-Type: application/json

{
  "symbolId":           158,
  "orderType":          "MARKET",
  "tradeSide":          "BUY",
  "volume":             1000,
  "comment":            "my trade",
  "relativeStopLoss":   200,
  "relativeTakeProfit": 350
}
```

| Field | Required | Notes |
|---|---|---|
| `symbolId` | ✓ | Broker-specific integer |
| `orderType` | ✓ | `"MARKET"` `"LIMIT"` `"STOP"` |
| `tradeSide` | ✓ | `"BUY"` `"SELL"` |
| `volume` | ✓ | Units (not lots). 1000 = 0.01 lot |
| `price` | For LIMIT/STOP | e.g. `0.62500` |
| `comment` | Optional | Order label |
| `relativeStopLoss` | Optional | Pips from entry, MARKET only |
| `relativeTakeProfit` | Optional | Pips from entry, MARKET only |

Volume reference:
| `volume` | Lots |
|---|---|
| `1000` | 0.01 (micro — typical minimum) |
| `10000` | 0.1 (mini) |
| `100000` | 1.0 (standard) |

---

## GET /get-data?command=...

### All supported commands

| Command | Args | Description |
|---|---|---|
| `setAccount` | `accountId` | Switch active account |
| `ProtoOAVersionReq` | — | Get API version |
| `ProtoOAGetAccountListByAccessTokenReq` | — | List all accounts on this token |
| `ProtoOASymbolsListReq` | — | **List all tradeable symbols with their IDs** |
| `ProtoOAAssetListReq` | — | List all assets (currencies, etc.) |
| `ProtoOAAssetClassListReq` | — | List asset classes |
| `ProtoOASymbolCategoryListReq` | — | List symbol categories |
| `ProtoOATraderReq` | — | Account details (balance, equity, leverage) |
| `ProtoOAReconcileReq` | — | Open positions and pending orders |
| `ProtoOAGetTrendbarsReq` | `fromTs toTs period symbolId` | OHLC candle data |
| `ProtoOAGetTickDataReq` | `seconds quoteType symbolId` | Tick/quote data |
| `NewMarketOrder` | `symbolId tradeSide volume comment [sl] [tp]` | Place market order |
| `NewLimitOrder` | `symbolId tradeSide volume price` | Place limit order |
| `NewStopOrder` | `symbolId tradeSide volume price` | Place stop order |
| `ClosePosition` | `positionId volumeInUnits` | Close an open position |
| `CancelOrder` | `orderId` | Cancel a pending order |
| `OrderDetails` | `orderId` | Details of a specific order |
| `OrderListByPositionId` | `positionId` | Order history for a position |
| `DealOffsetList` | `dealId` | Offset deals for a deal |
| `GetPositionUnrealizedPnL` | — | Unrealised P&L for all open positions |
| `ProtoOAExpectedMarginReq` | `symbolId volumeInUnits` | Expected margin for a trade |

### Usage examples

```bash
# Get API version
curl -s "http://localhost:9009/get-data?command=ProtoOAVersionReq"

# List all symbols (find symbolIds)
curl -s "http://localhost:9009/get-data?command=ProtoOASymbolsListReq"

# Account details (balance, equity)
curl -s "http://localhost:9009/get-data?command=ProtoOATraderReq"

# Open positions + pending orders
curl -s "http://localhost:9009/get-data?command=ProtoOAReconcileReq"

# Close position 123456 (1000 units = 0.01 lot)
curl -s "http://localhost:9009/get-data?command=ClosePosition%20123456%201000"

# Cancel pending order 789
curl -s "http://localhost:9009/get-data?command=CancelOrder%20789"

# Unrealised P&L
curl -s "http://localhost:9009/get-data?command=GetPositionUnrealizedPnL"

# Expected margin for 1 lot of symbol 158
curl -s "http://localhost:9009/get-data?command=ProtoOAExpectedMarginReq%20158%20100000"
```


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)