First-time user onboarding to set up investment profile, watchlists, portfolio, and preferences.
Help new users set up their investment profile through a natural, conversational flow. The agent gathers preferences and stores them as rich, descriptive text that future conversations can reference for personalized advice.
This skill provides 5 tools:
get_user_data - Read user dataupdate_user_data - Create or update user dataremove_user_data - Delete user datamanage_workspaces - Create workspaces (via action="create")ptc_agent - Dispatch a research question to a workspaceYou should call these tools directly instead of using ExecuteCode tool.
Retrieve user data by entity type.
| Entity |
|---|
| Description |
|---|
| entity_id |
|---|
all | Complete user data (profile, preferences, watchlists with items, portfolio) | Not used |
profile | User info (name, timezone, locale) | Not used |
preferences | All preferences (risk, investment, agent) | Not used |
watchlists | List of all watchlists | Not used |
watchlist_items | Items in a specific watchlist | Optional watchlist_id |
portfolio | All portfolio holdings | Not used |
# Get complete user data (recommended at start of onboarding)
get_user_data(entity="all")
Create or update user data (upsert semantics). Preference entities merge by default.
| Entity | Description |
|---|---|
profile | User info (name, timezone, locale, onboarding_completed) |
risk_preference | Risk tolerance settings |
investment_preference | Investment style settings |
agent_preference | Agent behavior settings |
watchlist | Create or update a watchlist |
watchlist_item | Add or update item in watchlist |
portfolio_holding | Add or update a portfolio holding |
All preference fields accept any descriptive string. Extra fields are allowed and persisted.
# Good - rich context that helps future conversations
update_user_data(entity="risk_preference", data={
"risk_tolerance": "Moderate - comfortable with market swings but avoids concentrated bets",
"notes": "Lost money in 2022 tech crash, now prefers diversification"
})
# Bad - keyword with no context
update_user_data(entity="risk_preference", data={"risk_tolerance": "medium"})
Delete user data by entity type.
| Entity | Identifier fields |
|---|---|
watchlist | watchlist_id or name |
watchlist_item | symbol (+ optional watchlist_id) |
portfolio_holding | symbol (+ optional account_name) |
Create the user's first workspace. This requires user approval — the user sees a card and must approve.
| Parameter | Type | Description |
|---|---|---|
action | string | Must be "create" |
name | string | Name for the workspace (e.g. "My Portfolio Analysis") |
description | string | Brief description of the workspace purpose |
manage_workspaces(action="create", name="My Portfolio Analysis", description="Track and analyze my stock portfolio")
Returns { success: true, workspace_id: "...", workspace_name: "..." } on approval, or "User declined workspace creation." on rejection.
Dispatch a personalized research question to a workspace. This requires user approval — the user sees the question and can approve to start the analysis.
| Parameter | Type | Description |
|---|---|---|
question | string | An actionable question related to the user's interests |
workspace_id | string | The workspace ID (from manage_workspaces result) |
ptc_agent(
question="Analyze my NVDA position — what's the current technical setup and any upcoming catalysts I should watch for?",
workspace_id="abc-123"
)
Returns { success: true, workspace_id: "...", thread_id: "...", status: "dispatched" } on approval, or "User declined research dispatch." on rejection.
At least one stock must be added to the watchlist or portfolio before onboarding can complete. Use the structured watchlist_item or portfolio_holding entities.
# Watchlist item
update_user_data(entity="watchlist_item", data={
"symbol": "NVDA", "notes": "Watching for AI chip growth"
})
# Portfolio holding
update_user_data(entity="portfolio_holding", data={
"symbol": "AAPL", "quantity": 50, "average_cost": 175.0
})
Gather enough context so future conversations can give personalized advice. At minimum, capture risk_tolerance on risk_preference. Topics to explore:
Store these as descriptive text across risk_preference and investment_preference:
update_user_data(entity="risk_preference", data={
"risk_tolerance": "Conservative - prioritizes capital preservation, uncomfortable with >10% drawdowns",
"notes": "Nearing retirement in 5 years, shifting from growth to income"
})
update_user_data(entity="investment_preference", data={
"company_interest": "Dividend-paying blue chips and REITs for income",
"holding_period": "Long-term (5+ years), rarely sells",
"analysis_focus": "Dividend sustainability, payout ratio, and balance sheet strength",
"avoid_sectors": "Crypto, speculative biotech"
})
How does the user want the agent to behave? Topics to explore:
update_user_data(entity="agent_preference", data={
"output_style": "Balanced summary with key numbers highlighted",
"data_visualization": "Include charts when comparing multiple stocks",
"proactive_questions": "Use your judgment, only ask when the decision significantly impacts the analysis",
"instruction": "Always mention if a stock has upcoming earnings within 2 weeks"
})
Present options so the user can tap instead of type. Options are starting points for richer conversation, not rigid mappings. After the user selects an option, capture the full context of their choice (including any follow-up detail) as descriptive text.
Example AskUserQuestion options by topic:
Risk comfort:
Investment style:
Time horizon:
Analysis preference:
Output style:
Visualization:
Proactive questions:
After the user selects an option (or provides a custom answer), store the descriptive text, not a keyword:
# User selected "Moderate - balanced risk and reward" and added
# "but I get nervous during big market drops"
update_user_data(entity="risk_preference", data={
"risk_tolerance": "Moderate - balanced risk and reward, but gets nervous during big market drops"
})
manage_workspaces(action="create", name="...", description="...") with a name and description that fits the user's interests. Then use ptc_agent(question="...", workspace_id="...") with the returned workspace_id to dispatch an actionable starter question based on the user's stocks or interests. The question should be specific and immediately useful (e.g. "Analyze my NVDA position — what are the key technical levels and upcoming catalysts?" rather than "Tell me about stocks").Don't ask all questions at once. Let the conversation flow naturally. If the user wants to skip optional topics, respect that.
The listed topics are a starting point. If the conversation naturally reveals other preferences (e.g., specific sectors to avoid, earnings season behavior, news sensitivity), store those too. Any extra fields are accepted via extra="allow" on the models.
Before marking onboarding complete, verify:
risk_preference)# Mark onboarding complete
update_user_data(entity="profile", data={"onboarding_completed": true})
If missing: