dhanhq mcp
用于DhanHQ交易API的模型上下文协议(MCP)适配器
一个生产就绪的基础设施级Ruby gem,通过模型上下文协议(MCP)公开DhanHQ交易服务。该gem专为人工智能代理而设计,为交易操作提供安全、合规和以工具为中心的工具。
 
______________________________________________________________________
🎯 目的
dhanhq-mcp 是一个 协议适配器 即:
- 暴露a 安全子集 的
dhanhq-clientAI代理的功能 - 强制执行 合规性检查 在抽象层(ASM/GSM,交易权限)
- 提供 仅意图 订单准备(无自动执行)
- 跟随一个 以仪器为中心 正确交易语义的设计
- 使一致 1:1与
dhanhq-client建筑
______________________________________________________________________
🏗️ 建筑
以仪器为中心的设计
与公开原始客户端调用的典型API包装器不同, dhanhq-mcp 跟随上级 仪器驱动 架构:
# ❌ Wrong (client-centric)
client.quote(security_id: "123", exchange_segment: "NSE_EQ")
# ✅ Correct (instrument-centric)
instrument = DhanHQ::Models::Instrument.find("NSE_EQ", "RELIANCE")
instrument.quote为什么这很重要:
- 工具附带交易规则(ASM/GSM、BO/CO支持、保证金)
- 合规性自动执行
- 交易元数据的单一真实来源
- MTF、杠杆和风险功能经得起未来考验
设计安全
- 只读工具(12):市场数据、投资组合、工具发现
- 仅限意图的工具(2):订单准备但不执行
- 零自动执行:没有
place_order,modify_order,或cancel_order暴露
______________________________________________________________________
📦 安装
来源
git clone https://github.com/shubhamtaywade82/dhanhq-mcp.git
cd dhanhq-mcp
bundle install
bundle exec rake installRubyGems(未来)
gem install dhanhq-mcp______________________________________________________________________
⚙️ 配置
设置凭据
- 复制示例环境文件:
cp .env.example .env- 编辑
.env使用您的DhanHQ凭据:
# Get your credentials from: https://dhanhq.co/
CLIENT_ID=your_client_id_here
ACCESS_TOKEN=your_access_token_here- 获取您的凭据:
- 登录到 DhanHQ - 导航到API设置 - 生成或复制您的 客户端ID 和 访问令牌
环境变量
| 变量 | 必填 | 描述 | 默认值 |
|---|---|---|---|
CLIENT_ID | ✅ 是 | 您的DhanHQ客户ID | - |
ACCESS_TOKEN | ✅ 是 | 您的DhanHQ API访问令牌 | - |
DHAN_LOG_LEVEL | ❌ 否 | 日志记录级别(调试、信息、警告、错误) | INFO |
DHAN_BASE_URL | ❌ 没有 | API基础URL覆盖 | https://api.dhan.co |
DHAN_CONNECT_TIMEOUT | ❌ 否 | 连接超时(秒) | 10 |
DHAN_READ_TIMEOUT | ❌ 否 | 读取超时(秒) | 30 |
注: 这 .env git会自动忽略该文件,以保护您的凭据安全。
______________________________________________________________________
🚀 用法
💡 Cursor用户快速入门:配置后,只需在聊天中自然提问!看 用法_IN_CURSOR.md 例如。
1.STDIO模式(光标、克劳德桌面、Ollama)
最适合通过STDIO协议的AI助手:
export CLIENT_ID=your_client_id
export ACCESS_TOKEN=your_access_token
bundle exec ruby bin/dhanhq-mcp-stdioMCP协议(JSON-RPC 2.0):
服务器实现了完整的MCP生命周期。交互示例:
1.初始化:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}答复:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {"tools": {}},
"serverInfo": {
"name": "dhanhq-mcp",
"version": "0.1.0"
}
}
}2.工具清单:
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}答复:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{"name":"portfolio.holdings","description":"Get current holdings",...},
...
]
}
}3.工具调用:
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"portfolio.holdings","arguments":{}}}答复:
{
"jsonrpc": "2.0",
"id": 3,
"result": [...]
}⚠️ 光标特定注释:
- Cursor要求严格遵守MCP协议
- 使用
bundle exec确保正确装载宝石 .env文件是 不 由游标自动加载-显式设置环境变量- 所有回复必须包括
jsonrpc,id,以及result/error领域 - 除了JSON-RPC消息外,没有输出到STDOUT(日志转到STDERR)
2.Rails集成
在Rails应用程序中使用依赖注入:
# config/initializers/dhanhq_mcp.rb
require 'dhanhq-mcp'
client = DhanHQ::Client.new(access_token: ENV['DHAN_ACCESS_TOKEN'])
$mcp_context = Dhanhq::Mcp::Context.new(client: client)控制器使用:
class TradingController "NSE_EQ",
"symbol" => "RELIANCE",
"transaction_type" => "BUY",
"product_type" => "INTRADAY",
"order_type" => "MARKET",
"quantity" => 10
},
$mcp_context
)
render json: result
end
end3.HTTP服务器模式
机架运行应用程序:
# config.ru
require 'dhanhq-mcp'
client = DhanHQ::Client.new(access_token: ENV['DHAN_ACCESS_TOKEN'])
app = Dhanhq::Mcp::Server.new(
context_provider: ->(req) {
Dhanhq::Mcp::Context.new(client: client)
}
)
run apprackup -p 3000______________________________________________________________________
🔧 完整的工具清单(17个工具)
投资组合工具(5)-只读
| 工具 | 描述 | 参数 |
|---|---|---|
portfolio.holdings | 获取当前持有量 | 无 |
portfolio.positions | 获取当前职位 | 无 |
portfolio.funds | 获取可用资金 | 无 |
portfolio.orders | 获取订单簿历史记录 | 无 |
portfolio.trades | 获取交易簿历史记录 | 无 |
例子:
Dhanhq::Mcp::Router.call("portfolio.positions", {}, context)
# => [{symbol: "RELIANCE", quantity: 10, avg_price: 2500.0, ...}, ...]仪器工具(7)-只读
| 工具 | 描述 | 参数 |
|---|---|---|
instrument.find | 发现并验证仪器 | exchange_segment, symbol |
instrument.info | 交易权限和风险元数据 | exchange_segment, symbol |
instrument.ltp | 最后交易价格 | exchange_segment, symbol |
instrument.quote | 完整市场报价 | exchange_segment, symbol |
instrument.ohlc | OHLC快照 | exchange_segment, symbol |
instrument.daily | 每日历史蜡烛 | exchange_segment, symbol, from, to |
instrument.intraday | 日间蜡烛 | exchange_segment, symbol, from, to, interval |
例子:
Dhanhq::Mcp::Router.call(
"instrument.quote",
{"exchange_segment" => "NSE_EQ", "symbol" => "RELIANCE"},
context
)
# => {ltp: 2500.0, bid: 2499.5, ask: 2500.5, volume: 1000000, ...}选项工具(4)
| 工具 | 描述 | 参数 | 类型 |
|---|---|---|---|
option.expiries | 可用期权到期 | exchange_segment, symbol | 只读 |
option.chain | 期权链数据 | exchange_segment, symbol, expiry | 只读 |
option.select | 基于规则的罢工选择 | exchange_segment, symbol, expiry, direction, spot_price等等。 | 只读 |
option.prepare | 准备期权购买意向 | exchange_segment, symbol, security_id, option_type, strike, expiry, quantity, stop_loss, target | 仅限意图 |
例子:
# 1. Get expiries
Dhanhq::Mcp::Router.call(
"option.expiries",
{"exchange_segment" => "IDX_I", "symbol" => "NIFTY"},
context
)
# => ["2026-01-30", "2026-02-06", ...]
# 2. Get chain
Dhanhq::Mcp::Router.call(
"option.chain",
{"exchange_segment" => "IDX_I", "symbol" => "NIFTY", "expiry" => "2026-01-30"},
context
)
# => [{strike: 23000, option_type: "CE", ltp: 150, ...}, ...]
# 3. Select strike
Dhanhq::Mcp::Router.call(
"option.select",
{
"exchange_segment" => "IDX_I",
"symbol" => "NIFTY",
"expiry" => "2026-01-30",
"direction" => "BULLISH",
"spot_price" => 23100,
"max_distance_pct" => 1.0,
"min_premium" => 50,
"max_premium" => 300
},
context
)
# => [{strike: 23200, option_type: "CE", ltp: 150, ...}]
# 4. Prepare trade intent
Dhanhq::Mcp::Router.call(
"option.prepare",
{
"exchange_segment" => "IDX_I",
"symbol" => "NIFTY",
"security_id" => "52175",
"option_type" => "CE",
"strike" => 23200,
"expiry" => "2026-01-30",
"quantity" => 50,
"stop_loss" => 100,
"target" => 200
},
context
)
# => {trade_type: "OPTIONS_BUY", instrument: "NIFTY 23200 CE", note: "Await human confirmation", ...}订单工具(1)-仅限意图
| 工具 | 描述 | 参数 |
|---|---|---|
orders.prepare | 准备股票/期货交易意向 | exchange_segment, symbol, transaction_type, product_type, order_type, quantity, price (可选), trigger_price (可选), amo, bo_flag, co_flag, stop_loss, target |
例子:
Dhanhq::Mcp::Router.call(
"orders.prepare",
{
"exchange_segment" => "NSE_EQ",
"symbol" => "RELIANCE",
"transaction_type" => "BUY",
"product_type" => "INTRADAY",
"order_type" => "MARKET",
"quantity" => 10
},
context
)
# => {trade_type: "EQUITY_FUTURES", instrument: "RELIANCE (NSE_EQ)", security_id: "1234", note: "Await human confirmation", ...}______________________________________________________________________
🔄 完整的交易工作流程
期权交易工作流程
# 1. Check available funds
funds = Dhanhq::Mcp::Router.call("portfolio.funds", {}, context)
puts "Available margin: #{funds[:available_balance]}"
# 2. Discover index instrument
inst = Dhanhq::Mcp::Router.call(
"instrument.find",
{"exchange_segment" => "IDX_I", "symbol" => "NIFTY"},
context
)
# 3. Check trading permissions
info = Dhanhq::Mcp::Router.call(
"instrument.info",
{"exchange_segment" => "IDX_I", "symbol" => "NIFTY"},
context
)
raise "Trading not allowed" unless info[:trading_allowed]
# 4. Get current spot price
ltp = Dhanhq::Mcp::Router.call(
"instrument.ltp",
{"exchange_segment" => "IDX_I", "symbol" => "NIFTY"},
context
)
spot = ltp[:ltp]
# 5. Get option expiries
expiries = Dhanhq::Mcp::Router.call(
"option.expiries",
{"exchange_segment" => "IDX_I", "symbol" => "NIFTY"},
context
)
expiry = expiries.first
# 6. Get option chain
chain = Dhanhq::Mcp::Router.call(
"option.chain",
{"exchange_segment" => "IDX_I", "symbol" => "NIFTY", "expiry" => expiry},
context
)
# 7. Rule-based strike selection
strikes = Dhanhq::Mcp::Router.call(
"option.select",
{
"exchange_segment" => "IDX_I",
"symbol" => "NIFTY",
"expiry" => expiry,
"direction" => "BULLISH",
"spot_price" => spot,
"max_distance_pct" => 1.0,
"min_premium" => 50,
"max_premium" => 300
},
context
)
selected = strikes.first
# 8. Prepare trade intent
intent = Dhanhq::Mcp::Router.call(
"option.prepare",
{
"exchange_segment" => "IDX_I",
"symbol" => "NIFTY",
"security_id" => selected[:security_id],
"option_type" => selected[:option_type],
"strike" => selected[:strike],
"expiry" => expiry,
"quantity" => 50,
"stop_loss" => 100,
"target" => 200
},
context
)
# 9. ⚠️ HUMAN CONFIRMATION REQUIRED ⚠️
puts "Trade Intent: #{intent}"
puts "Awaiting human confirmation..."
# 10. (In Rails/external system) Execute after confirmation
# client.place_option_order(...) ← NOT exposed via MCP股票交易工作流程
# 1. Check current positions
positions = Dhanhq::Mcp::Router.call("portfolio.positions", {}, context)
# 2. Discover equity instrument
inst = Dhanhq::Mcp::Router.call(
"instrument.find",
{"exchange_segment" => "NSE_EQ", "symbol" => "RELIANCE"},
context
)
# 3. Analyze price history
daily = Dhanhq::Mcp::Router.call(
"instrument.daily",
{
"exchange_segment" => "NSE_EQ",
"symbol" => "RELIANCE",
"from" => "2026-01-01",
"to" => "2026-01-17"
},
context
)
# 4. Get current quote
quote = Dhanhq::Mcp::Router.call(
"instrument.quote",
{"exchange_segment" => "NSE_EQ", "symbol" => "RELIANCE"},
context
)
# 5. Prepare order intent
intent = Dhanhq::Mcp::Router.call(
"orders.prepare",
{
"exchange_segment" => "NSE_EQ",
"symbol" => "RELIANCE",
"transaction_type" => "BUY",
"product_type" => "INTRADAY",
"order_type" => "LIMIT",
"quantity" => 10,
"price" => quote[:ltp] * 0.99 # 1% below LTP
},
context
)
# 6. ⚠️ HUMAN CONFIRMATION REQUIRED ⚠️
puts "Trade Intent: #{intent}"
puts "Awaiting human confirmation..."
# 7. (In Rails/external system) Execute after confirmation
# client.place_order(...) ← NOT exposed via MCP______________________________________________________________________
🛡️ 合规与安全特性
自动合规性检查
所有订单/期权准备工具均强制执行:
- ✅ 交易权限 (
buy_sell_indicator == "A") - ✅ ASM/GSM限制 (如果受到限制,则引发错误)
- ✅ 仪器类型验证 (仅适用于INDEX仪器的选项)
- ✅ 数量验证 (数量>0)
- ✅ 风险回报验证 (目标>stop_loss)
- ✅ 订单类型验证 (LIMIT所需价格,STOP_LOSS触发价格)
- ✅ 产品支持 (根据仪器性能检查BO/CO标志)
无自动执行
- ❌
place_order-未暴露 - ❌
modify_order-未暴露 - ❌
cancel_order-未暴露 - ✅
orders.prepare-仅返回意图 - ✅
option.prepare-仅返回意图
执行流程:
MCP (prepare) → Intent → Rails/Human → Confirmation → dhanhq-client (execute)______________________________________________________________________
🧪 测试
运行完整测试套件
bundle exec rake使用STDIO进行手动测试
# Test portfolio tools
bin/test-portfolio
# Test instrument tools
bin/test-instrument
# Test options tools
bin/test-options
# Test order preparation
bin/test-orders测试单个工具
echo '{"method":"tools/call","params":{"name":"portfolio.funds","arguments":{}}}' | bin/dhanhq-mcp-stdio______________________________________________________________________
📊 代码质量
- ✅ RuboCop:0次违规,执行Clean Ruby原则
- ✅ RSpec:具有覆盖跟踪功能的测试套件
- ✅ 码场:100%文件覆盖率
- ✅ 方法长度:所有方法\<10行
- ✅ 复杂性:低圈复杂度
- ✅ 命名:有意透露姓名
运行质量检查
# Linting
bundle exec rubocop
# Documentation
bundle exec yard doc
bundle exec yard stats
# Coverage
bundle exec rake spec______________________________________________________________________
🔧 发展
项目结构
lib/dhanhq/mcp/
├── server.rb # Rack-based MCP HTTP server
├── router.rb # Routes MCP calls to tools
├── tool_spec.rb # MCP tool specifications (contract)
├── context.rb # Dependency injection container
├── errors.rb # Custom error classes
└── tools/
├── base.rb # Base class for all tools
├── portfolio.rb # Portfolio read-only tools
├── instrument.rb # Instrument discovery & market data
├── orders.rb # Order preparation (intent-only)
└── options/
├── expiries.rb # Option expiry list
├── chain.rb # Option chain fetcher
├── selector.rb # Rule-based strike selector
└── prepare.rb # Options trade preparation添加新工具
- 将工具规格添加到
tool_spec.rb - 创建继承自的工具类
Tools::Base - 更新
router.rb为工具布线 - 需要工具
lib/dhanhq/mcp.rb
例子:
# 1. tool_spec.rb
{
name: "portfolio.summary",
description: "Get portfolio summary",
input_schema: { type: "object", properties: {} }
}
# 2. tools/portfolio.rb
def summary
{
total_value: client.funds[:available_balance],
holdings_count: client.holdings.count,
positions_count: client.positions.count
}
end
# 3. router.rb (already handles portfolio.* automatically via public_send)
# 4. Done! Tool is now available.______________________________________________________________________
🎯 设计原则
1.以仪器为中心
每个交易操作都始于 Instrument.find 以确保合规性和适当的抽象。
2.纯意向订单
MCP工具准备交易意图;执行过程在MCP外部进行,需要人工确认。
3.清洁红宝石
- 方法只做一件事
- 名字揭示意图
- 无过早优化
- 持续重构
4.依赖注入
所有工具都会收到 Context 对象与 dhanhq-client 实例,实现可测试性。
5.协议适配器
dhanhq-mcp 是一个薄而安全的适配器,而不是重新实现。它暴露了一个有界的子集 dhanhq-client.
______________________________________________________________________
🚀 生产部署
环境变量
DHAN_ACCESS_TOKEN=your_token_hereDocker(可选)
FROM ruby:3.3.4-alpine
RUN apk add --no-cache build-base git
WORKDIR /app
COPY Gemfile* ./
RUN bundle install
COPY . .
CMD ["bin/dhanhq-mcp-stdio"]系统服务(STDIO)
[Unit]
Description=DhanHQ MCP STDIO Server
After=network.target
[Service]
Type=simple
User=trading
WorkingDirectory=/opt/dhanhq-mcp
Environment="DHAN_ACCESS_TOKEN=your_token"
ExecStart=/usr/local/bin/dhanhq-mcp-stdio
Restart=always
[Install]
WantedBy=multi-user.target______________________________________________________________________
📚 资源
- DhanHQ客户宝石: dhanhq客户端
- 推荐响应模式: 建议_SCHEMA.md
- 模型上下文协议: MCP规范
- DhanHQ API文档: https://dhanhq.co/docs/v2/
______________________________________________________________________
🤝 贡献
- 分叉存储库
- 创建要素分支(
git checkout -b feature/amazing-feature) - 提交您的更改(
git commit -m 'Add amazing feature') - 推到分支(
git push origin feature/amazing-feature) - 打开拉取请求
代码质量要求:
- RuboCop以0次违规通过
- 所有测试均通过
- 覆盖率保持在90%以上
- YARD文件已更新
______________________________________________________________________
📝 许可证
此项目根据MIT许可证获得许可-请参阅 许可证 文件以获取详细信息。
______________________________________________________________________
⚠️ 免责声明
这个宝石是通过人工智能代理进行算法交易的。真钱有风险。
- 始终先进行纸笔交易测试
- 实施适当的风险管理
- 执行前审查所有交易意图
- 此软件按原样提供,不提供任何保修
- 作者不对交易损失承担责任
______________________________________________________________________
🙏 致谢
- 建在上面
dhanhq-client - 遵循Clean Ruby原则 鲍勃·马丁叔叔
- 坚持 模型上下文协议 规格
______________________________________________________________________
由...制作❤️ 用于基础设施级交易系统
