mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-07 09:52:13 +00:00
remove finviz (#7577)
This commit is contained in:
111
openbb_platform/providers/finviz/README.md
vendored
111
openbb_platform/providers/finviz/README.md
vendored
@@ -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)
|
||||
@@ -1 +0,0 @@
|
||||
"""Finviz provider."""
|
||||
@@ -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",
|
||||
)
|
||||
@@ -1 +0,0 @@
|
||||
"""Finviz Provider models."""
|
||||
@@ -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]
|
||||
@@ -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]
|
||||
@@ -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]
|
||||
@@ -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]
|
||||
@@ -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]
|
||||
@@ -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]
|
||||
@@ -1 +0,0 @@
|
||||
"""Finviz provider utils."""
|
||||
@@ -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",
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
"""Finviz Screener Presets."""
|
||||
@@ -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]
|
||||
@@ -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
|
||||
@@ -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]
|
||||
@@ -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%
|
||||
@@ -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
|
||||
@@ -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]
|
||||
@@ -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)
|
||||
@@ -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%
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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]
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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]
|
||||
@@ -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]
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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]
|
||||
@@ -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
2162
openbb_platform/providers/finviz/poetry.lock
generated
vendored
2162
openbb_platform/providers/finviz/poetry.lock
generated
vendored
File diff suppressed because it is too large
Load Diff
21
openbb_platform/providers/finviz/pyproject.toml
vendored
21
openbb_platform/providers/finviz/pyproject.toml
vendored
@@ -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"
|
||||
@@ -1 +0,0 @@
|
||||
"""Finviz provider tests."""
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -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
|
||||
Reference in New Issue
Block a user