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.
@@ -0,0 +1,256 @@
1
+ """
2
+ Pre-built scanner configurations for common use cases.
3
+
4
+ Provides ready-to-use Query objects for popular screening scenarios.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from tradingview_mcp.column import Column
10
+ from tradingview_mcp.query import Query
11
+
12
+
13
+ DEFAULT_COLUMNS = ["name", "close", "volume", "market_cap_basic"]
14
+
15
+
16
+ class Scanner:
17
+ """
18
+ Collection of pre-built stock screeners.
19
+
20
+ Examples:
21
+ >>> Scanner.premarket_gainers.get_scanner_data()
22
+ >>> Scanner.top_volume.get_scanner_data()
23
+ >>> Scanner.oversold_rsi.get_scanner_data()
24
+ """
25
+
26
+ # Pre-market Scanners
27
+ premarket_gainers = (
28
+ Query()
29
+ .select(*DEFAULT_COLUMNS, "premarket_change", "premarket_change_abs", "premarket_volume")
30
+ .order_by("premarket_change", ascending=False)
31
+ )
32
+
33
+ premarket_losers = (
34
+ Query()
35
+ .select(*DEFAULT_COLUMNS, "premarket_change", "premarket_change_abs", "premarket_volume")
36
+ .order_by("premarket_change", ascending=True)
37
+ )
38
+
39
+ premarket_most_active = (
40
+ Query()
41
+ .select(*DEFAULT_COLUMNS, "premarket_change", "premarket_change_abs", "premarket_volume")
42
+ .order_by("premarket_volume", ascending=False)
43
+ )
44
+
45
+ premarket_gappers = (
46
+ Query()
47
+ .select(*DEFAULT_COLUMNS, "premarket_change", "premarket_change_abs", "premarket_volume", "premarket_gap")
48
+ .order_by("premarket_gap", ascending=False)
49
+ )
50
+
51
+ # Post-market Scanners
52
+ postmarket_gainers = (
53
+ Query()
54
+ .select(*DEFAULT_COLUMNS, "postmarket_change", "postmarket_change_abs", "postmarket_volume")
55
+ .order_by("postmarket_change", ascending=False)
56
+ )
57
+
58
+ postmarket_losers = (
59
+ Query()
60
+ .select(*DEFAULT_COLUMNS, "postmarket_change", "postmarket_change_abs", "postmarket_volume")
61
+ .order_by("postmarket_change", ascending=True)
62
+ )
63
+
64
+ postmarket_most_active = (
65
+ Query()
66
+ .select(*DEFAULT_COLUMNS, "postmarket_change", "postmarket_change_abs", "postmarket_volume")
67
+ .order_by("postmarket_volume", ascending=False)
68
+ )
69
+
70
+ # Volume Scanners
71
+ top_volume = (
72
+ Query()
73
+ .select(*DEFAULT_COLUMNS, "change", "relative_volume_10d_calc")
74
+ .order_by("volume", ascending=False)
75
+ )
76
+
77
+ unusual_volume = (
78
+ Query()
79
+ .select(*DEFAULT_COLUMNS, "change", "relative_volume_10d_calc")
80
+ .where(Column("relative_volume_10d_calc") > 2.0)
81
+ .order_by("relative_volume_10d_calc", ascending=False)
82
+ )
83
+
84
+ # Price Change Scanners
85
+ top_gainers = (
86
+ Query()
87
+ .select(*DEFAULT_COLUMNS, "change", "change_abs")
88
+ .where(Column("change") > 0)
89
+ .order_by("change", ascending=False)
90
+ )
91
+
92
+ top_losers = (
93
+ Query()
94
+ .select(*DEFAULT_COLUMNS, "change", "change_abs")
95
+ .where(Column("change") < 0)
96
+ .order_by("change", ascending=True)
97
+ )
98
+
99
+ # Technical Scanners
100
+ oversold_rsi = (
101
+ Query()
102
+ .select(*DEFAULT_COLUMNS, "change", "RSI", "RSI7")
103
+ .where(Column("RSI") < 30)
104
+ .order_by("RSI", ascending=True)
105
+ )
106
+
107
+ overbought_rsi = (
108
+ Query()
109
+ .select(*DEFAULT_COLUMNS, "change", "RSI", "RSI7")
110
+ .where(Column("RSI") > 70)
111
+ .order_by("RSI", ascending=False)
112
+ )
113
+
114
+ macd_bullish_crossover = (
115
+ Query()
116
+ .select(*DEFAULT_COLUMNS, "change", "MACD.macd", "MACD.signal")
117
+ .where(Column("MACD.macd").crosses_above(Column("MACD.signal")))
118
+ .order_by("change", ascending=False)
119
+ )
120
+
121
+ macd_bearish_crossover = (
122
+ Query()
123
+ .select(*DEFAULT_COLUMNS, "change", "MACD.macd", "MACD.signal")
124
+ .where(Column("MACD.macd").crosses_below(Column("MACD.signal")))
125
+ .order_by("change", ascending=True)
126
+ )
127
+
128
+ # 52-Week Scanners
129
+ near_52_week_high = (
130
+ Query()
131
+ .select(*DEFAULT_COLUMNS, "change", "price_52_week_high", "price_52_week_low")
132
+ .where(Column("close").above_pct("price_52_week_high", 0.95))
133
+ .order_by("change", ascending=False)
134
+ )
135
+
136
+ near_52_week_low = (
137
+ Query()
138
+ .select(*DEFAULT_COLUMNS, "change", "price_52_week_high", "price_52_week_low")
139
+ .where(Column("close").below_pct("price_52_week_low", 1.05))
140
+ .order_by("change", ascending=True)
141
+ )
142
+
143
+ # Bollinger Band Scanners
144
+ bb_squeeze = (
145
+ Query()
146
+ .select(*DEFAULT_COLUMNS, "change", "BB.upper", "BB.lower", "SMA20", "ATR")
147
+ .order_by("Volatility.D", ascending=True)
148
+ )
149
+
150
+ above_upper_bb = (
151
+ Query()
152
+ .select(*DEFAULT_COLUMNS, "change", "BB.upper", "BB.lower", "SMA20")
153
+ .where(Column("close") > Column("BB.upper"))
154
+ .order_by("change", ascending=False)
155
+ )
156
+
157
+ below_lower_bb = (
158
+ Query()
159
+ .select(*DEFAULT_COLUMNS, "change", "BB.upper", "BB.lower", "SMA20")
160
+ .where(Column("close") < Column("BB.lower"))
161
+ .order_by("change", ascending=True)
162
+ )
163
+
164
+ # Moving Average Scanners
165
+ golden_cross = (
166
+ Query()
167
+ .select(*DEFAULT_COLUMNS, "change", "SMA50", "SMA200")
168
+ .where(Column("SMA50").crosses_above(Column("SMA200")))
169
+ .order_by("volume", ascending=False)
170
+ )
171
+
172
+ death_cross = (
173
+ Query()
174
+ .select(*DEFAULT_COLUMNS, "change", "SMA50", "SMA200")
175
+ .where(Column("SMA50").crosses_below(Column("SMA200")))
176
+ .order_by("volume", ascending=False)
177
+ )
178
+
179
+ above_all_mas = (
180
+ Query()
181
+ .select(*DEFAULT_COLUMNS, "change", "SMA20", "SMA50", "SMA200")
182
+ .where(
183
+ Column("close") > Column("SMA20"),
184
+ Column("close") > Column("SMA50"),
185
+ Column("close") > Column("SMA200"),
186
+ )
187
+ .order_by("change", ascending=False)
188
+ )
189
+
190
+ @classmethod
191
+ def names(cls) -> list[str]:
192
+ """Get list of all available scanner names."""
193
+ return [x for x in cls.__dict__.keys() if not x.startswith("_") and x != "names"]
194
+
195
+
196
+ class CryptoScanner:
197
+ """
198
+ Collection of pre-built cryptocurrency screeners.
199
+
200
+ Examples:
201
+ >>> CryptoScanner.top_gainers.get_scanner_data()
202
+ >>> CryptoScanner.high_volume.get_scanner_data()
203
+ """
204
+
205
+ CRYPTO_COLUMNS = ["name", "close", "volume", "change", "market_cap_basic"]
206
+
207
+ top_gainers = (
208
+ Query()
209
+ .set_markets("crypto")
210
+ .select(*CRYPTO_COLUMNS)
211
+ .where(Column("change") > 0)
212
+ .order_by("change", ascending=False)
213
+ )
214
+
215
+ top_losers = (
216
+ Query()
217
+ .set_markets("crypto")
218
+ .select(*CRYPTO_COLUMNS)
219
+ .where(Column("change") < 0)
220
+ .order_by("change", ascending=True)
221
+ )
222
+
223
+ high_volume = (
224
+ Query()
225
+ .set_markets("crypto")
226
+ .select(*CRYPTO_COLUMNS, "24h_vol|5")
227
+ .order_by("volume", ascending=False)
228
+ )
229
+
230
+ most_volatile = (
231
+ Query()
232
+ .set_markets("crypto")
233
+ .select(*CRYPTO_COLUMNS, "Volatility.D")
234
+ .order_by("Volatility.D", ascending=False)
235
+ )
236
+
237
+ oversold = (
238
+ Query()
239
+ .set_markets("crypto")
240
+ .select(*CRYPTO_COLUMNS, "RSI")
241
+ .where(Column("RSI") < 30)
242
+ .order_by("RSI", ascending=True)
243
+ )
244
+
245
+ overbought = (
246
+ Query()
247
+ .set_markets("crypto")
248
+ .select(*CRYPTO_COLUMNS, "RSI")
249
+ .where(Column("RSI") > 70)
250
+ .order_by("RSI", ascending=False)
251
+ )
252
+
253
+ @classmethod
254
+ def names(cls) -> list[str]:
255
+ """Get list of all available crypto scanner names."""
256
+ return [x for x in cls.__dict__.keys() if not x.startswith("_") and x not in ("names", "CRYPTO_COLUMNS")]