remove finviz (#7577)

This commit is contained in:
Danglewood
2026-09-14 09:20:46 -07:00
committed by GitHub
parent 0aa6039532
commit 869edd20ee
51 changed files with 0 additions and 18358 deletions

View File

@@ -1,111 +0,0 @@
# OpenBB Finviz Data Provider Extension
This extension integrates the [Finviz](https://finviz.com/) data provider into the OpenBB Platform.
It will install, [finvizfinance](https://github.com/lit26/finvizfinance/), to power the functions.
## Installation
To install the extension:
```bash
pip install openbb-finviz
```
## Endpoints
- obb.equity.compare.groups
- obb.equity.estimates.price_target
- obb.equity.fundamental.metrics
- obb.equity.profile
- obb.equity.price.performance
- obb.equity.screener
## Screener
The screener is a faithful replication of the public-facing stock screener - <https://finviz.com/screener.ashx>
Some options are directly accessible through the function parameters, all others are exposed via `presets` or `filters_dict`.
The filters list below are exposed in the function, with choices visible in the docstring:
- `exchange`
- `index`
- `sector`
- `industry`
- `mktcap`
- `recommendation` (analyst's mean score from 1-5)
- `signal` (same as the "Signal" on the Finviz page)
When the function is run without any parameters, it will default to the "top_gainers" signal.
```python
res = obb.equity.screener(provider="finviz")
```
### Metric
The `metric` parameter defines the type of data fields to return. Choices are:
- `overview`
- `ownership`
- `performance`
- `technical`
- `valuation`
Default is, "overview".
```python
res = obb.equity.screener(provider="finviz", metric="performance")
```
### Preset Files
Presets can be created and customized in the "OpenBBUserData" folder. Template and default presets are created on the first run of the function.
Files are loaded on runtime, changes are effective without restarting the Python interpreter.
The `preset` parameter will override all others, except `metric` and `limit`.
Run the function to create the template and default presets in your `OpenBBUserData` folder.
Presets from the legacy OpenBB Terminal will continue to work, simply move your presets into the folder below.
```python
res = obb.equity.screener(provider="finviz", index="nasdaq")
```
Then find the presets here: `$HOME/OpenBBUserData/presets/finviz`
```python
res = obb.equity.screener(provider="finviz", preset="short_squeeze")
```
### Filters Dict
The `filters_dict` parameter acts as an alternative to `preset`, accepting a dictionary or JSON encoded string.
```python
res = obb.equity.screener(provider="finviz", filters_dict={"Index": "NASDAQ 100"})
```
Or as a JSON:
```python
res = obb.equity.screener(provider="finviz", filters_dict='{"Index": "NASDAQ 100"}')
```
When using the Fast API, this is sent in the request body.
### Error Messages
All parameters are validated, incorrect keys and choices will raise an error with information to help correct. For example:
```python
obb.equity.screener(provider="finviz", filters_dict='{"Index": "NASDAQ"}')
```
```console
Invalid filter option 'NASDAQ'. Possible filter options: ['Any', 'S&P 500', 'NASDAQ 100', 'DJIA', 'RUSSELL 2000']
```
Read the OpenBB Platform documentation [here](https://docs.openbb.co)

View File

@@ -1 +0,0 @@
"""Finviz provider."""

View File

@@ -1,26 +0,0 @@
"""Finviz provider module."""
from openbb_core.provider.abstract.provider import Provider
from openbb_finviz.models.compare_groups import FinvizCompareGroupsFetcher
from openbb_finviz.models.equity_profile import FinvizEquityProfileFetcher
from openbb_finviz.models.equity_screener import FinvizEquityScreenerFetcher
from openbb_finviz.models.key_metrics import FinvizKeyMetricsFetcher
from openbb_finviz.models.price_performance import FinvizPricePerformanceFetcher
from openbb_finviz.models.price_target import FinvizPriceTargetFetcher
finviz_provider = Provider(
name="finviz",
website="https://finviz.com",
description="Unofficial Finviz API - https://github.com/lit26/finvizfinance/releases",
credentials=None,
fetcher_dict={
"CompareGroups": FinvizCompareGroupsFetcher,
"EtfPricePerformance": FinvizPricePerformanceFetcher,
"EquityInfo": FinvizEquityProfileFetcher,
"EquityScreener": FinvizEquityScreenerFetcher,
"KeyMetrics": FinvizKeyMetricsFetcher,
"PricePerformance": FinvizPricePerformanceFetcher,
"PriceTarget": FinvizPriceTargetFetcher,
},
repr_name="FinViz",
)

View File

@@ -1 +0,0 @@
"""Finviz Provider models."""

View File

@@ -1,273 +0,0 @@
"""Finviz Compare Groups Model."""
# pylint: disable=unused-argument
from typing import Any
from openbb_core.provider.abstract.data import ForceInt
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.compare_groups import (
CompareGroupsData,
CompareGroupsQueryParams,
)
from openbb_core.provider.utils.descriptions import DATA_DESCRIPTIONS
from openbb_finviz.utils.definitions import GROUPS, GROUPS_DICT, METRICS
from pydantic import Field, field_validator
GROUPS_CHOICES = sorted(list(GROUPS_DICT))
class FinvizCompareGroupsQueryParams(CompareGroupsQueryParams):
"""Finviz Compare Groups Query Params."""
__json_schema_extra__ = {
"group": {
"multiple_items_allowed": False,
"choices": GROUPS_CHOICES,
},
"metric": {
"multiple_items_allowed": False,
"choices": ["performance", "valuation", "overview"],
},
}
group: GROUPS = Field(
default="sector",
description="US-listed stocks only."
+ " When an individual sector is selected, it is broken down by industry."
+ " The default is 'sector'.",
)
metric: METRICS = Field(
default="performance",
description="Statistical metric to return. Select from: ['performance', 'valuation', 'overview']"
+ " The default is 'performance'.",
)
class FinvizCompareGroupsData(CompareGroupsData):
"""Finviz Compare Groups Data."""
__alias_dict__ = {
"name": "Name",
"stocks": "Number of Stocks",
"market_cap": "Market Cap",
"performance_1d": "Change",
"performance_1w": "Perf Week",
"performance_1m": "Perf Month",
"performance_3m": "Perf Quart",
"performance_6m": "Perf Half",
"performance_1y": "Perf Year",
"performance_ytd": "Perf YTD",
"volume": "Volume",
"volume_average": "Avg Volume",
"volume_relative": "Rel Volume",
"pe": "P/E",
"forward_pe": "Fwd P/E",
"peg": "PEG",
"eps_growth_past_5y": "EPS past 5Y",
"eps_growth_next_5y": "EPS next 5Y",
"sales_growth_past_5_years": "Sales past 5Y",
"price_to_sales": "P/S",
"price_to_book": "P/B",
"price_to_cash": "P/C",
"price_to_free_cash_flow": "P/FCF",
"dividend_yield": "Dividend",
"float_short": "Float Short",
"analyst_recommendation": "Recom",
}
name: str = Field(description="Name or label of the group.")
stocks: int | None = Field(
default=None,
description="The number of stocks in the group.",
)
market_cap: ForceInt | None = Field(
default=None,
description="The market cap of the group.",
)
performance_1d: float | None = Field(
default=None,
description="The performance in the last day, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
performance_1w: float | None = Field(
default=None,
description="The performance in the last week, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
performance_1m: float | None = Field(
default=None,
description="The performance in the last month, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
performance_3m: float | None = Field(
default=None,
description="The performance in the last quarter, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
performance_6m: float | None = Field(
default=None,
description="The performance in the last half year, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
performance_1y: float | None = Field(
default=None,
description="The performance in the last year, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
performance_ytd: float | None = Field(
default=None,
description="The performance in the year to date, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
dividend_yield: float | None = Field(
default=None,
description="The dividend yield of the group, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
pe: float | None = Field(
default=None,
description="The P/E ratio of the group.",
)
forward_pe: float | None = Field(
default=None,
description="The forward P/E ratio of the group.",
)
peg: float | None = Field(
default=None,
description="The PEG ratio of the group.",
)
eps_growth_past_5y: float | None = Field(
default=None,
description="The EPS growth of the group for the past 5 years, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
eps_growth_next_5y: float | None = Field(
default=None,
description="The estimated EPS growth of the groupo for the next 5 years,"
+ " as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
sales_growth_past_5y: float | None = Field(
default=None,
description="The sales growth of the group for the past 5 years, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
float_short: float | None = Field(
default=None,
description="The percent of the float shorted for the group, as a normalized value.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
analyst_recommendation: float | None = Field(
default=None,
description="The analyst consensus, on a scale of 1-5 where 1 is a buy and 5 is a sell.",
)
volume: ForceInt | None = Field(
default=None, description=DATA_DESCRIPTIONS.get("volume", "")
)
volume_average: ForceInt | None = Field(
default=None,
description="The 3-month average volume of the group.",
)
volume_relative: float | None = Field(
default=None,
description="The relative volume compared to the 3-month average volume.",
)
@field_validator(
"performance_1w",
"performance_1m",
"performance_3m",
"performance_6m",
"performance_1y",
"performance_ytd",
"dividend_yield",
"eps_growth_past_5y",
"eps_growth_next_5y",
"sales_growth_past_5y",
"float_short",
mode="before",
check_fields=False,
)
@classmethod
def validate_percent_values(cls, v):
"""Check fields for the presence of a % character."""
if v is not None and "%" in str(v):
return float(v.replace("%", "")) / 100
return v if v else None
@field_validator("market_cap", "volume", mode="before", check_fields=False)
@classmethod
def validate_abbreviated_numbers(cls, v):
"""Check for abbreviated string values."""
if v is not None and isinstance(v, str):
v = (
v.replace("M", "e+6")
.replace("B", "e+9")
.replace("T", "e+12")
.replace("K", "e+3")
)
return int(float(v))
return v if v else None
class FinvizCompareGroupsFetcher(
Fetcher[FinvizCompareGroupsQueryParams, list[FinvizCompareGroupsData]]
):
"""Finviz Compare Groups Fetcher."""
@staticmethod
def transform_query(params: dict[str, Any]) -> FinvizCompareGroupsQueryParams:
"""Transform the query params."""
if params.get("group") is None:
params["group"] = "sector"
if params.get("metric") is None:
params["metric"] = "performance"
return FinvizCompareGroupsQueryParams(**params)
@staticmethod
def extract_data(
query: FinvizCompareGroupsQueryParams,
credentials: dict[str, str] | None,
**kwargs: Any,
) -> list[dict]:
"""Extract the raw data from Finviz."""
# pylint: disable=import-outside-toplevel
from finvizfinance import util
from finvizfinance.group import Overview, Performance, Valuation
from openbb_core.provider.utils.helpers import get_requests_session
from pandas import DataFrame
util.session = get_requests_session()
results: list = []
data = DataFrame()
if query.metric == "performance":
data = Performance().screener_view(
group=GROUPS_DICT[query.group], # type: ignore
order="Performance (Week)",
)
if query.metric == "valuation":
data = Valuation().screener_view(
group=GROUPS_DICT[query.group], # type: ignore
order="Forward Price/Earnings",
)
if query.metric == "overview":
data = Overview().screener_view(
group=GROUPS_DICT[query.group], # type: ignore
order="Change",
)
if not data.empty:
results = data.fillna("N/A").replace("N/A", None).to_dict(orient="records")
return results
@staticmethod
def transform_data(
query: FinvizCompareGroupsQueryParams,
data: list[dict],
**kwargs: Any,
) -> list[FinvizCompareGroupsData]:
"""Transform the raw data."""
return [FinvizCompareGroupsData.model_validate(d) for d in data]

View File

@@ -1,241 +0,0 @@
"""Finviz Equity Profile Model."""
# pylint: disable=unused-argument
from typing import Any
from warnings import warn
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.equity_info import (
EquityInfoData,
EquityInfoQueryParams,
)
from pydantic import Field
class FinvizEquityProfileQueryParams(EquityInfoQueryParams):
"""
Finviz Equity Profile Query.
Source: https://finviz.com/screener.ashx
"""
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
class FinvizEquityProfileData(EquityInfoData):
"""Finviz Equity Profile Data."""
__alias_dict__ = {
"stock_exchange": "exchange",
}
index: str | None = Field(
default=None,
description="Included in indices - i.e., Dow, Nasdaq, or S&P.",
)
optionable: str | None = Field(
default=None,
description="Whether options trade against the ticker.",
)
shortable: str | None = Field(
default=None,
description="If the asset is shortable.",
)
shares_outstanding: str | None = Field(
default=None,
description="The number of shares outstanding, as an abbreviated string.",
)
shares_float: str | None = Field(
default=None,
description="The number of shares in the public float, as an abbreviated string.",
)
short_interest: str | None = Field(
default=None,
description="The last reported number of shares sold short, as an abbreviated string.",
)
institutional_ownership: float | None = Field(
default=None,
description="The institutional ownership of the stock, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
market_cap: str | None = Field(
default=None,
description="The market capitalization of the stock, as an abbreviated string.",
)
dividend_yield: float | None = Field(
default=None,
description="The dividend yield of the stock, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
earnings_date: str | None = Field(
default=None,
description="The last, or next confirmed, earnings date and announcement time, as a string."
+ " The format is Nov 02 AMC - for after market close.",
)
beta: float | None = Field(
default=None,
description="The beta of the stock relative to the broad market.",
)
class FinvizEquityProfileFetcher(
Fetcher[FinvizEquityProfileQueryParams, list[FinvizEquityProfileData]]
):
"""Finviz Equity Profile Fetcher."""
@staticmethod
def transform_query(params: dict[str, Any]) -> FinvizEquityProfileQueryParams:
"""Transform the query params."""
return FinvizEquityProfileQueryParams(**params)
@staticmethod
def extract_data(
query: FinvizEquityProfileQueryParams,
credentials: dict[str, str] | None,
**kwargs: Any,
) -> list[dict]:
"""Extract the raw data from Finviz."""
# pylint: disable=import-outside-toplevel
from finvizfinance import util
from finvizfinance.quote import finvizfinance
from openbb_core.app.model.abstract.error import OpenBBError
from openbb_core.provider.utils.errors import EmptyDataError
from openbb_core.provider.utils.helpers import get_requests_session
util.session = get_requests_session()
results: list = []
messages: list = []
def get_one(symbol) -> dict:
"""Get the data for one symbol."""
result: dict = {}
try:
data = finvizfinance(symbol)
fundament = data.ticker_fundament()
description = data.ticker_description()
except Exception as e: # pylint: disable=W0718
messages.append(f"Failed to get data for {symbol} -> {e}")
return result
div_yield = (
float(str(fundament.get("Dividend %", None)).replace("%", "")) / 100
if fundament.get("Dividend %", "-") != "-"
else None
)
inst_own = (
float(str(fundament.get("Inst Own", None)).replace("%", "")) / 100
if fundament.get("Inst Own", "-") != "-"
else None
)
result.update(
{
"symbol": symbol,
"exchange": (
fundament.get("Exchange", None)
if fundament.get("Exchange", "-") != "-"
else None
),
"name": (
fundament.get("Company", None)
if fundament.get("Company", "-") != "-"
else None
),
"sector": (
fundament.get("Sector", None)
if fundament.get("Sector", "-") != "-"
else None
),
"industry_category": (
fundament.get("Industry", None)
if fundament.get("Industry", "-") != "-"
else None
),
"hq_country": (
fundament.get("Country", None)
if fundament.get("Country", "-") != "-"
else None
),
"employees": (
fundament.get("Employees", None)
if fundament.get("Employees", "-") != "-"
else None
),
"index": (
fundament.get("Index", None)
if fundament.get("Index", "-") != "-"
else None
),
"beta": (
fundament.get("Beta", None)
if fundament.get("Beta", "-") != "-"
else None
),
"optionable": (
fundament.get("Optionable", None)
if fundament.get("Optionable", "-") != "-"
else None
),
"shortable": (
fundament.get("Shortable", None)
if fundament.get("Shortable", "-") != "-"
else None
),
"shares_outstanding": (
fundament.get("Shs Outstand", None)
if fundament.get("Shs Outstand", "-") != "-"
else None
),
"shares_float": (
fundament.get("Shs Float", None)
if fundament.get("Shs Float", "-") != "-"
else None
),
"short_interest": (
fundament.get("Short Interest", None)
if fundament.get("Short Interest", "-") != "-"
else None
),
"institutional_ownership": inst_own if inst_own else None,
"market_cap": (
fundament.get("Market Cap", None)
if fundament.get("Market Cap", "-") != "-"
else None
),
"dividend_yield": div_yield if div_yield else None,
"earnings_date": (
fundament.get("Earnings", None)
if fundament.get("Earnings", "-") != "-"
else None
),
"long_description": description if description else None,
}
)
return result
symbols = query.symbol.split(",")
for symbol in symbols:
result = get_one(symbol)
if result is not None and result:
results.append(result)
if not results and messages:
raise OpenBBError("\n".join(messages))
if not results and not messages:
raise EmptyDataError("No data was returned for any symbol")
if results and messages:
for message in messages:
warn(message)
return results
@staticmethod
def transform_data(
query: FinvizEquityProfileQueryParams,
data: list[dict],
**kwargs: Any,
) -> list[FinvizEquityProfileData]:
"""Transform and validate the raw data."""
return [FinvizEquityProfileData.model_validate(d) for d in data]

View File

@@ -1,737 +0,0 @@
"""Finviz Equity Screener Model."""
# pylint: disable=unused-argument,too-many-statements,too-many-branches,too-many-locals
from typing import Any, Literal
from warnings import warn
from openbb_core.app.model.abstract.error import OpenBBError
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.equity_screener import (
EquityScreenerData,
EquityScreenerQueryParams,
)
from openbb_core.provider.utils.descriptions import (
DATA_DESCRIPTIONS,
QUERY_DESCRIPTIONS,
)
from openbb_core.provider.utils.errors import EmptyDataError
from openbb_finviz.utils.screener_helper import (
EXCHANGE_MAP,
INDEX_MAP,
INDUSTRY_MAP,
MARKET_CAP_MAP,
RECOMMENDATION_MAP,
SECTOR_MAP,
SIGNALS,
SIGNALS_DESC_STR,
MarketCap,
Recommendation,
Sectors,
)
from pydantic import Field, field_validator, model_validator
class FinvizEquityScreenerQueryParams(EquityScreenerQueryParams):
"""Finviz Equity Screener Query Params."""
__json_schema_extra__ = {
"metric": {
"choices": [
"overview",
"valuation",
"financial",
"ownership",
"performance",
"technical",
],
},
"signal": {"choices": SIGNALS},
"exchange": {"choices": list(EXCHANGE_MAP)},
"index": {"choices": list(INDEX_MAP)},
"sector": {"choices": list(SECTOR_MAP)},
"industry": {"choices": list(INDUSTRY_MAP)},
"market_cap": {"choices": list(MARKET_CAP_MAP)},
"recommendation": {"choices": list(RECOMMENDATION_MAP)},
}
metric: Literal[
"overview",
"valuation",
"financial",
"ownership",
"performance",
"technical",
] = Field(
default="overview",
description="The data group to return, default is 'overview'.",
)
exchange: Literal["all", "amex", "nasdaq", "nyse"] = Field(
default="all", description="Filter by exchange."
)
index: Literal["all", "dow", "nasdaq", "sp500", "russell"] = Field(
default="all",
description="Filter by index.",
)
sector: Sectors = Field(
default="all",
description="Filter by sector.",
)
industry: str | None = Field(
default="all",
description="Filter by industry.",
)
mktcap: MarketCap = Field(
default="all",
description="Filter by market cap."
+ "\n Mega - > 200B"
+ "\n Large - 10B - 200B"
+ "\n Mid - 2B - 10B"
+ "\n Small - 300M - 2B"
+ "\n Micro - 50M - 300M"
+ "\n Nano - < 50M",
)
recommendation: Recommendation = Field(
default="all", description="Filter by analyst recommendation."
)
signal: str | None = Field(
default=None,
description="The Finviz screener signal to use."
+ " When no parameters are provided, the screener defaults to 'top_gainers'."
+ f" Available signals are:{SIGNALS_DESC_STR}",
)
preset: str | None = Field(
default=None,
description="A configured preset file to use for the query."
+ " This overrides all other query parameters except 'metric', and 'limit'."
+ " Presets (.ini text files) can be created and modified in the '~/OpenBBUserData/finviz/presets' directory."
+ " If the path does not exist, it will be created and populated with the default presets on the first run."
+ " Refer to the file, 'screener_template.ini', for the format and options."
+ "\n\nNote: Syntax of parameters in preset files must follow the template file exactly "
+ " - i.e, Analyst Recom. = Strong Buy (1)",
)
filters_dict: dict | str | None = Field(
default=None,
kw_only=True,
description="A formatted dictionary, or serialized JSON string, of additional filters to apply to the query."
+ " This parameter can be used as an alternative to preset files, and is ignored when a preset is supplied."
+ " Invalid entries will raise an error. Syntax should follow the 'screener_template.ini' file.",
)
limit: int | None = Field(
default=None,
description=QUERY_DESCRIPTIONS.get("limit", ""),
)
@field_validator("signal", mode="before", check_fields=False)
@classmethod
def validate_signal(cls, v):
"""Validate the signal."""
if v is not None and v not in SIGNALS:
raise OpenBBError(
f"Invalid signal '{v}'. Available signals are:\n{SIGNALS_DESC_STR}"
)
return v if v else None
@field_validator("industry", mode="before", check_fields=False)
@classmethod
def validate_industry(cls, v):
"""Validate the industry."""
if v is not None and v not in INDUSTRY_MAP:
raise OpenBBError(
f"Invalid industry '{v}'. Available industries are:\n{', '.join(INDUSTRY_MAP)}"
)
return v if v else None
@field_validator("preset", mode="before", check_fields=False)
@classmethod
def validate_preset(cls, v):
"""Check to reject running template file."""
if v is not None and v == "screener_template":
raise OpenBBError(
f"Invalid preset '{v}'. Please rename the file to use as a preset."
)
return v if v else None
@field_validator("filters_dict", mode="before", check_fields=False)
@classmethod
def validate_filters_dict(cls, v):
"""Validate the filters_dict."""
if isinstance(v, str):
# pylint: disable=import-outside-toplevel
import json
try:
v = json.loads(v)
except json.JSONDecodeError as e:
raise OpenBBError(f"Invalid JSON format for 'filters_dict': {e}") from e
if v is not None and not isinstance(v, dict):
raise OpenBBError(
"Invalid 'filters_dict' format. Must be a dictionary or serialized JSON string."
)
return v
class FinvizEquityScreenerData(EquityScreenerData):
"""Finviz Equity Screener Data. Actual returned data varies by the 'metric' parameter."""
__alias_dict__ = {
"symbol": "Ticker",
"name": "Company",
"earnings_date": "Earnings",
"sector": "Sector",
"industry": "Industry",
"country": "Country",
"shares_outstanding": "Outstanding",
"shares_float": "Float",
"short_interest": "Float Short",
"short_ratio": "Short Ratio",
"insider_ownership": "Insider Own",
"insider_ownership_change": "Insider Trans",
"institutional_ownership": "Inst Own",
"institutional_ownership_change": "Inst Trans",
"analyst_recommendation": "Recom",
"beta": "Beta",
"market_cap": "Market Cap",
"price": "Price",
"change_percent": "Change",
"change_from_open": "from Open",
"gap": "Gap",
"year_high_percent": "52W High",
"year_low_percent": "52W Low",
"sma20_percent": "SMA20",
"sma50_percent": "SMA50",
"sma200_percent": "SMA200",
"rsi": "RSI",
"volume": "Volume",
"volume_avg": "Avg Volume",
"volume_relative": "Rel Volume",
"average_true_range": "ATR",
"price_change_1w": "Perf Week",
"price_change_1m": "Perf Month",
"price_change_3m": "Perf Quart",
"price_change_6m": "Perf Half",
"price_change_1y": "Perf Year",
"price_change_ytd": "Perf YTD",
"volatility_1w": "Volatility W",
"volatility_1m": "Volatility M",
"price_to_earnings": "P/E",
"forward_pe": "Fwd P/E",
"peg_ratio": "PEG",
"price_to_sales": "P/S",
"price_to_book": "P/B",
"price_to_cash": "P/C",
"price_to_free_cash_flow": "P/FCF",
"eps_growth_past_1y": "EPS This Y",
"eps_growth_next_1y": "EPS Next Y",
"eps_growth_past_5y": "EPS Past 5Y",
"eps_growth_next_5y": "EPS Next 5Y",
"sales_growth_past_5y": "Sales Past 5Y",
"dividend_yield": "Dividend",
"return_on_assets": "ROA",
"return_on_equity": "ROE",
"return_on_investment": "ROI",
"current_ratio": "Curr R",
"quick_ratio": "Quick R",
"long_term_debt_to_equity": "LTDebt/Eq",
"debt_to_equity": "Debt/Eq",
"gross_margin": "Gross M",
"operating_margin": "Oper M",
"profit_margin": "Profit M",
}
earnings_date: str | None = Field(
default=None,
description="Earnings date, where 'a' and 'b' mean after and before market close, respectively.",
)
country: str | None = Field(
default=None,
description="Country of the company.",
)
sector: str | None = Field(
default=None,
description="Sector of the company.",
)
industry: str | None = Field(
default=None,
description="Industry of the company.",
)
beta: float | None = Field(
default=None,
description="Beta of the stock.",
)
analyst_recommendation: float | None = Field(
default=None,
description="Analyst's mean recommendation. (1=Buy 5=Sell).",
)
market_cap: float | None = Field(
default=None,
description="Market capitalization of the company.",
json_schema_extra={"x-unit_measurement": "currency"},
)
price: float | None = Field(
default=None,
description="Price of a share.",
json_schema_extra={"x-unit_measurement": "currency"},
)
change_percent: float | None = Field(
default=None,
description="Price change percentage.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
change_from_open: float | None = Field(
default=None,
description="Price change percentage, from the opening price.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
gap: float | None = Field(
default=None,
description="Price gap percentage, from the previous close.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
volume: int | float | None = Field(
default=None,
description=DATA_DESCRIPTIONS.get("volume", ""),
)
volume_avg: int | float | None = Field(
default=None,
description="3-month average daily volume.",
)
volume_relative: float | None = Field(
default=None,
description="Current volume relative to the average.",
)
average_true_range: float | None = Field(
default=None,
description="Average true range (14).",
json_schema_extra={"x-unit_measurement:": "currency"},
)
price_change_1w: float | None = Field(
default=None,
description="One-week price return.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
price_change_1m: float | None = Field(
default=None,
description="One-month price return.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
price_change_3m: float | None = Field(
default=None,
description="Three-month price return.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
price_change_6m: float | None = Field(
default=None,
description="Six-month price return.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
price_change_1y: float | None = Field(
default=None,
description="One-year price return.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
price_change_ytd: float | None = Field(
default=None,
description="Year-to-date price return.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
volatility_1w: float | None = Field(
default=None,
description="One-week volatility.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
volatility_1m: float | None = Field(
default=None,
description="One-month volatility.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
year_high_percent: float | None = Field(
default=None,
description="Percent difference from current price to the 52-week high.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
year_low_percent: float | None = Field(
default=None,
description="Percent difference from current price to the 52-week low.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
sma20_percent: float | None = Field(
default=None,
description="Percent difference from current price to the 20-day simple moving average.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
sma50_percent: float | None = Field(
default=None,
description="Percent difference from current price to the 50-day simple moving average.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
sma200_percent: float | None = Field(
default=None,
description="Percent difference from current price to the 200-day simple moving average.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
rsi: float | None = Field(
default=None,
description="Relative strength index (14).",
)
shares_outstanding: int | float | None = Field(
default=None,
description="Number of shares outstanding.",
)
shares_float: int | float | None = Field(
default=None,
description="Number of shares available to trade.",
)
short_interest: float | None = Field(
default=None,
description="Percent of float reported as short.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
short_ratio: float | None = Field(
default=None,
description="Short interest ratio",
)
insider_ownership: float | None = Field(
default=None,
description="Insider ownership as a percentage.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
insider_ownership_change: float | None = Field(
default=None,
description="6-month change in insider ownership percentage.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
institutional_ownership: float | None = Field(
default=None,
description="Institutional ownership as a percentage.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
institutional_ownership_change: float | None = Field(
default=None,
description="3-month change in institutional ownership percentage.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
price_to_earnings: float | None = Field(
default=None,
description="Price to earnings ratio.",
)
forward_pe: float | None = Field(
default=None,
description="Forward price to earnings ratio.",
)
peg_ratio: float | None = Field(
default=None,
description="Price/Earnings-To-Growth (PEG) ratio.",
)
price_to_sales: float | None = Field(
default=None,
description="Price to sales ratio.",
)
price_to_book: float | None = Field(
default=None,
description="Price to book ratio.",
)
price_to_cash: float | None = Field(
default=None,
description="Price to cash ratio.",
)
price_to_fcf: float | None = Field(
default=None,
description="Price to free cash flow ratio.",
)
eps_growth_past_1y: float | None = Field(
default=None,
description="EPS growth for this year.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
eps_growth_next_1y: float | None = Field(
default=None,
description="EPS growth next year.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
eps_growth_past_5y: float | None = Field(
default=None,
description="EPS growth for the previous 5 years.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
eps_growth_next_5y: float | None = Field(
default=None,
description="EPS growth for the next 5 years.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
sales_growth_past_5y: float | None = Field(
default=None,
description="Sales growth for the previous 5 years.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
dividend_yield: float | None = Field(
default=None,
description="Annualized dividend yield.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
return_on_assets: float | None = Field(
default=None,
description="Return on assets.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
return_on_equity: float | None = Field(
default=None,
description="Return on equity.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
return_on_investment: float | None = Field(
default=None,
description="Return on investment.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
current_ratio: float | None = Field(
default=None,
description="Current ratio.",
)
quick_ratio: float | None = Field(
default=None,
description="Quick ratio.",
)
long_term_debt_to_equity: float | None = Field(
default=None,
description="Long term debt to equity ratio.",
)
debt_to_equity: float | None = Field(
default=None,
description="Total debt to equity ratio.",
)
gross_margin: float | None = Field(
default=None,
description="Gross margin.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
operating_margin: float | None = Field(
default=None,
description="Operating margin.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
profit_margin: float | None = Field(
default=None,
description="Profit margin.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
@model_validator(mode="before")
@classmethod
def validate_data(cls, values):
"""Strip % from strings and convert to float."""
for k, v in values.items():
if isinstance(v, str) and v.strip().endswith("%"):
values[k] = float(v.strip().rstrip("%")) / 100
return values
class FinvizEquityScreenerFetcher(
Fetcher[FinvizEquityScreenerQueryParams, list[FinvizEquityScreenerData]]
):
"""Finviz Equity Screener Fetcher."""
@staticmethod
def transform_query(params: dict[str, Any]) -> FinvizEquityScreenerQueryParams:
"""Transform query parameters."""
transformed_params = params.copy()
if not transformed_params.get("filters_dict"):
transformed_params["filters_dict"] = {}
return FinvizEquityScreenerQueryParams(**transformed_params)
@staticmethod
def extract_data( # noqa = PRL0912
query: FinvizEquityScreenerQueryParams,
credentials: dict[str, str] | None,
**kwargs: Any,
) -> list[dict]:
"""Extract data from Finviz."""
# pylint: disable=import-outside-toplevel
import configparser # noqa
from finvizfinance import util
from finvizfinance.screener import (
financial,
overview,
ownership,
performance,
technical,
valuation,
)
from numpy import nan
from openbb_core.provider.utils.helpers import get_requests_session
from openbb_finviz.utils.screener_helper import (
get_preset_choices,
d_check_screener,
d_signals,
)
from pandas import DataFrame
preset = None
util.session = get_requests_session()
try:
data_dir = kwargs.get("preferences", {}).get("data_directory")
preset_choices = get_preset_choices(data_dir)
preset = query.preset
if preset is not None and preset not in preset_choices:
raise OpenBBError(
f"Invalid preset '{preset}'. Available presets are:\n{list(preset_choices)}"
)
except Exception as e:
if preset is not None:
raise e from e
warn(f"Error loading presets -> {e.__class__.__name__}: {e}")
preset = None
data_type = query.metric
ascend = False
limit = query.limit
sleep = 0.1 # For optimized pagination speed without creating too many requests error from Finviz.
sort_by = "Change"
df_screen = DataFrame()
screen_type = {
"overview": overview.Overview,
"valuation": valuation.Valuation,
"financial": financial.Financial,
"ownership": ownership.Ownership,
"performance": performance.Performance,
"technical": technical.Technical,
}
if data_type in screen_type:
screen = screen_type[data_type]()
if preset is not None:
preset_filter = configparser.RawConfigParser()
preset_filter.optionxform = str # type: ignore
preset_filter.read(preset_choices[preset])
d_general = preset_filter["General"]
d_filters = {
**preset_filter["Descriptive"],
**preset_filter["Fundamental"],
**preset_filter["Technical"],
}
for section in ["General", "Descriptive", "Fundamental", "Technical"]:
for key, val in {**preset_filter[section]}.items():
if key not in d_check_screener:
raise OpenBBError(
f"The screener variable {section}.{key} shouldn't exist!\n"
)
if val not in d_check_screener[key]:
raise OpenBBError(
f"Invalid [{section}] {key}={val}. "
f"Choose one of the following options:\n{', '.join(d_check_screener[key])}.\n"
)
d_filters = {k: v for k, v in d_filters.items() if v is not None}
screen.set_filter(filters_dict=d_filters)
asc = None
asc = d_general.get("Ascend")
if asc is not None:
ascend = asc == "true"
df_screen = screen.screener_view(
order=d_general.get("Order", "Change"),
limit=limit if limit else 100000,
ascend=ascend,
sleep_sec=sleep,
verbose=0,
)
# If no preset is supplied, then set the filters based on the query parameters.
else:
if query.signal is not None:
screen.set_filter(signal=d_signals[query.signal])
if query.signal in ["unusual_volume", "most_active"]:
sort_by = "Relative Volume"
elif query.signal == "top_losers":
ascend = True
elif query.signal in ["new_low", "multiple_bottom", "double_bottom"]:
sort_by = "52-Week Low (Relative)"
ascend = True
elif query.signal in ["new_high", "multiple_top", "double_top"]:
sort_by = "52-Week High (Relative)"
elif query.signal == "oversold":
sort_by = "Relative Strength Index (14)"
ascend = True
elif query.signal == "overbought":
sort_by = "Relative Strength Index (14)"
elif query.signal == "most_volatile":
sort_by = "Volatility (Week)"
else:
pass
filters_dict: dict = {}
if query.sector != "all":
filters_dict["Sector"] = SECTOR_MAP[query.sector]
if query.industry != "all":
filters_dict["Industry"] = INDUSTRY_MAP[query.industry] # type: ignore
if query.exchange != "all":
filters_dict["Exchange"] = EXCHANGE_MAP[query.exchange]
if query.index != "all":
filters_dict["Index"] = INDEX_MAP[query.index]
if query.recommendation != "all":
filters_dict["Analyst Recom."] = RECOMMENDATION_MAP[
query.recommendation
]
if query.mktcap != "all":
filters_dict["Market Cap."] = MARKET_CAP_MAP[query.mktcap]
if query.filters_dict is not None:
_filters_dict = query.filters_dict.copy() # type: ignore
order = _filters_dict.pop("Order", None)
asc = _filters_dict.pop("Ascend", None)
if asc:
ascend = asc == "true"
if order:
sort_by = order
filters_dict.update(_filters_dict)
if filters_dict:
screen.set_filter(filters_dict=filters_dict)
if not filters_dict and query.signal is None:
screen.set_filter(signal=d_signals["top_gainers"])
warn(
"No filters or signal provided. Defaulting to 'top_gainers' signal."
+ " Use the preset, 'all_stocks', to explicitly return every stock on Finviz."
+ " Returning 10K symbols can take several minutes."
)
df_screen = screen.screener_view(
order=sort_by,
limit=limit if limit else 100000,
ascend=ascend,
sleep_sec=sleep,
verbose=0,
)
if df_screen is None or df_screen.empty:
raise EmptyDataError(
"No tickers found for the supplied parameters. Try relaxing the constraints."
)
df_screen.columns = [val.strip("\n") for val in df_screen.columns]
# Commas in the company name can cause issues with delimiters.
if "Company" in df_screen.columns:
df_screen["Company"] = df_screen["Company"].str.replace(",", "")
return df_screen.convert_dtypes().replace({nan: None}).to_dict(orient="records")
@staticmethod
def transform_data(
query: FinvizEquityScreenerQueryParams,
data: list[dict],
**kwargs: Any,
) -> list[FinvizEquityScreenerData]:
"""Transform data."""
return [FinvizEquityScreenerData.model_validate(d) for d in data]

View File

@@ -1,299 +0,0 @@
"""Finviz Key Metrics Model."""
# pylint: disable=unused-argument
from typing import Any
from warnings import warn
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.key_metrics import (
KeyMetricsData,
KeyMetricsQueryParams,
)
from pydantic import Field
class FinvizKeyMetricsQueryParams(KeyMetricsQueryParams):
"""
Finviz Key Metrics Query.
Source: https://finviz.com/screener.ashx
"""
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
class FinvizKeyMetricsData(KeyMetricsData):
"""Finviz Key Metrics Data."""
pe_ratio: float | None = Field(
default=None, description="Price-to-earnings ratio (TTM)."
)
forward_pe: float | None = Field(
default=None, description="Forward price-to-earnings ratio (forward P/E)"
)
eps: float | None = Field(default=None, description="Earnings per share (EPS)")
price_to_sales: float | None = Field(
default=None, description="Price-to-sales ratio (P/S)"
)
price_to_book: float | None = Field(
default=None, description="Price-to-book ratio (P/B)"
)
book_value_per_share: float | None = Field(
default=None, description="Book value per share (Book/sh)"
)
price_to_cash: float | None = Field(
default=None, description="Price-to-cash ratio (P/C)"
)
cash_per_share: float | None = Field(
default=None, description="Cash per share (Cash/sh)"
)
price_to_free_cash_flow: float | None = Field(
default=None, description="Price-to-free cash flow ratio (P/FCF)"
)
debt_to_equity: float | None = Field(
default=None, description="Debt-to-equity ratio (Debt/Eq)"
)
long_term_debt_to_equity: float | None = Field(
default=None, description="Long-term debt-to-equity ratio (LT Debt/Eq)"
)
quick_ratio: float | None = Field(default=None, description="Quick ratio")
current_ratio: float | None = Field(default=None, description="Current ratio")
gross_margin: float | None = Field(
default=None,
description="Gross margin, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
profit_margin: float | None = Field(
default=None,
description="Profit margin, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
operating_margin: float | None = Field(
default=None,
description="Operating margin, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
return_on_assets: float | None = Field(
default=None,
description="Return on assets (ROA), as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
return_on_investment: float | None = Field(
default=None,
description="Return on investment (ROI), as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
return_on_equity: float | None = Field(
default=None,
description="Return on equity (ROE), as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
payout_ratio: float | None = Field(
default=None,
description="Payout ratio, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
dividend_yield: float | None = Field(
default=None,
description="Dividend yield, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
class FinvizKeyMetricsFetcher(
Fetcher[FinvizKeyMetricsQueryParams, list[FinvizKeyMetricsData]]
):
"""Finviz Key Metrics Fetcher."""
@staticmethod
def transform_query(params: dict[str, Any]) -> FinvizKeyMetricsQueryParams:
"""Transform the query params."""
return FinvizKeyMetricsQueryParams(**params)
@staticmethod
def extract_data(
query: FinvizKeyMetricsQueryParams,
credentials: dict[str, str] | None,
**kwargs: Any,
) -> list[dict]:
"""Extract the raw data from Finviz."""
# pylint: disable=import-outside-toplevel
from finvizfinance import util
from finvizfinance.quote import finvizfinance
from openbb_core.app.model.abstract.error import OpenBBError
from openbb_core.provider.utils.errors import EmptyDataError
from openbb_core.provider.utils.helpers import get_requests_session
results: list = []
messages: list = []
util.session = get_requests_session()
def get_one(symbol) -> dict:
"""Get the data for one symbol."""
result: dict = {}
try:
data = finvizfinance(symbol)
fundament = data.ticker_fundament()
mkt_cap = (
fundament.get("Market Cap", None)
if fundament.get("Market Cap", "-") != "-"
else None
)
if mkt_cap:
mkt_cap = float(
str(mkt_cap)
.replace("B", "e+9")
.replace("M", "e+6")
.replace("K", "e+3")
)
except Exception as e: # pylint: disable=W0718
warn(f"Failed to get data for {symbol} -> {e}")
return result
result.update(
{
"symbol": symbol,
"market_cap": int(mkt_cap) if mkt_cap is not None else None,
"pe_ratio": (
fundament.get("P/E", None)
if fundament.get("P/E", "-") != "-"
else None
),
"eps": (
fundament.get("EPS (ttm)", None)
if fundament.get("EPS (ttm)", "-") != "-"
else None
),
"forward_pe": (
fundament.get("Forward P/E", None)
if fundament.get("Forward P/E", "-") != "-"
else None
),
"price_to_sales": (
fundament.get("P/S", None)
if fundament.get("P/S", "-") != "-"
else None
),
"price_to_book": (
fundament.get("P/B", None)
if fundament.get("P/B", "-") != "-"
else None
),
"book_value_per_share": (
fundament.get("Book/sh", None)
if fundament.get("Book/sh", "-") != "-"
else None
),
"price_to_cash": (
fundament.get("P/C", None)
if fundament.get("P/C", "-") != "-"
else None
),
"cash_per_share": (
fundament.get("Cash/sh", None)
if fundament.get("Cash/sh", "-") != "-"
else None
),
"price_to_free_cash_flow": (
fundament.get("P/FCF", None)
if fundament.get("P/FCF", "-") != "-"
else None
),
"debt_to_equity": (
fundament.get("Debt/Eq", None)
if fundament.get("Debt/Eq", "-") != "-"
else None
),
"long_term_debt_to_equity": (
fundament.get("LT Debt/Eq", None)
if fundament.get("LT Debt/Eq", "-") != "-"
else None
),
"quick_ratio": (
fundament.get("Quick Ratio", None)
if fundament.get("Quick Ratio", "-") != "-"
else None
),
"current_ratio": (
fundament.get("Current Ratio", None)
if fundament.get("Current Ratio", "-") != "-"
else None
),
"gross_margin": (
float(str(fundament.get("Gross Margin", None)).replace("%", ""))
/ 100
if fundament.get("Gross Margin", "-") != "-"
else None
),
"profit_margin": (
float(
str(fundament.get("Profit Margin", None)).replace("%", "")
)
/ 100
if fundament.get("Profit Margin", "-") != "-"
else None
),
"operating_margin": (
float(str(fundament.get("Oper. Margin", None)).replace("%", ""))
/ 100
if fundament.get("Oper. Margin", "-") != "-"
else None
),
"return_on_assets": (
float(str(fundament.get("ROA", None)).replace("%", "")) / 100
if fundament.get("ROA", "-") != "-"
else None
),
"return_on_investment": (
float(str(fundament.get("ROI", None)).replace("%", "")) / 100
if fundament.get("ROI", "-") != "-"
else None
),
"return_on_equity": (
float(str(fundament.get("ROE", None)).replace("%", "")) / 100
if fundament.get("ROE", "-") != "-"
else None
),
"payout_ratio": (
float(str(fundament.get("Payout", None)).replace("%", "")) / 100
if fundament.get("Payout", "-") != "-"
else None
),
"dividend_yield": (
float(str(fundament.get("Dividend %", None)).replace("%", ""))
/ 100
if fundament.get("Dividend %", "-") != "-"
else None
),
}
)
return result
symbols = query.symbol.split(",")
for symbol in symbols:
result = get_one(symbol)
if result is not None and result:
results.append(result)
if not results and messages:
raise OpenBBError("\n".join(messages))
if not results and not messages:
raise EmptyDataError("No data was returned for any symbol")
if results and messages:
for message in messages:
warn(message)
return results
@staticmethod
def transform_data(
query: FinvizKeyMetricsQueryParams,
data: list[dict],
**kwargs: Any,
) -> list[FinvizKeyMetricsData]:
"""Transform and validate the raw data."""
return [FinvizKeyMetricsData.model_validate(d) for d in data]

View File

@@ -1,136 +0,0 @@
"""Finviz Price Performance Model."""
# pylint: disable=unused-argument
from typing import Any
from warnings import warn
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.recent_performance import (
RecentPerformanceData,
RecentPerformanceQueryParams,
)
from openbb_core.provider.utils.errors import EmptyDataError
from pydantic import Field
class FinvizPricePerformanceQueryParams(RecentPerformanceQueryParams):
"""
Finviz Price Performance Query.
Source: https://finviz.com/screener.ashx
"""
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
class FinvizPricePerformanceData(RecentPerformanceData):
"""Finviz Price Performance Data."""
__alias_dict__ = {
"symbol": "Ticker",
"one_week": "Perf Week",
"one_month": "Perf Month",
"three_month": "Perf Quart",
"six_month": "Perf Half",
"one_year": "Perf Year",
"ytd": "Perf YTD",
"volatility_week": "Volatility W",
"volatility_month": "Volatility M",
"price": "Price",
"one_day": "Change",
"volume": "Volume",
"average_volume": "Avg Volume",
"relative_volume": "Rel Volume",
"analyst_score": "Recom",
}
volatility_week: float | None = Field(
default=None,
description="One-week realized volatility, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
volatility_month: float | None = Field(
default=None,
description="One-month realized volatility, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
price: float | None = Field(
default=None,
description="Last Price.",
)
volume: float | None = Field(
default=None,
description="Current volume.",
)
average_volume: float | None = Field(
default=None,
description="Average daily volume.",
)
relative_volume: float | None = Field(
default=None,
description="Relative volume as a ratio of current volume to average volume.",
)
analyst_recommendation: float | None = Field(
default=None,
description="The analyst consensus, on a scale of 1-5 where 1 is a buy and 5 is a sell.",
)
symbol: str | None = Field(
default=None,
description="The ticker symbol.",
)
class FinvizPricePerformanceFetcher(
Fetcher[FinvizPricePerformanceQueryParams, list[FinvizPricePerformanceData]]
):
"""Finviz Price Performance Fetcher."""
@staticmethod
def transform_query(params: dict[str, Any]) -> FinvizPricePerformanceQueryParams:
"""Transform the query params."""
return FinvizPricePerformanceQueryParams(**params)
@staticmethod
def extract_data(
query: FinvizPricePerformanceQueryParams,
credentials: dict[str, str] | None,
**kwargs: Any,
) -> list[dict]:
"""Extract the raw data from Finviz."""
# pylint: disable=import-outside-toplevel
from finvizfinance import util
from finvizfinance.screener import performance
from openbb_core.provider.utils.helpers import get_requests_session
util.session = get_requests_session()
screen = performance.Performance()
screen.set_filter(ticker=query.symbol)
try:
screen_df = screen.screener_view(verbose=0)
if screen_df is None:
raise EmptyDataError()
screen_df.columns = screen_df.columns.str.strip() # type: ignore
screen_df = screen_df.fillna("N/A").replace("N/A", None) # type: ignore
except Exception as e:
raise e from e
symbols = query.symbol.split(",")
# Check for missing symbols and warn of the missing symbols.
for symbol in symbols:
if symbol not in screen_df["Ticker"].tolist():
warn(f"Symbol Error: {symbol} was not found.")
return sorted(
screen_df.to_dict(orient="records"),
key=(lambda item: (symbols.index(item.get("Ticker", len(symbols))))),
)
@staticmethod
def transform_data(
query: FinvizPricePerformanceQueryParams,
data: list[dict],
**kwargs: Any,
) -> list[FinvizPricePerformanceData]:
"""Transform the raw data."""
return [FinvizPricePerformanceData.model_validate(d) for d in data]

View File

@@ -1,134 +0,0 @@
"""Finviz Price Target Model."""
# pylint: disable=unused-argument
from typing import Any
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.price_target import (
PriceTargetData,
PriceTargetQueryParams,
)
from pydantic import Field
class FinvizPriceTargetQueryParams(PriceTargetQueryParams):
"""Finviz Price Target Query.
Source: https://finviz.com/quote.ashx?
"""
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
class FinvizPriceTargetData(PriceTargetData):
"""Finviz Price Target Data."""
__alias_dict__ = {
"published_date": "Date",
"status": "Status",
"analyst_company": "Outer",
"rating_change": "Rating",
}
status: str | None = Field(
default=None,
description="The action taken by the firm."
+ " This could be 'Upgrade', 'Downgrade', 'Reiterated', etc.",
)
rating_change: str | None = Field(
default=None,
description="The rating given by the analyst."
+ " This could be 'Buy', 'Sell', 'Underweight', etc."
+ " If the rating is a revision, the change is indicated by '->'",
)
class FinvizPriceTargetFetcher(
Fetcher[FinvizPriceTargetQueryParams, list[FinvizPriceTargetData]]
):
"""Finviz Price Target Fetcher."""
@staticmethod
def transform_query(params: dict[str, Any]) -> FinvizPriceTargetQueryParams:
"""Transform the query params."""
return FinvizPriceTargetQueryParams(**params)
@staticmethod
def extract_data(
query: FinvizPriceTargetQueryParams,
credentials: dict[str, str] | None,
**kwargs: Any,
) -> list[dict]:
"""Return the raw data from the Finviz endpoint."""
# pylint: disable=import-outside-toplevel
from finvizfinance import util # noqa
from finvizfinance.quote import finvizfinance
from openbb_core.app.model.abstract.error import OpenBBError
from openbb_core.provider.utils.errors import EmptyDataError
from openbb_core.provider.utils.helpers import get_requests_session
from pandas import DataFrame
from warnings import warn
results: list[dict] = []
util.session = get_requests_session()
messages: list = []
def get_one(symbol) -> list[dict]:
"""Get the data for one symbol."""
price_targets = DataFrame()
result: list[dict] = []
try:
data = finvizfinance(symbol)
price_targets = data.ticker_outer_ratings()
if price_targets is None or len(price_targets) == 0:
messages.append(f"Failed to get data for {symbol}")
return result
price_targets["symbol"] = symbol
prices = (
price_targets["Price"].astype(str).str.replace("$", "", regex=False)
)
price_targets["price_target"] = (
prices.str.split("→").str.get(0).str.strip()
)
price_targets["adj_price_target"] = (
prices.str.split("→").str.get(-1).str.strip()
)
price_targets.loc[
price_targets["adj_price_target"] == price_targets["price_target"],
"price_target",
] = None
price_targets = price_targets.replace("", None).drop(columns="Price")
except Exception as e: # pylint: disable=W0718
messages.append(f"Failed to get data for {symbol} -> {e}")
return result
result = price_targets.to_dict(orient="records")
return result
symbols = query.symbol.split(",") if query.symbol else []
for symbol in symbols:
result = get_one(symbol)
if result:
results.extend(result)
if not results and messages:
raise OpenBBError("\n".join(messages))
if not results and not messages:
raise EmptyDataError("No data was returned for any symbol")
if results and messages:
for message in messages:
warn(message)
return results
@staticmethod
def transform_data(
query: FinvizPriceTargetQueryParams,
data: list[dict],
**kwargs: Any,
) -> list[FinvizPriceTargetData]:
"""Transform and validate the raw data."""
return [FinvizPriceTargetData.model_validate(d) for d in data]

View File

@@ -1 +0,0 @@
"""Finviz provider utils."""

View File

@@ -1,45 +0,0 @@
"""Finviz Definitions."""
from typing import Literal
GROUPS = Literal[
"sector",
"industry",
"country",
"capitalization",
"energy",
"materials",
"industrials",
"consumer_cyclical",
"consumer_defensive",
"healthcare",
"financial",
"technology",
"communication_services",
"utilities",
"real_estate",
]
GROUPS_DICT = {
"sector": "Sector",
"industry": "Industry",
"country": "Country (U.S. listed stocks only)",
"capitalization": "Capitalization",
"energy": "Industry (Energy)",
"materials": "Industry (Basic Materials)",
"industrials": "Industry (Industrials)",
"consumer_cyclical": "Industry (Consumer Cyclical)",
"consumer_defensive": "Industry (Consumer Defensive)",
"healthcare": "Industry (Healthcare)",
"financial": "Industry (Financial)",
"technology": "Industry (Technology)",
"communication_services": "Industry (Communication Services)",
"utilities": "Industry (Utilities)",
"real_estate": "Industry (Real Estate)",
}
METRICS = Literal[
"performance",
"valuation",
"overview",
]

View File

@@ -1 +0,0 @@
"""Finviz Screener Presets."""

View File

@@ -1,12 +0,0 @@
# Author of preset: OpenBB
# Description: All stocks available from Finviz, with no filters. This preset will take a couple of minutes to execute, and may not be useful for most users.
[General]
Order = Market Cap.
[Descriptive]
[Fundamental]
[Technical]

View File

@@ -1,17 +0,0 @@
# Author of preset: JohnnyDankseed
# Description: Analyst Strong Buy
[General]
Order = Change
Signal = Top Gainers
[Descriptive]
Analyst Recom. = Strong Buy (1)
[Fundamental]
Debt/Equity = Under 0.6
[Technical]
50-Day Simple Moving Average = Price above SMA50
200-Day Simple Moving Average = Price above SMA200

View File

@@ -1,18 +0,0 @@
# Author of preset: Traceabl3
# Description: Buffet like value screener (Value invsting for long term growth)
[General]
[Descriptive]
Market Cap. = +Mid (over $2bln)
Dividend Yield = Positive (>0%)
[Fundamental]
EPS growthnext 5 years = Positive (>0%)
Debt/Equity = Under 0.5
Price/Free Cash Flow = Under 50
Sales growthpast 5 years = Positive (>0%)
Return on Investment = Over +15%
P/B = Under 3
[Technical]

View File

@@ -1,13 +0,0 @@
# Author of preset: OpenBB
# Description: Bull runs over 10%
[General]
Order = Change
Signal = Top Gainers
[Descriptive]
[Fundamental]
[Technical]
Performance = Today +10%

View File

@@ -1,28 +0,0 @@
# Author of preset: mmistroni@gmail.com
# Description: Canslim stocks are ones that are likely to explode , cfr https://en.wikipedia.org/wiki/CAN_SLIM
# Params picked from https://medium.com/the-investors-handbook/the-best-finviz-screens-for-growth-investors-72795f507b91
# This config add some extra filters to make the list more robust
[General]
Order = Market Cap.
[Descriptive]
Average Volume = Over 200K
Float = Under 50M
[Fundamental]
EPS growththis year = Over 20%
EPS growthnext year = Over 20%
EPS growthqtr over qtr = Over 20%
Sales growthqtr over qtr = Over 20%
EPS growthpast 5 years = Over 20%
Gross Margin = Positive (>0%)
Return on Equity = Positive (>0%)
InstitutionalOwnership = Over 20%
[Technical]
20-Day Simple Moving Average = Price above SMA20
50-Day Simple Moving Average = Price above SMA50
200-Day Simple Moving Average = Price above SMA200
52-Week High/Low = 0-10% below High

View File

@@ -1,14 +0,0 @@
# Author of preset: Traceabl3
# Description: Cheap dividend stocks
[General]
Signal = Oversold
[Descriptive]
Dividend Yield = Over 1%
[Fundamental]
P/E = Under 15
P/B = Under 1
[Technical]

View File

@@ -1,12 +0,0 @@
# Author of preset: Traceabl3
# Description: Cheap stonks that are oversold: under 10% above the low, and oversold on the RSI.
[General]
[Descriptive]
[Fundamental]
[Technical]
52-Week High/Low = 0-10% above Low
RSI (14) = Oversold (20)

View File

@@ -1,20 +0,0 @@
# Author of preset: JohnnyDankseed
# Description: Continued Momentum Scan
[General]
Order = Change
Signal = Top Gainers
[Descriptive]
Market Cap. = -Mid (under $10bln)
Average Volume = Over 50K
Relative Volume = Over 2
Current Volume = Over 1M
Price = Under $30
[Fundamental]
[Technical]
Beta = Over 1
Volatility = Week - Over 3%

View File

@@ -1,11 +0,0 @@
# Author of preset: Traceabl3
# Description: When the 50 sma crosses below the 200 sma. More information can be found in https://www.investopedia.com/terms/d/deathcross.asp
[General]
[Descriptive]
[Fundamental]
[Technical]
200-Day Simple Moving Average = SMA200 crossed SMA50 above

View File

@@ -1,23 +0,0 @@
# Author of preset: mmistroni@gmail.com
# Description: Fastest growth watch list. Again, from Medium article https://medium.com/the-investors-handbook/the-best-finviz-screens-for-growth-investors-72795f507b91
#
[General]
Order = Market Cap.
[Descriptive]
Market Cap. = +Mid (over $2bln)
Average Volume = Over 200K
Price = Over $10
[Fundamental]
EPS growththis year = Over 20%
EPS growthnext year = Over 20%
EPS growthqtr over qtr = Over 20%
Sales growthqtr over qtr = Over 20%
Return on Equity = Over +20%
Gross Margin = Over 20%
[Technical]
20-Day Simple Moving Average = Price above SMA20
50-Day Simple Moving Average = Price above SMA50
200-Day Simple Moving Average = Price above SMA200

View File

@@ -1,11 +0,0 @@
# Author of preset: Traceabl3
# Description: Golden Cross when the 50 day moves above the200 day from below.
[General]
[Descriptive]
[Fundamental]
[Technical]
50-Day Simple Moving Average = SMA50 crossed SMA200 above

View File

@@ -1,24 +0,0 @@
# Author of preset: JohnnyDankseed
# Description: Growth Stocks
[General]
Order = PEG (Price/Earnings/Growth)
Signal = Top Gainers
[Descriptive]
Market Cap. = +Micro (over $50mln)
Average Volume = Over 300K
Country = USA
Price = Over $10
[Fundamental]
EPS growthnext 5 years = Over 15%
Debt/Equity = Under 0.5
PEG = Under 2
EPS growththis year = Over 15%
EPS growthqtr over qtr = Over 15%
EPS growthpast 5 years = Over 15%
[Technical]
20-Day Simple Moving Average = SMA20 above SMA200
50-Day Simple Moving Average = SMA50 above SMA200

View File

@@ -1,15 +0,0 @@
# Author of preset: Traceabl3
# Description: Heavily owned by institutions and insiders (>30% each)
[General]
[Descriptive]
Analyst Recom. = Hold or better
[Fundamental]
InsiderOwnership = High (>30%)
InsiderTransactions = Positive (>0%)
PEG = Under 1
InstitutionalOwnership = Over 30%
[Technical]

View File

@@ -1,17 +0,0 @@
# Author of preset: OpenBB
# Description: Large caps with RSI under 20
[General]
Order = Relative Strength Index (14)
Ascend = true
[Descriptive]
Market Cap. = +Large (over $10bln)
[Fundamental]
[Technical]
RSI (14) = Oversold (20)

View File

@@ -1,17 +0,0 @@
# Author of preset: OpenBB
# Description: Large caps with RSI over 80
[General]
Order = Relative Strength Index (14)
Ascend = false
[Descriptive]
Market Cap. = +Large (over $10bln)
[Fundamental]
[Technical]
RSI (14) = Overbought (80)

View File

@@ -1,17 +0,0 @@
# Author of preset: Traceabl3
# Description: Modified Version of the Dreman Screener.
[General]
Order = Dividend Yield
[Descriptive]
Dividend Yield = Over 1%
[Fundamental]
P/E = Under 20
EPS growthnext 5 years = Positive Low (<10%)
Sales growthpast 5 years = Positive Low (0-10%)
EPS growththis year = Positive Low (0-10%)
[Technical]

View File

@@ -1,16 +0,0 @@
# Author of preset: Traceabl3
# Description: Neff Screener with modifications // operational margin <50%. More information can be found in https://marketxls.com/template/neff-screen/
[General]
Order = Sales growth past 5 years
[Descriptive]
[Fundamental]
Sales growthpast 5 years = Under 20%
PEG = Under 2
EPS growththis year = Under 20%
Operating Margin = Under 50%
[Technical]

View File

@@ -1,28 +0,0 @@
# Author of preset: mmistroni@gmail.com
# Description: Custom filters for new highs. Copied from an interesting Medium article
# https://medium.com/the-investors-handbook/the-best-finviz-screens-for-growth-investors-72795f507b91
[General]
Order = Market Cap.
[Descriptive]
Market Cap. = +Small (over $300mln)
Average Volume = Over 200K
Relative Volume = Over 1
Price = Over $10
[Fundamental]
EPS growththis year = Positive (>0%)
EPS growthnext year = Positive (>0%)
EPS growthqtr over qtr = Positive (>0%)
Sales growthqtr over qtr = Positive (>0%)
Return on Equity = Positive (>0%)
[Technical]
Performance = Today Up
20-Day Simple Moving Average = Price above SMA20
50-Day Simple Moving Average = Price above SMA50
200-Day Simple Moving Average = Price above SMA200
Change = Up
Change from Open = Up
52-Week High/Low = New High

View File

@@ -1,21 +0,0 @@
# Author of preset: OpenBB
# Description: Profitable companies with high sales growth, low P/S, and decent trading volume.
[General]
Order = Price/Sales
Ascend = true
[Descriptive]
Market Cap. = +Mid (over $2bln)
Average Volume = Over 1M
[Fundamental]
P/S = Under 10
Sales growthqtr over qtr = High (>25%)
Sales growthpast 5 years = Over 30%
Net Profit Margin = Positive (>0%)
[Technical]

View File

@@ -1,19 +0,0 @@
# Author of preset: OpenBB
# Description: Recent Growth, Support
[General]
Order = 200-Day SMA (Relative)
Signal = Top Gainers
Ascend = false
[Descriptive]
Average Volume = Over 500K
Country = USA
Price = Over $10
[Fundamental]
[Technical]
Performance = Quarter +20%
Performance 2 = Month +10%
Pattern = TL Support

View File

@@ -1,353 +0,0 @@
# Author of preset: OpenBBTerminal (https://github.com/OpenBB-finance/OpenBB)
# Description: Template with all available filters, with choices for each.
# More information can be found in https://finviz.com/help/screener.ashx
# and https://finviz.com/help/technical-analysis/charts-patterns.ashx
# To use this template, place the file in your OpenBBUserData folder, `~/OpenBBUserData/presets/finviz` and then launch the program.
# Uncomment (remomve the "#") the lines below, containing,"=", corresponding with the filter you want to use.
# Valid choices for each filter are listed directly above the corresponding line to un-comment, i.e. "Order = Ticker".
# It is recommended to modify filters one-at-a-time.
# Presets are loaded on runtime, there is no need to restart the program after modifying a file.
# This file must be renamed in order to use it.
[General]
# Ticker, Company, Sector, Industry, Country, Market Cap., Price/Earnings, Forward Price/Earnings, PEG (Price/Earnings/Growth), Price/Sales, Price/Book, Price/Cash, Price/Free Cash Flow, Dividend Yield, Payout Ratio, EPS (ttm), EPS growth this year, EPS growth next year, EPS growth past 5 years, EPS growth next 5 years, Sales growth past 5 years, EPS growth qtr over qtr, Sales growth qtr over qtr, Shares Outstanding, Shares Float, Insider Ownership, Insider Transactions, Institutional Ownership, Institutional Transactions, Short Interest Share, Short Interest Ratio, Earnings Date, Return on Assets, Return on Equity, Return on Investment, Current Ratio, Quick Ratio, LT Debt/Equity, Total Debt/Equity, Gross Margin, Operating Margin, Net Profit Margin, Analyst Recommendation, Performance (Week), Performance (Month), Performance (Quarter), Performance (Half Year), Performance (Year), Performance (Year To Date), Beta, Average True Range, Volatility (Week), Volatility (Month), 20-Day SMA (Relative), 50-Day SMA (Relative), 200-Day SMA (Relative), 50-Day High (Relative), 50-Day Low (Relative), 52-Week High (Relative), 52-Week Low (Relative), Relative Strength Index (14), Average Volume (3 Month), Relative Volume, Change, Change from Open, Gap, Volume, Price, Target Price, IPO Date
#Order = Ticker
# true, false
#Ascend = false
# None (all stocks), Top Gainers, Top Losers, New High, New Low, Most Volatile, Most Active, Unusual Volume, Overbought, Oversold, Downgrades, Upgrades, Earnings Before, Earnings After, Recent Insider Buying, Recent Insider Selling, Major News, Horizontal S/R, TL Resistance, TL Support, Wedge Up, Wedge Down, Triangle Ascending, Triangle Descending, Wedge, Channel Up, Channel Down, Channel, Double Top, Double Bottom, Multiple Top, Multiple Bottom, Head & Shoulders, Head & Shoulders Inverse
#Signal = Top Gainers
[Descriptive]
# Any, AMEX, NASDAQ, NYSE
#Exchange = NASDAQ
# Any, Mega ($200bln and more), Large ($10bln to $200bln), Mid ($2bl to $10bln), Small ($300mln to $2bln), Micro ($50mln to $300mln), Nano (under $50mln), +Large (over $10bln), +Mid (over $2bln), +Small (over $300mln), +Micro (over $50mln), -Large (under $200bln), -Mid (under $10bln), -Small (under $2bln), -Micro (under $300mln)
#Market Cap. = Large ($10bln to $200bln)
# Any, Today, Today Before Market Open, Today Before Market Close, Tomorrow, Tomorrow Before Market Open, Tomorrow Before Market Close, Yesterday, Yesterday Before Market Open, Yesterday Before Market Close, Next 5 Days, Previous 5 Days, This Week, Next Week, Previous Week, This Month
#Earnings Date = Any
# Any, 50% Above Price, 40% Above Price, 30% Above Price, 20% Above Price, 10% Above Price, 5% Above Price, Above Price, Below Price, 5% Below Price, 10% Below Price, 20% Below Price, 30% Below Price, 40% Below Price, 50% Below Price
#Target Price = Any
# Any, S&P 500, DJIA
#Index = Any
# Any, None (0%), Positive (>0%), High (>5%), Very High (>10%), Over 1%, Over 2%, Over 3%, Over 4%, Over 5%, Over 6%, Over 7%, Over 8%, Over 9%, Over 10%
#Dividend Yield = Over 5%
# Any, Under 50K, Under 100K, Under 500K, Under 750K, Under 1M, Over 50K, Over 100K, Over 300K, Over 400K, Over 500K, Over 750K, Over 1M, Over 2M, 100K to 500K, 100K to 1M, 500K to 1M, 500K to 10M
#Average Volume = Over 1M
# Any, Today, Yesterday, In the last week, In the last month, In the last quarter, In the last year, In the last 2 years, In the last 3 years, In the last 5 years, More than a year ago, More that 5 years ago, More than 10 years ago, More than 15 years ago, More than 20 years ago, More than 25 years ago
#IPO Date = Any
# Any, Basic Materials, Communication Services, Consumer Cyclical, Consumer Defensive, Energy, Financial, Healthcare, Industrials, Real Estate, Technology, Utilities
#Sector = Technology
# Any, Low (<5%), High (>20%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30%
#Float Short = Any
# Any, Over 10, Over 5, Over 3, Over 2, Over 1.5, Over 1, Over 0.75, Over 0.5, Over 0.25, Under 2, Under 1, Under 1.5, Under 1, Under 0.75, Under 0.5, Under 0.25, Under 0.1
#Relative Volume = Over 1.5
# Any, Under 1M, Under 5M, Under 10M, Under 20M, Under 50M, Under 100M, Over 1M, Over 2M, Over 5M, Over 10M, Over 20M, Over 50M, Over 100M, Over 200M, Over 500M, Over 1000M
#Shares Outstanding = Any
# Any, Stocks only (ex-Funds), Exchange Traded Fund, Advertising Agencies, Aerospace & Defense, Agricultural Inputs, Airlines, Airports & Air Services, Aluminum, Apparel Manufacturing, Apparel Retail, Asset Management, Auto Manufacturers, Auto Parts, Auto & Truck Dealerships, Banks - Diversified, Banks - Regional, Beverages - Brewers, Beverages - Non-Alcoholic, Beverages - Wineries & Distilleries, Biotechnology, Broadcasting, Building Materials, Building Products & Equipment, Business Equipment & Supplies, Capital Markets, Chemicals, Closed-End Fund - Debt, Closed-End Fund - Equity, Closed-End Fund - Foreign, Coking Coal, Communication Equipment, Computer Hardware, Confectioners, Conglomerates, Consulting Services, Consumer Electronics, Copper, Credit Services, Department Stores, Diagnostics & Research, Discount Stores, Drug Manufacturers - General, Drug Manufacturers - Specialty & Generic, Education & Training Services, Electrical Equipment & Parts, Electronic Components, Electronic Gaming & Multimedia, Electronics & Computer Distribution, Engineering & Construction, Entertainment, Farm & Heavy Construction Machinery, Farm Products, Financial Conglomerates, Financial Data & Stock Exchanges, Food Distribution, Footwear & Accessories, Furnishings, Fixtures & Appliances, Gambling, Gold, Grocery Stores, Healthcare Plans, Health Information Services, Home Improvement Retail, Household & Personal Products, Industrial Distribution, Information Technology Services, Infrastructure Operations, Insurance Brokers, Insurance - Diversified, Insurance - Life, Insurance - Property & Casualty, Insurance - Reinsurance, Insurance - Specialty, Integrated Freight & Logistics, Internet Content & Information, Internet Retail, Leisure, Lodging, Lumber & Wood Production, Luxury Goods, Marine Shipping, Medical Care Facilities, Medical Devices, Medical Distribution, Medical Instruments & Supplies, Metal Fabrication, Mortgage Finance, Oil & Gas Drilling, Oil & Gas E&P, Oil & Gas Equipment & Services, Oil & Gas Integrated, Oil & Gas Midstream, Oil & Gas Refining & Marketing, Other Industrial Metals & Mining, Other Precious Metals & Mining, Packaged Foods, Packaging & Containers, Paper & Paper Products, Personal Services, Pharmaceutical Retailers, Pollution & Treatment Controls, Publishing, Railroads, Real Estate - Development, Real Estate - Diversified, Real Estate Services, Recreational Vehicles, REIT - Diversified, REIT - Healthcare Facilities, REIT - Hotel & Motel, REIT - Industrial, REIT - Mortgage, REIT - Office, REIT - Residential, REIT - Retail, REIT - Specialty, Rental & Leasing Services, Residential Construction, Resorts & Casinos, Restaurants, Scientific & Technical Instruments, Security & Protection Services, Semiconductor Equipment & Materials, Semiconductors, Shell Companies, Silver, Software - Application, Software - Infrastructure, Solar, Specialty Business Services, Specialty Chemicals, Specialty Industrial Machinery, Specialty Retail, Staffing & Employment Services, Steel, Telecom Services, Textile Manufacturing, Thermal Coal, Tobacco, Tools & Accessories, Travel Services, Trucking, Uranium, Utilities - Diversified, Utilities - Independent Power Producers, Utilities - Regulated Electric, Utilities - Regulated Gas, Utilities - Regulated Water, Utilities - Renewable, Waste Management
#Industry = Any
# Any, Strong Buy (1), Buy or better, Buy, Hold or better, Hold, Hold or worse, Sell, Sell or worse, Strong Sell (5)
#Analyst Recom. = Any
# Any, Under 100K, Under 500K, Under 750K, Under 1M, Over 0, Over 50K, Over 100K, Over 200K, Over 300K, Over 400K, Over 500K, Over 750K, Over 1M, Over 2M, Over 5M, Over 10M, Over 20M
#Current Volume = Any
# Any, Under 1M, Under 5M, Under 10M, Under 20M, Under 50M, Under 100M, Over 1M, Over 2M, Over 5M, Over 10M, Over 20M, Over 50M, Over 100M, Over 200M, Over 500M, Over 1000M
#Float = Any
# Any, Asia, Europe, Latin America, BRIC, Argentina, Australia, Bahamas, Belgium, BeNeLux, Bermuda, Brazil, Canada, Cayman Islands, Chile, China, China & Hong Kong, Colombia, Cyprus, Denmark, Finland, France, Germany, Greece, Hong Kong, Hungary, Iceland, India, Indonesia, Ireland, Israel, Italy, Japan, Kazakhstan, Luxembourg, Malaysia, Malta, Mexico, Monaco, Netherlands, New Zealand, Norway, Panama, Peru, Philippines, Portugal, Russia, Singapore, South Africa, South Korea, Spain, Sweden, Switzerland, Taiwan, Turkey, United Arab Emirates, United Kingdom, Uruguay
#Country = Any
# Any, Optionable, Shortable, Optionable and shortable
#Option/Short = Any
# Any, Under $1, Under $2, Under $3, Under $4, Under $5, Under $7, Under $10, Under $15, Under $20, Under $30, Under $40, Under $50, Over $1, Over $2, Over $3, Over $4, Over $5 ,Over $7, Over $10, Over $15, Over $20, Over $30, Over $40, Over $50, Over $60, Over $70, Over $80, Over $90, Over $100, $1 to $5, $1 to $10, $1 to $20, $5 to $10, $5 to $20, $5 to $50, $10 to $20, $10 to $50, $20 to $50, $50 to $100
#Price = Any
[Fundamental]
# Any, Low (<15), Profitable (>0), High (>50), Under 5, Under 10, Under 15, Under 20, Under 25, Under 30, Under 35, Under 40, Under 45, Under 50, Over 5, Over 10, Over 15, Over 25, Over 30, Over 35, Over 40, Over 45, Over 50
#P/E = Any
# Any, Low (<3), High (>50), Under 1, Under 2, Under 3, Under 4, Under 5, Under 6, Under 7, Under 8, Under 9, Under 10, Over 1, Over 2, Over 3, Over 4, Over 5, Over 6, Over 7, Over 8, Over 9, Over 10, Over 20, Over 30, Over 40, Over 50
#Price/Cash = Any
# Any, Negative (<0%), Positive (>0%), Positive Low (<10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30%
#EPS growthnext 5 years = Any
# Any, Positive (>0%), Negative (<0%), Very Positive (>30%), Very Negative (<-15%), Under -50%, Under -40%, Under -35%, Under -30%, Under -25%, Under -20%, Under -15%, Under -10%, Under -5%, Over +50%, Over +45%, Over +40%, Over +35%, Over +30%, Over +25%, Over +20%, Over +15%, Over +10%, Over +5%
#Return on Equity = Any
# Any, High (>0.5), Low (<0.1), Under 1, Under 0.9, Under 0.8, Under 0.7, Under 0.6, Under 0.5, Under 0.4, Under 0.3, Under 0.2, Under 0.1, Over 0.1, Over 0.2, Over 0.3, Over 0.4, Over 0.5, Over 0.6, Over 0.7, Over 0.8, Over 0.9, Over 1
#Debt/Equity = Any
# Any, Low (<5%), High (>30%), Very High (>50%), Over 10%, Over 20%, Over 30%, Over 40%, Over 50%, Over 60%, Over 70%, Over 80%, Over 90%
#InsiderOwnership = Any
# Any, Low (<15), Profitable (>0), High (>50), Under 5, Under 10, Under 15, Under 20, Under 25, Under 30, Under 35, Under 40, Under 45, Under 50, Over 5, Over 10, Over 15, Over 20, Over 25, Over 30, Over 35, Over 40, Over 45, Over 50
#Forward P/E = Any
# Any, Low (<15), High (>50), Under 5, Under 10, Under 15, Under 20, Under 25, Under 30, Under 35, Under 40, Under 45, Under 50, Under 60, Under 70, Under 80, Under 90, Under 100, Over 5, Over 10, Over 15, Over 20, Over 25, Over 30, Over 35, Over 40, Over 45, Over 50, Over 60, Over 70, Over 80, Over 90, Over 100
#Price/Free Cash Flow = Any
# Any, Negative (<0%), Positive (>0%), Positive Low (0-10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30%
#Sales growthpast 5 years = Any
# Any, Positive (>0%), Negative (<0%), Very Positive (>25%), Very Negative (<-10%), Under -50%, Under -45%, Under -40%, Under -35%, Under -30%, Under -25%, Under -20%, Under -15%, Under -10%, Under -5%, Over +5%, Over +10%, Over +15%, Over +20%, Over +25%, Over +30%, Over +35%, Over +40%, Over +45%, Over +50%
#Return on Investment = Any
# Any, Positive (>0%), Negative (<0%), High (>50%), Under 90%, Under 80%, Under 70%, Under 60%, Under 50%, Under 45%, Under 40%, Under 35%, Under 30%, Under 25%, Under 20%, Under 15%, Under 10%, Under 5%, Under 0%, Under -10%, Under -20%, Under -30%, Under -50%, Under -70%, Under -100%, Over 0%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30%, Over 40%, Over 45%, Over 50%, Over 60%, Over 70%, Over 80%, Over 90%
#Gross Margin = Any
# Any, Very Negative (<20%), Negative (<0%), Positive (>0%), Very Positive (>20%), Under -90%, Under -80%, Under -70%, Under -60%, Under -50%, Under -45%, Under -40%, Under -35%, Under -30%, Under -25%, Under -20%, Under -15%, Under -10%, Under -5%, Over +5%, Over +10%, Over +15%, Over +20%, Over +25%, Over +30%, Over +35%, Over +40%, Over +45%, Over +50%, Over +60%, Over +70%, Over +80%, Over +90%
#InsiderTransactions = Any
# Any, Low (<1), High (>2), Under 1, Under 2, Under 3, Over 1, Over 2, Over 3
#PEG = Any
# Any, Negative (<0%), Positive (>0%), Positive Low (0-10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30%
#EPS growththis year = Any
# Any, Negative (<0%), Positive (>0%), Positive Low (0-10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30%
#EPS growthqtr over qtr = Any
# Any, High (>3), Low (<1), Under 1, Under 0.5, Over 0.5, Over 1, Over 1.5, Over 2, Over 3, Over 4, Over 5, Over 10
#Current Ratio = Any
# Any, Negative (<0%), Positive (>0%), Very Negative (<-20%), High (>25%), Under 90%, Under 80%, Under 70%, Under 60%, Under 50%, Under 45%, Under 40%, Under 35%, Under 30%, Under 25%, Under 20%, Under 15%, Under 10%, Under 5%, Under 0%, Under -10%, Under -20%, Under -30%, Under -50%, Under -70%, Under -100%, Over 0%, Over 10%, Under 15%, Over 20%, Over 25%, Over 30%, Over 35%, Over 40%, Over 45%, Over 50%, Over 60%, Over 70%, Over 80%, Over 90%
#Operating Margin = Any
# Any, Low (<5%), High (>90%), Under 90%, Under 80%, Under 70%, Under 60%, Under 50%, Under 40%, Under 30%, Under 20%, Under 10%, Over 10%, Over 20%, Over 30%, Over 40%, Over 50%, Over 60%, Over 70%, Over 80%, Over 90%
#InstitutionalOwnership = Any
# Any, Low (<1), High (>10), Under 1, Under 2, Under 3, Under 4, Under 5, Under 6, Under 7, Under 8, Under 9, Under 10, Over 1, Over 2, Over 3, Over 4, Over 5, Over 6, Over 6, Over 7, Over 8, Over 9, Over 10
#P/S = Any
# Any, Negative (<0%), Positive (>0%), Positive Low (0-10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30%
#EPS growthnext year = Any
# Any, Negative (<0%), Positive (>0%), Positive Low (0-10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30%
#Sales growthqtr over qtr = Any
# Any, High (>3), Low (<0.5), Under 1, Under 0.5, Over 0.5, Over 1, Over 1.5, Over 2, Over 3, Over 4, Over 5, Over 10
#Quick Ratio = Any
# Any, Positive (>0%), Negative (<0%), Very Negative (<-20%), High (>20%), Under 90%, Under 80%, Under 70%, Under 60%, Under 50%, Under 45%, Under 40%, Under 35%, Under 30%, Under 25%, Under 20%, Under 15%, Under 10%, Under 5%, Under 0%, Under -10%, Under -20%, Under -30%, Under -50%, Under -70%, Under -100%, Over 0%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30%, Over 35%, Over 40%, Over 45%, Over 50%, Over 60%, Over 70%, Over 80%, Over 90%
#Net Profit Margin = Any
# Any, Very Negative (<20%), Negative (<0%), Positive (>0%), Very Positive (>20%), Under -50%, Under -45%, Under -40%, Under -35%, Under -30%, Under -25%, Under -20%, Under -15%, Under -10%, Under -5%, Over +5%, Over +10%, Over +15%, Over +20%, Over +25%, Over +30%, Over +35%, Over +40%, Over +45%, Over +50%
#InstitutionalTransactions = Any
# Any, Low (<1), High (>5), Under 1, Under 2, Under 3, Under 4, Under 5, Under 6, Under 7, Under 8, Under 9, Under 10, Over 1, Over 2, Over 3, Over 4, Over 5, Over 6, Over 7, Over 8, Over 9, Over 10
#P/B = Any
# Any, Negative (<0%), Positive (>0%), Positive Low (0-10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30%
#EPS growthpast 5 years = Any
# Any, Positive (>0%), Negative (<0%), Very Positive (>15%), Very Negative (<-15%), Under -50%, Under -45%, Under -40%, Under -35%, Under -30%, Under -25%, Under -20%, Under -15%, Under -10%, Under -5%, Over +5%, Over +10%, Over +15%, Over +20%, Over +25%, Over +30%, Over +35%, Over +40%, Over +45%, Over +50%
#Return on Assets = Any
# Any, High (>0.5), Low (<0.1), Under 1, Under 0.9, Under 0.8, Under 0.7, Under 0.6, Under 0.5, Under 0.4, Under 0.3, Under 0.2, Under 0.1, Over 0.1, Over 0.2, Over 0.3, Over 0.4, Over.5, Over 0.6, Over 0.7, Over 0.8, Over 0.9, Over 1
#LT Debt/Equity = Any
# Any, None (0%), Positive (>0%), Low (<20%), High (>50%), Over 0%, Over 10%, Over 20%, Over 30%, Over 40%, Over 50%, Over 60%, Over 70%, Over 80%, Over 90%, Over 100%, Under 10%, Under 20%, Under 30%, Under 40%, Under 50%, Under 60%, Under 70%, Under 80%, Under 90%, Under 100%
#Payout Ratio = Any
[Technical]
# Any, Today Up, Today Down, Today -15%, Today -10%, Today -5%, Today +5%, Today +10%, Today +15%, Week -30%, Week -20%, Week -10%, Week Down, Week Up, Week +10%, Week +20%, Week +30%, Month -50%, Month -30%, Month -20%, Month -10%, Month Down, Month Up, Month +10%, Month +20%, Month +30%, Month +50%, Quarter -50%, Quarter -30%, Quarter -20%, Quarter -10%, Quarter Down, Quarter Up, Quarter +10%, Quarter +20%, Quarter +30%, Quarter +50%, Half -75%, Half -50%, Half -30%, Half -20%, Half -10%, Half Down, Half Up, Half +10%, Half +20%, Half +30%, Half +50%, Half +100%, Year -75%, Year -50%, Year -30%, Year -20%, Year -10%, Year Down, Year Up, Year +10%, Year +20%, Year +30%, Year +50%, Year +100%, Year +200%, Year +300%, Year +500%, YTD -75%, YTD -50%, YTD -30%, YTD -20%, YTD -10%, YTD -5%, YTD Down, YTD Up, YTD +5%, YTD +10%, YTD +20%, YTD +30, YTD +50%, YTD +100%
#Performance = Any
# Any, Price below SMA20, Price 10% below SMA20, Price 20% below SMA20, Price 30% below SMA20, Price 40% below SMA20, Price 50% below SMA20, Price above SMA20, Price 10% above SMA20, Price 20% above SMA20, Price 30% above SMA20, Price 40% above SMA20, Price 50% above SMA20, Price crossed SMA20, Price crossed SMA20 above, Price crossed SMA20 below, SMA20 crossed SMA50, SMA20 crossed SMA50 above, SMA20 crossed SMA50 below, SMA20 cross SMA200, SMA20 crossed SMA200 below, SMA20 crossed SMA200 above, SMA20 above SMA50, SMA20 below SMA50, SMA20 above SMA200, SMA20 below SMA200
#20-Day Simple Moving Average = Any
# Any, New High, New Low, 5% or more below High, 10% or more below High, 15% or more below High, 20% or more below High, 30% or more below High, 40% or more below High, 50% or more below High, 0-3% below High, 0-5% below High, 0-10% below High, 5% or more above Low, 10% or more above Low, 15% or more above Low, 20% or more above Low, 30% or more above Low, 40% or more above Low, 50% or more above Low, 0-3% above Low, 0-5% above Low, 0-10% above Low
#20-Day High/Low = Any
# Any, Under 0, Under 0.5, Under 1, Under 1.5, Under 2, Over 0, Over 0.5, Over 1, Over 1.5, Over 2, Over 2.5, Over 3, Over 4, 0 to 0.5, 0 to 1, 0.5 to 1, 0.5 to 1.5, 1 to 1.5, 1 to 2
#Beta = Any
# Any, Today Up, Today Down, Today -15%, Today -10%, Today -5%, Today +5%, Today +10%, Today +15%, Week -30%, Week -20%, Week -10%, Week Down, Week Up, Week +10%, Week +20%, Week +30%, Month -50%, Month -30%, Month -20%, Month -10%, Month Down, Month Up, Month +10%, Month +20%, Month +30%, Month +50%, Quarter -50%, Quarter -30%, Quarter -20%, Quarter -10%, Quarter Down, Quarter Up, Quarter +10%, Quarter +20%, Quarter +30%, Quarter +50%, Half -75%, Half -50%, Half -30%, Half -20%, Half -10%, Half Down, Half Up, Half +10%, Half +20%, Half +30%, Half +50%, Half +100%, Year -75%, Year -50%, Year -30%, Year -20%, Year -10%, Year Down, Year Up, Year +10%, Year +20%, Year +30%, Year +50%, Year +100%, Year +200%, Year +300%, Year +500%, YTD -75%, YTD -50%, YTD -30%, YTD -20%, YTD -10%, YTD -5%, YTD Down, YTD Up, YTD +5%, YTD +10%, YTD +20%, YTD +30, YTD +50%, YTD +100%
#Performance 2 = Any
# Any, Price below SMA50, Price 10% below SMA50, Price 20% below SMA50, Price 30% below SMA50, Price 40% below SMA50, Price 50% below SMA50, Price above SMA50, Price 10% above SMA50, Price 20% above SMA50, Price 30% above SMA50, Price 40% above SMA50, Price 50% above SMA50, Price crossed SMA50, Price crossed SMA50 above, Price crossed SMA50 below, SMA50 crossed SMA20, SMA50 crossed SMA20 above, SMA50 crossed SMA20 below, SMA50 cross SMA200, SMA50 crossed SMA200 below, SMA50 crossed SMA200 above, SMA50 above SMA20, SMA50 below SMA20, SMA50 above SMA200, SMA50 below SMA200
#50-Day Simple Moving Average = Any
# Any, New High, New Low, 5% or more below High, 10% or more below High, 15% or more below High, 20% or more below High, 30% or more below High, 40% or more below High, 50% or more below High, 0-3% below High, 0-5% below High, 0-10% below High, 5% or more above Low, 10% or more above Low, 15% or more above Low, 20% or more above Low, 30% or more above Low, 40% or more above Low, 50% or more above Low, 0-3% above Low, 0-5% above Low, 0-10% above Low
#50-Day High/Low = Any
# Any, Over 0.25, Over 0.5, Over 0.75, Over 1, Over 1.5, Over 2. Over 2.5, Over 3, Over 3.5, Over 4, Over 4.5, Over 5, Under 0.25, Under 0.5, Under 0.75, Under 1, Under 1.5, Under 2, Under 2.5, Under 3, Under 3.5, Under 4, Under 4.5, Under 5
#Average True Range = Any
# Any, Week - Over 3%, Week - Over 4%, Week - Over 5%, Week - Over 6%, Week - Over 7%, Week - Over 8%, Week - Over 9%, Week - Over 10%, Week - Over 12%, Week - Over 15%, Month - Over2%, Month - Over3%, Month - Over4%, Month - Over 5%, Month - Over 5%, Month - Over 6%, Month - Over 7%, Month - Over 8%, Month - Over 9%, Month - Over 10%, Month - Over 12%, Month - Over 15%
#Volatility = Any
# Any, Price below SMA200, Price 10% below SMA200, Price 20% below SMA200, Price 30% below SMA200, Price 40% below SMA200, Price 50% below SMA200, Price above SMA200, Price 10% above SMA200, Price 20% above SMA200, Price 30% above SMA200, Price 40% above SMA200, Price 50% above SMA200, Price crossed SMA200, Price crossed SMA200 above, Price crossed SMA200 below, SMA200 crossed SMA20, SMA20 crossed SMA20 above, SMA20 crossed SMA20 below, SMA200 cross SMA50, SMA200 crossed SMA50 below, SMA200 crossed SMA50 above, SMA200 above SMA20, SMA200 below SMA20, SMA200 above SMA50, SMA200 below SMA50
#200-Day Simple Moving Average = Any
# Any, New High, New Low, 5% or more below High, 10% or more below High, 15% or more below High, 20% or more below High, 30% or more below High, 40% or more below High, 50% or more below High, 0-3% below High, 0-5% below High, 0-10% below High, 5% or more above Low, 10% or more above Low, 15% or more above Low, 20% or more above Low, 30% or more above Low, 40% or more above Low, 50% or more above Low, 0-3% above Low, 0-5% above Low, 0-10% above Low
#52-Week High/Low = Any
# Any, Overbought (90), Overbought (80), Overbought (70), Overbought (60), Oversold (40), Oversold (30), Oversold (20), Oversold (10), Not Overbought (<60), Not Overbought (<50), Not Oversold (>50), Not Oversold (>40)
#RSI (14) = Any
# Any, Up, Up 1%, Up 2%, Up 3%, Up 4%, Up 5%, Up 6%, Up 7%, Up 8%, Up 9%, Up 10%, Up 15%, Up 20%, Down, Down 1%, Down 2%, Down 3%, Down 4%, Down 5%, Down 6%, Down 7%, Down 8%, Down 9%, Down 10%, Down 15%, Down 20%
#Change = Any
# Any, Horizontal S/R, Horizontal S/R (Strong), TL Resistance, TL Resistance (Strong), TL Support, TL Support (Strong), Wedge Up, Wedge Up (Strong), Wedge Down, Wedge Down (Strong), Triangle Ascending, Triangle Ascending (Strong), Triangle Descending, Triangle Descending (Strong), Wedge, Wedge (Strong), Channel Up, Channel Up (Strong), Channel Down, Channel Down (Strong), Channel, Channel (Strong), Double Top, Double Bottom, Multiple Top, Multiple Bottom, Head & Shoulders, Head & Shoulders Inverse
#Pattern = Any
# Any, Up, Up 1%, Up 2%, Up 3%, Up 4%, Up 5%, Up 6%, Up 7%, Up 8%, Up 9%, Up 10%, Up 15%, Up 20%, Down, Down 1%, Down 2%, Down 3%, Down 4%, Down 5%, Down 6%, Down 7%, Down 8%, Down 9%, Down 10%, Down 15%, Down 20%
#Gap = Any
# Any, Up, Up 1%, Up 2%, Up 3%, Up 4%, Up 5%, Up 6%, Up 7%, Up 8%, Up 9%, Up 10%, Up 15%, Up 20%, Down, Down 1%, Down 2%, Down 3%, Down 4%, Down 5%, Down 6%, Down 7%, Down 8%, Down 9%, Down 10%, Down 15%, Down 20%
#Change from Open = Any
# Any, Long Lower Shadow, Long Upper Shadow, Hammer, Inverted Hammer, Spinning Top White, Spinning Top Black, Doji, Dragonfly Doji, Gravestone Doji, Marubozu White, Marubozu Black
#Candlestick = Any

View File

@@ -1,20 +0,0 @@
# Author of preset: OpenBB
# Description: Short Squeeze Candidates
[General]
Order = Relative Volume
Ascend = false
Signal = Top Gainers
[Descriptive]
Float Short = Over 20%
Relative Volume = Over 1
[Fundamental]
[Technical]
Performance = Week Up
Beta = Over 1

View File

@@ -1,16 +0,0 @@
# Author of preset: Traceabl3
# Description: Potential Undervalued stocks
[General]
Signal = Upgrades
Order = Market Cap.
Ascend = false
[Descriptive]
[Fundamental]
PEG = Low (<1)
P/B = Under 3
EPS growthpast 5 years = Over 15%
[Technical]

View File

@@ -1,20 +0,0 @@
# Author of preset: OpenBB
# Description: Weak support trendlines among top performers
[General]
Signal = Top Gainers
[Descriptive]
Option/Short = Optionable and shortable
Average Volume = Over 500K
Country = USA
Price = Over $20
[Fundamental]
[Technical]
Performance = Quarter +20%
Performance 2 = Month +10%
Pattern = TL Support

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
[tool.poetry]
name = "openbb-finviz"
version = "1.5.1"
description = "Finviz extension for OpenBB"
authors = ["OpenBB Team <hello@openbb.co>"]
license = "AGPL-3.0-only"
readme = "README.md"
packages = [{ include = "openbb_finviz" }]
[tool.poetry.dependencies]
python = ">=3.10,<4"
openbb-core = "^1.6.10"
finvizfinance = "^1.1.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry.plugins."openbb_provider_extension"]
finviz = "openbb_finviz:finviz_provider"

View File

@@ -1 +0,0 @@
"""Finviz provider tests."""

View File

@@ -1,90 +0,0 @@
"""Finviz Fetcher Tests."""
import pytest
from openbb_core.app.service.user_service import UserService
from openbb_finviz.models.compare_groups import FinvizCompareGroupsFetcher
from openbb_finviz.models.equity_profile import FinvizEquityProfileFetcher
from openbb_finviz.models.equity_screener import FinvizEquityScreenerFetcher
from openbb_finviz.models.key_metrics import FinvizKeyMetricsFetcher
from openbb_finviz.models.price_performance import FinvizPricePerformanceFetcher
from openbb_finviz.models.price_target import FinvizPriceTargetFetcher
test_credentials = UserService().default_user_settings.credentials.model_dump(
mode="json"
)
@pytest.fixture(scope="module")
def vcr_config():
"""VCR configuration."""
return {
"filter_headers": [
("User-Agent", None),
("Cookie", "MOCK_COOKIE"),
("Set-Cookie", "MOCK_COOKIE"),
],
"filter_query_parameters": [
("Cookie", "MOCK_COOKIE"),
("Set-Cookie", "MOCK_COOKIE"),
],
}
@pytest.mark.record_http
def test_finviz_price_target_fetcher(credentials=test_credentials):
"""Test Finviz Price Target Fetcher."""
params = {"symbol": "AAPL"}
fetcher = FinvizPriceTargetFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_finviz_price_performance_fetcher(credentials=test_credentials):
"""Test Finviz Price Performance Fetcher."""
params = {"symbol": "AAPL"}
fetcher = FinvizPricePerformanceFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_finviz_key_metrics_fetcher(credentials=test_credentials):
"""Test Finviz Key Metrics Fetcher."""
params = {"symbol": "AAPL"}
fetcher = FinvizKeyMetricsFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_finviz_equity_profile_fetcher(credentials=test_credentials):
"""Test Finviz Equity Profile Fetcher."""
params = {"symbol": "AAPL"}
fetcher = FinvizEquityProfileFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_finviz_compare_groups_fetcher(credentials=test_credentials):
"""Test Finviz Compare Groups Fetcher."""
params = {"group": "country", "metric": "performance"}
fetcher = FinvizCompareGroupsFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_finviz_equity_screener_fetcher(credentials=test_credentials):
"""Test Finviz Equity Screener Fetcher."""
params = {"signal": "most_active", "limit": 20}
fetcher = FinvizEquityScreenerFetcher()
result = fetcher.test(params, credentials)
assert result is None