tradingview_screener.query
1from __future__ import annotations 2 3__all__ = ['Query', 'Column'] 4 5import pprint 6from typing import TypedDict, Any, Literal 7 8import requests 9import pandas as pd 10 11from tradingview_screener.constants import COLUMNS, MARKETS, HEADERS, URL 12 13 14class FilterOperationDict(TypedDict): 15 left: str 16 operation: Literal[ 17 'greater', 18 'egreater', 19 'less', 20 'eless', 21 'equal', 22 'nequal', 23 'in_range', 24 'not_in_range', 25 'match', # the same as: `LOWER(col) LIKE '%pattern%'` 26 'crosses', 27 'crosses_above', 28 'crosses_below', 29 ] 30 right: Any 31 32 33class SortByDict(TypedDict): 34 sortBy: str 35 sortOrder: Literal['asc', 'desc'] 36 37 38class QueryDict(TypedDict): 39 """ 40 The fields that can be passed to the tradingview scan API 41 """ 42 43 # TODO: test which optional ... 44 markets: list[str] 45 symbols: dict 46 options: dict 47 columns: list[str] 48 filter: list[FilterOperationDict] 49 sort: SortByDict 50 range: list[int] # a with two integers, i.e. `[0, 100]` 51 52 53class Column: 54 """ 55 A Column object represents a field in the tradingview stock screener, 56 and it's used in SELECT queries and WHERE queries with the `Query` object. 57 58 A `Column` supports all the comparison operations: 59 `<`, `<=`, `>`, `>=`, `==`, `!=`, and also other methods like `between()`, `isin()`, etc. 60 61 Examples: 62 63 Some of the operations that you can do with `Column` objects: 64 >>> Column('close') >= 2.5 65 >>> Column('close').between(2.5, 15) 66 >>> Column('high') > Column('VWAP') 67 >>> Column('close').between(Column('EMA5'), Column('EMA20') 68 >>> Column('type').isin(['stock', 'fund']) 69 >>> Column('description').like('apple') # the same as `description LIKE '%apple%'` 70 """ 71 72 def __init__(self, name: str) -> None: 73 """ 74 Create a column object from a given column name 75 76 :param name: string, should be either a key or a value from the `COLUMNS` dictionary 77 """ 78 # if `name` is a dictionary key: get its value. otherwise make sure that it's a 79 # dictionary value. 80 self.name = COLUMNS.get(name, name) 81 82 # disable this method and do the column/field validation through the server 83 # @classmethod 84 # def from_unknown_name(cls, name: str) -> Column: 85 # """ 86 # Create a column object from a column name that isn't in the `COLUMNS` dictionary 87 # 88 # :param name: string, column name 89 # :return: Column 90 # """ 91 # # close is just a temporary column, so it won't raise an error at `__init__` 92 # column = cls(name='close') 93 # column.name = name 94 # return column 95 96 @staticmethod 97 def _extract_value(obj) -> ...: 98 if isinstance(obj, Column): 99 return obj.name 100 return obj 101 102 def __gt__(self, other) -> FilterOperationDict: 103 return FilterOperationDict( 104 left=self.name, operation='greater', right=self._extract_value(other) 105 ) 106 107 def __ge__(self, other) -> FilterOperationDict: 108 return FilterOperationDict( 109 left=self.name, operation='egreater', right=self._extract_value(other) 110 ) 111 112 def __lt__(self, other) -> FilterOperationDict: 113 return FilterOperationDict( 114 left=self.name, operation='less', right=self._extract_value(other) 115 ) 116 117 def __le__(self, other) -> FilterOperationDict: 118 return FilterOperationDict( 119 left=self.name, operation='eless', right=self._extract_value(other) 120 ) 121 122 def __eq__(self, other) -> FilterOperationDict: 123 return FilterOperationDict( 124 left=self.name, operation='equal', right=self._extract_value(other) 125 ) 126 127 def __ne__(self, other) -> FilterOperationDict: 128 return FilterOperationDict( 129 left=self.name, operation='nequal', right=self._extract_value(other) 130 ) 131 132 def crosses(self, other) -> FilterOperationDict: 133 return FilterOperationDict( 134 left=self.name, operation='crosses', right=self._extract_value(other) 135 ) 136 137 def crosses_above(self, other) -> FilterOperationDict: 138 return FilterOperationDict( 139 left=self.name, operation='crosses_above', right=self._extract_value(other) 140 ) 141 142 def crosses_below(self, other) -> FilterOperationDict: 143 return FilterOperationDict( 144 left=self.name, operation='crosses_below', right=self._extract_value(other) 145 ) 146 147 def between(self, left, right) -> FilterOperationDict: 148 return FilterOperationDict( 149 left=self.name, 150 operation='in_range', 151 right=[self._extract_value(left), self._extract_value(right)], 152 ) 153 154 def not_between(self, left, right) -> FilterOperationDict: 155 return FilterOperationDict( 156 left=self.name, 157 operation='not_in_range', 158 right=[self._extract_value(left), self._extract_value(right)], 159 ) 160 161 def isin(self, values) -> FilterOperationDict: 162 return FilterOperationDict(left=self.name, operation='in_range', right=list(values)) 163 164 def like(self, other) -> FilterOperationDict: 165 return FilterOperationDict( 166 left=self.name, operation='match', right=self._extract_value(other) 167 ) 168 169 def __repr__(self) -> str: 170 return f'< Column({self.name!r}) >' 171 172 173class Query: 174 """ 175 This class allows you to perform SQL-like queries on the tradingview stock-screener. 176 177 The `Query` object reppresents a query that can be made to the official tradingview API, and it 178 stores all the data as JSON internally. 179 180 Examples: 181 182 To perform a simple query all you have to do is: 183 >>> from tradingview_screener import Query 184 >>> Query().get_scanner_data() 185 (18060, 186 ticker name close volume market_cap_basic 187 0 AMEX:SPY SPY 410.68 107367671 NaN 188 1 NASDAQ:QQQ QQQ 345.31 63475390 NaN 189 2 NASDAQ:TSLA TSLA 207.30 94879471 6.589904e+11 190 3 NASDAQ:NVDA NVDA 405.00 41677185 1.000350e+12 191 4 NASDAQ:AMZN AMZN 127.74 125309313 1.310658e+12 192 .. ... ... ... ... ... 193 45 NYSE:UNH UNH 524.66 2585616 4.859952e+11 194 46 NASDAQ:DXCM DXCM 89.29 14954605 3.449933e+10 195 47 NYSE:MA MA 364.08 3624883 3.429080e+11 196 48 NYSE:ABBV ABBV 138.93 9427212 2.452179e+11 197 49 AMEX:XLK XLK 161.12 8115780 NaN 198 [50 rows x 5 columns]) 199 200 The `get_scanner_data()` method will return a tuple with the first element being the number of 201 records that were found (like a `COUNT(*)`), and the second element contains the data that was 202 found as a DataFrame. 203 204 --- 205 206 By default, the `Query` will select the columns: `name`, `close`, `volume`, `market_cap_basic`, 207 but you override that 208 >>> (Query() 209 ... .select('open', 'high', 'low', 'VWAP', 'MACD.macd', 'RSI', 'Price to Earnings Ratio (TTM)') 210 ... .get_scanner_data()) 211 (18060, 212 ticker open high ... MACD.macd RSI price_earnings_ttm 213 0 AMEX:SPY 414.19 414.600 ... -5.397135 29.113396 NaN 214 1 NASDAQ:QQQ 346.43 348.840 ... -4.321482 34.335449 NaN 215 2 NASDAQ:TSLA 210.60 212.410 ... -12.224250 28.777229 66.752536 216 3 NASDAQ:NVDA 411.30 412.060 ... -8.738986 37.845668 97.835540 217 4 NASDAQ:AMZN 126.20 130.020 ... -2.025378 48.665666 66.697995 218 .. ... ... ... ... ... ... ... 219 45 NYSE:UNH 525.99 527.740 ... 6.448129 54.614775 22.770713 220 46 NASDAQ:DXCM 92.73 92.988 ... -2.376942 52.908093 98.914368 221 47 NYSE:MA 366.49 368.285 ... -7.496065 22.614078 31.711800 222 48 NYSE:ABBV 138.77 143.000 ... -1.708497 27.117232 37.960054 223 49 AMEX:XLK 161.17 162.750 ... -1.520828 36.868658 NaN 224 [50 rows x 8 columns]) 225 226 You can find the 250+ columns available in `tradingview_screener.constants.COLUMNS`. 227 228 Now let's do some queries using the `WHERE` statement, select all the stocks that the `close` is 229 bigger or equal than 350 230 >>> (Query() 231 ... .select('close', 'volume', '52 Week High') 232 ... .where(Column('close') >= 350) 233 ... .get_scanner_data()) 234 (159, 235 ticker close volume price_52_week_high 236 0 AMEX:SPY 410.68 107367671 459.44 237 1 NASDAQ:NVDA 405.00 41677185 502.66 238 2 NYSE:BRK.A 503375.05 7910 566569.97 239 3 AMEX:IVV 412.55 5604525 461.88 240 4 AMEX:VOO 377.32 5638752 422.15 241 .. ... ... ... ... 242 45 NASDAQ:EQIX 710.39 338549 821.63 243 46 NYSE:MCK 448.03 527406 465.90 244 47 NYSE:MTD 976.25 241733 1615.97 245 48 NASDAQ:CTAS 496.41 464631 525.37 246 49 NASDAQ:ROP 475.57 450141 508.90 247 [50 rows x 4 columns]) 248 249 You can even use other columns in these kind of operations 250 >>> (Query() 251 ... .select('close', 'VWAP') 252 ... .where(Column('close') >= Column('VWAP')) 253 ... .get_scanner_data()) 254 (9044, 255 ticker close VWAP 256 0 NASDAQ:AAPL 168.22 168.003333 257 1 NASDAQ:META 296.73 296.336667 258 2 NASDAQ:GOOGL 122.17 121.895233 259 3 NASDAQ:AMD 96.43 96.123333 260 4 NASDAQ:GOOG 123.40 123.100000 261 .. ... ... ... 262 45 NYSE:GD 238.25 238.043333 263 46 NYSE:GOLD 16.33 16.196667 264 47 AMEX:SLV 21.18 21.041667 265 48 AMEX:VXX 27.08 26.553333 266 49 NYSE:SLB 55.83 55.676667 267 [50 rows x 3 columns]) 268 269 Let's find all the stocks that the price is between the EMA 5 and 20, and the type is a stock 270 or fund 271 >>> (Query() 272 ... .select('close', 'volume', 'EMA5', 'EMA20', 'type') 273 ... .where( 274 ... Column('close').between(Column('EMA5'), Column('EMA20')), 275 ... Column('type').isin(['stock', 'fund']) 276 ... ) 277 ... .get_scanner_data()) 278 (1730, 279 ticker close volume EMA5 EMA20 type 280 0 NASDAQ:AMZN 127.74 125309313 125.033517 127.795142 stock 281 1 AMEX:HYG 72.36 35621800 72.340776 72.671058 fund 282 2 AMEX:LQD 99.61 21362859 99.554272 100.346388 fund 283 3 NASDAQ:IEF 90.08 11628236 89.856804 90.391503 fund 284 4 NYSE:SYK 261.91 3783608 261.775130 266.343290 stock 285 .. ... ... ... ... ... ... 286 45 NYSE:EMN 72.58 1562328 71.088034 72.835394 stock 287 46 NYSE:KIM 16.87 6609420 16.858920 17.096582 fund 288 47 NASDAQ:COLM 71.34 1516675 71.073116 71.658864 stock 289 48 NYSE:AOS 67.81 1586796 67.561619 67.903168 stock 290 49 NASDAQ:IGIB 47.81 2073538 47.761338 48.026795 fund 291 [50 rows x 6 columns]) 292 293 There are also the `ORDER BY`, `OFFSET`, and `LIMIT` statements. 294 Let's select all the tickers with a market cap between 1M and 50M, that have a relative volume 295 bigger than 1.2, and that the MACD is positive 296 >>> (Query() 297 ... .select('name', 'close', 'volume', 'relative_volume_10d_calc') 298 ... .where( 299 ... Column('market_cap_basic').between(1_000_000, 50_000_000), 300 ... Column('relative_volume_10d_calc') > 1.2, 301 ... Column('MACD.macd') >= Column('MACD.signal') 302 ... ) 303 ... .order_by('volume', ascending=False) 304 ... .offset(5) 305 ... .limit(15) 306 ... .get_scanner_data()) 307 (393, 308 ticker name close volume relative_volume_10d_calc 309 0 OTC:YCRM YCRM 0.0120 19626514 1.887942 310 1 OTC:PLPL PLPL 0.0002 17959914 3.026059 311 2 NASDAQ:ABVC ABVC 1.3800 16295824 1.967505 312 3 OTC:TLSS TLSS 0.0009 15671157 1.877976 313 4 OTC:GVSI GVSI 0.0128 14609774 2.640792 314 5 OTC:IGEX IGEX 0.0012 14285592 1.274861 315 6 OTC:EEGI EEGI 0.0004 12094000 2.224749 316 7 NASDAQ:GLG GLG 0.0591 9811974 1.990526 317 8 NASDAQ:TCRT TCRT 0.0890 8262894 2.630553 318 9 OTC:INKW INKW 0.0027 7196404 1.497134) 319 320 To avoid rewriting the same query again and again, you can save the query to a variable and 321 just call `get_scanner_data()` again and again to get the latest data: 322 >>> top_50_bullish = (Query() 323 ... .select('name', 'close', 'volume', 'relative_volume_10d_calc') 324 ... .where( 325 ... Column('market_cap_basic').between(1_000_000, 50_000_000), 326 ... Column('relative_volume_10d_calc') > 1.2, 327 ... Column('MACD.macd') >= Column('MACD.signal') 328 ... ) 329 ... .order_by('volume', ascending=False) 330 ... .limit(50)) 331 >>> top_50_bullish.get_scanner_data() 332 (393, 333 ticker name close volume relative_volume_10d_calc 334 0 OTC:BEGI BEGI 0.001050 127874055 3.349924 335 1 OTC:HCMC HCMC 0.000100 126992562 1.206231 336 2 OTC:HEMP HEMP 0.000150 101382713 1.775458 337 3 OTC:SONG SONG 0.000800 92640779 1.805721 338 4 OTC:APRU APRU 0.001575 38104499 29.028958 339 .. ... ... ... ... ... 340 45 OTC:BSHPF BSHPF 0.001000 525000 1.280899 341 46 OTC:GRHI GRHI 0.033000 507266 1.845738 342 47 OTC:OMGGF OMGGF 0.035300 505000 4.290059 343 48 NASDAQ:GBNH GBNH 0.273000 500412 9.076764 344 49 OTC:CLRMF CLRMF 0.032500 496049 17.560935 345 [50 rows x 5 columns]) 346 """ 347 348 def __init__(self) -> None: 349 # noinspection PyTypeChecker 350 self.query: QueryDict = { 351 'markets': ['america'], 352 'symbols': {'query': {'types': []}, 'tickers': []}, 353 'options': {'lang': 'en'}, 354 'columns': ['name', 'close', 'volume', 'market_cap_basic'], 355 # 'filter': ..., 356 'sort': {'sortBy': 'Value.Traded', 'sortOrder': 'desc'}, 357 'range': [0, 50], 358 } 359 self.url = 'https://scanner.tradingview.com/america/scan' 360 361 def set_markets(self, *markets: str) -> Query: 362 """ 363 This method allows you to select the market/s which you want to query. 364 365 By default, the screener will only scan US equities, but you can change it to scan any 366 or even multiple markets, that includes a list of 67 countries, and also the following 367 commodities: `bonds`, `cfd`, `coin`, `crypto`, `economics2`, `euronext`, `forex`, 368 `futures`, `options`. 369 370 You may choose any value from `tradingview_screener.constants.MARKETS`. 371 372 Examples: 373 374 By default, the screener will search the `america` market 375 >>> default_columns = ['close', 'market', 'country', 'currency'] 376 >>> Query().select(*default_columns).get_scanner_data() 377 (17898, 378 ticker close market country currency 379 0 AMEX:SPY 419.9900 america United States USD 380 1 NASDAQ:TSLA 201.7201 america United States USD 381 2 NASDAQ:NVDA 416.3800 america United States USD 382 3 NASDAQ:AMD 106.4499 america United States USD 383 4 NASDAQ:QQQ 353.4000 america United States USD 384 .. ... ... ... ... ... 385 45 NASDAQ:ADBE 538.0000 america United States USD 386 46 NYSE:BA 188.9000 america United States USD 387 47 NASDAQ:SBUX 90.9100 america United States USD 388 48 NYSE:HUM 500.6350 america United States USD 389 49 NYSE:CAT 227.3400 america United States USD 390 [50 rows x 5 columns]) 391 392 But you can change it (note the difference between `market` and `country`) 393 >>> (Query() 394 ... .select(*default_columns) 395 ... .set_markets('italy') 396 ... .get_scanner_data()) 397 (2346, 398 ticker close market country currency 399 0 MIL:UCG 23.9150 italy Italy EUR 400 1 MIL:ISP 2.4910 italy Italy EUR 401 2 MIL:STLAM 17.9420 italy Netherlands EUR 402 3 MIL:ENEL 6.0330 italy Italy EUR 403 4 MIL:ENI 15.4800 italy Italy EUR 404 .. ... ... ... ... ... 405 45 MIL:UNI 5.1440 italy Italy EUR 406 46 MIL:3OIS 0.4311 italy Ireland EUR 407 47 MIL:3SIL 35.2300 italy Ireland EUR 408 48 MIL:IWDE 69.1300 italy Ireland EUR 409 49 MIL:QQQS 19.2840 italy Ireland EUR 410 [50 rows x 5 columns]) 411 412 You can also select multiple markets 413 >>> (Query() 414 ... .select(*default_columns) 415 ... .set_markets('america', 'israel', 'hongkong', 'switzerland') 416 ... .get_scanner_data()) 417 (23964, 418 ticker close market country currency 419 0 AMEX:SPY 420.1617 america United States USD 420 1 NASDAQ:TSLA 201.2000 america United States USD 421 2 NASDAQ:NVDA 416.7825 america United States USD 422 3 NASDAQ:AMD 106.6600 america United States USD 423 4 NASDAQ:QQQ 353.7985 america United States USD 424 .. ... ... ... ... ... 425 45 NASDAQ:GOOGL 124.9200 america United States USD 426 46 HKEX:1211 233.2000 hongkong China HKD 427 47 TASE:ALHE 1995.0000 israel Israel ILA 428 48 AMEX:BIL 91.4398 america United States USD 429 49 NASDAQ:GOOG 126.1500 america United States USD 430 [50 rows x 5 columns]) 431 432 You may also select different financial instruments 433 >>> (Query() 434 ... .select('close', 'market') 435 ... .set_markets('cfd', 'crypto', 'futures', 'options') 436 ... .get_scanner_data()) 437 (118076, 438 ticker ... market 439 0 UNISWAP3ETH:WETHVGT ... crypto 440 1 UNISWAP3POLYGON:BONKWMATIC ... crypto 441 2 UNISWAP3ARBITRUM:WETHTROVE ... crypto 442 3 UNISWAP3ETH:USDTBRD ... crypto 443 4 UNISWAP3ETH:WBTCAUSD ... crypto 444 .. ... ... ... 445 45 NSE:IDEAF2024 ... futures 446 46 NSE:INDUSTOWERX2023 ... futures 447 47 NSE:INDUSTOWER1! ... futures 448 48 BIST:XU100 ... cfd 449 49 BYBIT:BTCUSD.P ... crypto 450 [50 rows x 3 columns]) 451 452 To select all the avaialble markets you can do this trick 453 >>> from tradingview_screener.constants import MARKETS 454 >>> len(MARKETS) 455 76 456 >>> (Query() 457 ... .select('close', 'market') 458 ... .set_markets(*MARKETS) 459 ... .get_scanner_data()) # notice how many records we find: over 240k 460 (241514, 461 ticker ... market 462 0 UNISWAP3ETH:WETHVGT ... crypto 463 1 UNISWAP3POLYGON:BONKWMATIC ... crypto 464 2 UNISWAP3ARBITRUM:WETHTROVE ... crypto 465 3 UNISWAP3ETH:USDTBRD ... crypto 466 4 UNISWAP3ETH:WBTCAUSD ... crypto 467 .. ... ... ... 468 45 NSE:IDEAF2024 ... futures 469 46 NSE:INDUSTOWER1! ... futures 470 47 NSE:INDUSTOWERX2023 ... futures 471 48 BIST:XU100 ... cfd 472 49 BYBIT:BTCUSD.P ... crypto 473 474 [50 rows x 3 columns]) 475 476 :param markets: one or more markets from `tradingview_screener.constants.MARKETS` 477 :return: Self 478 """ 479 if len(markets) == 1: 480 market = markets[0] 481 assert market in MARKETS 482 483 self.url = URL.format(market=market) 484 self.query['markets'] = [market] 485 486 elif len(markets) >= 1: 487 for m in markets: 488 assert m in MARKETS 489 490 self.url = URL.format(market='global') 491 self.query['markets'] = list(markets) 492 493 return self 494 495 def set_tickers(self, *tickers: str) -> Query: 496 """ 497 Set the tickers you wish to receive information on. 498 499 Examples: 500 501 >>> Query().limit(5).get_scanner_data() 502 (17879, 503 ticker name close volume market_cap_basic 504 0 NASDAQ:TSLA TSLA 248.50 118559595 7.887376e+11 505 1 AMEX:SPY SPY 445.52 62066984 NaN 506 2 NASDAQ:NVDA NVDA 455.72 47389801 1.125628e+12 507 3 NASDAQ:QQQ QQQ 372.58 35846281 NaN 508 4 NASDAQ:AAPL AAPL 178.18 65600673 2.785707e+12) 509 510 >>> q = Query().select('name', 'market', 'close', 'volume', 'VWAP', 'MACD.macd') 511 >>> q.set_tickers('NASDAQ:TSLA').get_scanner_data() 512 (2, 513 ticker name market close volume VWAP MACD.macd 514 0 NASDAQ:TSLA TSLA america 248.50 118559595 250.563333 0.730376 515 1 NASDAQ:NVDA NVDA america 455.72 47389801 458.163333 7.927189) 516 517 >>> q.set_tickers('NYSE:GME', 'AMEX:SPY', 'MIL:RACE', 'HOSE:VIX').get_scanner_data() 518 (4, 519 ticker name market close volume VWAP MACD.macd 520 0 HOSE:VIX VIX vietnam 19800.00 26292400 19883.333333 1291.359459 521 1 AMEX:SPY SPY america 445.52 62066984 445.720000 0.484263 522 2 NYSE:GME GME america 17.71 4693902 17.853333 -0.660342 523 3 MIL:RACE RACE italy 279.30 246547 279.033327 -1.398701) 524 525 :param tickers: One or more tickers, syntax: `exchange:symbol` 526 :return: Self 527 """ 528 # no need to select the market if we specify the symbol we want 529 # noinspection PyTypedDict 530 self.query.pop('markets', None) 531 532 self.query['symbols'] = {'tickers': list(tickers)} 533 self.url = 'https://scanner.tradingview.com/global/scan' 534 return self 535 536 def select(self, *columns: Column | str) -> Query: 537 self.query['columns'] = [ 538 col.name if isinstance(col, Column) else Column(col).name for col in columns 539 ] 540 return self 541 542 def where(self, *expressions: FilterOperationDict) -> Query: 543 self.query['filter'] = list(expressions) # convert tuple[dict] -> list[dict] 544 return self 545 546 def order_by(self, column: Column | str, ascending: bool = True) -> Query: 547 column = column.name if isinstance(column, Column) else Column(column).name 548 sort_order = 'asc' if ascending else 'desc' 549 # noinspection PyTypeChecker 550 self.query['sort'] = SortByDict(sortBy=column, sortOrder=sort_order) 551 return self 552 553 def offset(self, offset: int) -> Query: 554 self.query['range'][0] = offset 555 return self 556 557 def limit(self, limit: int) -> Query: 558 self.query['range'][1] = limit 559 return self 560 561 # def set_options(self, options) -> None: 562 # raise NotImplementedError 563 564 def get_scanner_data(self, **kwargs) -> tuple[int, pd.DataFrame]: 565 """ 566 Perform a POST web-request and return the data from the API as a DataFrame. 567 568 Note that you can pass extra keyword-arguments that will be forwarded to `requests.post()`, 569 this can be very useful if you want to pass your own headers/cookies. 570 571 (if you have paid for a live data add-on with TradingView, you want to pass your own 572 headers and cookies to access that real-time data) 573 574 :param kwargs: kwargs to pass to `requests.post()` 575 :return: a tuple consisting of: (total_count, dataframe) 576 """ 577 kwargs.setdefault('headers', HEADERS) 578 kwargs.setdefault('timeout', 20) 579 r = requests.post(self.url, json=self.query, **kwargs) 580 581 if r.status_code >= 400: 582 # add the body to the error message for debugging purposes 583 r.reason += f'\n Body: {r.text}\n' 584 r.raise_for_status() 585 586 json_obj = r.json() 587 rows_count = json_obj['totalCount'] 588 data = json_obj['data'] 589 590 df = pd.DataFrame( 591 data=([row['s'], *row['d']] for row in data), 592 columns=['ticker', *self.query.get('columns', ())], 593 ) 594 return rows_count, df 595 596 def copy(self) -> Query: 597 new = Query() 598 new.query = self.query.copy() 599 return new 600 601 def __repr__(self) -> str: 602 return f'< {pprint.pformat(self.query)} >' 603 604 def __eq__(self, other) -> bool: 605 return isinstance(other, Query) and self.query == other.query
174class Query: 175 """ 176 This class allows you to perform SQL-like queries on the tradingview stock-screener. 177 178 The `Query` object reppresents a query that can be made to the official tradingview API, and it 179 stores all the data as JSON internally. 180 181 Examples: 182 183 To perform a simple query all you have to do is: 184 >>> from tradingview_screener import Query 185 >>> Query().get_scanner_data() 186 (18060, 187 ticker name close volume market_cap_basic 188 0 AMEX:SPY SPY 410.68 107367671 NaN 189 1 NASDAQ:QQQ QQQ 345.31 63475390 NaN 190 2 NASDAQ:TSLA TSLA 207.30 94879471 6.589904e+11 191 3 NASDAQ:NVDA NVDA 405.00 41677185 1.000350e+12 192 4 NASDAQ:AMZN AMZN 127.74 125309313 1.310658e+12 193 .. ... ... ... ... ... 194 45 NYSE:UNH UNH 524.66 2585616 4.859952e+11 195 46 NASDAQ:DXCM DXCM 89.29 14954605 3.449933e+10 196 47 NYSE:MA MA 364.08 3624883 3.429080e+11 197 48 NYSE:ABBV ABBV 138.93 9427212 2.452179e+11 198 49 AMEX:XLK XLK 161.12 8115780 NaN 199 [50 rows x 5 columns]) 200 201 The `get_scanner_data()` method will return a tuple with the first element being the number of 202 records that were found (like a `COUNT(*)`), and the second element contains the data that was 203 found as a DataFrame. 204 205 --- 206 207 By default, the `Query` will select the columns: `name`, `close`, `volume`, `market_cap_basic`, 208 but you override that 209 >>> (Query() 210 ... .select('open', 'high', 'low', 'VWAP', 'MACD.macd', 'RSI', 'Price to Earnings Ratio (TTM)') 211 ... .get_scanner_data()) 212 (18060, 213 ticker open high ... MACD.macd RSI price_earnings_ttm 214 0 AMEX:SPY 414.19 414.600 ... -5.397135 29.113396 NaN 215 1 NASDAQ:QQQ 346.43 348.840 ... -4.321482 34.335449 NaN 216 2 NASDAQ:TSLA 210.60 212.410 ... -12.224250 28.777229 66.752536 217 3 NASDAQ:NVDA 411.30 412.060 ... -8.738986 37.845668 97.835540 218 4 NASDAQ:AMZN 126.20 130.020 ... -2.025378 48.665666 66.697995 219 .. ... ... ... ... ... ... ... 220 45 NYSE:UNH 525.99 527.740 ... 6.448129 54.614775 22.770713 221 46 NASDAQ:DXCM 92.73 92.988 ... -2.376942 52.908093 98.914368 222 47 NYSE:MA 366.49 368.285 ... -7.496065 22.614078 31.711800 223 48 NYSE:ABBV 138.77 143.000 ... -1.708497 27.117232 37.960054 224 49 AMEX:XLK 161.17 162.750 ... -1.520828 36.868658 NaN 225 [50 rows x 8 columns]) 226 227 You can find the 250+ columns available in `tradingview_screener.constants.COLUMNS`. 228 229 Now let's do some queries using the `WHERE` statement, select all the stocks that the `close` is 230 bigger or equal than 350 231 >>> (Query() 232 ... .select('close', 'volume', '52 Week High') 233 ... .where(Column('close') >= 350) 234 ... .get_scanner_data()) 235 (159, 236 ticker close volume price_52_week_high 237 0 AMEX:SPY 410.68 107367671 459.44 238 1 NASDAQ:NVDA 405.00 41677185 502.66 239 2 NYSE:BRK.A 503375.05 7910 566569.97 240 3 AMEX:IVV 412.55 5604525 461.88 241 4 AMEX:VOO 377.32 5638752 422.15 242 .. ... ... ... ... 243 45 NASDAQ:EQIX 710.39 338549 821.63 244 46 NYSE:MCK 448.03 527406 465.90 245 47 NYSE:MTD 976.25 241733 1615.97 246 48 NASDAQ:CTAS 496.41 464631 525.37 247 49 NASDAQ:ROP 475.57 450141 508.90 248 [50 rows x 4 columns]) 249 250 You can even use other columns in these kind of operations 251 >>> (Query() 252 ... .select('close', 'VWAP') 253 ... .where(Column('close') >= Column('VWAP')) 254 ... .get_scanner_data()) 255 (9044, 256 ticker close VWAP 257 0 NASDAQ:AAPL 168.22 168.003333 258 1 NASDAQ:META 296.73 296.336667 259 2 NASDAQ:GOOGL 122.17 121.895233 260 3 NASDAQ:AMD 96.43 96.123333 261 4 NASDAQ:GOOG 123.40 123.100000 262 .. ... ... ... 263 45 NYSE:GD 238.25 238.043333 264 46 NYSE:GOLD 16.33 16.196667 265 47 AMEX:SLV 21.18 21.041667 266 48 AMEX:VXX 27.08 26.553333 267 49 NYSE:SLB 55.83 55.676667 268 [50 rows x 3 columns]) 269 270 Let's find all the stocks that the price is between the EMA 5 and 20, and the type is a stock 271 or fund 272 >>> (Query() 273 ... .select('close', 'volume', 'EMA5', 'EMA20', 'type') 274 ... .where( 275 ... Column('close').between(Column('EMA5'), Column('EMA20')), 276 ... Column('type').isin(['stock', 'fund']) 277 ... ) 278 ... .get_scanner_data()) 279 (1730, 280 ticker close volume EMA5 EMA20 type 281 0 NASDAQ:AMZN 127.74 125309313 125.033517 127.795142 stock 282 1 AMEX:HYG 72.36 35621800 72.340776 72.671058 fund 283 2 AMEX:LQD 99.61 21362859 99.554272 100.346388 fund 284 3 NASDAQ:IEF 90.08 11628236 89.856804 90.391503 fund 285 4 NYSE:SYK 261.91 3783608 261.775130 266.343290 stock 286 .. ... ... ... ... ... ... 287 45 NYSE:EMN 72.58 1562328 71.088034 72.835394 stock 288 46 NYSE:KIM 16.87 6609420 16.858920 17.096582 fund 289 47 NASDAQ:COLM 71.34 1516675 71.073116 71.658864 stock 290 48 NYSE:AOS 67.81 1586796 67.561619 67.903168 stock 291 49 NASDAQ:IGIB 47.81 2073538 47.761338 48.026795 fund 292 [50 rows x 6 columns]) 293 294 There are also the `ORDER BY`, `OFFSET`, and `LIMIT` statements. 295 Let's select all the tickers with a market cap between 1M and 50M, that have a relative volume 296 bigger than 1.2, and that the MACD is positive 297 >>> (Query() 298 ... .select('name', 'close', 'volume', 'relative_volume_10d_calc') 299 ... .where( 300 ... Column('market_cap_basic').between(1_000_000, 50_000_000), 301 ... Column('relative_volume_10d_calc') > 1.2, 302 ... Column('MACD.macd') >= Column('MACD.signal') 303 ... ) 304 ... .order_by('volume', ascending=False) 305 ... .offset(5) 306 ... .limit(15) 307 ... .get_scanner_data()) 308 (393, 309 ticker name close volume relative_volume_10d_calc 310 0 OTC:YCRM YCRM 0.0120 19626514 1.887942 311 1 OTC:PLPL PLPL 0.0002 17959914 3.026059 312 2 NASDAQ:ABVC ABVC 1.3800 16295824 1.967505 313 3 OTC:TLSS TLSS 0.0009 15671157 1.877976 314 4 OTC:GVSI GVSI 0.0128 14609774 2.640792 315 5 OTC:IGEX IGEX 0.0012 14285592 1.274861 316 6 OTC:EEGI EEGI 0.0004 12094000 2.224749 317 7 NASDAQ:GLG GLG 0.0591 9811974 1.990526 318 8 NASDAQ:TCRT TCRT 0.0890 8262894 2.630553 319 9 OTC:INKW INKW 0.0027 7196404 1.497134) 320 321 To avoid rewriting the same query again and again, you can save the query to a variable and 322 just call `get_scanner_data()` again and again to get the latest data: 323 >>> top_50_bullish = (Query() 324 ... .select('name', 'close', 'volume', 'relative_volume_10d_calc') 325 ... .where( 326 ... Column('market_cap_basic').between(1_000_000, 50_000_000), 327 ... Column('relative_volume_10d_calc') > 1.2, 328 ... Column('MACD.macd') >= Column('MACD.signal') 329 ... ) 330 ... .order_by('volume', ascending=False) 331 ... .limit(50)) 332 >>> top_50_bullish.get_scanner_data() 333 (393, 334 ticker name close volume relative_volume_10d_calc 335 0 OTC:BEGI BEGI 0.001050 127874055 3.349924 336 1 OTC:HCMC HCMC 0.000100 126992562 1.206231 337 2 OTC:HEMP HEMP 0.000150 101382713 1.775458 338 3 OTC:SONG SONG 0.000800 92640779 1.805721 339 4 OTC:APRU APRU 0.001575 38104499 29.028958 340 .. ... ... ... ... ... 341 45 OTC:BSHPF BSHPF 0.001000 525000 1.280899 342 46 OTC:GRHI GRHI 0.033000 507266 1.845738 343 47 OTC:OMGGF OMGGF 0.035300 505000 4.290059 344 48 NASDAQ:GBNH GBNH 0.273000 500412 9.076764 345 49 OTC:CLRMF CLRMF 0.032500 496049 17.560935 346 [50 rows x 5 columns]) 347 """ 348 349 def __init__(self) -> None: 350 # noinspection PyTypeChecker 351 self.query: QueryDict = { 352 'markets': ['america'], 353 'symbols': {'query': {'types': []}, 'tickers': []}, 354 'options': {'lang': 'en'}, 355 'columns': ['name', 'close', 'volume', 'market_cap_basic'], 356 # 'filter': ..., 357 'sort': {'sortBy': 'Value.Traded', 'sortOrder': 'desc'}, 358 'range': [0, 50], 359 } 360 self.url = 'https://scanner.tradingview.com/america/scan' 361 362 def set_markets(self, *markets: str) -> Query: 363 """ 364 This method allows you to select the market/s which you want to query. 365 366 By default, the screener will only scan US equities, but you can change it to scan any 367 or even multiple markets, that includes a list of 67 countries, and also the following 368 commodities: `bonds`, `cfd`, `coin`, `crypto`, `economics2`, `euronext`, `forex`, 369 `futures`, `options`. 370 371 You may choose any value from `tradingview_screener.constants.MARKETS`. 372 373 Examples: 374 375 By default, the screener will search the `america` market 376 >>> default_columns = ['close', 'market', 'country', 'currency'] 377 >>> Query().select(*default_columns).get_scanner_data() 378 (17898, 379 ticker close market country currency 380 0 AMEX:SPY 419.9900 america United States USD 381 1 NASDAQ:TSLA 201.7201 america United States USD 382 2 NASDAQ:NVDA 416.3800 america United States USD 383 3 NASDAQ:AMD 106.4499 america United States USD 384 4 NASDAQ:QQQ 353.4000 america United States USD 385 .. ... ... ... ... ... 386 45 NASDAQ:ADBE 538.0000 america United States USD 387 46 NYSE:BA 188.9000 america United States USD 388 47 NASDAQ:SBUX 90.9100 america United States USD 389 48 NYSE:HUM 500.6350 america United States USD 390 49 NYSE:CAT 227.3400 america United States USD 391 [50 rows x 5 columns]) 392 393 But you can change it (note the difference between `market` and `country`) 394 >>> (Query() 395 ... .select(*default_columns) 396 ... .set_markets('italy') 397 ... .get_scanner_data()) 398 (2346, 399 ticker close market country currency 400 0 MIL:UCG 23.9150 italy Italy EUR 401 1 MIL:ISP 2.4910 italy Italy EUR 402 2 MIL:STLAM 17.9420 italy Netherlands EUR 403 3 MIL:ENEL 6.0330 italy Italy EUR 404 4 MIL:ENI 15.4800 italy Italy EUR 405 .. ... ... ... ... ... 406 45 MIL:UNI 5.1440 italy Italy EUR 407 46 MIL:3OIS 0.4311 italy Ireland EUR 408 47 MIL:3SIL 35.2300 italy Ireland EUR 409 48 MIL:IWDE 69.1300 italy Ireland EUR 410 49 MIL:QQQS 19.2840 italy Ireland EUR 411 [50 rows x 5 columns]) 412 413 You can also select multiple markets 414 >>> (Query() 415 ... .select(*default_columns) 416 ... .set_markets('america', 'israel', 'hongkong', 'switzerland') 417 ... .get_scanner_data()) 418 (23964, 419 ticker close market country currency 420 0 AMEX:SPY 420.1617 america United States USD 421 1 NASDAQ:TSLA 201.2000 america United States USD 422 2 NASDAQ:NVDA 416.7825 america United States USD 423 3 NASDAQ:AMD 106.6600 america United States USD 424 4 NASDAQ:QQQ 353.7985 america United States USD 425 .. ... ... ... ... ... 426 45 NASDAQ:GOOGL 124.9200 america United States USD 427 46 HKEX:1211 233.2000 hongkong China HKD 428 47 TASE:ALHE 1995.0000 israel Israel ILA 429 48 AMEX:BIL 91.4398 america United States USD 430 49 NASDAQ:GOOG 126.1500 america United States USD 431 [50 rows x 5 columns]) 432 433 You may also select different financial instruments 434 >>> (Query() 435 ... .select('close', 'market') 436 ... .set_markets('cfd', 'crypto', 'futures', 'options') 437 ... .get_scanner_data()) 438 (118076, 439 ticker ... market 440 0 UNISWAP3ETH:WETHVGT ... crypto 441 1 UNISWAP3POLYGON:BONKWMATIC ... crypto 442 2 UNISWAP3ARBITRUM:WETHTROVE ... crypto 443 3 UNISWAP3ETH:USDTBRD ... crypto 444 4 UNISWAP3ETH:WBTCAUSD ... crypto 445 .. ... ... ... 446 45 NSE:IDEAF2024 ... futures 447 46 NSE:INDUSTOWERX2023 ... futures 448 47 NSE:INDUSTOWER1! ... futures 449 48 BIST:XU100 ... cfd 450 49 BYBIT:BTCUSD.P ... crypto 451 [50 rows x 3 columns]) 452 453 To select all the avaialble markets you can do this trick 454 >>> from tradingview_screener.constants import MARKETS 455 >>> len(MARKETS) 456 76 457 >>> (Query() 458 ... .select('close', 'market') 459 ... .set_markets(*MARKETS) 460 ... .get_scanner_data()) # notice how many records we find: over 240k 461 (241514, 462 ticker ... market 463 0 UNISWAP3ETH:WETHVGT ... crypto 464 1 UNISWAP3POLYGON:BONKWMATIC ... crypto 465 2 UNISWAP3ARBITRUM:WETHTROVE ... crypto 466 3 UNISWAP3ETH:USDTBRD ... crypto 467 4 UNISWAP3ETH:WBTCAUSD ... crypto 468 .. ... ... ... 469 45 NSE:IDEAF2024 ... futures 470 46 NSE:INDUSTOWER1! ... futures 471 47 NSE:INDUSTOWERX2023 ... futures 472 48 BIST:XU100 ... cfd 473 49 BYBIT:BTCUSD.P ... crypto 474 475 [50 rows x 3 columns]) 476 477 :param markets: one or more markets from `tradingview_screener.constants.MARKETS` 478 :return: Self 479 """ 480 if len(markets) == 1: 481 market = markets[0] 482 assert market in MARKETS 483 484 self.url = URL.format(market=market) 485 self.query['markets'] = [market] 486 487 elif len(markets) >= 1: 488 for m in markets: 489 assert m in MARKETS 490 491 self.url = URL.format(market='global') 492 self.query['markets'] = list(markets) 493 494 return self 495 496 def set_tickers(self, *tickers: str) -> Query: 497 """ 498 Set the tickers you wish to receive information on. 499 500 Examples: 501 502 >>> Query().limit(5).get_scanner_data() 503 (17879, 504 ticker name close volume market_cap_basic 505 0 NASDAQ:TSLA TSLA 248.50 118559595 7.887376e+11 506 1 AMEX:SPY SPY 445.52 62066984 NaN 507 2 NASDAQ:NVDA NVDA 455.72 47389801 1.125628e+12 508 3 NASDAQ:QQQ QQQ 372.58 35846281 NaN 509 4 NASDAQ:AAPL AAPL 178.18 65600673 2.785707e+12) 510 511 >>> q = Query().select('name', 'market', 'close', 'volume', 'VWAP', 'MACD.macd') 512 >>> q.set_tickers('NASDAQ:TSLA').get_scanner_data() 513 (2, 514 ticker name market close volume VWAP MACD.macd 515 0 NASDAQ:TSLA TSLA america 248.50 118559595 250.563333 0.730376 516 1 NASDAQ:NVDA NVDA america 455.72 47389801 458.163333 7.927189) 517 518 >>> q.set_tickers('NYSE:GME', 'AMEX:SPY', 'MIL:RACE', 'HOSE:VIX').get_scanner_data() 519 (4, 520 ticker name market close volume VWAP MACD.macd 521 0 HOSE:VIX VIX vietnam 19800.00 26292400 19883.333333 1291.359459 522 1 AMEX:SPY SPY america 445.52 62066984 445.720000 0.484263 523 2 NYSE:GME GME america 17.71 4693902 17.853333 -0.660342 524 3 MIL:RACE RACE italy 279.30 246547 279.033327 -1.398701) 525 526 :param tickers: One or more tickers, syntax: `exchange:symbol` 527 :return: Self 528 """ 529 # no need to select the market if we specify the symbol we want 530 # noinspection PyTypedDict 531 self.query.pop('markets', None) 532 533 self.query['symbols'] = {'tickers': list(tickers)} 534 self.url = 'https://scanner.tradingview.com/global/scan' 535 return self 536 537 def select(self, *columns: Column | str) -> Query: 538 self.query['columns'] = [ 539 col.name if isinstance(col, Column) else Column(col).name for col in columns 540 ] 541 return self 542 543 def where(self, *expressions: FilterOperationDict) -> Query: 544 self.query['filter'] = list(expressions) # convert tuple[dict] -> list[dict] 545 return self 546 547 def order_by(self, column: Column | str, ascending: bool = True) -> Query: 548 column = column.name if isinstance(column, Column) else Column(column).name 549 sort_order = 'asc' if ascending else 'desc' 550 # noinspection PyTypeChecker 551 self.query['sort'] = SortByDict(sortBy=column, sortOrder=sort_order) 552 return self 553 554 def offset(self, offset: int) -> Query: 555 self.query['range'][0] = offset 556 return self 557 558 def limit(self, limit: int) -> Query: 559 self.query['range'][1] = limit 560 return self 561 562 # def set_options(self, options) -> None: 563 # raise NotImplementedError 564 565 def get_scanner_data(self, **kwargs) -> tuple[int, pd.DataFrame]: 566 """ 567 Perform a POST web-request and return the data from the API as a DataFrame. 568 569 Note that you can pass extra keyword-arguments that will be forwarded to `requests.post()`, 570 this can be very useful if you want to pass your own headers/cookies. 571 572 (if you have paid for a live data add-on with TradingView, you want to pass your own 573 headers and cookies to access that real-time data) 574 575 :param kwargs: kwargs to pass to `requests.post()` 576 :return: a tuple consisting of: (total_count, dataframe) 577 """ 578 kwargs.setdefault('headers', HEADERS) 579 kwargs.setdefault('timeout', 20) 580 r = requests.post(self.url, json=self.query, **kwargs) 581 582 if r.status_code >= 400: 583 # add the body to the error message for debugging purposes 584 r.reason += f'\n Body: {r.text}\n' 585 r.raise_for_status() 586 587 json_obj = r.json() 588 rows_count = json_obj['totalCount'] 589 data = json_obj['data'] 590 591 df = pd.DataFrame( 592 data=([row['s'], *row['d']] for row in data), 593 columns=['ticker', *self.query.get('columns', ())], 594 ) 595 return rows_count, df 596 597 def copy(self) -> Query: 598 new = Query() 599 new.query = self.query.copy() 600 return new 601 602 def __repr__(self) -> str: 603 return f'< {pprint.pformat(self.query)} >' 604 605 def __eq__(self, other) -> bool: 606 return isinstance(other, Query) and self.query == other.query
This class allows you to perform SQL-like queries on the tradingview stock-screener.
The Query
object reppresents a query that can be made to the official tradingview API, and it
stores all the data as JSON internally.
Examples:
To perform a simple query all you have to do is:
>>> from tradingview_screener import Query
>>> Query().get_scanner_data()
(18060,
ticker name close volume market_cap_basic
0 AMEX:SPY SPY 410.68 107367671 NaN
1 NASDAQ:QQQ QQQ 345.31 63475390 NaN
2 NASDAQ:TSLA TSLA 207.30 94879471 6.589904e+11
3 NASDAQ:NVDA NVDA 405.00 41677185 1.000350e+12
4 NASDAQ:AMZN AMZN 127.74 125309313 1.310658e+12
.. ... ... ... ... ...
45 NYSE:UNH UNH 524.66 2585616 4.859952e+11
46 NASDAQ:DXCM DXCM 89.29 14954605 3.449933e+10
47 NYSE:MA MA 364.08 3624883 3.429080e+11
48 NYSE:ABBV ABBV 138.93 9427212 2.452179e+11
49 AMEX:XLK XLK 161.12 8115780 NaN
[50 rows x 5 columns])
The get_scanner_data()
method will return a tuple with the first element being the number of
records that were found (like a COUNT(*)
), and the second element contains the data that was
found as a DataFrame.
By default, the Query
will select the columns: name
, close
, volume
, market_cap_basic
,
but you override that
>>> (Query()
... .select('open', 'high', 'low', 'VWAP', 'MACD.macd', 'RSI', 'Price to Earnings Ratio (TTM)')
... .get_scanner_data())
(18060,
ticker open high ... MACD.macd RSI price_earnings_ttm
0 AMEX:SPY 414.19 414.600 ... -5.397135 29.113396 NaN
1 NASDAQ:QQQ 346.43 348.840 ... -4.321482 34.335449 NaN
2 NASDAQ:TSLA 210.60 212.410 ... -12.224250 28.777229 66.752536
3 NASDAQ:NVDA 411.30 412.060 ... -8.738986 37.845668 97.835540
4 NASDAQ:AMZN 126.20 130.020 ... -2.025378 48.665666 66.697995
.. ... ... ... ... ... ... ...
45 NYSE:UNH 525.99 527.740 ... 6.448129 54.614775 22.770713
46 NASDAQ:DXCM 92.73 92.988 ... -2.376942 52.908093 98.914368
47 NYSE:MA 366.49 368.285 ... -7.496065 22.614078 31.711800
48 NYSE:ABBV 138.77 143.000 ... -1.708497 27.117232 37.960054
49 AMEX:XLK 161.17 162.750 ... -1.520828 36.868658 NaN
[50 rows x 8 columns])
You can find the 250+ columns available in tradingview_screener.constants.COLUMNS
.
Now let's do some queries using the WHERE
statement, select all the stocks that the close
is
bigger or equal than 350
>>> (Query()
... .select('close', 'volume', '52 Week High')
... .where(Column('close') >= 350)
... .get_scanner_data())
(159,
ticker close volume price_52_week_high
0 AMEX:SPY 410.68 107367671 459.44
1 NASDAQ:NVDA 405.00 41677185 502.66
2 NYSE:BRK.A 503375.05 7910 566569.97
3 AMEX:IVV 412.55 5604525 461.88
4 AMEX:VOO 377.32 5638752 422.15
.. ... ... ... ...
45 NASDAQ:EQIX 710.39 338549 821.63
46 NYSE:MCK 448.03 527406 465.90
47 NYSE:MTD 976.25 241733 1615.97
48 NASDAQ:CTAS 496.41 464631 525.37
49 NASDAQ:ROP 475.57 450141 508.90
[50 rows x 4 columns])
You can even use other columns in these kind of operations
>>> (Query()
... .select('close', 'VWAP')
... .where(Column('close') >= Column('VWAP'))
... .get_scanner_data())
(9044,
ticker close VWAP
0 NASDAQ:AAPL 168.22 168.003333
1 NASDAQ:META 296.73 296.336667
2 NASDAQ:GOOGL 122.17 121.895233
3 NASDAQ:AMD 96.43 96.123333
4 NASDAQ:GOOG 123.40 123.100000
.. ... ... ...
45 NYSE:GD 238.25 238.043333
46 NYSE:GOLD 16.33 16.196667
47 AMEX:SLV 21.18 21.041667
48 AMEX:VXX 27.08 26.553333
49 NYSE:SLB 55.83 55.676667
[50 rows x 3 columns])
Let's find all the stocks that the price is between the EMA 5 and 20, and the type is a stock or fund
>>> (Query()
... .select('close', 'volume', 'EMA5', 'EMA20', 'type')
... .where(
... Column('close').between(Column('EMA5'), Column('EMA20')),
... Column('type').isin(['stock', 'fund'])
... )
... .get_scanner_data())
(1730,
ticker close volume EMA5 EMA20 type
0 NASDAQ:AMZN 127.74 125309313 125.033517 127.795142 stock
1 AMEX:HYG 72.36 35621800 72.340776 72.671058 fund
2 AMEX:LQD 99.61 21362859 99.554272 100.346388 fund
3 NASDAQ:IEF 90.08 11628236 89.856804 90.391503 fund
4 NYSE:SYK 261.91 3783608 261.775130 266.343290 stock
.. ... ... ... ... ... ...
45 NYSE:EMN 72.58 1562328 71.088034 72.835394 stock
46 NYSE:KIM 16.87 6609420 16.858920 17.096582 fund
47 NASDAQ:COLM 71.34 1516675 71.073116 71.658864 stock
48 NYSE:AOS 67.81 1586796 67.561619 67.903168 stock
49 NASDAQ:IGIB 47.81 2073538 47.761338 48.026795 fund
[50 rows x 6 columns])
There are also the ORDER BY
, OFFSET
, and LIMIT
statements.
Let's select all the tickers with a market cap between 1M and 50M, that have a relative volume
bigger than 1.2, and that the MACD is positive
>>> (Query()
... .select('name', 'close', 'volume', 'relative_volume_10d_calc')
... .where(
... Column('market_cap_basic').between(1_000_000, 50_000_000),
... Column('relative_volume_10d_calc') > 1.2,
... Column('MACD.macd') >= Column('MACD.signal')
... )
... .order_by('volume', ascending=False)
... .offset(5)
... .limit(15)
... .get_scanner_data())
(393,
ticker name close volume relative_volume_10d_calc
0 OTC:YCRM YCRM 0.0120 19626514 1.887942
1 OTC:PLPL PLPL 0.0002 17959914 3.026059
2 NASDAQ:ABVC ABVC 1.3800 16295824 1.967505
3 OTC:TLSS TLSS 0.0009 15671157 1.877976
4 OTC:GVSI GVSI 0.0128 14609774 2.640792
5 OTC:IGEX IGEX 0.0012 14285592 1.274861
6 OTC:EEGI EEGI 0.0004 12094000 2.224749
7 NASDAQ:GLG GLG 0.0591 9811974 1.990526
8 NASDAQ:TCRT TCRT 0.0890 8262894 2.630553
9 OTC:INKW INKW 0.0027 7196404 1.497134)
To avoid rewriting the same query again and again, you can save the query to a variable and
just call get_scanner_data()
again and again to get the latest data:
>>> top_50_bullish = (Query()
... .select('name', 'close', 'volume', 'relative_volume_10d_calc')
... .where(
... Column('market_cap_basic').between(1_000_000, 50_000_000),
... Column('relative_volume_10d_calc') > 1.2,
... Column('MACD.macd') >= Column('MACD.signal')
... )
... .order_by('volume', ascending=False)
... .limit(50))
>>> top_50_bullish.get_scanner_data()
(393,
ticker name close volume relative_volume_10d_calc
0 OTC:BEGI BEGI 0.001050 127874055 3.349924
1 OTC:HCMC HCMC 0.000100 126992562 1.206231
2 OTC:HEMP HEMP 0.000150 101382713 1.775458
3 OTC:SONG SONG 0.000800 92640779 1.805721
4 OTC:APRU APRU 0.001575 38104499 29.028958
.. ... ... ... ... ...
45 OTC:BSHPF BSHPF 0.001000 525000 1.280899
46 OTC:GRHI GRHI 0.033000 507266 1.845738
47 OTC:OMGGF OMGGF 0.035300 505000 4.290059
48 NASDAQ:GBNH GBNH 0.273000 500412 9.076764
49 OTC:CLRMF CLRMF 0.032500 496049 17.560935
[50 rows x 5 columns])
362 def set_markets(self, *markets: str) -> Query: 363 """ 364 This method allows you to select the market/s which you want to query. 365 366 By default, the screener will only scan US equities, but you can change it to scan any 367 or even multiple markets, that includes a list of 67 countries, and also the following 368 commodities: `bonds`, `cfd`, `coin`, `crypto`, `economics2`, `euronext`, `forex`, 369 `futures`, `options`. 370 371 You may choose any value from `tradingview_screener.constants.MARKETS`. 372 373 Examples: 374 375 By default, the screener will search the `america` market 376 >>> default_columns = ['close', 'market', 'country', 'currency'] 377 >>> Query().select(*default_columns).get_scanner_data() 378 (17898, 379 ticker close market country currency 380 0 AMEX:SPY 419.9900 america United States USD 381 1 NASDAQ:TSLA 201.7201 america United States USD 382 2 NASDAQ:NVDA 416.3800 america United States USD 383 3 NASDAQ:AMD 106.4499 america United States USD 384 4 NASDAQ:QQQ 353.4000 america United States USD 385 .. ... ... ... ... ... 386 45 NASDAQ:ADBE 538.0000 america United States USD 387 46 NYSE:BA 188.9000 america United States USD 388 47 NASDAQ:SBUX 90.9100 america United States USD 389 48 NYSE:HUM 500.6350 america United States USD 390 49 NYSE:CAT 227.3400 america United States USD 391 [50 rows x 5 columns]) 392 393 But you can change it (note the difference between `market` and `country`) 394 >>> (Query() 395 ... .select(*default_columns) 396 ... .set_markets('italy') 397 ... .get_scanner_data()) 398 (2346, 399 ticker close market country currency 400 0 MIL:UCG 23.9150 italy Italy EUR 401 1 MIL:ISP 2.4910 italy Italy EUR 402 2 MIL:STLAM 17.9420 italy Netherlands EUR 403 3 MIL:ENEL 6.0330 italy Italy EUR 404 4 MIL:ENI 15.4800 italy Italy EUR 405 .. ... ... ... ... ... 406 45 MIL:UNI 5.1440 italy Italy EUR 407 46 MIL:3OIS 0.4311 italy Ireland EUR 408 47 MIL:3SIL 35.2300 italy Ireland EUR 409 48 MIL:IWDE 69.1300 italy Ireland EUR 410 49 MIL:QQQS 19.2840 italy Ireland EUR 411 [50 rows x 5 columns]) 412 413 You can also select multiple markets 414 >>> (Query() 415 ... .select(*default_columns) 416 ... .set_markets('america', 'israel', 'hongkong', 'switzerland') 417 ... .get_scanner_data()) 418 (23964, 419 ticker close market country currency 420 0 AMEX:SPY 420.1617 america United States USD 421 1 NASDAQ:TSLA 201.2000 america United States USD 422 2 NASDAQ:NVDA 416.7825 america United States USD 423 3 NASDAQ:AMD 106.6600 america United States USD 424 4 NASDAQ:QQQ 353.7985 america United States USD 425 .. ... ... ... ... ... 426 45 NASDAQ:GOOGL 124.9200 america United States USD 427 46 HKEX:1211 233.2000 hongkong China HKD 428 47 TASE:ALHE 1995.0000 israel Israel ILA 429 48 AMEX:BIL 91.4398 america United States USD 430 49 NASDAQ:GOOG 126.1500 america United States USD 431 [50 rows x 5 columns]) 432 433 You may also select different financial instruments 434 >>> (Query() 435 ... .select('close', 'market') 436 ... .set_markets('cfd', 'crypto', 'futures', 'options') 437 ... .get_scanner_data()) 438 (118076, 439 ticker ... market 440 0 UNISWAP3ETH:WETHVGT ... crypto 441 1 UNISWAP3POLYGON:BONKWMATIC ... crypto 442 2 UNISWAP3ARBITRUM:WETHTROVE ... crypto 443 3 UNISWAP3ETH:USDTBRD ... crypto 444 4 UNISWAP3ETH:WBTCAUSD ... crypto 445 .. ... ... ... 446 45 NSE:IDEAF2024 ... futures 447 46 NSE:INDUSTOWERX2023 ... futures 448 47 NSE:INDUSTOWER1! ... futures 449 48 BIST:XU100 ... cfd 450 49 BYBIT:BTCUSD.P ... crypto 451 [50 rows x 3 columns]) 452 453 To select all the avaialble markets you can do this trick 454 >>> from tradingview_screener.constants import MARKETS 455 >>> len(MARKETS) 456 76 457 >>> (Query() 458 ... .select('close', 'market') 459 ... .set_markets(*MARKETS) 460 ... .get_scanner_data()) # notice how many records we find: over 240k 461 (241514, 462 ticker ... market 463 0 UNISWAP3ETH:WETHVGT ... crypto 464 1 UNISWAP3POLYGON:BONKWMATIC ... crypto 465 2 UNISWAP3ARBITRUM:WETHTROVE ... crypto 466 3 UNISWAP3ETH:USDTBRD ... crypto 467 4 UNISWAP3ETH:WBTCAUSD ... crypto 468 .. ... ... ... 469 45 NSE:IDEAF2024 ... futures 470 46 NSE:INDUSTOWER1! ... futures 471 47 NSE:INDUSTOWERX2023 ... futures 472 48 BIST:XU100 ... cfd 473 49 BYBIT:BTCUSD.P ... crypto 474 475 [50 rows x 3 columns]) 476 477 :param markets: one or more markets from `tradingview_screener.constants.MARKETS` 478 :return: Self 479 """ 480 if len(markets) == 1: 481 market = markets[0] 482 assert market in MARKETS 483 484 self.url = URL.format(market=market) 485 self.query['markets'] = [market] 486 487 elif len(markets) >= 1: 488 for m in markets: 489 assert m in MARKETS 490 491 self.url = URL.format(market='global') 492 self.query['markets'] = list(markets) 493 494 return self
This method allows you to select the market/s which you want to query.
By default, the screener will only scan US equities, but you can change it to scan any
or even multiple markets, that includes a list of 67 countries, and also the following
commodities: bonds
, cfd
, coin
, crypto
, economics2
, euronext
, forex
,
futures
, options
.
You may choose any value from tradingview_screener.constants.MARKETS
.
Examples:
By default, the screener will search the america
market
>>> default_columns = ['close', 'market', 'country', 'currency']
>>> Query().select(*default_columns).get_scanner_data()
(17898,
ticker close market country currency
0 AMEX:SPY 419.9900 america United States USD
1 NASDAQ:TSLA 201.7201 america United States USD
2 NASDAQ:NVDA 416.3800 america United States USD
3 NASDAQ:AMD 106.4499 america United States USD
4 NASDAQ:QQQ 353.4000 america United States USD
.. ... ... ... ... ...
45 NASDAQ:ADBE 538.0000 america United States USD
46 NYSE:BA 188.9000 america United States USD
47 NASDAQ:SBUX 90.9100 america United States USD
48 NYSE:HUM 500.6350 america United States USD
49 NYSE:CAT 227.3400 america United States USD
[50 rows x 5 columns])
But you can change it (note the difference between market
and country
)
>>> (Query()
... .select(*default_columns)
... .set_markets('italy')
... .get_scanner_data())
(2346,
ticker close market country currency
0 MIL:UCG 23.9150 italy Italy EUR
1 MIL:ISP 2.4910 italy Italy EUR
2 MIL:STLAM 17.9420 italy Netherlands EUR
3 MIL:ENEL 6.0330 italy Italy EUR
4 MIL:ENI 15.4800 italy Italy EUR
.. ... ... ... ... ...
45 MIL:UNI 5.1440 italy Italy EUR
46 MIL:3OIS 0.4311 italy Ireland EUR
47 MIL:3SIL 35.2300 italy Ireland EUR
48 MIL:IWDE 69.1300 italy Ireland EUR
49 MIL:QQQS 19.2840 italy Ireland EUR
[50 rows x 5 columns])
You can also select multiple markets
>>> (Query()
... .select(*default_columns)
... .set_markets('america', 'israel', 'hongkong', 'switzerland')
... .get_scanner_data())
(23964,
ticker close market country currency
0 AMEX:SPY 420.1617 america United States USD
1 NASDAQ:TSLA 201.2000 america United States USD
2 NASDAQ:NVDA 416.7825 america United States USD
3 NASDAQ:AMD 106.6600 america United States USD
4 NASDAQ:QQQ 353.7985 america United States USD
.. ... ... ... ... ...
45 NASDAQ:GOOGL 124.9200 america United States USD
46 HKEX:1211 233.2000 hongkong China HKD
47 TASE:ALHE 1995.0000 israel Israel ILA
48 AMEX:BIL 91.4398 america United States USD
49 NASDAQ:GOOG 126.1500 america United States USD
[50 rows x 5 columns])
You may also select different financial instruments
>>> (Query()
... .select('close', 'market')
... .set_markets('cfd', 'crypto', 'futures', 'options')
... .get_scanner_data())
(118076,
ticker ... market
0 UNISWAP3ETH:WETHVGT ... crypto
1 UNISWAP3POLYGON:BONKWMATIC ... crypto
2 UNISWAP3ARBITRUM:WETHTROVE ... crypto
3 UNISWAP3ETH:USDTBRD ... crypto
4 UNISWAP3ETH:WBTCAUSD ... crypto
.. ... ... ...
45 NSE:IDEAF2024 ... futures
46 NSE:INDUSTOWERX2023 ... futures
47 NSE:INDUSTOWER1! ... futures
48 BIST:XU100 ... cfd
49 BYBIT:BTCUSD.P ... crypto
[50 rows x 3 columns])
To select all the avaialble markets you can do this trick
>>> from tradingview_screener.constants import MARKETS
>>> len(MARKETS)
76
>>> (Query()
... .select('close', 'market')
... .set_markets(*MARKETS)
... .get_scanner_data()) # notice how many records we find: over 240k
(241514,
ticker ... market
0 UNISWAP3ETH:WETHVGT ... crypto
1 UNISWAP3POLYGON:BONKWMATIC ... crypto
2 UNISWAP3ARBITRUM:WETHTROVE ... crypto
3 UNISWAP3ETH:USDTBRD ... crypto
4 UNISWAP3ETH:WBTCAUSD ... crypto
.. ... ... ...
45 NSE:IDEAF2024 ... futures
46 NSE:INDUSTOWER1! ... futures
47 NSE:INDUSTOWERX2023 ... futures
48 BIST:XU100 ... cfd
49 BYBIT:BTCUSD.P ... crypto
[50 rows x 3 columns])
Parameters
- markets: one or more markets from
tradingview_screener.constants.MARKETS
Returns
Self
496 def set_tickers(self, *tickers: str) -> Query: 497 """ 498 Set the tickers you wish to receive information on. 499 500 Examples: 501 502 >>> Query().limit(5).get_scanner_data() 503 (17879, 504 ticker name close volume market_cap_basic 505 0 NASDAQ:TSLA TSLA 248.50 118559595 7.887376e+11 506 1 AMEX:SPY SPY 445.52 62066984 NaN 507 2 NASDAQ:NVDA NVDA 455.72 47389801 1.125628e+12 508 3 NASDAQ:QQQ QQQ 372.58 35846281 NaN 509 4 NASDAQ:AAPL AAPL 178.18 65600673 2.785707e+12) 510 511 >>> q = Query().select('name', 'market', 'close', 'volume', 'VWAP', 'MACD.macd') 512 >>> q.set_tickers('NASDAQ:TSLA').get_scanner_data() 513 (2, 514 ticker name market close volume VWAP MACD.macd 515 0 NASDAQ:TSLA TSLA america 248.50 118559595 250.563333 0.730376 516 1 NASDAQ:NVDA NVDA america 455.72 47389801 458.163333 7.927189) 517 518 >>> q.set_tickers('NYSE:GME', 'AMEX:SPY', 'MIL:RACE', 'HOSE:VIX').get_scanner_data() 519 (4, 520 ticker name market close volume VWAP MACD.macd 521 0 HOSE:VIX VIX vietnam 19800.00 26292400 19883.333333 1291.359459 522 1 AMEX:SPY SPY america 445.52 62066984 445.720000 0.484263 523 2 NYSE:GME GME america 17.71 4693902 17.853333 -0.660342 524 3 MIL:RACE RACE italy 279.30 246547 279.033327 -1.398701) 525 526 :param tickers: One or more tickers, syntax: `exchange:symbol` 527 :return: Self 528 """ 529 # no need to select the market if we specify the symbol we want 530 # noinspection PyTypedDict 531 self.query.pop('markets', None) 532 533 self.query['symbols'] = {'tickers': list(tickers)} 534 self.url = 'https://scanner.tradingview.com/global/scan' 535 return self
Set the tickers you wish to receive information on.
Examples:
>>> Query().limit(5).get_scanner_data()
(17879,
ticker name close volume market_cap_basic
0 NASDAQ:TSLA TSLA 248.50 118559595 7.887376e+11
1 AMEX:SPY SPY 445.52 62066984 NaN
2 NASDAQ:NVDA NVDA 455.72 47389801 1.125628e+12
3 NASDAQ:QQQ QQQ 372.58 35846281 NaN
4 NASDAQ:AAPL AAPL 178.18 65600673 2.785707e+12)
>>> q = Query().select('name', 'market', 'close', 'volume', 'VWAP', 'MACD.macd')
>>> q.set_tickers('NASDAQ:TSLA').get_scanner_data()
(2,
ticker name market close volume VWAP MACD.macd
0 NASDAQ:TSLA TSLA america 248.50 118559595 250.563333 0.730376
1 NASDAQ:NVDA NVDA america 455.72 47389801 458.163333 7.927189)
>>> q.set_tickers('NYSE:GME', 'AMEX:SPY', 'MIL:RACE', 'HOSE:VIX').get_scanner_data()
(4,
ticker name market close volume VWAP MACD.macd
0 HOSE:VIX VIX vietnam 19800.00 26292400 19883.333333 1291.359459
1 AMEX:SPY SPY america 445.52 62066984 445.720000 0.484263
2 NYSE:GME GME america 17.71 4693902 17.853333 -0.660342
3 MIL:RACE RACE italy 279.30 246547 279.033327 -1.398701)
Parameters
- **tickers: One or more tickers, syntax:
exchange**: symbol
Returns
Self
547 def order_by(self, column: Column | str, ascending: bool = True) -> Query: 548 column = column.name if isinstance(column, Column) else Column(column).name 549 sort_order = 'asc' if ascending else 'desc' 550 # noinspection PyTypeChecker 551 self.query['sort'] = SortByDict(sortBy=column, sortOrder=sort_order) 552 return self
565 def get_scanner_data(self, **kwargs) -> tuple[int, pd.DataFrame]: 566 """ 567 Perform a POST web-request and return the data from the API as a DataFrame. 568 569 Note that you can pass extra keyword-arguments that will be forwarded to `requests.post()`, 570 this can be very useful if you want to pass your own headers/cookies. 571 572 (if you have paid for a live data add-on with TradingView, you want to pass your own 573 headers and cookies to access that real-time data) 574 575 :param kwargs: kwargs to pass to `requests.post()` 576 :return: a tuple consisting of: (total_count, dataframe) 577 """ 578 kwargs.setdefault('headers', HEADERS) 579 kwargs.setdefault('timeout', 20) 580 r = requests.post(self.url, json=self.query, **kwargs) 581 582 if r.status_code >= 400: 583 # add the body to the error message for debugging purposes 584 r.reason += f'\n Body: {r.text}\n' 585 r.raise_for_status() 586 587 json_obj = r.json() 588 rows_count = json_obj['totalCount'] 589 data = json_obj['data'] 590 591 df = pd.DataFrame( 592 data=([row['s'], *row['d']] for row in data), 593 columns=['ticker', *self.query.get('columns', ())], 594 ) 595 return rows_count, df
Perform a POST web-request and return the data from the API as a DataFrame.
Note that you can pass extra keyword-arguments that will be forwarded to requests.post()
,
this can be very useful if you want to pass your own headers/cookies.
(if you have paid for a live data add-on with TradingView, you want to pass your own headers and cookies to access that real-time data)
Parameters
- kwargs: kwargs to pass to
requests.post()
Returns
a tuple consisting of: (total_count, dataframe)
54class Column: 55 """ 56 A Column object represents a field in the tradingview stock screener, 57 and it's used in SELECT queries and WHERE queries with the `Query` object. 58 59 A `Column` supports all the comparison operations: 60 `<`, `<=`, `>`, `>=`, `==`, `!=`, and also other methods like `between()`, `isin()`, etc. 61 62 Examples: 63 64 Some of the operations that you can do with `Column` objects: 65 >>> Column('close') >= 2.5 66 >>> Column('close').between(2.5, 15) 67 >>> Column('high') > Column('VWAP') 68 >>> Column('close').between(Column('EMA5'), Column('EMA20') 69 >>> Column('type').isin(['stock', 'fund']) 70 >>> Column('description').like('apple') # the same as `description LIKE '%apple%'` 71 """ 72 73 def __init__(self, name: str) -> None: 74 """ 75 Create a column object from a given column name 76 77 :param name: string, should be either a key or a value from the `COLUMNS` dictionary 78 """ 79 # if `name` is a dictionary key: get its value. otherwise make sure that it's a 80 # dictionary value. 81 self.name = COLUMNS.get(name, name) 82 83 # disable this method and do the column/field validation through the server 84 # @classmethod 85 # def from_unknown_name(cls, name: str) -> Column: 86 # """ 87 # Create a column object from a column name that isn't in the `COLUMNS` dictionary 88 # 89 # :param name: string, column name 90 # :return: Column 91 # """ 92 # # close is just a temporary column, so it won't raise an error at `__init__` 93 # column = cls(name='close') 94 # column.name = name 95 # return column 96 97 @staticmethod 98 def _extract_value(obj) -> ...: 99 if isinstance(obj, Column): 100 return obj.name 101 return obj 102 103 def __gt__(self, other) -> FilterOperationDict: 104 return FilterOperationDict( 105 left=self.name, operation='greater', right=self._extract_value(other) 106 ) 107 108 def __ge__(self, other) -> FilterOperationDict: 109 return FilterOperationDict( 110 left=self.name, operation='egreater', right=self._extract_value(other) 111 ) 112 113 def __lt__(self, other) -> FilterOperationDict: 114 return FilterOperationDict( 115 left=self.name, operation='less', right=self._extract_value(other) 116 ) 117 118 def __le__(self, other) -> FilterOperationDict: 119 return FilterOperationDict( 120 left=self.name, operation='eless', right=self._extract_value(other) 121 ) 122 123 def __eq__(self, other) -> FilterOperationDict: 124 return FilterOperationDict( 125 left=self.name, operation='equal', right=self._extract_value(other) 126 ) 127 128 def __ne__(self, other) -> FilterOperationDict: 129 return FilterOperationDict( 130 left=self.name, operation='nequal', right=self._extract_value(other) 131 ) 132 133 def crosses(self, other) -> FilterOperationDict: 134 return FilterOperationDict( 135 left=self.name, operation='crosses', right=self._extract_value(other) 136 ) 137 138 def crosses_above(self, other) -> FilterOperationDict: 139 return FilterOperationDict( 140 left=self.name, operation='crosses_above', right=self._extract_value(other) 141 ) 142 143 def crosses_below(self, other) -> FilterOperationDict: 144 return FilterOperationDict( 145 left=self.name, operation='crosses_below', right=self._extract_value(other) 146 ) 147 148 def between(self, left, right) -> FilterOperationDict: 149 return FilterOperationDict( 150 left=self.name, 151 operation='in_range', 152 right=[self._extract_value(left), self._extract_value(right)], 153 ) 154 155 def not_between(self, left, right) -> FilterOperationDict: 156 return FilterOperationDict( 157 left=self.name, 158 operation='not_in_range', 159 right=[self._extract_value(left), self._extract_value(right)], 160 ) 161 162 def isin(self, values) -> FilterOperationDict: 163 return FilterOperationDict(left=self.name, operation='in_range', right=list(values)) 164 165 def like(self, other) -> FilterOperationDict: 166 return FilterOperationDict( 167 left=self.name, operation='match', right=self._extract_value(other) 168 ) 169 170 def __repr__(self) -> str: 171 return f'< Column({self.name!r}) >'
A Column object represents a field in the tradingview stock screener,
and it's used in SELECT queries and WHERE queries with the Query
object.
A Column
supports all the comparison operations:
<
, <=
, >
, >=
, ==
, !=
, and also other methods like between()
, isin()
, etc.
Examples:
Some of the operations that you can do with Column
objects:
>>> Column('close') >= 2.5
>>> Column('close').between(2.5, 15)
>>> Column('high') > Column('VWAP')
>>> Column('close').between(Column('EMA5'), Column('EMA20')
>>> Column('type').isin(['stock', 'fund'])
>>> Column('description').like('apple') # the same as `description LIKE '%apple%'`
73 def __init__(self, name: str) -> None: 74 """ 75 Create a column object from a given column name 76 77 :param name: string, should be either a key or a value from the `COLUMNS` dictionary 78 """ 79 # if `name` is a dictionary key: get its value. otherwise make sure that it's a 80 # dictionary value. 81 self.name = COLUMNS.get(name, name)
Create a column object from a given column name
Parameters
- name: string, should be either a key or a value from the
COLUMNS
dictionary