Calls, Call Episodes and Positioning
When an account or channel posts that it is long or short an asset, Elfa records it, at three levels:
| Endpoint | One row is | Answers |
|---|---|---|
/v3/calls | A call: one post that took or changed a position | What did accounts post? |
/v3/calls/episodes | A call episode: one position, from open to close | What does an account hold, and how did it end? |
/v3/calls/positioning | One asset, now | How many are long and short it? |
calls open SPY ─▶ reaffirm ─▶ trim ─▶ close 4 posts: episodeId
episode long SPY, closed 1 position: calls[].callId
Not returned: post text, a recommendation, or a win flag per position. An
episode has its entry and exit prices; track scores the author across all of
them.
Filtering by Account
Both sources appear unless you filter by handle:
| X | Telegram | |
|---|---|---|
source | twitter | telegram |
handle | The X account, e.g. 3PeaksTrading | null |
channel | null | The channel or group it was posted in, never the poster. Null when we cannot name it |
handle takes X accounts only, with or without @, any case, up to 40
characters. Telegram channels cannot be filtered on yet: filter by entityIds
and time, and keep the rows whose channel matches. An X account we hold with no calls returns an
empty list.
Calls
GET /v3/calls: calls in a time range, newest first, or up to 30 by id.
{
"calls": [
{
"id": "07465e06-12bd-4117-b68d-0226d3faed1e",
"episodeId": "9dfbbc44-4919-4da7-9999-426d83963d5d",
"handle": "3PeaksTrading",
"source": "twitter",
"channel": null,
"asset": {
"id": "42703feb-7611-4c9d-8062-b455bae2bcc5",
"symbol": "SPCX",
"name": "Space Exploration Technologies Corp. Class A Common Stock",
"entityType": "equity"
},
"callAction": "close",
"setup": null,
"thesis": null,
"sourceRecordId": "2107190165538320484",
"occurredAt": 1791228259
},
{
"id": "8a069b20-aef5-4fb9-984c-de7a6a6cd826",
"episodeId": "3809d877-e764-437c-a58f-a237a1b0ca35",
"handle": null,
"source": "telegram",
"channel": "J7’s - Trading Journal (笔记) 📝",
"asset": {
"id": "5a158802-db76-4bd5-b962-9e0c512222ef",
"symbol": "NEAR",
"name": "NEAR Protocol",
"entityType": "token"
},
"callAction": "open",
"setup": "technical",
"thesis": null,
"sourceRecordId": "telegram_group:59eff3d5c0ca5f8f2525b5220194d56e",
"occurredAt": 1791188119
}
],
"nextCursor": null,
"hasMore": false,
"servedFrom": null
}
GET /v3/calls?callIds=8a069b20-aef5-4fb9-984c-de7a6a6cd826,07465e06-12bd-4117-b68d-0226d3faed1e,
read from production on 6 October 2026: an account closing a long, and a
channel opening one.
| Field | Meaning |
|---|---|
id | The call id. Pass it back in callIds |
episodeId | Its call episode |
handle, source, channel | The X account, or the channel or group it was posted in. source is null when the post cannot be traced |
asset | id (a family id), symbol, name, entityType. Null when we cannot name it |
callAction | open, reaffirm, add, trim, reverse, close or stopOut. See Call |
setup | How the post reads the trade: technical, momentum, fade (against the move) or accumulation. Null if unsaid |
thesis | What will drive the move: macro, narrative or event. Null if unsaid |
sourceRecordId | The post. On X its id: https://x.com/i/web/status/<id>. On Telegram, a reference to a group of messages |
occurredAt | When the post was made |
One post can be two calls. A reverse closes one episode and opens the
opposite one, so it appears twice with the same sourceRecordId. A second
open in a direction already held is recorded as reaffirm.
Parameters
| Parameter | Values | Default | Notes |
|---|---|---|---|
from | Unix seconds | required unless callIds | Against occurredAt |
to | Unix seconds | now | |
handle | X handle | See Filtering by Account | |
entityIds | up to 30 token or equity ids, a list | Calls on any of them | |
callIds | up to 30 call ids, a list | Other filters still apply | |
order | desc, asc | desc | |
limit | 1 to 30 | 10 | |
cursor |
/v3/calls has no market filter. For one market, filter episodes by
assetClass and fetch their calls.
Errors
The common errors apply. Calls, episodes and
positioning take tokens and equities only: a person, project or organisation
id is a 400, … is not an asset family.
Call Episodes
GET /v3/calls/episodes: call episodes open at any point in a time range, newest opened first, or up to
30 by id.
{
"episodes": [
{
"id": "9dfbbc44-4919-4da7-9999-426d83963d5d",
"handle": "3PeaksTrading",
"source": "twitter",
"channel": null,
"asset": {
"id": "42703feb-7611-4c9d-8062-b455bae2bcc5",
"symbol": "SPCX",
"name": "Space Exploration Technologies Corp. Class A Common Stock",
"entityType": "equity"
},
"direction": "long",
"horizon": "swing",
"status": "closed",
"openedAt": 1791210989,
"closedAt": 1791228259,
"observedAt": 1791211102,
"entryPrice": 164.29,
"exitPrice": 168.04,
"closeReason": "close",
"setup": "technical",
"thesis": "event",
"statedTarget": 180,
"statedStop": null,
"track": {
"alphaScore": -0.027325837327777693,
"hitRate": 0.42424242424242425,
"avgReturn": 0.006040354648865286,
"scoredEpisodes": 66,
"totalEpisodes": 104
},
"calls": [
{ "callId": "16e05ecf-1cee-42b1-92fd-735c014e3adb", "callAction": "open", "occurredAt": 1791210989 },
{ "callId": "07465e06-12bd-4117-b68d-0226d3faed1e", "callAction": "close", "occurredAt": 1791228259 }
]
},
{
"id": "3809d877-e764-437c-a58f-a237a1b0ca35",
"handle": null,
"source": "telegram",
"channel": "J7’s - Trading Journal (笔记) 📝",
"asset": {
"id": "5a158802-db76-4bd5-b962-9e0c512222ef",
"symbol": "NEAR",
"name": "NEAR Protocol",
"entityType": "token"
},
"direction": "long",
"horizon": "unspecified",
"status": "open",
"openedAt": 1791188119,
"closedAt": null,
"observedAt": 1791188894,
"entryPrice": 5.16425,
"exitPrice": null,
"closeReason": null,
"setup": "technical",
"thesis": null,
"statedTarget": 6,
"statedStop": null,
"track": {
"alphaScore": -0.14406656638938875,
"hitRate": 0.5333333333333333,
"avgReturn": 0.026908979741187455,
"scoredEpisodes": 30,
"totalEpisodes": 53
},
"calls": [
{ "callId": "8a069b20-aef5-4fb9-984c-de7a6a6cd826", "callAction": "open", "occurredAt": 1791188119 }
]
}
],
"nextCursor": null,
"hasMore": false,
"servedFrom": null
}
GET /v3/calls/episodes?episodeIds=9dfbbc44-4919-4da7-9999-426d83963d5d,3809d877-e764-437c-a58f-a237a1b0ca35,
read from production on 6 October 2026: the episodes of the two calls above.
The SPCX long closed about five hours after opening, at 168.04 against a
164.29 entry. The NEAR long is still open.
| Field | Meaning |
|---|---|
id | The episode id. Pass it back in episodeIds |
handle, source, channel | The X account, or the channel or group, that holds it. source is where the opening post was made |
asset | As on calls. Null when we cannot name it |
direction | long or short |
horizon | scalp, swing, position or unspecified. A later post can change it. Sets the expiry |
status | open or closed |
openedAt, closedAt | The opening post, and the close. closedAt is null while open |
observedAt | When we recorded it. See Point in Time |
entryPrice, exitPrice | USD at open and at close. Null when we could not price it |
closeReason | close, reverse or stopOut by the account. expiry when nobody closed it |
setup, thesis | The opening call's. Null when it gave none |
statedTarget, statedStop | The prices the author named when opening. Null when none |
track | The author's record. See Track |
calls | Its calls, oldest first: callId, callAction, occurredAt |
A position opened before your history keeps its
prices and status, but lists only its calls inside the history, and its
opening setup, thesis, statedTarget and statedStop are null.
Track
The author's record over every position they have opened, in every market, not cut to your history. The same object is on top accounts. Scored daily from closed positions.
| Field | Meaning |
|---|---|
alphaScore | -1 to 1. How well scored positions did, against the market (BTC for crypto, SPY for equities) and outright. Pulled toward 0 while there are few |
hitRate | 0 to 1. Share of scored positions that made money |
avgReturn | Mean realized return, direction-adjusted: 0.05 is 5% |
scoredEpisodes, totalEpisodes | Closed positions with a score, and every position opened |
The three scores are null until the author has a scored position.
Expiry
A position nobody closes expires after its last open, add, trim or
reaffirm, with closeReason: "expiry" and closedAt set to the deadline. It
can read open for up to a day past the deadline, until the expiry is
recorded. Its exitPrice is read then, and is often null.
horizon | scalp | swing | position | unspecified |
|---|---|---|---|---|
| Expires after | 48 hours | 14 days | 90 days | 30 days |
Point in Time
An episode can be recorded hours after its post. To backtest what was known at
a moment T:
- keep episodes with
observedAt <= T; - treat one with
closedAt > Tas still open atT; - use only calls with
occurredAt <= T.
Parameters
| Parameter | Values | Default | Notes |
|---|---|---|---|
from | Unix seconds | required unless episodeIds | Positions open at any point since: still open, or closed at or after from |
to | Unix seconds | now | Opened at or before to |
handle | X handle | See Filtering by Account | |
entityIds | up to 30 token or equity ids, a list | Episodes on any of them | |
status | open, closed | both | |
direction | long, short | both | |
assetClass | crypto, equity | both | The market of the episode's asset |
episodeIds | up to 30 episode ids, a list | Other filters still apply | |
order | desc, asc | desc | By openedAt |
limit | 1 to 30 | 10 | |
cursor |
status=open&from=<now> reads what every matching account holds right now.
Positioning
GET /v3/calls/positioning: open long and open short episodes on one asset, right now, every source
together. Counts, not shares, so two assets compare by size.
{
"id": "b23e0b47-deec-4161-b49c-7e2652f07fe6",
"symbol": "BTC",
"name": "Bitcoin",
"openLong": 3252,
"openShort": 402,
"asOf": 1790931501
}
GET /v3/calls/positioning?entityId=b23e0b47-deec-4161-b49c-7e2652f07fe6
| Field | Meaning |
|---|---|
id, symbol, name | The asset's family |
openLong, openShort | Open episodes each way, scalp included. One account can count on both sides |
asOf | When an open episode on it last changed. When none is open, when it was read |
An episode past its expiry can count here for up to a day; the call books drop it at the deadline.
| Parameter | Values | Notes |
|---|---|---|
entityId | one token or equity id, required | Singular: entityIds is a 400 |