tradingview_screener.column
1from __future__ import annotations 2 3from typing import TYPE_CHECKING 4 5 6if TYPE_CHECKING: 7 from typing import Optional, Iterable 8 from tradingview_screener.models import FilterOperationDict 9 10 11class Column: 12 """ 13 A Column object represents a field in the tradingview stock screener, 14 and it's used in SELECT queries and WHERE queries with the `Query` object. 15 16 A `Column` supports all the comparison operations: 17 `<`, `<=`, `>`, `>=`, `==`, `!=`, and also other methods like `between()`, `isin()`, etc. 18 19 Examples: 20 21 Some of the operations you can do: 22 >>> Column('close') > 2.5 23 >>> Column('High.All') <= 'high' 24 >>> Column('high') > 'VWAP' 25 >>> Column('high') > Column('VWAP') # same thing as above 26 >>> Column('is_primary') == True 27 >>> Column('exchange') != 'OTC' 28 29 >>> Column('close').above_pct('VWAP', 1.03) 30 >>> Column('close').above_pct('price_52_week_low', 2.5) 31 >>> Column('close').below_pct('VWAP', 1.03) 32 >>> Column('close').between_pct('EMA200', 1.2, 1.5) 33 >>> Column('close').not_between_pct('EMA200', 1.2, 1.5) 34 35 >>> Column('close').between(2.5, 15) 36 >>> Column('close').between('EMA5', 'EMA20') 37 38 >>> Column('type').isin(['stock', 'fund']) 39 >>> Column('exchange').isin(['AMEX', 'NASDAQ', 'NYSE']) 40 >>> Column('sector').not_in(['Health Technology', 'Health Services']) 41 >>> Column('typespecs').has(['common']), 42 >>> Column('typespecs').has_none_of(['reit', 'etn', 'etf']), 43 44 >>> Column('description').like('apple') # the same as `description LIKE '%apple%'` 45 >>> Column('premarket_change').not_empty() # same as `Column('premarket_change') != None` 46 >>> Column('earnings_release_next_trading_date_fq').in_day_range(0, 0) # same day 47 """ 48 49 def __init__(self, name: str) -> None: 50 self.name = name 51 52 @staticmethod 53 def _extract_name(obj) -> ...: 54 if isinstance(obj, Column): 55 return obj.name 56 return obj 57 58 def __gt__(self, other) -> FilterOperationDict: 59 return {'left': self.name, 'operation': 'greater', 'right': self._extract_name(other)} 60 61 def __ge__(self, other) -> FilterOperationDict: 62 return {'left': self.name, 'operation': 'egreater', 'right': self._extract_name(other)} 63 64 def __lt__(self, other) -> FilterOperationDict: 65 return {'left': self.name, 'operation': 'less', 'right': self._extract_name(other)} 66 67 def __le__(self, other) -> FilterOperationDict: 68 return {'left': self.name, 'operation': 'eless', 'right': self._extract_name(other)} 69 70 def __eq__(self, other) -> FilterOperationDict: # pyright: ignore [reportIncompatibleMethodOverride] 71 return {'left': self.name, 'operation': 'equal', 'right': self._extract_name(other)} 72 73 def __ne__(self, other) -> FilterOperationDict: # pyright: ignore [reportIncompatibleMethodOverride] 74 return {'left': self.name, 'operation': 'nequal', 'right': self._extract_name(other)} 75 76 def crosses(self, other) -> FilterOperationDict: 77 return {'left': self.name, 'operation': 'crosses', 'right': self._extract_name(other)} 78 79 def crosses_above(self, other) -> FilterOperationDict: 80 return {'left': self.name, 'operation': 'crosses_above', 'right': self._extract_name(other)} 81 82 def crosses_below(self, other) -> FilterOperationDict: 83 return {'left': self.name, 'operation': 'crosses_below', 'right': self._extract_name(other)} 84 85 def between(self, left, right) -> FilterOperationDict: 86 return { 87 'left': self.name, 88 'operation': 'in_range', 89 'right': [self._extract_name(left), self._extract_name(right)], 90 } 91 92 def not_between(self, left, right) -> FilterOperationDict: 93 return { 94 'left': self.name, 95 'operation': 'not_in_range', 96 'right': [self._extract_name(left), self._extract_name(right)], 97 } 98 99 def isin(self, values: Iterable) -> FilterOperationDict: 100 return {'left': self.name, 'operation': 'in_range', 'right': list(values)} 101 102 def not_in(self, values: Iterable) -> FilterOperationDict: 103 return {'left': self.name, 'operation': 'not_in_range', 'right': list(values)} 104 105 def has(self, values: str | list[str]) -> FilterOperationDict: 106 """ 107 Field contains any of the values 108 109 (it's the same as `isin()`, except that it works on fields of type `set`) 110 """ 111 return {'left': self.name, 'operation': 'has', 'right': values} 112 113 def has_none_of(self, values: str | list[str]) -> FilterOperationDict: 114 """ 115 Field doesn't contain any of the values 116 117 (it's the same as `not_in()`, except that it works on fields of type `set`) 118 """ 119 return {'left': self.name, 'operation': 'has_none_of', 'right': values} 120 121 def in_day_range(self, a: int, b: int) -> FilterOperationDict: 122 return {'left': self.name, 'operation': 'in_day_range', 'right': [a, b]} 123 124 def in_week_range(self, a: int, b: int) -> FilterOperationDict: 125 return {'left': self.name, 'operation': 'in_week_range', 'right': [a, b]} 126 127 def in_month_range(self, a: int, b: int) -> FilterOperationDict: 128 return {'left': self.name, 'operation': 'in_month_range', 'right': [a, b]} 129 130 def above_pct(self, column: Column | str, pct: float) -> FilterOperationDict: 131 """ 132 Examples: 133 134 The closing price is higher than the VWAP by more than 3% 135 >>> Column('close').above_pct('VWAP', 1.03) 136 137 closing price is above the 52-week-low by more than 150% 138 >>> Column('close').above_pct('price_52_week_low', 2.5) 139 """ 140 return { 141 'left': self.name, 142 'operation': 'above%', 143 'right': [self._extract_name(column), pct], 144 } 145 146 def below_pct(self, column: Column | str, pct: float) -> FilterOperationDict: 147 """ 148 Examples: 149 150 The closing price is lower than the VWAP by 3% or more 151 >>> Column('close').below_pct('VWAP', 1.03) 152 """ 153 return { 154 'left': self.name, 155 'operation': 'below%', 156 'right': [self._extract_name(column), pct], 157 } 158 159 def between_pct( 160 self, column: Column | str, pct1: float, pct2: Optional[float] = None 161 ) -> FilterOperationDict: 162 """ 163 Examples: 164 165 The percentage change between the Close and the EMA is between 20% and 50% 166 >>> Column('close').between_pct('EMA200', 1.2, 1.5) 167 """ 168 return { 169 'left': self.name, 170 'operation': 'in_range%', 171 'right': [self._extract_name(column), pct1, pct2], 172 } 173 174 def not_between_pct( 175 self, column: Column | str, pct1: float, pct2: Optional[float] = None 176 ) -> FilterOperationDict: 177 """ 178 Examples: 179 180 The percentage change between the Close and the EMA is between 20% and 50% 181 >>> Column('close').not_between_pct('EMA200', 1.2, 1.5) 182 """ 183 return { 184 'left': self.name, 185 'operation': 'not_in_range%', 186 'right': [self._extract_name(column), pct1, pct2], 187 } 188 189 def like(self, other) -> FilterOperationDict: 190 return {'left': self.name, 'operation': 'match', 'right': self._extract_name(other)} 191 192 def not_like(self, other) -> FilterOperationDict: 193 return {'left': self.name, 'operation': 'nmatch', 'right': self._extract_name(other)} 194 195 def empty(self) -> FilterOperationDict: 196 # it seems like the `right` key is optional 197 return {'left': self.name, 'operation': 'empty', 'right': None} 198 199 def not_empty(self) -> FilterOperationDict: 200 """ 201 This method can be used to check if a field is not None/null. 202 """ 203 return {'left': self.name, 'operation': 'nempty', 'right': None} 204 205 def __repr__(self) -> str: 206 return f'< Column({self.name!r}) >' 207 208 209col = Column # create a short alias for convenience
12class Column: 13 """ 14 A Column object represents a field in the tradingview stock screener, 15 and it's used in SELECT queries and WHERE queries with the `Query` object. 16 17 A `Column` supports all the comparison operations: 18 `<`, `<=`, `>`, `>=`, `==`, `!=`, and also other methods like `between()`, `isin()`, etc. 19 20 Examples: 21 22 Some of the operations you can do: 23 >>> Column('close') > 2.5 24 >>> Column('High.All') <= 'high' 25 >>> Column('high') > 'VWAP' 26 >>> Column('high') > Column('VWAP') # same thing as above 27 >>> Column('is_primary') == True 28 >>> Column('exchange') != 'OTC' 29 30 >>> Column('close').above_pct('VWAP', 1.03) 31 >>> Column('close').above_pct('price_52_week_low', 2.5) 32 >>> Column('close').below_pct('VWAP', 1.03) 33 >>> Column('close').between_pct('EMA200', 1.2, 1.5) 34 >>> Column('close').not_between_pct('EMA200', 1.2, 1.5) 35 36 >>> Column('close').between(2.5, 15) 37 >>> Column('close').between('EMA5', 'EMA20') 38 39 >>> Column('type').isin(['stock', 'fund']) 40 >>> Column('exchange').isin(['AMEX', 'NASDAQ', 'NYSE']) 41 >>> Column('sector').not_in(['Health Technology', 'Health Services']) 42 >>> Column('typespecs').has(['common']), 43 >>> Column('typespecs').has_none_of(['reit', 'etn', 'etf']), 44 45 >>> Column('description').like('apple') # the same as `description LIKE '%apple%'` 46 >>> Column('premarket_change').not_empty() # same as `Column('premarket_change') != None` 47 >>> Column('earnings_release_next_trading_date_fq').in_day_range(0, 0) # same day 48 """ 49 50 def __init__(self, name: str) -> None: 51 self.name = name 52 53 @staticmethod 54 def _extract_name(obj) -> ...: 55 if isinstance(obj, Column): 56 return obj.name 57 return obj 58 59 def __gt__(self, other) -> FilterOperationDict: 60 return {'left': self.name, 'operation': 'greater', 'right': self._extract_name(other)} 61 62 def __ge__(self, other) -> FilterOperationDict: 63 return {'left': self.name, 'operation': 'egreater', 'right': self._extract_name(other)} 64 65 def __lt__(self, other) -> FilterOperationDict: 66 return {'left': self.name, 'operation': 'less', 'right': self._extract_name(other)} 67 68 def __le__(self, other) -> FilterOperationDict: 69 return {'left': self.name, 'operation': 'eless', 'right': self._extract_name(other)} 70 71 def __eq__(self, other) -> FilterOperationDict: # pyright: ignore [reportIncompatibleMethodOverride] 72 return {'left': self.name, 'operation': 'equal', 'right': self._extract_name(other)} 73 74 def __ne__(self, other) -> FilterOperationDict: # pyright: ignore [reportIncompatibleMethodOverride] 75 return {'left': self.name, 'operation': 'nequal', 'right': self._extract_name(other)} 76 77 def crosses(self, other) -> FilterOperationDict: 78 return {'left': self.name, 'operation': 'crosses', 'right': self._extract_name(other)} 79 80 def crosses_above(self, other) -> FilterOperationDict: 81 return {'left': self.name, 'operation': 'crosses_above', 'right': self._extract_name(other)} 82 83 def crosses_below(self, other) -> FilterOperationDict: 84 return {'left': self.name, 'operation': 'crosses_below', 'right': self._extract_name(other)} 85 86 def between(self, left, right) -> FilterOperationDict: 87 return { 88 'left': self.name, 89 'operation': 'in_range', 90 'right': [self._extract_name(left), self._extract_name(right)], 91 } 92 93 def not_between(self, left, right) -> FilterOperationDict: 94 return { 95 'left': self.name, 96 'operation': 'not_in_range', 97 'right': [self._extract_name(left), self._extract_name(right)], 98 } 99 100 def isin(self, values: Iterable) -> FilterOperationDict: 101 return {'left': self.name, 'operation': 'in_range', 'right': list(values)} 102 103 def not_in(self, values: Iterable) -> FilterOperationDict: 104 return {'left': self.name, 'operation': 'not_in_range', 'right': list(values)} 105 106 def has(self, values: str | list[str]) -> FilterOperationDict: 107 """ 108 Field contains any of the values 109 110 (it's the same as `isin()`, except that it works on fields of type `set`) 111 """ 112 return {'left': self.name, 'operation': 'has', 'right': values} 113 114 def has_none_of(self, values: str | list[str]) -> FilterOperationDict: 115 """ 116 Field doesn't contain any of the values 117 118 (it's the same as `not_in()`, except that it works on fields of type `set`) 119 """ 120 return {'left': self.name, 'operation': 'has_none_of', 'right': values} 121 122 def in_day_range(self, a: int, b: int) -> FilterOperationDict: 123 return {'left': self.name, 'operation': 'in_day_range', 'right': [a, b]} 124 125 def in_week_range(self, a: int, b: int) -> FilterOperationDict: 126 return {'left': self.name, 'operation': 'in_week_range', 'right': [a, b]} 127 128 def in_month_range(self, a: int, b: int) -> FilterOperationDict: 129 return {'left': self.name, 'operation': 'in_month_range', 'right': [a, b]} 130 131 def above_pct(self, column: Column | str, pct: float) -> FilterOperationDict: 132 """ 133 Examples: 134 135 The closing price is higher than the VWAP by more than 3% 136 >>> Column('close').above_pct('VWAP', 1.03) 137 138 closing price is above the 52-week-low by more than 150% 139 >>> Column('close').above_pct('price_52_week_low', 2.5) 140 """ 141 return { 142 'left': self.name, 143 'operation': 'above%', 144 'right': [self._extract_name(column), pct], 145 } 146 147 def below_pct(self, column: Column | str, pct: float) -> FilterOperationDict: 148 """ 149 Examples: 150 151 The closing price is lower than the VWAP by 3% or more 152 >>> Column('close').below_pct('VWAP', 1.03) 153 """ 154 return { 155 'left': self.name, 156 'operation': 'below%', 157 'right': [self._extract_name(column), pct], 158 } 159 160 def between_pct( 161 self, column: Column | str, pct1: float, pct2: Optional[float] = None 162 ) -> FilterOperationDict: 163 """ 164 Examples: 165 166 The percentage change between the Close and the EMA is between 20% and 50% 167 >>> Column('close').between_pct('EMA200', 1.2, 1.5) 168 """ 169 return { 170 'left': self.name, 171 'operation': 'in_range%', 172 'right': [self._extract_name(column), pct1, pct2], 173 } 174 175 def not_between_pct( 176 self, column: Column | str, pct1: float, pct2: Optional[float] = None 177 ) -> FilterOperationDict: 178 """ 179 Examples: 180 181 The percentage change between the Close and the EMA is between 20% and 50% 182 >>> Column('close').not_between_pct('EMA200', 1.2, 1.5) 183 """ 184 return { 185 'left': self.name, 186 'operation': 'not_in_range%', 187 'right': [self._extract_name(column), pct1, pct2], 188 } 189 190 def like(self, other) -> FilterOperationDict: 191 return {'left': self.name, 'operation': 'match', 'right': self._extract_name(other)} 192 193 def not_like(self, other) -> FilterOperationDict: 194 return {'left': self.name, 'operation': 'nmatch', 'right': self._extract_name(other)} 195 196 def empty(self) -> FilterOperationDict: 197 # it seems like the `right` key is optional 198 return {'left': self.name, 'operation': 'empty', 'right': None} 199 200 def not_empty(self) -> FilterOperationDict: 201 """ 202 This method can be used to check if a field is not None/null. 203 """ 204 return {'left': self.name, 'operation': 'nempty', 'right': None} 205 206 def __repr__(self) -> str: 207 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 you can do:
>>> Column('close') > 2.5
>>> Column('High.All') <= 'high'
>>> Column('high') > 'VWAP'
>>> Column('high') > Column('VWAP') # same thing as above
>>> Column('is_primary') == True
>>> Column('exchange') != 'OTC'
>>> Column('close').above_pct('VWAP', 1.03)
>>> Column('close').above_pct('price_52_week_low', 2.5)
>>> Column('close').below_pct('VWAP', 1.03)
>>> Column('close').between_pct('EMA200', 1.2, 1.5)
>>> Column('close').not_between_pct('EMA200', 1.2, 1.5)
>>> Column('close').between(2.5, 15)
>>> Column('close').between('EMA5', 'EMA20')
>>> Column('type').isin(['stock', 'fund'])
>>> Column('exchange').isin(['AMEX', 'NASDAQ', 'NYSE'])
>>> Column('sector').not_in(['Health Technology', 'Health Services'])
>>> Column('typespecs').has(['common']),
>>> Column('typespecs').has_none_of(['reit', 'etn', 'etf']),
>>> Column('description').like('apple') # the same as `description LIKE '%apple%'`
>>> Column('premarket_change').not_empty() # same as `Column('premarket_change') != None`
>>> Column('earnings_release_next_trading_date_fq').in_day_range(0, 0) # same day
106 def has(self, values: str | list[str]) -> FilterOperationDict: 107 """ 108 Field contains any of the values 109 110 (it's the same as `isin()`, except that it works on fields of type `set`) 111 """ 112 return {'left': self.name, 'operation': 'has', 'right': values}
Field contains any of the values
(it's the same as isin()
, except that it works on fields of type set
)
114 def has_none_of(self, values: str | list[str]) -> FilterOperationDict: 115 """ 116 Field doesn't contain any of the values 117 118 (it's the same as `not_in()`, except that it works on fields of type `set`) 119 """ 120 return {'left': self.name, 'operation': 'has_none_of', 'right': values}
Field doesn't contain any of the values
(it's the same as not_in()
, except that it works on fields of type set
)
131 def above_pct(self, column: Column | str, pct: float) -> FilterOperationDict: 132 """ 133 Examples: 134 135 The closing price is higher than the VWAP by more than 3% 136 >>> Column('close').above_pct('VWAP', 1.03) 137 138 closing price is above the 52-week-low by more than 150% 139 >>> Column('close').above_pct('price_52_week_low', 2.5) 140 """ 141 return { 142 'left': self.name, 143 'operation': 'above%', 144 'right': [self._extract_name(column), pct], 145 }
Examples:
The closing price is higher than the VWAP by more than 3%
>>> Column('close').above_pct('VWAP', 1.03)
closing price is above the 52-week-low by more than 150%
>>> Column('close').above_pct('price_52_week_low', 2.5)
147 def below_pct(self, column: Column | str, pct: float) -> FilterOperationDict: 148 """ 149 Examples: 150 151 The closing price is lower than the VWAP by 3% or more 152 >>> Column('close').below_pct('VWAP', 1.03) 153 """ 154 return { 155 'left': self.name, 156 'operation': 'below%', 157 'right': [self._extract_name(column), pct], 158 }
Examples:
The closing price is lower than the VWAP by 3% or more
>>> Column('close').below_pct('VWAP', 1.03)
160 def between_pct( 161 self, column: Column | str, pct1: float, pct2: Optional[float] = None 162 ) -> FilterOperationDict: 163 """ 164 Examples: 165 166 The percentage change between the Close and the EMA is between 20% and 50% 167 >>> Column('close').between_pct('EMA200', 1.2, 1.5) 168 """ 169 return { 170 'left': self.name, 171 'operation': 'in_range%', 172 'right': [self._extract_name(column), pct1, pct2], 173 }
Examples:
The percentage change between the Close and the EMA is between 20% and 50%
>>> Column('close').between_pct('EMA200', 1.2, 1.5)
175 def not_between_pct( 176 self, column: Column | str, pct1: float, pct2: Optional[float] = None 177 ) -> FilterOperationDict: 178 """ 179 Examples: 180 181 The percentage change between the Close and the EMA is between 20% and 50% 182 >>> Column('close').not_between_pct('EMA200', 1.2, 1.5) 183 """ 184 return { 185 'left': self.name, 186 'operation': 'not_in_range%', 187 'right': [self._extract_name(column), pct1, pct2], 188 }
Examples:
The percentage change between the Close and the EMA is between 20% and 50%
>>> Column('close').not_between_pct('EMA200', 1.2, 1.5)