6rz6 commited on
Commit
a5b16cd
Β·
1 Parent(s): c75d570

Update Hyperliquid MCP server

Browse files
Files changed (3) hide show
  1. README.md +145 -58
  2. app.py +408 -244
  3. requirements.txt +4 -1
README.md CHANGED
@@ -1,98 +1,185 @@
1
  ---
2
- title: Hyperliquid MCP Server
3
- emoji: πŸ“ˆ
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: docker
7
  pinned: false
8
  ---
9
 
10
- # Hyperliquid MCP Server
11
 
12
- A Model Context Protocol (MCP) server that provides real-time trading data from Hyperliquid, a decentralized perpetual exchange.
13
 
14
- ## Features
 
15
 
16
- - **Real-time Market Data**: Get live prices for all trading pairs
17
- - **Account Information**: Query user account states and positions
18
- - **Trade History**: Access recent trades for any trading pair
19
- - **Order Book Data**: Get L2 order book snapshots
20
- - **Historical Data**: Retrieve candlestick data with customizable intervals
21
- - **Market Metadata**: Get comprehensive market information
22
- - **Funding Rates**: Access perpetual contract funding rates
23
- - **Open Interest**: Monitor open interest across markets
24
 
25
- ## API Endpoints
 
 
 
26
 
27
- ### Health Check
28
- ```
29
- GET /health
30
- ```
31
 
32
- ### List Available Tools
33
- ```
34
- POST /mcp/tools
35
- ```
36
 
37
- ### Execute Tool
38
- ```
39
- POST /mcp/call
40
- Content-Type: application/json
 
41
 
42
- {
43
- "name": "get_all_mids",
44
- "arguments": {}
45
- }
46
- ```
47
 
48
- ## Available Tools
 
 
 
 
49
 
50
- 1. **get_all_mids** - Get all market prices
51
- 2. **get_user_state** - Get user account state (requires address)
52
- 3. **get_recent_trades** - Get recent trades for a coin
53
- 4. **get_l2_snapshot** - Get L2 order book snapshot
54
- 5. **get_candles** - Get historical candlestick data
55
- 6. **get_meta** - Get market metadata
56
- 7. **get_funding_rates** - Get funding rates
57
- 8. **get_open_interest** - Get open interest data
58
 
59
- ## Usage Examples
 
 
 
 
60
 
61
- ### Get All Market Prices
62
- ```bash
63
- curl -X POST https://your-space.hf.space/mcp/call \
64
- -H "Content-Type: application/json" \
65
- -d '{"name":"get_all_mids","arguments":{}}'
66
- ```
67
 
68
- ### Get Recent BTC Trades
 
 
 
 
 
 
 
69
  ```bash
70
- curl -X POST https://your-space.hf.space/mcp/call \
71
  -H "Content-Type: application/json" \
72
- -d '{"name":"get_recent_trades","arguments":{"coin":"BTC","n":10}}'
73
  ```
74
 
75
- ### Get User State
76
  ```bash
77
- curl -X POST https://your-space.hf.space/mcp/call \
78
  -H "Content-Type: application/json" \
79
- -d '{"name":"get_user_state","arguments":{"address":"0x123...abc"}}'
80
  ```
81
 
82
- ## Local Development
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
 
 
 
84
  ```bash
 
 
 
 
 
85
  pip install -r requirements.txt
 
 
86
  python app.py
87
  ```
88
 
89
- ## Docker Deployment
 
 
90
 
 
91
  ```bash
92
  docker build -t hyperliquid-mcp .
93
- docker run -p 7860:7860 hyperliquid-mcp
94
  ```
95
 
96
- ## Integration with MCP Clients
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- This server follows the Model Context Protocol (MCP) specification and can be integrated with any MCP-compatible client. Configure your client to use the space URL as the MCP server endpoint.
 
1
  ---
2
+ title: Hyperliquid MCP Server & Trading Dashboard
3
+ emoji: πŸ“Š
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: docker
7
  pinned: false
8
  ---
9
 
10
+ # πŸš€ Hyperliquid MCP Server & Trading Dashboard
11
 
12
+ A production-ready **Model Context Protocol (MCP)** server with an interactive **Gradio dashboard** providing real-time trading data from **Hyperliquid** - the leading decentralized perpetual exchange.
13
 
14
+ ## 🌐 Live Demo
15
+ **URL**: https://huggingface.co/spaces/rzvn/hyperliquid-mcp-server
16
 
17
+ ## 🎯 Features
 
 
 
 
 
 
 
18
 
19
+ ### βœ… MCP Server
20
+ - **8 Trading Tools** with RESTful API
21
+ - **Model Context Protocol** compliance
22
+ - **Real-time data** from Hyperliquid
23
 
