schwab_mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.copilotignore +3 -0
- data/.rspec +3 -0
- data/.rubocop.yml +8 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +157 -0
- data/Rakefile +12 -0
- data/exe/schwab_mcp +19 -0
- data/exe/schwab_token_refresh +38 -0
- data/exe/schwab_token_reset +49 -0
- data/lib/schwab_mcp/loggable.rb +31 -0
- data/lib/schwab_mcp/logger.rb +62 -0
- data/lib/schwab_mcp/option_chain_filter.rb +213 -0
- data/lib/schwab_mcp/orders/iron_condor_order.rb +87 -0
- data/lib/schwab_mcp/orders/order_factory.rb +40 -0
- data/lib/schwab_mcp/orders/vertical_order.rb +62 -0
- data/lib/schwab_mcp/redactor.rb +210 -0
- data/lib/schwab_mcp/resources/.keep +0 -0
- data/lib/schwab_mcp/tools/cancel_order_tool.rb +226 -0
- data/lib/schwab_mcp/tools/get_market_hours_tool.rb +104 -0
- data/lib/schwab_mcp/tools/get_order_tool.rb +263 -0
- data/lib/schwab_mcp/tools/get_price_history_tool.rb +203 -0
- data/lib/schwab_mcp/tools/help_tool.rb +406 -0
- data/lib/schwab_mcp/tools/list_account_orders_tool.rb +295 -0
- data/lib/schwab_mcp/tools/list_account_transactions_tool.rb +311 -0
- data/lib/schwab_mcp/tools/list_movers_tool.rb +125 -0
- data/lib/schwab_mcp/tools/list_schwab_accounts_tool.rb +162 -0
- data/lib/schwab_mcp/tools/option_chain_tool.rb +274 -0
- data/lib/schwab_mcp/tools/option_strategy_finder_tool.rb +378 -0
- data/lib/schwab_mcp/tools/place_order_tool.rb +305 -0
- data/lib/schwab_mcp/tools/preview_order_tool.rb +259 -0
- data/lib/schwab_mcp/tools/quote_tool.rb +77 -0
- data/lib/schwab_mcp/tools/quotes_tool.rb +110 -0
- data/lib/schwab_mcp/tools/replace_order_tool.rb +312 -0
- data/lib/schwab_mcp/tools/schwab_account_details_tool.rb +208 -0
- data/lib/schwab_mcp/version.rb +5 -0
- data/lib/schwab_mcp.rb +107 -0
- data/sig/schwab_mcp.rbs +4 -0
- data/start_mcp_server.sh +4 -0
- metadata +115 -0
@@ -0,0 +1,203 @@
|
|
1
|
+
require "mcp"
|
2
|
+
require "schwab_rb"
|
3
|
+
require "json"
|
4
|
+
require "date"
|
5
|
+
require_relative "../loggable"
|
6
|
+
|
7
|
+
module SchwabMCP
|
8
|
+
module Tools
|
9
|
+
class GetPriceHistoryTool < MCP::Tool
|
10
|
+
extend Loggable
|
11
|
+
description "Get price history data for an instrument symbol using Schwab API"
|
12
|
+
|
13
|
+
input_schema(
|
14
|
+
properties: {
|
15
|
+
symbol: {
|
16
|
+
type: "string",
|
17
|
+
description: "Instrument symbol (e.g., 'AAPL', 'TSLA', '$SPX')",
|
18
|
+
pattern: "^[\\$A-Za-z]{1,6}$"
|
19
|
+
},
|
20
|
+
period_type: {
|
21
|
+
type: "string",
|
22
|
+
description: "Type of period for the price history",
|
23
|
+
enum: ["day", "month", "year", "ytd"]
|
24
|
+
},
|
25
|
+
period: {
|
26
|
+
type: "integer",
|
27
|
+
description: "Number of periods to retrieve. Valid values depend on period_type: day(1-10), month(1,2,3,6), year(1,2,3,5,10,15,20), ytd(1)"
|
28
|
+
},
|
29
|
+
frequency_type: {
|
30
|
+
type: "string",
|
31
|
+
description: "Type of frequency for the price history",
|
32
|
+
enum: ["minute", "daily", "weekly", "monthly"]
|
33
|
+
},
|
34
|
+
frequency: {
|
35
|
+
type: "integer",
|
36
|
+
description: "Frequency of data points. Valid values depend on frequency_type: minute(1,5,10,15,30), daily(1), weekly(1), monthly(1)"
|
37
|
+
},
|
38
|
+
start_datetime: {
|
39
|
+
type: "string",
|
40
|
+
description: "Start date/time in ISO format (e.g., '2024-01-01T00:00:00Z'). Cannot be used with period/period_type."
|
41
|
+
},
|
42
|
+
end_datetime: {
|
43
|
+
type: "string",
|
44
|
+
description: "End date/time in ISO format (e.g., '2024-01-31T23:59:59Z'). Cannot be used with period/period_type."
|
45
|
+
},
|
46
|
+
need_extended_hours_data: {
|
47
|
+
type: "boolean",
|
48
|
+
description: "Include extended hours data (pre-market and after-hours)"
|
49
|
+
},
|
50
|
+
need_previous_close: {
|
51
|
+
type: "boolean",
|
52
|
+
description: "Include previous day's closing price"
|
53
|
+
}
|
54
|
+
},
|
55
|
+
required: ["symbol"]
|
56
|
+
)
|
57
|
+
|
58
|
+
annotations(
|
59
|
+
title: "Get Price History",
|
60
|
+
read_only_hint: true,
|
61
|
+
destructive_hint: false,
|
62
|
+
idempotent_hint: true
|
63
|
+
)
|
64
|
+
|
65
|
+
def self.call(symbol:, period_type: nil, period: nil, frequency_type: nil, frequency: nil,
|
66
|
+
start_datetime: nil, end_datetime: nil, need_extended_hours_data: nil,
|
67
|
+
need_previous_close: nil, server_context:)
|
68
|
+
log_info("Getting price history for symbol: #{symbol}")
|
69
|
+
|
70
|
+
begin
|
71
|
+
client = SchwabRb::Auth.init_client_easy(
|
72
|
+
ENV['SCHWAB_API_KEY'],
|
73
|
+
ENV['SCHWAB_APP_SECRET'],
|
74
|
+
ENV['SCHWAB_CALLBACK_URI'],
|
75
|
+
ENV['TOKEN_PATH']
|
76
|
+
)
|
77
|
+
|
78
|
+
unless client
|
79
|
+
log_error("Failed to initialize Schwab client")
|
80
|
+
return MCP::Tool::Response.new([{
|
81
|
+
type: "text",
|
82
|
+
text: "**Error**: Failed to initialize Schwab client. Check your credentials."
|
83
|
+
}])
|
84
|
+
end
|
85
|
+
|
86
|
+
parsed_start = nil
|
87
|
+
parsed_end = nil
|
88
|
+
|
89
|
+
if start_datetime
|
90
|
+
begin
|
91
|
+
parsed_start = DateTime.parse(start_datetime)
|
92
|
+
rescue ArgumentError
|
93
|
+
return MCP::Tool::Response.new([{
|
94
|
+
type: "text",
|
95
|
+
text: "**Error**: Invalid start_datetime format. Use ISO format like '2024-01-01T00:00:00Z'"
|
96
|
+
}])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
if end_datetime
|
101
|
+
begin
|
102
|
+
parsed_end = DateTime.parse(end_datetime)
|
103
|
+
rescue ArgumentError
|
104
|
+
return MCP::Tool::Response.new([{
|
105
|
+
type: "text",
|
106
|
+
text: "**Error**: Invalid end_datetime format. Use ISO format like '2024-01-31T23:59:59Z'"
|
107
|
+
}])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
if (start_datetime || end_datetime) && (period_type || period)
|
112
|
+
return MCP::Tool::Response.new([{
|
113
|
+
type: "text",
|
114
|
+
text: "**Error**: Cannot use start_datetime/end_datetime with period_type/period. Choose one approach."
|
115
|
+
}])
|
116
|
+
end
|
117
|
+
|
118
|
+
period_type_enum = nil
|
119
|
+
frequency_type_enum = nil
|
120
|
+
|
121
|
+
if period_type
|
122
|
+
case period_type
|
123
|
+
when "day"
|
124
|
+
period_type_enum = SchwabRb::PriceHistory::PeriodTypes::DAY
|
125
|
+
when "month"
|
126
|
+
period_type_enum = SchwabRb::PriceHistory::PeriodTypes::MONTH
|
127
|
+
when "year"
|
128
|
+
period_type_enum = SchwabRb::PriceHistory::PeriodTypes::YEAR
|
129
|
+
when "ytd"
|
130
|
+
period_type_enum = SchwabRb::PriceHistory::PeriodTypes::YEAR_TO_DATE
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
if frequency_type
|
135
|
+
case frequency_type
|
136
|
+
when "minute"
|
137
|
+
frequency_type_enum = SchwabRb::PriceHistory::FrequencyTypes::MINUTE
|
138
|
+
when "daily"
|
139
|
+
frequency_type_enum = SchwabRb::PriceHistory::FrequencyTypes::DAILY
|
140
|
+
when "weekly"
|
141
|
+
frequency_type_enum = SchwabRb::PriceHistory::FrequencyTypes::WEEKLY
|
142
|
+
when "monthly"
|
143
|
+
frequency_type_enum = SchwabRb::PriceHistory::FrequencyTypes::MONTHLY
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
log_debug("Making price history API request for symbol: #{symbol}")
|
148
|
+
|
149
|
+
response = client.get_price_history(
|
150
|
+
symbol.upcase,
|
151
|
+
period_type: period_type_enum,
|
152
|
+
period: period,
|
153
|
+
frequency_type: frequency_type_enum,
|
154
|
+
frequency: frequency,
|
155
|
+
start_datetime: parsed_start,
|
156
|
+
end_datetime: parsed_end,
|
157
|
+
need_extended_hours_data: need_extended_hours_data,
|
158
|
+
need_previous_close: need_previous_close
|
159
|
+
)
|
160
|
+
|
161
|
+
if response&.body
|
162
|
+
log_info("Successfully retrieved price history for #{symbol}")
|
163
|
+
|
164
|
+
begin
|
165
|
+
data = JSON.parse(response.body)
|
166
|
+
candles = data.dig("candles") || []
|
167
|
+
|
168
|
+
summary = if candles.any?
|
169
|
+
"Retrieved #{candles.length} price candles"
|
170
|
+
else
|
171
|
+
"No price data available for the specified parameters"
|
172
|
+
end
|
173
|
+
|
174
|
+
MCP::Tool::Response.new([{
|
175
|
+
type: "text",
|
176
|
+
text: "**Price History for #{symbol.upcase}:**\n\n#{summary}\n\n```json\n#{response.body}\n```"
|
177
|
+
}])
|
178
|
+
rescue JSON::ParserError
|
179
|
+
MCP::Tool::Response.new([{
|
180
|
+
type: "text",
|
181
|
+
text: "**Price History for #{symbol.upcase}:**\n\n```json\n#{response.body}\n```"
|
182
|
+
}])
|
183
|
+
end
|
184
|
+
else
|
185
|
+
log_warn("Empty response from Schwab API for symbol: #{symbol}")
|
186
|
+
MCP::Tool::Response.new([{
|
187
|
+
type: "text",
|
188
|
+
text: "**No Data**: Empty response from Schwab API for symbol: #{symbol}"
|
189
|
+
}])
|
190
|
+
end
|
191
|
+
|
192
|
+
rescue => e
|
193
|
+
log_error("Error retrieving price history for #{symbol}: #{e.message}")
|
194
|
+
log_debug("Backtrace: #{e.backtrace.first(3).join('\n')}")
|
195
|
+
MCP::Tool::Response.new([{
|
196
|
+
type: "text",
|
197
|
+
text: "**Error** retrieving price history for #{symbol}: #{e.message}\n\n#{e.backtrace.first(3).join('\n')}"
|
198
|
+
}])
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,406 @@
|
|
1
|
+
require "mcp"
|
2
|
+
require_relative "../loggable"
|
3
|
+
|
4
|
+
module SchwabMCP
|
5
|
+
module Tools
|
6
|
+
class HelpTool < MCP::Tool
|
7
|
+
extend Loggable
|
8
|
+
description "Get comprehensive help and documentation for the Schwab MCP server tools and capabilities"
|
9
|
+
|
10
|
+
input_schema(
|
11
|
+
properties: {
|
12
|
+
topic: {
|
13
|
+
type: "string",
|
14
|
+
description: "Optional specific topic to get help for. Available: 'tools', 'setup'",
|
15
|
+
enum: ["tools", "setup"]
|
16
|
+
}
|
17
|
+
},
|
18
|
+
required: []
|
19
|
+
)
|
20
|
+
|
21
|
+
annotations(
|
22
|
+
title: "Get Help and Documentation",
|
23
|
+
read_only_hint: true,
|
24
|
+
destructive_hint: false,
|
25
|
+
idempotent_hint: true
|
26
|
+
)
|
27
|
+
|
28
|
+
def self.call(topic: nil, server_context:)
|
29
|
+
log_info("Help requested for topic: #{topic || 'general'}")
|
30
|
+
|
31
|
+
help_content = if topic
|
32
|
+
get_topic_help(topic)
|
33
|
+
else
|
34
|
+
get_general_help
|
35
|
+
end
|
36
|
+
|
37
|
+
MCP::Tool::Response.new([{
|
38
|
+
type: "text",
|
39
|
+
text: help_content
|
40
|
+
}])
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def self.get_general_help
|
46
|
+
<<~HELP
|
47
|
+
# 📊 Schwab MCP Server
|
48
|
+
|
49
|
+
## Available Tools:
|
50
|
+
|
51
|
+
### Market Data & Analysis:
|
52
|
+
- **quote_tool**: Get real-time quote for a single symbol
|
53
|
+
- **quotes_tool**: Get real-time quotes for multiple symbols
|
54
|
+
- **option_chain_tool**: Get option chain data for a symbol
|
55
|
+
- **get_price_history_tool**: Get historical price data for an instrument
|
56
|
+
- **get_market_hours_tool**: Get market hours for specified markets
|
57
|
+
- **list_movers_tool**: Get top ten movers for a given index
|
58
|
+
|
59
|
+
### Option Strategy Tools:
|
60
|
+
- **option_strategy_finder_tool**: Find option strategies (iron condor, call spread, put spread)
|
61
|
+
- **preview_order_tool**: Preview an options order before placing (⚠️ SAFE PREVIEW)
|
62
|
+
- **place_order_tool**: Place an options order for execution (⚠️ DESTRUCTIVE)
|
63
|
+
- **replace_order_tool**: Replace an existing order with a new one (⚠️ DESTRUCTIVE)
|
64
|
+
|
65
|
+
### Account Management:
|
66
|
+
- **schwab_account_details_tool**: Get account information using account name mapping
|
67
|
+
- **list_schwab_accounts_tool**: List all available Schwab accounts
|
68
|
+
- **list_account_orders_tool**: List orders for a specific account using account name mapping
|
69
|
+
- **list_account_transactions_tool**: List transactions for a specific account
|
70
|
+
- **get_order_tool**: Get detailed information for a specific order by order ID
|
71
|
+
- **cancel_order_tool**: Cancel a specific order by order ID (⚠️ DESTRUCTIVE)
|
72
|
+
|
73
|
+
### Documentation:
|
74
|
+
- **help_tool**: This help system
|
75
|
+
|
76
|
+
## Usage Examples:
|
77
|
+
```
|
78
|
+
# Market Data
|
79
|
+
quote_tool(symbol: "AAPL")
|
80
|
+
quotes_tool(symbols: ["AAPL", "TSLA", "MSFT"])
|
81
|
+
get_price_history_tool(symbol: "SPX", period_type: "day", period: 5, frequency_type: "minute", frequency: 5)
|
82
|
+
get_market_hours_tool(markets: ["equity", "option"])
|
83
|
+
list_movers_tool(index: "$SPX", sort_order: "PERCENT_CHANGE_UP")
|
84
|
+
|
85
|
+
# Options
|
86
|
+
option_chain_tool(symbol: "SPX", contract_type: "ALL")
|
87
|
+
option_strategy_finder_tool(strategy_type: "ironcondor", underlying_symbol: "SPX", expiration_date: "2025-01-17")
|
88
|
+
preview_order_tool(account_name: "TRADING_BROKERAGE_ACCOUNT", strategy_type: "ironcondor", price: 1.50, quantity: 1)
|
89
|
+
|
90
|
+
# Account Management
|
91
|
+
schwab_account_details_tool(account_name: "TRADING_BROKERAGE_ACCOUNT")
|
92
|
+
list_schwab_accounts_tool()
|
93
|
+
list_account_orders_tool(account_name: "TRADING_BROKERAGE_ACCOUNT", status: "WORKING")
|
94
|
+
list_account_transactions_tool(account_name: "TRADING_BROKERAGE_ACCOUNT", start_date: "2025-01-01")
|
95
|
+
get_order_tool(order_id: "123456789", account_name: "TRADING_BROKERAGE_ACCOUNT")
|
96
|
+
cancel_order_tool(order_id: "123456789", account_name: "TRADING_BROKERAGE_ACCOUNT")
|
97
|
+
|
98
|
+
# Help
|
99
|
+
help_tool(topic: "setup")
|
100
|
+
```
|
101
|
+
|
102
|
+
## Help Topics:
|
103
|
+
- `tools` - Detailed tool documentation
|
104
|
+
- `setup` - Configuration and authentication setup
|
105
|
+
|
106
|
+
Use `help_tool(topic: "setup")` for initial configuration.
|
107
|
+
HELP
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.get_topic_help(topic)
|
111
|
+
case topic
|
112
|
+
when "tools"
|
113
|
+
get_tools_help
|
114
|
+
when "setup"
|
115
|
+
get_setup_help
|
116
|
+
else
|
117
|
+
"**Error**: Unknown topic '#{topic}'. Available topics: tools, setup"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.get_tools_help
|
122
|
+
<<~HELP
|
123
|
+
# 🔧 Available Tools
|
124
|
+
|
125
|
+
## Market Data & Analysis Tools
|
126
|
+
|
127
|
+
### quote_tool
|
128
|
+
Get real-time quote for a single symbol.
|
129
|
+
**Parameters**: `symbol` (required) - e.g., "AAPL", "TSLA"
|
130
|
+
**Example**: `quote_tool(symbol: "AAPL")`
|
131
|
+
|
132
|
+
### quotes_tool
|
133
|
+
Get real-time quotes for multiple symbols.
|
134
|
+
**Parameters**:
|
135
|
+
- `symbols` (required) - Array of symbols, e.g., ["AAPL", "TSLA"]
|
136
|
+
- `fields` (optional) - Specific fields to return
|
137
|
+
- `indicative` (optional) - Boolean for indicative quotes
|
138
|
+
|
139
|
+
**Examples**:
|
140
|
+
```
|
141
|
+
quotes_tool(symbols: ["AAPL", "TSLA", "MSFT"])
|
142
|
+
quotes_tool(symbols: ["/ES"], indicative: true)
|
143
|
+
```
|
144
|
+
|
145
|
+
### get_price_history_tool
|
146
|
+
Get historical price data for an instrument symbol.
|
147
|
+
**Parameters**:
|
148
|
+
- `symbol` (required) - Instrument symbol (e.g., "AAPL", "$SPX")
|
149
|
+
- `period_type` (optional) - "day", "month", "year", "ytd"
|
150
|
+
- `period` (optional) - Number of periods based on period_type
|
151
|
+
- `frequency_type` (optional) - "minute", "daily", "weekly", "monthly"
|
152
|
+
- `frequency` (optional) - Frequency of data points
|
153
|
+
- `start_datetime`, `end_datetime` (optional) - ISO format date range
|
154
|
+
- `need_extended_hours_data` (optional) - Include pre/post market data
|
155
|
+
- `need_previous_close` (optional) - Include previous close
|
156
|
+
|
157
|
+
**Examples**:
|
158
|
+
```
|
159
|
+
get_price_history_tool(symbol: "SPX", period_type: "day", period: 5, frequency_type: "minute", frequency: 5)
|
160
|
+
get_price_history_tool(symbol: "AAPL", start_datetime: "2025-01-01T00:00:00Z", end_datetime: "2025-01-15T23:59:59Z")
|
161
|
+
```
|
162
|
+
|
163
|
+
### get_market_hours_tool
|
164
|
+
Get market hours for specified markets.
|
165
|
+
**Parameters**:
|
166
|
+
- `markets` (required) - Array of markets: ["equity", "option", "bond", "future", "forex"]
|
167
|
+
- `date` (optional) - Date in YYYY-MM-DD format (defaults to today)
|
168
|
+
|
169
|
+
**Examples**:
|
170
|
+
```
|
171
|
+
get_market_hours_tool(markets: ["equity", "option"])
|
172
|
+
get_market_hours_tool(markets: ["future"], date: "2025-01-17")
|
173
|
+
```
|
174
|
+
|
175
|
+
### list_movers_tool
|
176
|
+
Get top ten movers for a given index.
|
177
|
+
**Parameters**:
|
178
|
+
- `index` (required) - "$DJI", "$COMPX", "$SPX", "NYSE", "NASDAQ", etc.
|
179
|
+
- `sort_order` (optional) - "VOLUME", "TRADES", "PERCENT_CHANGE_UP", "PERCENT_CHANGE_DOWN"
|
180
|
+
- `frequency` (optional) - Magnitude filter: 0, 1, 5, 10, 30, 60
|
181
|
+
|
182
|
+
**Examples**:
|
183
|
+
```
|
184
|
+
list_movers_tool(index: "$SPX", sort_order: "PERCENT_CHANGE_UP")
|
185
|
+
list_movers_tool(index: "NASDAQ", frequency: 5)
|
186
|
+
```
|
187
|
+
|
188
|
+
## Option Tools
|
189
|
+
|
190
|
+
### option_chain_tool
|
191
|
+
Get option chain data for an optionable symbol.
|
192
|
+
**Parameters**:
|
193
|
+
- `symbol` (required) - Underlying symbol, e.g., "SPX", "AAPL"
|
194
|
+
- `contract_type` (optional) - "CALL", "PUT", or "ALL"
|
195
|
+
- `from_date`, `to_date` (optional) - Date range for expirations
|
196
|
+
- Many other optional parameters for filtering
|
197
|
+
|
198
|
+
**Example**: `option_chain_tool(symbol: "SPX", contract_type: "ALL")`
|
199
|
+
|
200
|
+
### option_strategy_finder_tool
|
201
|
+
Find option strategies using sophisticated algorithms.
|
202
|
+
**Parameters**:
|
203
|
+
- `strategy_type` (required) - "ironcondor", "callspread", or "putspread"
|
204
|
+
- `underlying_symbol` (required) - e.g., "SPX", "$SPX"
|
205
|
+
- `expiration_date` (required) - Target expiration "YYYY-MM-DD"
|
206
|
+
- `max_delta` (optional) - Maximum delta for short legs (default: 0.15)
|
207
|
+
- `max_spread` (optional) - Maximum spread width (default: 20.0)
|
208
|
+
- `min_credit` (optional) - Minimum credit in dollars (default: 100.0)
|
209
|
+
- `min_open_interest` (optional) - Minimum open interest (default: 0)
|
210
|
+
- `dist_from_strike` (optional) - Min distance from current price (default: 0.07)
|
211
|
+
- `expiration_type`, `settlement_type`, `option_root` (optional) - Filters
|
212
|
+
|
213
|
+
**Examples**:
|
214
|
+
```
|
215
|
+
option_strategy_finder_tool(strategy_type: "ironcondor", underlying_symbol: "SPX", expiration_date: "2025-01-17")
|
216
|
+
option_strategy_finder_tool(strategy_type: "callspread", underlying_symbol: "SPY", expiration_date: "2025-01-10", max_delta: 0.20, min_credit: 50.0)
|
217
|
+
```
|
218
|
+
|
219
|
+
## Order Management Tools
|
220
|
+
|
221
|
+
### preview_order_tool ⚠️ SAFE PREVIEW
|
222
|
+
Preview an options order before placing to validate structure and see estimated costs.
|
223
|
+
**Parameters**:
|
224
|
+
- `account_name` (required) - Account name ending with '_ACCOUNT'
|
225
|
+
- `strategy_type` (required) - "ironcondor", "callspread", "putspread"
|
226
|
+
- `price` (required) - Net price for the order
|
227
|
+
- `quantity` (optional) - Number of contracts (default: 1)
|
228
|
+
- `order_instruction` (optional) - "open" or "exit" (default: "open")
|
229
|
+
- Strategy-specific symbol parameters (e.g., put_short_symbol, call_long_symbol)
|
230
|
+
|
231
|
+
**Examples**:
|
232
|
+
```
|
233
|
+
preview_order_tool(account_name: "TRADING_BROKERAGE_ACCOUNT", strategy_type: "ironcondor", price: 1.50, quantity: 1)
|
234
|
+
preview_order_tool(account_name: "TRADING_BROKERAGE_ACCOUNT", strategy_type: "callspread", short_symbol: "SPY250117C00600000", long_symbol: "SPY250117C00610000", price: 2.50)
|
235
|
+
```
|
236
|
+
|
237
|
+
### place_order_tool ⚠️ DESTRUCTIVE OPERATION
|
238
|
+
Place an options order for execution. **WARNING**: This places real orders with real money.
|
239
|
+
**Parameters**: Same as preview_order_tool
|
240
|
+
**Safety Features**:
|
241
|
+
- Validates order structure before placing
|
242
|
+
- Returns order ID for tracking
|
243
|
+
- Provides detailed confirmation
|
244
|
+
|
245
|
+
**Examples**:
|
246
|
+
```
|
247
|
+
place_order_tool(account_name: "TRADING_BROKERAGE_ACCOUNT", strategy_type: "ironcondor", price: 1.50, quantity: 1)
|
248
|
+
```
|
249
|
+
|
250
|
+
### replace_order_tool ⚠️ DESTRUCTIVE OPERATION
|
251
|
+
Replace an existing order with a new one. **WARNING**: Cancels existing order and places new one.
|
252
|
+
**Parameters**:
|
253
|
+
- `order_id` (required) - ID of existing order to replace
|
254
|
+
- All other parameters same as place_order_tool
|
255
|
+
|
256
|
+
**Examples**:
|
257
|
+
```
|
258
|
+
replace_order_tool(order_id: "123456789", account_name: "TRADING_BROKERAGE_ACCOUNT", strategy_type: "ironcondor", price: 1.75, quantity: 1)
|
259
|
+
```
|
260
|
+
|
261
|
+
## Account Management Tools
|
262
|
+
|
263
|
+
### schwab_account_details_tool
|
264
|
+
Get detailed account information using account name mapping.
|
265
|
+
**Parameters**:
|
266
|
+
- `account_name` (required) - Account name ending with '_ACCOUNT' (e.g., "TRADING_BROKERAGE_ACCOUNT")
|
267
|
+
- `fields` (optional) - Array of specific fields to retrieve ["balances", "positions", "orders"]
|
268
|
+
|
269
|
+
**Examples**:
|
270
|
+
```
|
271
|
+
schwab_account_details_tool(account_name: "TRADING_BROKERAGE_ACCOUNT")
|
272
|
+
schwab_account_details_tool(account_name: "IRA_ACCOUNT", fields: ["balances", "positions"])
|
273
|
+
```
|
274
|
+
|
275
|
+
### list_schwab_accounts_tool
|
276
|
+
List all available Schwab accounts with their details.
|
277
|
+
**Parameters**: None required
|
278
|
+
**Example**: `list_schwab_accounts_tool()`
|
279
|
+
|
280
|
+
### list_account_orders_tool
|
281
|
+
List orders for a specific account using account name mapping.
|
282
|
+
**Parameters**:
|
283
|
+
- `account_name` (required) - Account name ending with '_ACCOUNT'
|
284
|
+
- `max_results` (optional) - Maximum number of orders to retrieve
|
285
|
+
- `from_date` (optional) - Start date in YYYY-MM-DD format (default: 60 days ago)
|
286
|
+
- `to_date` (optional) - End date in YYYY-MM-DD format (default: today)
|
287
|
+
- `status` (optional) - Filter by order status (WORKING, FILLED, CANCELED, etc.)
|
288
|
+
|
289
|
+
**Examples**:
|
290
|
+
```
|
291
|
+
list_account_orders_tool(account_name: "TRADING_BROKERAGE_ACCOUNT")
|
292
|
+
list_account_orders_tool(account_name: "TRADING_BROKERAGE_ACCOUNT", status: "WORKING")
|
293
|
+
list_account_orders_tool(account_name: "IRA_ACCOUNT", from_date: "2025-01-01", to_date: "2025-01-15", max_results: 50)
|
294
|
+
```
|
295
|
+
|
296
|
+
### list_account_transactions_tool
|
297
|
+
List transactions for a specific account using account name mapping.
|
298
|
+
**Parameters**:
|
299
|
+
- `account_name` (required) - Account name ending with '_ACCOUNT'
|
300
|
+
- `start_date` (optional) - Start date in YYYY-MM-DD format (default: 60 days ago)
|
301
|
+
- `end_date` (optional) - End date in YYYY-MM-DD format (default: today)
|
302
|
+
- `transaction_types` (optional) - Array of transaction types to filter by
|
303
|
+
|
304
|
+
**Examples**:
|
305
|
+
```
|
306
|
+
list_account_transactions_tool(account_name: "TRADING_BROKERAGE_ACCOUNT")
|
307
|
+
list_account_transactions_tool(account_name: "TRADING_BROKERAGE_ACCOUNT", start_date: "2025-01-01", transaction_types: ["TRADE", "DIVIDEND_OR_INTEREST"])
|
308
|
+
```
|
309
|
+
|
310
|
+
### get_order_tool
|
311
|
+
Get detailed information for a specific order by order ID.
|
312
|
+
**Parameters**:
|
313
|
+
- `order_id` (required) - The numeric order ID to retrieve details for
|
314
|
+
- `account_name` (required) - Account name ending with '_ACCOUNT'
|
315
|
+
|
316
|
+
**Examples**:
|
317
|
+
```
|
318
|
+
get_order_tool(order_id: "123456789", account_name: "TRADING_BROKERAGE_ACCOUNT")
|
319
|
+
get_order_tool(order_id: "987654321", account_name: "IRA_ACCOUNT")
|
320
|
+
```
|
321
|
+
|
322
|
+
### cancel_order_tool ⚠️ DESTRUCTIVE OPERATION
|
323
|
+
Cancel a specific order by order ID. **WARNING**: This action cannot be undone.
|
324
|
+
**Parameters**:
|
325
|
+
- `order_id` (required) - The numeric order ID to cancel
|
326
|
+
- `account_name` (required) - Account name ending with '_ACCOUNT'
|
327
|
+
|
328
|
+
**Safety Features**:
|
329
|
+
- Verifies order exists before attempting cancellation
|
330
|
+
- Checks if order is cancelable (based on status)
|
331
|
+
- Provides detailed confirmation when successful
|
332
|
+
- Returns helpful error messages for invalid requests
|
333
|
+
|
334
|
+
**Examples**:
|
335
|
+
```
|
336
|
+
cancel_order_tool(order_id: "123456789", account_name: "TRADING_BROKERAGE_ACCOUNT")
|
337
|
+
cancel_order_tool(order_id: "987654321", account_name: "IRA_ACCOUNT")
|
338
|
+
```
|
339
|
+
|
340
|
+
**Important Notes**:
|
341
|
+
- Only working orders can typically be cancelled
|
342
|
+
- Filled, expired, or already cancelled orders cannot be cancelled
|
343
|
+
- Always verify cancellation using get_order_tool or list_account_orders_tool
|
344
|
+
|
345
|
+
## Documentation
|
346
|
+
|
347
|
+
### help_tool
|
348
|
+
Get help and documentation.
|
349
|
+
**Parameters**: `topic` (optional) - "tools" or "setup"
|
350
|
+
**Example**: `help_tool(topic: "setup")`
|
351
|
+
|
352
|
+
**Supported symbols**: Stocks (AAPL), Futures (/ES), ETFs, etc.
|
353
|
+
**Limits**: Max 500 symbols per quotes_tool request
|
354
|
+
HELP
|
355
|
+
end
|
356
|
+
|
357
|
+
def self.get_setup_help
|
358
|
+
<<~HELP
|
359
|
+
# 🚀 Setup Guide
|
360
|
+
|
361
|
+
## 1. Environment Variables
|
362
|
+
Set these required environment variables:
|
363
|
+
```bash
|
364
|
+
export SCHWAB_API_KEY="your_app_key"
|
365
|
+
export SCHWAB_APP_SECRET="your_app_secret"
|
366
|
+
export SCHWAB_CALLBACK_URI="https://localhost:8443/callback"
|
367
|
+
export TOKEN_PATH="./token.json"
|
368
|
+
```
|
369
|
+
|
370
|
+
**Account Environment Variables** (for account-specific tools):
|
371
|
+
```bash
|
372
|
+
export TRADING_BROKERAGE_ACCOUNT="your_account_number"
|
373
|
+
export IRA_ACCOUNT="your_ira_account_number"
|
374
|
+
# Add more accounts as needed, always ending with '_ACCOUNT'
|
375
|
+
```
|
376
|
+
|
377
|
+
## 2. Initial Authentication
|
378
|
+
Run the token refresh script to authenticate:
|
379
|
+
```bash
|
380
|
+
./exe/schwab_token_refresh
|
381
|
+
```
|
382
|
+
|
383
|
+
This will:
|
384
|
+
- Open a browser for Schwab login
|
385
|
+
- Complete OAuth2 flow
|
386
|
+
- Save tokens to TOKEN_PATH
|
387
|
+
|
388
|
+
## 3. Start Server
|
389
|
+
```bash
|
390
|
+
./exe/schwab_mcp
|
391
|
+
```
|
392
|
+
|
393
|
+
## Prerequisites:
|
394
|
+
- Schwab brokerage account
|
395
|
+
- Approved Schwab developer account (developer.schwab.com)
|
396
|
+
- Ruby 3.0+
|
397
|
+
|
398
|
+
## Troubleshooting:
|
399
|
+
- Ensure callback URL matches exactly in your Schwab app settings
|
400
|
+
- Check token file permissions
|
401
|
+
- Verify environment variables are set correctly
|
402
|
+
HELP
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|