How AI-Trader Tracks Position Source: Self-Opened vs Copied Trades
AI-Trader tracks position source using a nullable leader_id column in the positions table, where NULL values indicate self-opened trades and populated agent IDs indicate copies from leaders, with the /api/positions endpoint deriving a computed source field for client display.
The HKUDS/AI-Trader repository implements a clear audit trail to distinguish between trades initiated directly by an agent and those mirrored from followed leaders. This tracking mechanism relies on database schema design, conditional insertion logic in the service layer, and response transformation in the API routes. Understanding this flow is essential for debugging copy-trading behavior and ensuring accurate attribution of trade performance.
Database Schema Design for Position Source
At the storage layer, AI-Trader records position provenance through a single nullable foreign key reference.
The leader_id Column
The positions table schema in service/server/database.py (lines 561-567) defines a nullable leader_id column. When this column contains NULL, the position was opened directly by the agent itself. When populated with an agent identifier, it references the leader from whom the trade was copied. This schema design eliminates the need for separate tables or complex joins, keeping the lookup performant while maintaining clear data lineage.
Service Layer Logic for Recording Position Source
The decision to populate leader_id occurs during signal processing, where the system determines whether a trade originates from the agent's own strategy or a leader's signal.
Signal Processing in _update_position_from_signal
The function _update_position_from_signal in service/server/services.py handles position creation for both long and short trades. When processing a signal that includes a leader_id parameter, the service explicitly includes this value in the SQL INSERT statement.
For copied long positions (lines 27-33):
if leader_id:
cursor.execute(
"""
INSERT INTO positions (
agent_id, symbol, market, token_id, outcome,
side, quantity, entry_price, opened_at, leader_id
) VALUES (?, ?, ?, ?, ?, 'long', ?, ?, ?, ?)
""",
(agent_id, symbol, market, token_id, outcome,
quantity, price, executed_at, leader_id)
)
For copied short positions (lines 74-81), the same pattern applies, ensuring that short-side copies are equally attributed to their originating leader. Self-opened positions omit the leader_id parameter entirely, resulting in a NULL database value.
API Response Format
Raw database records are transformed into client-friendly representations at the API boundary, providing immediate visual clarity to frontend consumers.
Deriving the Source Field in routes_trading.py
The /api/positions endpoint in service/server/routes_trading.py (lines 504-506) constructs a source field dynamically for each position object. The logic follows this pattern:
source = 'self' if row['leader_id'] is None else f"copied:{row['leader_id']}"
This transformation ensures that API consumers receive a semantic string rather than requiring them to interpret raw database IDs. The resulting JSON response distinguishes clearly between self-directed and copied trades:
{
"positions": [
{
"id": 42,
"symbol": "AAPL",
"market": "us-stock",
"side": "long",
"quantity": 10,
"entry_price": 150.0,
"current_price": 155.2,
"pnl": 52.0,
"source": "self",
"opened_at": "2024-09-10T14:23:00Z"
},
{
"id": 43,
"symbol": "MSFT",
"market": "us-stock",
"side": "short",
"quantity": -5,
"entry_price": 300.0,
"current_price": 295.0,
"pnl": 25.0,
"source": "copied:7",
"opened_at": "2024-09-10T14:25:00Z"
}
],
"cash": 98500.0
}
Frontend Consumption
Client applications consume the source field to render appropriate UI indicators, displaying "self" or "copied:#" badges next to position entries. This allows traders to instantly recognize which positions resulted from their own strategy versus those mirrored from followed leaders, critical for risk management and performance attribution in copy-trading scenarios.
Summary
- Database Layer: The
positionstable uses a nullableleader_idcolumn inservice/server/database.pyto store the origin of each trade. - Service Layer: The
_update_position_from_signalfunction inservice/server/services.pyconditionally insertsleader_idonly when copying from a leader. - API Layer: The
/api/positionsendpoint inservice/server/routes_trading.pyderives a human-readablesourcefield showing "self" or "copied:{leader_id}". - Usage: Frontend components leverage the
sourcefield to visually distinguish between self-opened and copied positions.
Frequently Asked Questions
How does AI-Trader distinguish between a self-opened position and a copied position in the database?
AI-Trader uses a nullable leader_id column in the positions table defined in service/server/database.py. When leader_id is NULL, the position was opened directly by the agent. When it contains an agent identifier, the position was copied from that specific leader.
What function handles the insertion of the leader ID when copying a trade?
The _update_position_from_signal function in service/server/services.py handles this logic. It accepts an optional leader_id parameter and includes it in the SQL INSERT statement only when copying from a leader, leaving it NULL for self-directed trades.
How does the API indicate whether a position was copied to frontend clients?
The /api/positions endpoint in service/server/routes_trading.py adds a computed source field to each position response. This field contains the string "self" for directly opened positions or "copied:{leader_id}" for mirrored trades, providing immediate semantic clarity without requiring clients to interpret raw database values.
Can a position change from self-opened to copied after initial creation?
No, the leader_id value is immutable after insertion. Once a position is created in service/server/services.py with either a NULL or specific leader_id, that provenance is permanently recorded, ensuring consistent audit trails for trade attribution and performance analysis.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →