Skip to content

Commit 2c68f6d

Browse files
authored
added rich text to console output (#56)
1 parent a22e626 commit 2c68f6d

File tree

7 files changed

+157
-47
lines changed

7 files changed

+157
-47
lines changed

poetry.lock

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "py-alpaca-api"
3-
version = "2.1.4"
3+
version = "2.1.5"
44
description = "Python package, for communicating with Alpaca Markets REST API."
55
authors = ["TexasCoding <[email protected]>"]
66
readme = "README.md"
@@ -18,6 +18,7 @@ tqdm = "^4.66.4"
1818
prophet = "^1.1.5"
1919
beautifulsoup4 = "^4.12.3"
2020
yfinance = "^0.2.40"
21+
rich = "^13.7.1"
2122

2223

2324
[tool.poetry.group.dev.dependencies]

src/py_alpaca_api/stock/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from typing import Dict
2+
from py_alpaca_api.stock.latest_quote import LatestQuote
23
from py_alpaca_api.stock.predictor import Predictor
34
from py_alpaca_api.trading.market import Market
45
from py_alpaca_api.stock.assets import Assets
@@ -45,3 +46,4 @@ def _initialize_components(
4546
data_url=data_url, headers=headers, market=market, asset=self.assets
4647
)
4748
self.predictor = Predictor(history=self.history, screener=self.screener)
49+
self.quotes = LatestQuote(headers=headers)
Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,45 @@
1+
import json
2+
from typing import Dict, List, Optional
3+
4+
import pendulum
5+
6+
from py_alpaca_api.http.requests import Requests
7+
8+
19
class LatestQuote:
2-
def __init__(self) -> None:
3-
pass
10+
def __init__(self, headers: Dict[str, str]) -> None:
11+
self.headers = headers
12+
13+
def get(
14+
self,
15+
symbol: Optional[List[str] | str],
16+
feed: str = "iex",
17+
currency: str = "USD",
18+
) -> dict:
19+
url = "https://data.alpaca.markets/v2/stocks/quotes/latest"
20+
21+
params = {"symbols": symbol, "feed": feed, "currency": currency}
22+
23+
response = json.loads(
24+
Requests()
25+
.request(method="GET", url=url, headers=self.headers, params=params)
26+
.text
27+
)
28+
29+
quotes = []
30+
31+
for key, value in response["quotes"].items():
32+
quotes.append(
33+
{
34+
"symbol": key,
35+
"timestamp": pendulum.parse(
36+
value["t"], tz="America/New_York"
37+
).to_datetime_string(),
38+
"ask": value["ap"],
39+
"ask_size": value["as"],
40+
"bid": value["bp"],
41+
"bid_size": value["bs"],
42+
}
43+
)
44+
45+
print(quotes)

src/py_alpaca_api/stock/predictor.py

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,35 @@
33
import pandas as pd
44
import pendulum
55
from prophet import Prophet
6-
from tqdm import tqdm
6+
# from tqdm import tqdm
77

88
from py_alpaca_api.stock.history import History
99
from py_alpaca_api.stock.screener import Screener
1010

11+
from rich.console import Console
12+
13+
from rich.progress import (
14+
BarColumn,
15+
MofNCompleteColumn,
16+
Progress,
17+
TextColumn,
18+
TimeElapsedColumn,
19+
TimeRemainingColumn,
20+
)
21+
22+
# Define custom progress bar
23+
progress_bar = Progress(
24+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
25+
BarColumn(),
26+
MofNCompleteColumn(),
27+
TextColumn("•"),
28+
TimeElapsedColumn(),
29+
TextColumn("•"),
30+
TimeRemainingColumn(),
31+
)
32+
33+
console = Console()
34+
1135
yesterday = pendulum.now().subtract(days=1).format("YYYY-MM-DD")
1236
four_years_ago = pendulum.now().subtract(years=2).format("YYYY-MM-DD")
1337

@@ -102,7 +126,7 @@ def get_losers_to_gainers(
102126
self,
103127
gain_ratio: float = 10.0,
104128
losers_to_scan: int = 200,
105-
future_periods: int = 14,
129+
future_periods: int = 5,
106130
) -> list:
107131
"""
108132
Predicts future gainers based on the previous day's losers using Prophet forecasting.
@@ -123,26 +147,37 @@ def get_losers_to_gainers(
123147

124148
future_gainers = []
125149

126-
for i, ticker in tqdm(
127-
enumerate(losers_list),
128-
desc=f"• Predicting {len(losers_list)} future gainers with Prophet: ",
129-
):
130-
try:
131-
symbol_data = self.get_stock_data(ticker)
132-
symbol_model = self.train_prophet_model(symbol_data)
133-
symbol_forecast = self.generate_forecast(
134-
symbol_model, future_periods=future_periods
135-
)
136-
previous_price = previous_day_losers[
137-
previous_day_losers["symbol"] == ticker
138-
].iloc[0]["price"]
139-
gain_prediction = round(
140-
((symbol_forecast - previous_price) / previous_price) * 100, 2
141-
)
142-
if gain_prediction >= gain_ratio:
143-
future_gainers.append(ticker)
144-
except Exception as e:
145-
logger.error(f"Error predicting {ticker}: {e}")
146-
continue
147-
150+
with progress_bar as progress:
151+
# for i, ticker in tqdm(
152+
# enumerate(losers_list),
153+
# desc=f"• Predicting {len(losers_list)} future gainers with Prophet: ",
154+
# ):
155+
console.print(
156+
f"Getting predictions for [bold]{len(losers_list)}[/bold] future gainers with Prophet: ",
157+
style="green",
158+
)
159+
for i, ticker in progress.track(
160+
enumerate(losers_list), total=len(losers_list)
161+
):
162+
try:
163+
symbol_data = self.get_stock_data(ticker)
164+
symbol_model = self.train_prophet_model(symbol_data)
165+
symbol_forecast = self.generate_forecast(
166+
symbol_model, future_periods=future_periods
167+
)
168+
previous_price = previous_day_losers[
169+
previous_day_losers["symbol"] == ticker
170+
].iloc[0]["price"]
171+
gain_prediction = round(
172+
((symbol_forecast - previous_price) / previous_price) * 100, 2
173+
)
174+
if gain_prediction >= gain_ratio:
175+
future_gainers.append(ticker)
176+
except Exception as e:
177+
logger.error(f"Error predicting {ticker}: {e}")
178+
continue
179+
console.print(
180+
f"Predicted [bold]{len(future_gainers)}[/bold] future gainers.",
181+
style="yellow",
182+
)
148183
return future_gainers

src/py_alpaca_api/stock/screener.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@
55
import pandas as pd
66
import pendulum
77

8+
from rich.console import Console
9+
810
from py_alpaca_api.http.requests import Requests
911
from py_alpaca_api.trading.market import Market
1012
from py_alpaca_api.stock.assets import Assets
1113

14+
console = Console()
15+
1216

1317
class Screener:
1418
def __init__(
@@ -111,14 +115,15 @@ def losers(
111115
Returns:
112116
pd.DataFrame: A filtered DataFrame containing stocks that meet the specified conditions for losers.
113117
"""
114-
return self.filter_stocks(
115-
price_greater_than,
116-
lambda df: df["change"] < change_less_than,
117-
volume_greater_than,
118-
trade_count_greater_than,
119-
total_losers_returned,
120-
ascending_order=True,
121-
)
118+
with console.status("[bold green]Getting previous day's losers..."):
119+
return self.filter_stocks(
120+
price_greater_than,
121+
lambda df: df["change"] < change_less_than,
122+
volume_greater_than,
123+
trade_count_greater_than,
124+
total_losers_returned,
125+
ascending_order=True,
126+
)
122127

123128
##################################################
124129
# //////////////// Get Gainers \\\\\\\\\\\\\\\\\ #
@@ -148,14 +153,15 @@ def gainers(
148153
pd.DataFrame: A Pandas DataFrame containing the stocks that satisfy the criteria for being gainers.
149154
150155
"""
151-
return self.filter_stocks(
152-
price_greater_than,
153-
lambda df: df["change"] > change_greater_than,
154-
volume_greater_than,
155-
trade_count_greater_than,
156-
total_gainers_returned,
157-
ascending_order=False,
158-
)
156+
with console.status("[bold green]Getting previous day's biggest gainers..."):
157+
return self.filter_stocks(
158+
price_greater_than,
159+
lambda df: df["change"] > change_greater_than,
160+
volume_greater_than,
161+
trade_count_greater_than,
162+
total_gainers_returned,
163+
ascending_order=False,
164+
)
159165

160166
##################################################
161167
# /////////// Calculate Percentages \\\\\\\\\\\\ #

src/py_alpaca_api/trading/news.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
import yfinance as yf
99
from py_alpaca_api.http.requests import Requests
1010

11+
from rich.console import Console
12+
13+
console = Console()
14+
1115
logger = logging.getLogger("yfinance")
1216
logger.disabled = True
1317
logger.propagate = False
@@ -87,7 +91,7 @@ def truncate(text: str, length: int) -> str:
8791
else text
8892
)
8993

90-
def get_news(self, symbol: str, limit: int = 5) -> List[Dict[str, str]]:
94+
def get_news(self, symbol: str, limit: int = 6) -> List[Dict[str, str]]:
9195
"""
9296
Retrieves news articles related to a given symbol.
9397
@@ -98,8 +102,10 @@ def get_news(self, symbol: str, limit: int = 5) -> List[Dict[str, str]]:
98102
Returns:
99103
list: A list of news articles, sorted by publish date in descending order.
100104
"""
101-
yahoo_news = self._get_yahoo_news(symbol=symbol, limit=limit)
102-
benzinga_news = self._get_benzinga_news(symbol=symbol, limit=limit)
105+
with console.status("[bold green]Getting latest news from Yahoo Finance..."):
106+
yahoo_news = self._get_yahoo_news(symbol=symbol, limit=limit)
107+
with console.status("[bold red]Getting latest news from Benzinga..."):
108+
benzinga_news = self._get_benzinga_news(symbol=symbol, limit=limit)
103109

104110
news = yahoo_news + benzinga_news
105111

0 commit comments

Comments
 (0)