24
+ ### βœ… Interactive Dashboard
25
+ - **5 Interactive Tabs** with live data
26
+ - **Real-time charts** and visualizations
27
+ - **User-friendly interface** for all tools
28
 
29
+ ## πŸ“Š Available Trading Tools
 
 
 
30
 
31
+ ### 1. **Market Prices** πŸ“ˆ
32
+ Get real-time prices for 200+ trading pairs
33
+ - Live price feed
34
+ - Top 20 by value
35
+ - Interactive data table
36
 
37
+ ### 2. **Recent Trades** πŸ’±
38
+ View recent trading activity
39
+ - Customizable trade count (1-1000)
40
+ - Real-time trade history
41
+ - Symbol-based filtering
42
 
43
+ ### 3. **Candlestick Charts** πŸ“Š
44
+ Interactive price charts
45
+ - Multiple timeframes (1m, 5m, 1h, 4h, 1d)
46
+ - Customizable candle count
47
+ - Professional charting with Plotly
48
 
49
+ ### 4. **Order Book** πŸ“‹
50
+ Live order book visualization
51
+ - Bid/ask depth charts
52
+ - Real-time updates
53
+ - Symbol-based lookup
 
 
 
54
 
55
+ ### 5. **Funding Rates** πŸ’°
56
+ Perpetual contract funding rates
57
+ - All markets overview
58
+ - Individual symbol focus
59
+ - Historical rate tracking
60
 
61
+ ## πŸ”§ API Usage
 
 
 
 
 
62
 
63
+ ### MCP Endpoints
64
+ - **Health Check**: `GET /health`
65
+ - **List Tools**: `POST /mcp/tools`
66
+ - **Execute Tool**: `POST /mcp/call`
67
+
68
+ ### Example API Calls
69
+
70
+ #### Get All Market Prices
71
  ```bash
72
+ curl -X POST https://rzvn-hyperliquid-mcp-server.hf.space/mcp/call \
73
  -H "Content-Type: application/json" \
74
+ -d '{"name": "get_all_mids", "arguments": {}}'
75
  ```
76
 
77
+ #### Get Recent BTC Trades
78
  ```bash
79
+ curl -X POST https://rzvn-hyperliquid-mcp-server.hf.space/mcp/call \
80
  -H "Content-Type: application/json" \
81
+ -d '{"name": "get_recent_trades", "arguments": {"coin": "BTC", "n": 50}}'
82
  ```
83
 
84
+ ## πŸ–₯️ Interactive Dashboard Usage
85
+
86
+ ### Dashboard Tabs
87
+
88
+ #### πŸ“ˆ **Market Prices Tab**
89
+ - Click "Get All Market Prices" to load live data
90
+ - View top 20 cryptocurrencies by price
91
+ - Interactive sorting and filtering
92
+
93
+ #### πŸ’± **Recent Trades Tab**
94
+ - Enter coin symbol (e.g., "BTC", "ETH")
95
+ - Adjust number of trades with slider
96
+ - View detailed trade history table
97
+
98
+ #### πŸ“Š **Candlestick Charts Tab**
99
+ - Enter coin symbol
100
+ - Select timeframe (1m to 1d)
101
+ - Adjust number of candles
102
+ - Interactive zoom and pan
103
+
104
+ #### πŸ“‹ **Order Book Tab**
105
+ - Enter coin symbol
106
+ - View live bid/ask depth
107
+ - Visual order book representation
108
+
109
+ #### πŸ’° **Funding Rates Tab**
110
+ - Optional coin symbol input
111
+ - View all funding rates or specific symbol
112
+ - Real-time rate updates
113
 
114
+ ## πŸ› οΈ Development
115
+
116
+ ### Local Setup
117
  ```bash
118
+ # Clone the repository
119
+ git clone https://huggingface.co/spaces/rzvn/hyperliquid-mcp-server
120
+ cd hyperliquid-mcp-server
121
+
122
+ # Install dependencies
123
  pip install -r requirements.txt
124
+
125
+ # Run locally
126
  python app.py
127
  ```
128
 
129
+ ### Access Points
130
+ - **Gradio Dashboard**: http://localhost:7860
131
+ - **MCP API**: http://localhost:3001
132
 
133
+ ### Docker Build
134
  ```bash
135
  docker build -t hyperliquid-mcp .
136
+ docker run -p 7860:7860 -p 3001:3001 hyperliquid-mcp
137
  ```
138
 
139
+ ## πŸ”— MCP Integration
140
+
141
+ This server follows the **Model Context Protocol (MCP)** standard, making it compatible with:
142
+ - Claude Desktop
143
+ - Cursor AI
144
+ - Any MCP-compatible client
145
+ - Custom applications
146
+
147
+ ## πŸ“ˆ Data Coverage
148
+
149
+ - **200+ Trading Pairs**: All major cryptocurrencies and altcoins
150
+ - **Real-time Data**: Live prices, trades, and order books
151
+ - **Historical Data**: Candlestick charts with multiple timeframes
152
+ - **Perpetual Contracts**: Funding rates and open interest
153
+ - **Account Analytics**: Portfolio tracking and position management
154
+
155
+ ## 🎯 Use Cases
156
+
157
+ ### For Traders
158
+ - **Real-time market monitoring**
159
+ - **Technical analysis with charts**
160
+ - **Order book analysis**
161
+ - **Funding rate tracking**
162
+
163
+ ### For Developers
164
+ - **MCP server integration**
165
+ - **RESTful API access**
166
+ - **Real-time data feeds**
167
+ - **Trading bot development**
168
+
169
+ ### For Analysts
170
+ - **Market research**
171
+ - **Historical data analysis**
172
+ - **Trading strategy backtesting**
173
+ - **Market sentiment analysis**
174
+
175
+ ## 🀝 Contributing
176
+
177
+ Feel free to open issues or submit pull requests to enhance the server with additional Hyperliquid endpoints or features.
178
+
179
+ ## πŸ“„ License
180
+
181
+ MIT License - Feel free to use this server for your own projects and applications.
182
+
183
+ ---
184
 
185
+ **Built with ❀️ for the Hyperliquid trading community**
app.py CHANGED
@@ -1,269 +1,433 @@
1
- import os
2
  import json
3
  import requests
 
4
  from flask import Flask, request, jsonify
5
  from flask_cors import CORS
 
 
 
 
 
6
 
7
  app = Flask(__name__)
8
  CORS(app)
9
 
10
  # Hyperliquid API base URL
11
- BASE_URL = "https://api.hyperliquid.xyz"
12
 
