tradingview-mcp 1.0.0__py3-none-any.whl
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.
- tradingview_mcp/__init__.py +14 -0
- tradingview_mcp/column.py +231 -0
- tradingview_mcp/constants.py +425 -0
- tradingview_mcp/models.py +154 -0
- tradingview_mcp/query.py +367 -0
- tradingview_mcp/scanner.py +256 -0
- tradingview_mcp/server.py +1361 -0
- tradingview_mcp/utils.py +382 -0
- tradingview_mcp-1.0.0.dist-info/METADATA +182 -0
- tradingview_mcp-1.0.0.dist-info/RECORD +13 -0
- tradingview_mcp-1.0.0.dist-info/WHEEL +4 -0
- tradingview_mcp-1.0.0.dist-info/entry_points.txt +2 -0
- tradingview_mcp-1.0.0.dist-info/licenses/LICENSE +23 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TradingView MCP Server - A comprehensive MCP server for TradingView market screening.
|
|
3
|
+
|
|
4
|
+
This package provides:
|
|
5
|
+
- Direct TradingView API integration for market screening
|
|
6
|
+
- SQL-like query interface for building complex filters
|
|
7
|
+
- MCP tools for market analysis and technical indicators
|
|
8
|
+
- Support for 76+ markets including crypto, stocks, forex, and futures
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from tradingview_mcp.server import mcp, main
|
|
12
|
+
|
|
13
|
+
__version__ = "1.0.0"
|
|
14
|
+
__all__ = ["mcp", "main"]
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Column class for building filter expressions.
|
|
3
|
+
|
|
4
|
+
Provides a SQL-like interface for creating WHERE clauses.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING, Iterable, Optional
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from tradingview_mcp.models import FilterOperationDict
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Column:
|
|
16
|
+
"""
|
|
17
|
+
A Column object represents a field in the TradingView stock screener.
|
|
18
|
+
|
|
19
|
+
Used in SELECT queries and WHERE queries with the Query object.
|
|
20
|
+
Supports all comparison operations: <, <=, >, >=, ==, !=
|
|
21
|
+
Plus methods like between(), isin(), crosses(), etc.
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
>>> Column('close') > 2.5
|
|
25
|
+
>>> Column('close').between(2.5, 15)
|
|
26
|
+
>>> Column('high') > Column('VWAP')
|
|
27
|
+
>>> Column('type').isin(['stock', 'fund'])
|
|
28
|
+
>>> Column('description').like('apple')
|
|
29
|
+
>>> Column('RSI') < 30
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, name: str) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Create a Column object from a column name.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
name: The column/field name (e.g., 'close', 'RSI', 'volume')
|
|
38
|
+
"""
|
|
39
|
+
self.name = name
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def _extract_name(obj: "Column | str | int | float") -> str | int | float:
|
|
43
|
+
"""Extract the name from a Column or return the value as-is."""
|
|
44
|
+
if isinstance(obj, Column):
|
|
45
|
+
return obj.name
|
|
46
|
+
return obj
|
|
47
|
+
|
|
48
|
+
def __gt__(self, other: "Column | str | int | float") -> "FilterOperationDict":
|
|
49
|
+
"""Greater than comparison."""
|
|
50
|
+
return {"left": self.name, "operation": "greater", "right": self._extract_name(other)}
|
|
51
|
+
|
|
52
|
+
def __ge__(self, other: "Column | str | int | float") -> "FilterOperationDict":
|
|
53
|
+
"""Greater than or equal comparison."""
|
|
54
|
+
return {"left": self.name, "operation": "egreater", "right": self._extract_name(other)}
|
|
55
|
+
|
|
56
|
+
def __lt__(self, other: "Column | str | int | float") -> "FilterOperationDict":
|
|
57
|
+
"""Less than comparison."""
|
|
58
|
+
return {"left": self.name, "operation": "less", "right": self._extract_name(other)}
|
|
59
|
+
|
|
60
|
+
def __le__(self, other: "Column | str | int | float") -> "FilterOperationDict":
|
|
61
|
+
"""Less than or equal comparison."""
|
|
62
|
+
return {"left": self.name, "operation": "eless", "right": self._extract_name(other)}
|
|
63
|
+
|
|
64
|
+
def __eq__(self, other: "Column | str | int | float") -> "FilterOperationDict": # type: ignore[override]
|
|
65
|
+
"""Equal comparison."""
|
|
66
|
+
return {"left": self.name, "operation": "equal", "right": self._extract_name(other)}
|
|
67
|
+
|
|
68
|
+
def __ne__(self, other: "Column | str | int | float") -> "FilterOperationDict": # type: ignore[override]
|
|
69
|
+
"""Not equal comparison."""
|
|
70
|
+
return {"left": self.name, "operation": "nequal", "right": self._extract_name(other)}
|
|
71
|
+
|
|
72
|
+
def crosses(self, other: "Column | str") -> "FilterOperationDict":
|
|
73
|
+
"""Check if column crosses another column/value."""
|
|
74
|
+
return {"left": self.name, "operation": "crosses", "right": self._extract_name(other)}
|
|
75
|
+
|
|
76
|
+
def crosses_above(self, other: "Column | str") -> "FilterOperationDict":
|
|
77
|
+
"""Check if column crosses above another column/value."""
|
|
78
|
+
return {"left": self.name, "operation": "crosses_above", "right": self._extract_name(other)}
|
|
79
|
+
|
|
80
|
+
def crosses_below(self, other: "Column | str") -> "FilterOperationDict":
|
|
81
|
+
"""Check if column crosses below another column/value."""
|
|
82
|
+
return {"left": self.name, "operation": "crosses_below", "right": self._extract_name(other)}
|
|
83
|
+
|
|
84
|
+
def between(
|
|
85
|
+
self, left: "Column | str | int | float", right: "Column | str | int | float"
|
|
86
|
+
) -> "FilterOperationDict":
|
|
87
|
+
"""
|
|
88
|
+
Check if value is between two bounds (inclusive).
|
|
89
|
+
|
|
90
|
+
Examples:
|
|
91
|
+
>>> Column('close').between(10, 50)
|
|
92
|
+
>>> Column('close').between(Column('EMA5'), Column('EMA20'))
|
|
93
|
+
"""
|
|
94
|
+
return {
|
|
95
|
+
"left": self.name,
|
|
96
|
+
"operation": "in_range",
|
|
97
|
+
"right": [self._extract_name(left), self._extract_name(right)],
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
def not_between(
|
|
101
|
+
self, left: "Column | str | int | float", right: "Column | str | int | float"
|
|
102
|
+
) -> "FilterOperationDict":
|
|
103
|
+
"""Check if value is NOT between two bounds."""
|
|
104
|
+
return {
|
|
105
|
+
"left": self.name,
|
|
106
|
+
"operation": "not_in_range",
|
|
107
|
+
"right": [self._extract_name(left), self._extract_name(right)],
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
def isin(self, values: Iterable) -> "FilterOperationDict":
|
|
111
|
+
"""
|
|
112
|
+
Check if value is in a list of values.
|
|
113
|
+
|
|
114
|
+
Examples:
|
|
115
|
+
>>> Column('type').isin(['stock', 'fund'])
|
|
116
|
+
>>> Column('exchange').isin(['NASDAQ', 'NYSE'])
|
|
117
|
+
"""
|
|
118
|
+
return {"left": self.name, "operation": "in_range", "right": list(values)}
|
|
119
|
+
|
|
120
|
+
def not_in(self, values: Iterable) -> "FilterOperationDict":
|
|
121
|
+
"""Check if value is NOT in a list of values."""
|
|
122
|
+
return {"left": self.name, "operation": "not_in_range", "right": list(values)}
|
|
123
|
+
|
|
124
|
+
def has(self, values: str | list[str]) -> "FilterOperationDict":
|
|
125
|
+
"""
|
|
126
|
+
Check if field contains any of the values (for set-type fields).
|
|
127
|
+
|
|
128
|
+
Examples:
|
|
129
|
+
>>> Column('typespecs').has(['common', 'preferred'])
|
|
130
|
+
"""
|
|
131
|
+
return {"left": self.name, "operation": "has", "right": values}
|
|
132
|
+
|
|
133
|
+
def has_none_of(self, values: str | list[str]) -> "FilterOperationDict":
|
|
134
|
+
"""Check if field contains NONE of the values (for set-type fields)."""
|
|
135
|
+
return {"left": self.name, "operation": "has_none_of", "right": values}
|
|
136
|
+
|
|
137
|
+
def in_day_range(self, a: int, b: int) -> "FilterOperationDict":
|
|
138
|
+
"""Check if date is within day range (0 = today)."""
|
|
139
|
+
return {"left": self.name, "operation": "in_day_range", "right": [a, b]}
|
|
140
|
+
|
|
141
|
+
def in_week_range(self, a: int, b: int) -> "FilterOperationDict":
|
|
142
|
+
"""Check if date is within week range."""
|
|
143
|
+
return {"left": self.name, "operation": "in_week_range", "right": [a, b]}
|
|
144
|
+
|
|
145
|
+
def in_month_range(self, a: int, b: int) -> "FilterOperationDict":
|
|
146
|
+
"""Check if date is within month range."""
|
|
147
|
+
return {"left": self.name, "operation": "in_month_range", "right": [a, b]}
|
|
148
|
+
|
|
149
|
+
def above_pct(self, column: "Column | str", pct: float) -> "FilterOperationDict":
|
|
150
|
+
"""
|
|
151
|
+
Check if value is above another column by a percentage.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
column: Reference column
|
|
155
|
+
pct: Percentage multiplier (1.03 = 3% above)
|
|
156
|
+
|
|
157
|
+
Examples:
|
|
158
|
+
>>> Column('close').above_pct('VWAP', 1.03) # 3% above VWAP
|
|
159
|
+
>>> Column('close').above_pct('price_52_week_low', 2.5) # 150% above 52w low
|
|
160
|
+
"""
|
|
161
|
+
return {
|
|
162
|
+
"left": self.name,
|
|
163
|
+
"operation": "above%",
|
|
164
|
+
"right": [self._extract_name(column), pct],
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
def below_pct(self, column: "Column | str", pct: float) -> "FilterOperationDict":
|
|
168
|
+
"""
|
|
169
|
+
Check if value is below another column by a percentage.
|
|
170
|
+
|
|
171
|
+
Examples:
|
|
172
|
+
>>> Column('close').below_pct('VWAP', 1.03) # 3% below VWAP
|
|
173
|
+
"""
|
|
174
|
+
return {
|
|
175
|
+
"left": self.name,
|
|
176
|
+
"operation": "below%",
|
|
177
|
+
"right": [self._extract_name(column), pct],
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
def between_pct(
|
|
181
|
+
self, column: "Column | str", pct1: float, pct2: Optional[float] = None
|
|
182
|
+
) -> "FilterOperationDict":
|
|
183
|
+
"""
|
|
184
|
+
Check if percentage change relative to column is within range.
|
|
185
|
+
|
|
186
|
+
Examples:
|
|
187
|
+
>>> Column('close').between_pct('EMA200', 1.2, 1.5) # 20-50% above EMA200
|
|
188
|
+
"""
|
|
189
|
+
return {
|
|
190
|
+
"left": self.name,
|
|
191
|
+
"operation": "in_range%",
|
|
192
|
+
"right": [self._extract_name(column), pct1, pct2],
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
def not_between_pct(
|
|
196
|
+
self, column: "Column | str", pct1: float, pct2: Optional[float] = None
|
|
197
|
+
) -> "FilterOperationDict":
|
|
198
|
+
"""Check if percentage change relative to column is NOT within range."""
|
|
199
|
+
return {
|
|
200
|
+
"left": self.name,
|
|
201
|
+
"operation": "not_in_range%",
|
|
202
|
+
"right": [self._extract_name(column), pct1, pct2],
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
def like(self, pattern: str) -> "FilterOperationDict":
|
|
206
|
+
"""
|
|
207
|
+
Pattern matching (case-insensitive LIKE '%pattern%').
|
|
208
|
+
|
|
209
|
+
Examples:
|
|
210
|
+
>>> Column('description').like('apple')
|
|
211
|
+
"""
|
|
212
|
+
return {"left": self.name, "operation": "match", "right": pattern}
|
|
213
|
+
|
|
214
|
+
def not_like(self, pattern: str) -> "FilterOperationDict":
|
|
215
|
+
"""Negative pattern matching."""
|
|
216
|
+
return {"left": self.name, "operation": "nmatch", "right": pattern}
|
|
217
|
+
|
|
218
|
+
def empty(self) -> "FilterOperationDict":
|
|
219
|
+
"""Check if field is empty/null."""
|
|
220
|
+
return {"left": self.name, "operation": "empty", "right": None}
|
|
221
|
+
|
|
222
|
+
def not_empty(self) -> "FilterOperationDict":
|
|
223
|
+
"""Check if field is NOT empty/null."""
|
|
224
|
+
return {"left": self.name, "operation": "nempty", "right": None}
|
|
225
|
+
|
|
226
|
+
def __repr__(self) -> str:
|
|
227
|
+
return f"Column({self.name!r})"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# Convenience alias
|
|
231
|
+
col = Column
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Constants for TradingView Screener integration.
|
|
3
|
+
|
|
4
|
+
Contains API URLs, headers, supported markets, and column definitions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
# API Configuration
|
|
10
|
+
URL = "https://scanner.tradingview.com/{market}/scan"
|
|
11
|
+
|
|
12
|
+
HEADERS = {
|
|
13
|
+
"authority": "scanner.tradingview.com",
|
|
14
|
+
"sec-ch-ua": '" Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"',
|
|
15
|
+
"accept": "text/plain, */*; q=0.01",
|
|
16
|
+
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
17
|
+
"sec-ch-ua-mobile": "?0",
|
|
18
|
+
"user-agent": (
|
|
19
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
|
20
|
+
"(KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"
|
|
21
|
+
),
|
|
22
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
23
|
+
"origin": "https://www.tradingview.com",
|
|
24
|
+
"sec-fetch-site": "same-site",
|
|
25
|
+
"sec-fetch-mode": "cors",
|
|
26
|
+
"sec-fetch-dest": "empty",
|
|
27
|
+
"referer": "https://www.tradingview.com/",
|
|
28
|
+
"accept-language": "en-US,en;q=0.9",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
DEFAULT_RANGE = [0, 50]
|
|
32
|
+
|
|
33
|
+
# Supported Markets (76 total)
|
|
34
|
+
MARKETS = {
|
|
35
|
+
# Financial Instruments
|
|
36
|
+
"bonds",
|
|
37
|
+
"cfd",
|
|
38
|
+
"coin",
|
|
39
|
+
"crypto",
|
|
40
|
+
"economics2",
|
|
41
|
+
"forex",
|
|
42
|
+
"futures",
|
|
43
|
+
"options",
|
|
44
|
+
# Countries (67)
|
|
45
|
+
"america",
|
|
46
|
+
"argentina",
|
|
47
|
+
"australia",
|
|
48
|
+
"austria",
|
|
49
|
+
"bahrain",
|
|
50
|
+
"bangladesh",
|
|
51
|
+
"belgium",
|
|
52
|
+
"brazil",
|
|
53
|
+
"canada",
|
|
54
|
+
"chile",
|
|
55
|
+
"china",
|
|
56
|
+
"colombia",
|
|
57
|
+
"cyprus",
|
|
58
|
+
"czech",
|
|
59
|
+
"denmark",
|
|
60
|
+
"egypt",
|
|
61
|
+
"estonia",
|
|
62
|
+
"finland",
|
|
63
|
+
"france",
|
|
64
|
+
"germany",
|
|
65
|
+
"greece",
|
|
66
|
+
"hongkong",
|
|
67
|
+
"hungary",
|
|
68
|
+
"iceland",
|
|
69
|
+
"india",
|
|
70
|
+
"indonesia",
|
|
71
|
+
"israel",
|
|
72
|
+
"italy",
|
|
73
|
+
"japan",
|
|
74
|
+
"kenya",
|
|
75
|
+
"korea",
|
|
76
|
+
"ksa",
|
|
77
|
+
"kuwait",
|
|
78
|
+
"latvia",
|
|
79
|
+
"lithuania",
|
|
80
|
+
"luxembourg",
|
|
81
|
+
"malaysia",
|
|
82
|
+
"mexico",
|
|
83
|
+
"morocco",
|
|
84
|
+
"netherlands",
|
|
85
|
+
"newzealand",
|
|
86
|
+
"nigeria",
|
|
87
|
+
"norway",
|
|
88
|
+
"pakistan",
|
|
89
|
+
"peru",
|
|
90
|
+
"philippines",
|
|
91
|
+
"poland",
|
|
92
|
+
"portugal",
|
|
93
|
+
"qatar",
|
|
94
|
+
"romania",
|
|
95
|
+
"rsa",
|
|
96
|
+
"russia",
|
|
97
|
+
"serbia",
|
|
98
|
+
"singapore",
|
|
99
|
+
"slovakia",
|
|
100
|
+
"spain",
|
|
101
|
+
"srilanka",
|
|
102
|
+
"sweden",
|
|
103
|
+
"switzerland",
|
|
104
|
+
"taiwan",
|
|
105
|
+
"thailand",
|
|
106
|
+
"tunisia",
|
|
107
|
+
"turkey",
|
|
108
|
+
"uae",
|
|
109
|
+
"uk",
|
|
110
|
+
"venezuela",
|
|
111
|
+
"vietnam",
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# Exchange to Screener Mapping
|
|
115
|
+
EXCHANGE_SCREENER = {
|
|
116
|
+
# Crypto Exchanges
|
|
117
|
+
"all": "crypto",
|
|
118
|
+
"huobi": "crypto",
|
|
119
|
+
"kucoin": "crypto",
|
|
120
|
+
"coinbase": "crypto",
|
|
121
|
+
"gateio": "crypto",
|
|
122
|
+
"binance": "crypto",
|
|
123
|
+
"bitfinex": "crypto",
|
|
124
|
+
"bitget": "crypto",
|
|
125
|
+
"bybit": "crypto",
|
|
126
|
+
"okx": "crypto",
|
|
127
|
+
"kraken": "crypto",
|
|
128
|
+
"bitstamp": "crypto",
|
|
129
|
+
"gemini": "crypto",
|
|
130
|
+
"poloniex": "crypto",
|
|
131
|
+
# Stock Exchanges
|
|
132
|
+
"bist": "turkey",
|
|
133
|
+
"nasdaq": "america",
|
|
134
|
+
"nyse": "america",
|
|
135
|
+
"amex": "america",
|
|
136
|
+
# Malaysia
|
|
137
|
+
"bursa": "malaysia",
|
|
138
|
+
"myx": "malaysia",
|
|
139
|
+
"klse": "malaysia",
|
|
140
|
+
"ace": "malaysia",
|
|
141
|
+
"leap": "malaysia",
|
|
142
|
+
# Hong Kong
|
|
143
|
+
"hkex": "hongkong",
|
|
144
|
+
"hk": "hongkong",
|
|
145
|
+
"hsi": "hongkong",
|
|
146
|
+
# Others
|
|
147
|
+
"lse": "uk",
|
|
148
|
+
"tsx": "canada",
|
|
149
|
+
"asx": "australia",
|
|
150
|
+
"nse": "india",
|
|
151
|
+
"bse": "india",
|
|
152
|
+
"sse": "china",
|
|
153
|
+
"szse": "china",
|
|
154
|
+
"tse": "japan",
|
|
155
|
+
"krx": "korea",
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# Allowed Timeframes
|
|
159
|
+
ALLOWED_TIMEFRAMES = {"1m", "5m", "15m", "30m", "1h", "2h", "4h", "1D", "1W", "1M"}
|
|
160
|
+
|
|
161
|
+
# Timeframe to TradingView Resolution
|
|
162
|
+
TIMEFRAME_MAP = {
|
|
163
|
+
"1m": "1",
|
|
164
|
+
"5m": "5",
|
|
165
|
+
"15m": "15",
|
|
166
|
+
"30m": "30",
|
|
167
|
+
"1h": "60",
|
|
168
|
+
"2h": "120",
|
|
169
|
+
"4h": "240",
|
|
170
|
+
"1D": "1D",
|
|
171
|
+
"1W": "1W",
|
|
172
|
+
"1M": "1M",
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# Column Definitions - Human readable names to API column names
|
|
176
|
+
COLUMNS = {
|
|
177
|
+
# Technical Indicators
|
|
178
|
+
"Average Day Range (14)": "ADR",
|
|
179
|
+
"Average Directional Index (14)": "ADX",
|
|
180
|
+
"Positive Directional Indicator (14)": "ADX+DI",
|
|
181
|
+
"Negative Directional Indicator (14)": "ADX-DI",
|
|
182
|
+
"Awesome Oscillator": "AO",
|
|
183
|
+
"Average True Range (14)": "ATR",
|
|
184
|
+
"Aroon Down (14)": "Aroon.Down",
|
|
185
|
+
"Aroon Up (14)": "Aroon.Up",
|
|
186
|
+
"Bollinger Lower Band (20)": "BB.lower",
|
|
187
|
+
"Bollinger Upper Band (20)": "BB.upper",
|
|
188
|
+
"Bull Bear Power": "BBPower",
|
|
189
|
+
"Commodity Channel Index (20)": "CCI20",
|
|
190
|
+
"Chaikin Money Flow (20)": "ChaikinMoneyFlow",
|
|
191
|
+
"Donchian Channels Lower Band (20)": "DonchCh20.Lower",
|
|
192
|
+
"Donchian Channels Upper Band (20)": "DonchCh20.Upper",
|
|
193
|
+
# EMAs
|
|
194
|
+
"Exponential Moving Average (5)": "EMA5",
|
|
195
|
+
"Exponential Moving Average (10)": "EMA10",
|
|
196
|
+
"Exponential Moving Average (20)": "EMA20",
|
|
197
|
+
"Exponential Moving Average (30)": "EMA30",
|
|
198
|
+
"Exponential Moving Average (50)": "EMA50",
|
|
199
|
+
"Exponential Moving Average (100)": "EMA100",
|
|
200
|
+
"Exponential Moving Average (200)": "EMA200",
|
|
201
|
+
# Highs/Lows
|
|
202
|
+
"1-Month High": "High.1M",
|
|
203
|
+
"3-Month High": "High.3M",
|
|
204
|
+
"6-Month High": "High.6M",
|
|
205
|
+
"All Time High": "High.All",
|
|
206
|
+
"1-Month Low": "Low.1M",
|
|
207
|
+
"3-Month Low": "Low.3M",
|
|
208
|
+
"6-Month Low": "Low.6M",
|
|
209
|
+
"All Time Low": "Low.All",
|
|
210
|
+
# Hull MA
|
|
211
|
+
"Hull Moving Average (9)": "HullMA9",
|
|
212
|
+
# Ichimoku
|
|
213
|
+
"Ichimoku Base Line (9, 26, 52, 26)": "Ichimoku.BLine",
|
|
214
|
+
"Ichimoku Conversion Line (9, 26, 52, 26)": "Ichimoku.CLine",
|
|
215
|
+
"Ichimoku Leading Span A (9, 26, 52, 26)": "Ichimoku.Lead1",
|
|
216
|
+
"Ichimoku Leading Span B (9, 26, 52, 26)": "Ichimoku.Lead2",
|
|
217
|
+
# Keltner Channels
|
|
218
|
+
"Keltner Channels Lower Band (20)": "KltChnl.lower",
|
|
219
|
+
"Keltner Channels Upper Band (20)": "KltChnl.upper",
|
|
220
|
+
# MACD
|
|
221
|
+
"MACD Level (12, 26)": "MACD.macd",
|
|
222
|
+
"MACD Signal (12, 26)": "MACD.signal",
|
|
223
|
+
# Other Indicators
|
|
224
|
+
"Momentum (10)": "Mom",
|
|
225
|
+
"Money Flow (14)": "MoneyFlow",
|
|
226
|
+
"Parabolic SAR": "P.SAR",
|
|
227
|
+
# Performance
|
|
228
|
+
"Monthly Performance": "Perf.1M",
|
|
229
|
+
"3-Month Performance": "Perf.3M",
|
|
230
|
+
"5Y Performance": "Perf.5Y",
|
|
231
|
+
"6-Month Performance": "Perf.6M",
|
|
232
|
+
"All Time Performance": "Perf.All",
|
|
233
|
+
"Weekly Performance": "Perf.W",
|
|
234
|
+
"Yearly Performance": "Perf.Y",
|
|
235
|
+
"YTD Performance": "Perf.YTD",
|
|
236
|
+
# Pivot Points (Classic)
|
|
237
|
+
"Pivot Classic P": "Pivot.M.Classic.Middle",
|
|
238
|
+
"Pivot Classic R1": "Pivot.M.Classic.R1",
|
|
239
|
+
"Pivot Classic R2": "Pivot.M.Classic.R2",
|
|
240
|
+
"Pivot Classic R3": "Pivot.M.Classic.R3",
|
|
241
|
+
"Pivot Classic S1": "Pivot.M.Classic.S1",
|
|
242
|
+
"Pivot Classic S2": "Pivot.M.Classic.S2",
|
|
243
|
+
"Pivot Classic S3": "Pivot.M.Classic.S3",
|
|
244
|
+
# Pivot Points (Fibonacci)
|
|
245
|
+
"Pivot Fibonacci P": "Pivot.M.Fibonacci.Middle",
|
|
246
|
+
"Pivot Fibonacci R1": "Pivot.M.Fibonacci.R1",
|
|
247
|
+
"Pivot Fibonacci R2": "Pivot.M.Fibonacci.R2",
|
|
248
|
+
"Pivot Fibonacci R3": "Pivot.M.Fibonacci.R3",
|
|
249
|
+
"Pivot Fibonacci S1": "Pivot.M.Fibonacci.S1",
|
|
250
|
+
"Pivot Fibonacci S2": "Pivot.M.Fibonacci.S2",
|
|
251
|
+
"Pivot Fibonacci S3": "Pivot.M.Fibonacci.S3",
|
|
252
|
+
# Rate of Change
|
|
253
|
+
"Rate Of Change (9)": "ROC",
|
|
254
|
+
# RSI
|
|
255
|
+
"Relative Strength Index (14)": "RSI",
|
|
256
|
+
"Relative Strength Index (7)": "RSI7",
|
|
257
|
+
# Recommendations
|
|
258
|
+
"Technical Rating": "Recommend.All",
|
|
259
|
+
"Moving Averages Rating": "Recommend.MA",
|
|
260
|
+
"Oscillators Rating": "Recommend.Other",
|
|
261
|
+
# SMAs
|
|
262
|
+
"Simple Moving Average (5)": "SMA5",
|
|
263
|
+
"Simple Moving Average (10)": "SMA10",
|
|
264
|
+
"Simple Moving Average (20)": "SMA20",
|
|
265
|
+
"Simple Moving Average (30)": "SMA30",
|
|
266
|
+
"Simple Moving Average (50)": "SMA50",
|
|
267
|
+
"Simple Moving Average (100)": "SMA100",
|
|
268
|
+
"Simple Moving Average (200)": "SMA200",
|
|
269
|
+
# Stochastic
|
|
270
|
+
"Stochastic %D (14, 3, 3)": "Stoch.D",
|
|
271
|
+
"Stochastic %K (14, 3, 3)": "Stoch.K",
|
|
272
|
+
"Stochastic RSI Slow (3, 3, 14, 14)": "Stoch.RSI.D",
|
|
273
|
+
"Stochastic RSI Fast (3, 3, 14, 14)": "Stoch.RSI.K",
|
|
274
|
+
# Other
|
|
275
|
+
"Ultimate Oscillator (7, 14, 28)": "UO",
|
|
276
|
+
"Volume Weighted Average Price": "VWAP",
|
|
277
|
+
"Volume Weighted Moving Average (20)": "VWMA",
|
|
278
|
+
"Volume*Price": "Value.Traded",
|
|
279
|
+
"Volatility": "Volatility.D",
|
|
280
|
+
"Volatility Month": "Volatility.M",
|
|
281
|
+
"Volatility Week": "Volatility.W",
|
|
282
|
+
"Williams Percent Range (14)": "W.R",
|
|
283
|
+
# Fundamentals
|
|
284
|
+
"Net Margin (TTM)": "after_tax_margin",
|
|
285
|
+
"Average Volume (10 day)": "average_volume_10d_calc",
|
|
286
|
+
"Average Volume (30 day)": "average_volume_30d_calc",
|
|
287
|
+
"Average Volume (60 day)": "average_volume_60d_calc",
|
|
288
|
+
"Average Volume (90 day)": "average_volume_90d_calc",
|
|
289
|
+
"Basic EPS (FY)": "basic_eps_net_income",
|
|
290
|
+
"1-Year Beta": "beta_1_year",
|
|
291
|
+
"Cash & Equivalents (MRQ)": "cash_n_equivalents_fq",
|
|
292
|
+
"Cash & Equivalents (FY)": "cash_n_equivalents_fy",
|
|
293
|
+
# Price/Change
|
|
294
|
+
"Change %": "change",
|
|
295
|
+
"Change": "change_abs",
|
|
296
|
+
"Change from Open %": "change_from_open",
|
|
297
|
+
"Change from Open": "change_from_open_abs",
|
|
298
|
+
"Price": "close",
|
|
299
|
+
"Country": "country",
|
|
300
|
+
"Currency": "currency",
|
|
301
|
+
# Debt
|
|
302
|
+
"Current Ratio (MRQ)": "current_ratio",
|
|
303
|
+
"Debt to Equity Ratio (MRQ)": "debt_to_equity",
|
|
304
|
+
# Dividends
|
|
305
|
+
"Dividend Yield Forward": "dividend_yield_recent",
|
|
306
|
+
"Dividends Paid (FY)": "dividends_paid",
|
|
307
|
+
"Dividends per Share (MRQ)": "dividends_per_share_fq",
|
|
308
|
+
# EPS
|
|
309
|
+
"Basic EPS (TTM)": "earnings_per_share_basic_ttm",
|
|
310
|
+
"EPS Diluted (TTM)": "earnings_per_share_diluted_ttm",
|
|
311
|
+
"EPS Forecast (MRQ)": "earnings_per_share_forecast_next_fq",
|
|
312
|
+
"EPS Diluted (MRQ)": "earnings_per_share_fq",
|
|
313
|
+
# Earnings
|
|
314
|
+
"Recent Earnings Date": "earnings_release_date",
|
|
315
|
+
"Upcoming Earnings Date": "earnings_release_next_date",
|
|
316
|
+
"EBITDA (TTM)": "ebitda",
|
|
317
|
+
# Enterprise Value
|
|
318
|
+
"Enterprise Value/EBITDA (TTM)": "enterprise_value_ebitda_ttm",
|
|
319
|
+
"Enterprise Value (MRQ)": "enterprise_value_fq",
|
|
320
|
+
# Basic Info
|
|
321
|
+
"Exchange": "exchange",
|
|
322
|
+
"Shares Float": "float_shares_outstanding",
|
|
323
|
+
# Gap
|
|
324
|
+
"Gap %": "gap",
|
|
325
|
+
# Margins
|
|
326
|
+
"Gross Margin (TTM)": "gross_margin",
|
|
327
|
+
"Gross Profit (FY)": "gross_profit",
|
|
328
|
+
# OHLCV
|
|
329
|
+
"High": "high",
|
|
330
|
+
"Low": "low",
|
|
331
|
+
"Open": "open",
|
|
332
|
+
"Volume": "volume",
|
|
333
|
+
# Identification
|
|
334
|
+
"Industry": "industry",
|
|
335
|
+
"Sector": "sector",
|
|
336
|
+
"Market": "market",
|
|
337
|
+
"Name": "name",
|
|
338
|
+
"Description": "description",
|
|
339
|
+
"Type": "type",
|
|
340
|
+
# Market Cap
|
|
341
|
+
"Market Capitalization": "market_cap_basic",
|
|
342
|
+
# Net Income
|
|
343
|
+
"Net Debt (MRQ)": "net_debt",
|
|
344
|
+
"Net Income (FY)": "net_income",
|
|
345
|
+
# P/E Ratios
|
|
346
|
+
"Price to Earnings Ratio (TTM)": "price_earnings_ttm",
|
|
347
|
+
"Price to Book (MRQ)": "price_book_fq",
|
|
348
|
+
"Price to Sales (FY)": "price_sales_ratio",
|
|
349
|
+
"Price to Free Cash Flow (TTM)": "price_free_cash_flow_ttm",
|
|
350
|
+
# 52 Week
|
|
351
|
+
"52 Week High": "price_52_week_high",
|
|
352
|
+
"52 Week Low": "price_52_week_low",
|
|
353
|
+
# Pre/Post Market
|
|
354
|
+
"Pre-market Change %": "premarket_change",
|
|
355
|
+
"Pre-market Change": "premarket_change_abs",
|
|
356
|
+
"Pre-market Close": "premarket_close",
|
|
357
|
+
"Pre-market High": "premarket_high",
|
|
358
|
+
"Pre-market Low": "premarket_low",
|
|
359
|
+
"Pre-market Open": "premarket_open",
|
|
360
|
+
"Pre-market Volume": "premarket_volume",
|
|
361
|
+
"Pre-market Gap %": "premarket_gap",
|
|
362
|
+
"Post-market Change %": "postmarket_change",
|
|
363
|
+
"Post-market Change": "postmarket_change_abs",
|
|
364
|
+
"Post-market Close": "postmarket_close",
|
|
365
|
+
"Post-market High": "postmarket_high",
|
|
366
|
+
"Post-market Low": "postmarket_low",
|
|
367
|
+
"Post-market Open": "postmarket_open",
|
|
368
|
+
"Post-market Volume": "postmarket_volume",
|
|
369
|
+
# Relative Volume
|
|
370
|
+
"Relative Volume": "relative_volume_10d_calc",
|
|
371
|
+
"Relative Volume at Time": "relative_volume_intraday|5",
|
|
372
|
+
# Returns
|
|
373
|
+
"Return on Assets (TTM)": "return_on_assets",
|
|
374
|
+
"Return on Equity (TTM)": "return_on_equity",
|
|
375
|
+
"Return on Invested Capital (TTM)": "return_on_invested_capital",
|
|
376
|
+
# Revenue
|
|
377
|
+
"Total Revenue (FY)": "total_revenue",
|
|
378
|
+
"Revenue per Employee (FY)": "revenue_per_employee",
|
|
379
|
+
# Shares
|
|
380
|
+
"Total Shares Outstanding": "total_shares_outstanding_fundamental",
|
|
381
|
+
# Assets
|
|
382
|
+
"Total Assets (MRQ)": "total_assets",
|
|
383
|
+
"Total Current Assets (MRQ)": "total_current_assets",
|
|
384
|
+
"Total Debt (MRQ)": "total_debt",
|
|
385
|
+
"Total Liabilities (MRQ)": "total_liabilities_fq",
|
|
386
|
+
# Crypto specific
|
|
387
|
+
"24h Volume": "24h_vol|5",
|
|
388
|
+
"24h Change %": "24h_close_change|5",
|
|
389
|
+
"24h Volume Change %": "24h_vol_change|5",
|
|
390
|
+
"24h Volume to Market Cap": "24h_vol_to_market_cap",
|
|
391
|
+
# Quick ratio
|
|
392
|
+
"Quick Ratio (MRQ)": "quick_ratio",
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
# Default columns for different query types
|
|
396
|
+
DEFAULT_COLUMNS = ["name", "close", "volume", "market_cap_basic"]
|
|
397
|
+
|
|
398
|
+
PREMARKET_COLUMNS = ["name", "close", "volume", "premarket_change", "premarket_change_abs", "premarket_volume"]
|
|
399
|
+
|
|
400
|
+
POSTMARKET_COLUMNS = ["name", "close", "volume", "postmarket_change", "postmarket_change_abs", "postmarket_volume"]
|
|
401
|
+
|
|
402
|
+
CRYPTO_COLUMNS = ["name", "close", "volume", "change", "market_cap_basic", "24h_vol|5"]
|
|
403
|
+
|
|
404
|
+
TECHNICAL_COLUMNS = [
|
|
405
|
+
"name",
|
|
406
|
+
"close",
|
|
407
|
+
"volume",
|
|
408
|
+
"change",
|
|
409
|
+
"RSI",
|
|
410
|
+
"MACD.macd",
|
|
411
|
+
"MACD.signal",
|
|
412
|
+
"BB.upper",
|
|
413
|
+
"BB.lower",
|
|
414
|
+
"SMA20",
|
|
415
|
+
"EMA50",
|
|
416
|
+
"EMA200",
|
|
417
|
+
"ADX",
|
|
418
|
+
"Stoch.K",
|
|
419
|
+
"Stoch.D",
|
|
420
|
+
]
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def get_column_name(name: str) -> str:
|
|
424
|
+
"""Get the API column name from a human-readable name or return as-is."""
|
|
425
|
+
return COLUMNS.get(name, name)
|