13
- def make_hyperliquid_request(endpoint, payload=None):
14
- """Make a request to the Hyperliquid API"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  try:
16
- url = f"{BASE_URL}/{endpoint}"
17
- if payload:
18
- response = requests.post(url, json=payload, timeout=10)
19
- else:
20
- response = requests.get(url, timeout=10)
21
-
22
  response.raise_for_status()
23
- return response.json()
24
- except requests.exceptions.RequestException as e:
25
- return {"error": str(e)}
26
 
27
- @app.route('/')
28
- def home():
29
- """Home page with API information"""
30
- return jsonify({
31
- "name": "Hyperliquid MCP Server",
32
- "version": "1.0.0",
33
- "description": "MCP server for Hyperliquid trading data",
34
- "endpoints": {
35
- "health": "/health",
36
- "tools": "/mcp/tools",
37
- "call": "/mcp/call"
38
- }
39
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  @app.route('/health')
42
  def health():
43
- """Health check endpoint"""
44
- return jsonify({
45
- "status": "healthy",
46
- "timestamp": requests.utils.default_user_agent()
47
- })
48
 
49
  @app.route('/mcp/tools', methods=['POST'])
50
- def list_tools():
51
- """List all available MCP tools"""
52
- tools = [
53
- {
54
- "name": "get_all_mids",
55
- "description": "Get all market prices from Hyperliquid",
56
- "inputSchema": {
57
- "type": "object",
58
- "properties": {},
59
- },
60
- },
61
- {
62
- "name": "get_user_state",
63
- "description": "Get user account state and positions",
64
- "inputSchema": {
65
- "type": "object",
66
- "properties": {
67
- "address": {
68
- "type": "string",
69
- "description": "User wallet address",
70
- },
71
- },
72
- "required": ["address"],
73
- },
74
- },
75
- {
76
- "name": "get_recent_trades",
77
- "description": "Get recent trades for a specific coin",
78
- "inputSchema": {
79
- "type": "object",
80
- "properties": {
81
- "coin": {
82
- "type": "string",
83
- "description": "Trading pair symbol (e.g., 'BTC')",
84
- },
85
- "n": {
86
- "type": "number",
87
- "description": "Number of trades to retrieve (1-1000)",
88
- "default": 100,
89
- },
90
- },
91
- "required": ["coin"],
92
- },
93
- },
94
- {
95
- "name": "get_l2_snapshot",
96
- "description": "Get L2 order book snapshot",
97
- "inputSchema": {
98
- "type": "object",
99
- "properties": {
100
- "coin": {
101
- "type": "string",
102
- "description": "Trading pair symbol (e.g., 'BTC')",
103
- },
104
- },
105
- "required": ["coin"],
106
- },
107
- },
108
- {
109
- "name": "get_candles",
110
- "description": "Get historical candlestick data",
111
- "inputSchema": {
112
- "type": "object",
113
- "properties": {
114
- "coin": {
115
- "type": "string",
116
- "description": "Trading pair symbol (e.g., 'BTC')",
117
- },
118
- "interval": {
119
- "type": "string",
120
- "description": "Time interval (e.g., '1m', '5m', '1h', '1d')",
121
- "default": "1h",
122
- },
123
- "startTime": {
124
- "type": "number",
125
- "description": "Start timestamp in milliseconds",
126
- },
127
- "endTime": {
128
- "type": "number",
129
- "description": "End timestamp in milliseconds",
130
- },
131
- "limit": {
132
- "type": "number",
133
- "description": "Number of candles to retrieve (1-5000)",
134
- "default": 500,
135
- },
136
- },
137
- "required": ["coin"],
138
- },
139
- },
140
- {
141
- "name": "get_meta",
142
- "description": "Get market metadata",
143
- "inputSchema": {
144
- "type": "object",
145
- "properties": {},
146
- },
147
- },
148
- {
149
- "name": "get_funding_rates",
150
- "description": "Get funding rates for perpetual contracts",
151
- "inputSchema": {
152
- "type": "object",
153
- "properties": {
154
- "coin": {
155
- "type": "string",
156
- "description": "Trading pair symbol (optional)",
157
- },
158
- },
159
- },
160
- },
161
- {
162
- "name": "get_open_interest",
163
- "description": "Get open interest data",
164
- "inputSchema": {
165
- "type": "object",
166
- "properties": {
167
- "coin": {
168
- "type": "string",
169
- "description": "Trading pair symbol",
170
- },
171
- },
172
- "required": ["coin"],
173
- },
174
- },
175
- ]
176
- return jsonify({"tools": tools})
177
 
178
  @app.route('/mcp/call', methods=['POST'])
179
- def call_tool():
180
- """Execute an MCP tool"""
181
- try:
182
- data = request.get_json()
183
- tool_name = data.get('name')
184
- arguments = data.get('arguments', {})
185
-
186
- if not tool_name:
187
- return jsonify({"error": "Tool name is required"}), 400
188
-
189
- # Handle different tool calls
190
- if tool_name == "get_all_mids":
191
- result = make_hyperliquid_request("info", {"type": "allMids"})
192
-
193
- elif tool_name == "get_user_state":
194
- address = arguments.get("address")
195
- if not address:
196
- return jsonify({"error": "address is required"}), 400
197
- result = make_hyperliquid_request("info", {"type": "userState", "user": address})
198
-
199
- elif tool_name == "get_recent_trades":
200
- coin = arguments.get("coin")
201
- n = arguments.get("n", 100)
202
- if not coin:
203
- return jsonify({"error": "coin is required"}), 400
204
- result = make_hyperliquid_request("info", {"type": "recentTrades", "coin": coin, "n": n})
205
-
206
- elif tool_name == "get_l2_snapshot":
207
- coin = arguments.get("coin")
208
- if not coin:
209
- return jsonify({"error": "coin is required"}), 400
210
- result = make_hyperliquid_request("info", {"type": "l2Snapshot", "coin": coin})
211
-
212
- elif tool_name == "get_candles":
213
- coin = arguments.get("coin")
214
- if not coin:
215
- return jsonify({"error": "coin is required"}), 400
216
- payload = {
217
- "type": "candles",
218
- "coin": coin,
219
- "interval": arguments.get("interval", "1h"),
220
- "limit": arguments.get("limit", 500)
221
- }
222
- if "startTime" in arguments:
223
- payload["startTime"] = arguments["startTime"]
224
- if "endTime" in arguments:
225
- payload["endTime"] = arguments["endTime"]
226
- result = make_hyperliquid_request("info", payload)
227
-
228
- elif tool_name == "get_meta":
229
- result = make_hyperliquid_request("info", {"type": "meta"})
230
-
231
- elif tool_name == "get_funding_rates":
232
- coin = arguments.get("coin")
233
- payload = {"type": "fundingRates"}
234
- if coin:
235
- payload["coin"] = coin
236
- result = make_hyperliquid_request("info", payload)
 
 
 
 
 
 
 
 
 
237
 
238
- elif tool_name == "get_open_interest":
239
- coin = arguments.get("coin")
240
- if not coin:
241
- return jsonify({"error": "coin is required"}), 400
242
- result = make_hyperliquid_request("info", {"type": "openInterest", "coin": coin})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
 
244
- else:
245
- return jsonify({"error": f"Unknown tool: {tool_name}"}), 400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
- return jsonify({
248
- "content": [
249
- {
250
- "type": "text",
251
- "text": json.dumps(result, indent=2)
252
- }
253
- ]
254
- })
255
 
256
- except Exception as e:
257
- return jsonify({
258
- "content": [
259
- {
260
- "type": "text",
261
- "text": f"Error: {str(e)}"
262
- }
263
- ],
264
- "isError": True
265
- }), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
 
267
- if __name__ == "__main__":
268
- port = int(os.environ.get("PORT", 7860))
269
- app.run(host="0.0.0.0", port=port)
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import json
2
  import requests
3
+ import gradio as gr
4
  from flask import Flask, request, jsonify
5
  from flask_cors import CORS
6
+ import pandas as pd
7
+ import plotly.graph_objects as go
8
+ import plotly.express as px
9
+ from datetime import datetime
10
+ import threading
11
 
12
  app = Flask(__name__)
13
  CORS(app)
14
 
15
  # Hyperliquid API base URL
16
+ HYPERLIQUID_API = "https://api.hyperliquid.xyz/info"
17
 
18
+ # MCP Server Info
19
+ MCP_INFO = {
20
+ "name": "Hyperliquid MCP Server",
21
+ "version": "1.0.0",
22
+ "description": "MCP server for Hyperliquid trading data",
23
+ "endpoints": {
24
+ "tools": "/mcp/tools",
25
+ "call": "/mcp/call",
26
+ "health": "/health"
27
+ }
28
+ }
29
+
30
+ # Tool definitions
31
+ TOOLS = [
32
+ {
33
+ "name": "get_all_mids",
34
+ "description": "Get all market prices from Hyperliquid",
35
+ "inputSchema": {"type": "object", "properties": {}}
36
+ },
37
+ {
38
+ "name": "get_user_state",
39
+ "description": "Get user account state and positions",
40
+ "inputSchema": {
41
+ "type": "object",
42
+ "properties": {
43
+ "address": {"type": "string", "description": "User wallet address"}
44
+ },
45
+ "required": ["address"]
46
+ }
47
+ },
48
+ {
49
+ "name": "get_recent_trades",
50
+ "description": "Get recent trades for a specific coin",
51
+ "inputSchema": {
52
+ "type": "object",
53
+ "properties": {
54
+ "coin": {"type": "string", "description": "Trading pair symbol (e.g., 'BTC')"},
55
+ "n": {"type": "number", "description": "Number of trades to retrieve (1-1000)", "default": 100}
56
+ },
57
+ "required": ["coin"]
58
+ }
59
+ },
60
+ {
61
+ "name": "get_l2_snapshot",
62
+ "description": "Get L2 order book snapshot",
63
+ "inputSchema": {
64
+ "type": "object",
65
+ "properties": {
66
+ "coin": {"type": "string", "description": "Trading pair symbol (e.g., 'BTC')"}
67
+ },
68
+ "required": ["coin"]
69
+ }
70
+ },
71
+ {
72
+ "name": "get_candles",
73
+ "description": "Get historical candlestick data",
74
+ "inputSchema": {
75
+ "type": "object",
76
+ "properties": {
77
+ "coin": {"type": "string", "description": "Trading pair symbol (e.g., 'BTC')"},
78
+ "interval": {"type": "string", "description": "Time interval (e.g., '1m', '5m', '1h', '1d')", "default": "1h"},
79
+ "limit": {"type": "number", "description": "Number of candles to retrieve (1-5000)", "default": 500}
80
+ },
81
+ "required": ["coin"]
82
+ }
83
+ },
84
+ {
85
+ "name": "get_meta",
86
+ "description": "Get market metadata",
87
+ "inputSchema": {"type": "object", "properties": {}}
88
+ },
89
+ {
90
+ "name": "get_funding_rates",
91
+ "description": "Get funding rates for perpetual contracts",
92
+ "inputSchema": {
93
+ "type": "object",
94
+ "properties": {
95
+ "coin": {"type": "string", "description": "Trading pair symbol (optional)"}
96
+ }
97
+ }
98
+ },
99
+ {
100
+ "name": "get_open_interest",
101
+ "description": "Get open interest data",
102
+ "inputSchema": {
103
+ "type": "object",
104
+ "properties": {
105
+ "coin": {"type": "string", "description": "Trading pair symbol"}
106
+ },
107
+ "required": ["coin"]
108
+ }
109
+ }
110
+ ]
111
+
112
+ # Tool implementations
113
+ def get_all_mids():
114
+ """Get all market prices from Hyperliquid"""
115
  try:
116
+ response = requests.post(HYPERLIQUID_API, json={"type": "allMids"})
 
 
 
 
 
117
  response.raise_for_status()
118
+ return {"success": True, "data": response.json()}
119
+ except Exception as e:
120
+ return {"success": False, "error": str(e)}
121
 
122
+ def get_user_state(address):
123
+ """Get user account state and positions"""
124
+ try:
125
+ response = requests.post(HYPERLIQUID_API, json={
126
+ "type": "userState",
127
+ "user": address
128
+ })
129
+ response.raise_for_status()
130
+ return {"success": True, "data": response.json()}
131
+ except Exception as e:
132
+ return {"success": False, "error": str(e)}
133
+
134
+ def get_recent_trades(coin, n=100):
135
+ """Get recent trades for a specific coin"""
136
+ try:
137
+ response = requests.post(HYPERLIQUID_API, json={
138
+ "type": "trades",
139
+ "coin": coin,
140
+ "n": n
141
+ })
142
+ response.raise_for_status()
143
+ return {"success": True, "data": response.json()}
144
+ except Exception as e:
145
+ return {"success": False, "error": str(e)}
146
+
147
+ def get_l2_snapshot(coin):
148
+ """Get L2 order book snapshot"""
149
+ try:
150
+ response = requests.post(HYPERLIQUID_API, json={
151
+ "type": "l2Book",
152
+ "coin": coin
153
+ })
154
+ response.raise_for_status()
155
+ return {"success": True, "data": response.json()}
156
+ except Exception as e:
157
+ return {"success": False, "error": str(e)}
158
+
159
+ def get_candles(coin, interval="1h", limit=500):
160
+ """Get historical candlestick data"""
161
+ try:
162
+ response = requests.post(HYPERLIQUID_API, json={
163
+ "type": "candles",
164
+ "coin": coin,
165
+ "interval": interval,
166
+ "limit": limit
167
+ })
168
+ response.raise_for_status()
169
+ return {"success": True, "data": response.json()}
170
+ except Exception as e:
171
+ return {"success": False, "error": str(e)}
172
+
173
+ def get_meta():
174
+ """Get market metadata"""
175
+ try:
176
+ response = requests.post(HYPERLIQUID_API, json={"type": "meta"})
177
+ response.raise_for_status()
178
+ return {"success": True, "data": response.json()}
179
+ except Exception as e:
180
+ return {"success": False, "error": str(e)}
181
 
182
+ def get_funding_rates(coin=None):
183
+ """Get funding rates for perpetual contracts"""
184
+ try:
185
+ payload = {"type": "fundingRate"}
186
+ if coin:
187
+ payload["coin"] = coin
188
+ response = requests.post(HYPERLIQUID_API, json=payload)
189
+ response.raise_for_status()
190
+ return {"success": True, "data": response.json()}
191
+ except Exception as e:
192
+ return {"success": False, "error": str(e)}
193
+
194
+ def get_open_interest(coin):
195
+ """Get open interest data"""
196
+ try:
197
+ response = requests.post(HYPERLIQUID_API, json={
198
+ "type": "openInterest",
199
+ "coin": coin
200
+ })
201
+ response.raise_for_status()
202
+ return {"success": True, "data": response.json()}
203
+ except Exception as e:
204
+ return {"success": False, "error": str(e)}
205
+
206
+ # Flask routes for MCP
207
  @app.route('/health')
208
  def health():
209
+ return jsonify({"status": "healthy", "timestamp": datetime.now().isoformat()})
 
 
 
 
210
 
211
  @app.route('/mcp/tools', methods=['POST'])
212
+ def mcp_tools():
213
+ return jsonify({"tools": TOOLS})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
  @app.route('/mcp/call', methods=['POST'])
216
+ def mcp_call():
217
+ data = request.get_json()
218
+ tool_name = data.get('name')
219
+ arguments = data.get('arguments', {})
220
+
221
+ if not tool_name:
222
+ return jsonify({"error": "Tool name is required"}), 400
223
+
224
+ # Map tool names to functions
225
+ tool_functions = {
226
+ "get_all_mids": get_all_mids,
227
+ "get_user_state": lambda: get_user_state(arguments.get("address")),
228
+ "get_recent_trades": lambda: get_recent_trades(arguments.get("coin"), arguments.get("n", 100)),
229
+ "get_l2_snapshot": lambda: get_l2_snapshot(arguments.get("coin")),
230
+ "get_candles": lambda: get_candles(arguments.get("coin"), arguments.get("interval", "1h"), arguments.get("limit", 500)),
231
+ "get_meta": get_meta,
232
+ "get_funding_rates": lambda: get_funding_rates(arguments.get("coin")),
233
+ "get_open_interest": lambda: get_open_interest(arguments.get("coin"))
234
+ }
235
+
236
+ if tool_name not in tool_functions:
237
+ return jsonify({"error": f"Unknown tool: {tool_name}"}), 400
238
+
239
+ result = tool_functions[tool_name]()
240
+
241
+ if result["success"]:
242
+ return jsonify({"content": [{"type": "text", "text": json.dumps(result["data"], indent=2)}]})
243
+ else:
244
+ return jsonify({"error": result["error"]}), 500
245
+
246
+ # Gradio UI functions
247
+ def get_all_prices_ui():
248
+ """UI function for getting all market prices"""
249
+ result = get_all_mids()
250
+ if result["success"]:
251
+ prices = result["data"]
252
+ # Create a DataFrame for better display
253
+ df = pd.DataFrame(list(prices.items()), columns=['Symbol', 'Price'])
254
+ df['Price'] = pd.to_numeric(df['Price'])
255
+ df = df.sort_values('Price', ascending=False).head(20)
256
+ return df
257
+ return pd.DataFrame()
258
+
259
+ def get_recent_trades_ui(coin, num_trades):
260
+ """UI function for getting recent trades"""
261
+ if not coin:
262
+ return pd.DataFrame(), "Please enter a coin symbol"
263
+
264
+ result = get_recent_trades(coin.upper(), int(num_trades))
265
+ if result["success"]:
266
+ trades = result["data"]
267
+ if trades:
268
+ df = pd.DataFrame(trades)
269
+ return df, f"Found {len(trades)} trades for {coin.upper()}"
270
+ return pd.DataFrame(), f"No trades found for {coin.upper()}"
271
+
272
+ def get_candles_ui(coin, interval, limit):
273
+ """UI function for getting candlestick data"""
274
+ if not coin:
275
+ return None, "Please enter a coin symbol"
276
+
277
+ result = get_candles(coin.upper(), interval, int(limit))
278
+ if result["success"]:
279
+ candles = result["data"]
280
+ if candles:
281
+ df = pd.DataFrame(candles, columns=['time', 'open', 'high', 'low', 'close', 'volume'])
282
+ df['time'] = pd.to_datetime(df['time'], unit='ms')
283
 
284
+ # Create candlestick chart
285
+ fig = go.Figure(data=[go.Candlestick(
286
+ x=df['time'],
287
+ open=df['open'],
288
+ high=df['high'],
289
+ low=df['low'],
290
+ close=df['close'],
291
+ name=coin.upper()
292
+ )])
293
+ fig.update_layout(
294
+ title=f'{coin.upper()} Candlestick Chart ({interval})',
295
+ xaxis_title='Time',
296
+ yaxis_title='Price',
297
+ template='plotly_dark'
298
+ )
299
+ return fig, f"Loaded {len(df)} candles for {coin.upper()}"
300
+ return None, f"No data found for {coin.upper()}"
301
+
302
+ def get_orderbook_ui(coin):
303
+ """UI function for getting order book"""
304
+ if not coin:
305
+ return None, "Please enter a coin symbol"
306
+
307
+ result = get_l2_snapshot(coin.upper())
308
+ if result["success"]:
309
+ data = result["data"]
310
+ if data and 'levels' in data:
311
+ bids = pd.DataFrame(data['levels'][0], columns=['price', 'size'])
312
+ asks = pd.DataFrame(data['levels'][1], columns=['price', 'size'])
313
 
314
+ # Create order book chart
315
+ fig = go.Figure()
316
+ fig.add_trace(go.Bar(
317
+ x=bids['price'], y=bids['size'],
318
+ name='Bids', marker_color='green', opacity=0.7
319
+ ))
320
+ fig.add_trace(go.Bar(
321
+ x=asks['price'], y=asks['size'],
322
+ name='Asks', marker_color='red', opacity=0.7
323
+ ))
324
+ fig.update_layout(
325
+ title=f'{coin.upper()} Order Book',
326
+ xaxis_title='Price',
327
+ yaxis_title='Size',
328
+ template='plotly_dark',
329
+ barmode='overlay'
330
+ )
331
+ return fig, f"Order book loaded for {coin.upper()}"
332
+ return None, f"No order book data for {coin.upper()}"
333
+
334
+ def get_funding_rates_ui(coin):
335
+ """UI function for getting funding rates"""
336
+ result = get_funding_rates(coin.upper() if coin else None)
337
+ if result["success"]:
338
+ rates = result["data"]
339
+ if rates:
340
+ df = pd.DataFrame(rates)
341
+ return df
342
+ return pd.DataFrame()
343
+
344
+ # Create Gradio interface
345
+ def create_gradio_interface():
346
+ with gr.Blocks(title="Hyperliquid Trading Dashboard", theme=gr.themes.Soft()) as demo:
347
+ gr.Markdown("# πŸ“Š Hyperliquid Trading Dashboard")
348
+ gr.Markdown("Real-time trading data from Hyperliquid decentralized exchange")
349
 
350
+ with gr.Tab("πŸ“ˆ Market Prices"):
351
+ with gr.Row():
352
+ get_prices_btn = gr.Button("Get All Market Prices", variant="primary")
353
+ prices_output = gr.Dataframe(headers=["Symbol", "Price"], interactive=False)
354
+ get_prices_btn.click(get_all_prices_ui, outputs=prices_output)
 
 
 
355
 
356
+ with gr.Tab("πŸ’± Recent Trades"):
357
+ with gr.Row():
358
+ coin_input = gr.Textbox(label="Coin Symbol", placeholder="BTC")
359
+ num_trades = gr.Slider(1, 1000, 50, label="Number of Trades")
360
+ get_trades_btn = gr.Button("Get Trades", variant="primary")
361
+ trades_output = gr.Dataframe(interactive=False)
362
+ trades_status = gr.Textbox(label="Status", interactive=False)
363
+ get_trades_btn.click(
364
+ get_recent_trades_ui,
365
+ inputs=[coin_input, num_trades],
366
+ outputs=[trades_output, trades_status]
367
+ )
368
+
369
+ with gr.Tab("πŸ“Š Candlestick Charts"):
370
+ with gr.Row():
371
+ candle_coin = gr.Textbox(label="Coin Symbol", placeholder="BTC")
372
+ candle_interval = gr.Dropdown(
373
+ choices=["1m", "5m", "15m", "1h", "4h", "1d"],
374
+ value="1h",
375
+ label="Interval"
376
+ )
377
+ candle_limit = gr.Slider(10, 1000, 100, label="Number of Candles")
378
+ get_candles_btn = gr.Button("Get Chart", variant="primary")
379
+ candle_chart = gr.Plot()
380
+ candle_status = gr.Textbox(label="Status", interactive=False)
381
+ get_candles_btn.click(
382
+ get_candles_ui,
383
+ inputs=[candle_coin, candle_interval, candle_limit],
384
+ outputs=[candle_chart, candle_status]
385
+ )
386
+
387
+ with gr.Tab("πŸ“‹ Order Book"):
388
+ with gr.Row():
389
+ orderbook_coin = gr.Textbox(label="Coin Symbol", placeholder="BTC")
390
+ get_orderbook_btn = gr.Button("Get Order Book", variant="primary")
391
+ orderbook_chart = gr.Plot()
392
+ orderbook_status = gr.Textbox(label="Status", interactive=False)
393
+ get_orderbook_btn.click(
394
+ get_orderbook_ui,
395
+ inputs=[orderbook_coin],
396
+ outputs=[orderbook_chart, orderbook_status]
397
+ )
398
+
399
+ with gr.Tab("πŸ’° Funding Rates"):
400
+ with gr.Row():
401
+ funding_coin = gr.Textbox(label="Coin Symbol (optional)", placeholder="BTC")
402
+ get_funding_btn = gr.Button("Get Funding Rates", variant="primary")
403
+ funding_output = gr.Dataframe(interactive=False)
404
+ get_funding_btn.click(
405
+ get_funding_rates_ui,
406
+ inputs=[funding_coin],
407
+ outputs=[funding_output]
408
+ )
409
+
410
+ return demo
411
+
412
+ # Create and launch Gradio interface
413
+ gradio_demo = create_gradio_interface()
414
+
415
+ # Flask routes
416
+ @app.route('/')
417
+ def home():
418
+ return jsonify(MCP_INFO)
419
 
420
+ # Start both Flask and Gradio
421
+ if __name__ == '__main__':
422
+ # Start Gradio in a separate thread
423
+ def run_gradio():
424
+ gradio_demo.launch(
425
+ server_name="0.0.0.0",
426
+ server_port=7860,
427
+ share=False,
428
+ quiet=True
429
+ )
430
+
431
+ # Start Flask in main thread
432
+ threading.Thread(target=run_gradio, daemon=True).start()
433
+ app.run(host='0.0.0.0', port=3001, debug=False)
requirements.txt CHANGED
@@ -1,4 +1,7 @@
1
  flask==2.3.3
2
  flask-cors==4.0.0
3
  requests==2.31.0
4
- gunicorn==21.2.0
 
 
 
 
1
  flask==2.3.3
2
  flask-cors==4.0.0
3
  requests==2.31.0
4
+ gunicorn==21.2.0
5
+ gradio==4.44.0
6
+ pandas==2.1.4
7
+ plotly==5.17.0