Skip to content

Commit 238f233

Browse files
Implement remaining methods for historical stock data
1 parent e0ddc4e commit 238f233

File tree

6 files changed

+454
-158
lines changed

6 files changed

+454
-158
lines changed

main.py renamed to src/__init__.py

File renamed without changes.

stocks.py renamed to src/main.py

File renamed without changes.

src/stocks.py

Whitespace-only changes.

src/stocks_historical.py

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
import requests
2+
import logging
3+
import pandas as pd
4+
5+
import requests
6+
import logging
7+
import pandas as pd
8+
9+
10+
class ThetaDataStocksHistorical:
11+
def __init__(self, log_level: int = logging.WARNING, use_df: bool = False) -> None:
12+
self.use_df = use_df
13+
14+
# Configure logging
15+
logging.basicConfig(
16+
level=log_level, format="%(asctime)s - %(levelname)s - %(message)s"
17+
)
18+
self.logger = logging.getLogger(__name__)
19+
20+
def __init__(self, enable_logging: bool = False, use_df: bool = True) -> None:
21+
self.enable_logging = enable_logging
22+
self.use_df = use_df
23+
24+
# Configure logging
25+
if self.enable_logging:
26+
logging.basicConfig(
27+
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
28+
)
29+
self.logger = logging.getLogger(__name__)
30+
31+
def send_request(self, endpoint: str, params: dict) -> dict | None:
32+
url = f"http://127.0.0.1:25510{endpoint}"
33+
headers = {"Accept": "application/json"}
34+
response = None
35+
36+
try:
37+
if self.enable_logging:
38+
self.logger.info(f"Sending request to {url} with params: {params}")
39+
response = requests.get(url, headers=headers, params=params)
40+
response.raise_for_status()
41+
if self.enable_logging:
42+
self.logger.info("Request successful")
43+
return response.json()
44+
except requests.RequestException as e:
45+
if self.enable_logging:
46+
self.logger.error(f"An error occurred: {e}")
47+
self.logger.error(
48+
f"Response text: {response.text if response else 'No response'}"
49+
)
50+
return None
51+
52+
def get_eod_report(
53+
self, symbol: str, start_date: str, end_date: str
54+
) -> pd.DataFrame | dict | None:
55+
# Check if dates have the correct format
56+
if not (
57+
self._is_valid_date_format(start_date)
58+
and self._is_valid_date_format(end_date)
59+
):
60+
if self.enable_logging:
61+
self.logger.error(
62+
f"Invalid date format. Expected format: 'YYYYMMDD'. Got start_date: {start_date}, end_date: {end_date}"
63+
)
64+
return None
65+
66+
if self.enable_logging:
67+
self.logger.info(
68+
f"Getting EOD report for {symbol} from {start_date} to {end_date}"
69+
)
70+
endpoint = "/v2/hist/stock/eod"
71+
params = {"root": symbol, "start_date": start_date, "end_date": end_date}
72+
response = self.send_request(endpoint, params)
73+
74+
if response and self.use_df:
75+
columns = response["header"]["format"]
76+
data = response["response"]
77+
return pd.DataFrame(data, columns=columns)
78+
else:
79+
return response
80+
81+
def _is_valid_date_format(self, date_string: str) -> bool:
82+
return len(date_string) == 8 and date_string.isdigit()
83+
84+
def get_quotes(
85+
self, symbol: str, start_date: str, end_date: str, interval: str = "900000"
86+
) -> pd.DataFrame | dict | None:
87+
if self.enable_logging:
88+
self.logger.info(
89+
f"Getting quotes for {symbol} from {start_date} to {end_date}"
90+
)
91+
endpoint = "/v2/hist/stock/quote"
92+
params = {
93+
"root": symbol,
94+
"start_date": start_date,
95+
"end_date": end_date,
96+
"ivl": interval,
97+
}
98+
response = self.send_request(endpoint, params)
99+
100+
if response and self.use_df:
101+
columns = response["header"]["format"]
102+
data = response["response"]
103+
return pd.DataFrame(data, columns=columns)
104+
else:
105+
return response
106+
107+
def get_ohlc(
108+
self, symbol: str, start_date: str, end_date: str, interval: str = "900000"
109+
) -> pd.DataFrame | dict | None:
110+
if self.enable_logging:
111+
self.logger.info(
112+
f"Getting OHLC for {symbol} from {start_date} to {end_date}"
113+
)
114+
endpoint = "/v2/hist/stock/ohlc"
115+
params = {
116+
"root": symbol,
117+
"start_date": start_date,
118+
"end_date": end_date,
119+
"ivl": interval,
120+
}
121+
response = self.send_request(endpoint, params)
122+
123+
if response and self.use_df:
124+
columns = response["header"]["format"]
125+
data = response["response"]
126+
return pd.DataFrame(data, columns=columns)
127+
else:
128+
return response
129+
130+
def get_trades(
131+
self, symbol: str, start_date: str, end_date: str
132+
) -> pd.DataFrame | dict | None:
133+
if self.enable_logging:
134+
self.logger.info(
135+
f"Getting trades for {symbol} from {start_date} to {end_date}"
136+
)
137+
endpoint = "/v2/hist/stock/trade"
138+
params = {
139+
"root": symbol,
140+
"start_date": start_date,
141+
"end_date": end_date,
142+
}
143+
response = self.send_request(endpoint, params)
144+
145+
if response and self.use_df:
146+
columns = response["header"]["format"]
147+
data = response["response"]
148+
return pd.DataFrame(data, columns=columns)
149+
else:
150+
return response
151+
152+
def get_trade_quote(
153+
self, symbol: str, start_date: str, end_date: str
154+
) -> pd.DataFrame | dict | None:
155+
if self.enable_logging:
156+
self.logger.info(
157+
f"Getting trade quotes for {symbol} from {start_date} to {end_date}"
158+
)
159+
endpoint = "/v2/hist/stock/trade_quote"
160+
params = {
161+
"root": symbol,
162+
"start_date": start_date,
163+
"end_date": end_date,
164+
}
165+
response = self.send_request(endpoint, params)
166+
167+
if response and self.use_df:
168+
columns = response["header"]["format"]
169+
data = response["response"]
170+
return pd.DataFrame(data, columns=columns)
171+
else:
172+
return response
173+
174+
def get_splits(
175+
self, symbol: str, start_date: str, end_date: str
176+
) -> pd.DataFrame | dict | None:
177+
if self.enable_logging:
178+
self.logger.info(
179+
f"Getting splits for {symbol} from {start_date} to {end_date}"
180+
)
181+
endpoint = "/v2/hist/stock/split"
182+
params = {
183+
"root": symbol,
184+
"start_date": start_date,
185+
"end_date": end_date,
186+
}
187+
response = self.send_request(endpoint, params)
188+
189+
if response and self.use_df:
190+
columns = response["header"]["format"]
191+
data = response["response"]
192+
return pd.DataFrame(data, columns=columns)
193+
else:
194+
return response
195+
196+
def get_dividends(
197+
self, symbol: str, start_date: str, end_date: str
198+
) -> pd.DataFrame | dict | None:
199+
if self.enable_logging:
200+
self.logger.info(
201+
f"Getting dividends for {symbol} from {start_date} to {end_date}"
202+
)
203+
endpoint = "/v2/hist/stock/dividend"
204+
params = {
205+
"root": symbol,
206+
"start_date": start_date,
207+
"end_date": end_date,
208+
}
209+
response = self.send_request(endpoint, params)
210+
211+
if response and self.use_df:
212+
columns = response["header"]["format"]
213+
data = response["response"]
214+
return pd.DataFrame(data, columns=columns)
215+
else:
216+
return response
217+
218+
219+
# Usage examples
220+
221+
if __name__ == "__main__":
222+
# Toggle example cases
223+
run_apple_eod_example = False
224+
run_microsoft_quotes_example = False
225+
run_google_ohlc_example = False
226+
run_tesla_trades_example = False
227+
run_amazon_trade_quote_example = True
228+
run_nvidia_splits_example = True
229+
run_intel_dividends_example = True
230+
231+
historical_data = ThetaDataStocksHistorical(enable_logging=True, use_df=True)
232+
233+
# Example 1: Get EOD report for AAPL
234+
if run_apple_eod_example:
235+
apple_eod = historical_data.get_eod_report("AAPL", "20240101", "20240131")
236+
if apple_eod is None:
237+
historical_data.logger.warning("Failed to get Apple EOD Report")
238+
elif apple_eod.empty:
239+
historical_data.logger.warning("Apple EOD Report is empty")
240+
else:
241+
historical_data.logger.info("Apple EOD Report received")
242+
print("Apple EOD Report:")
243+
print(apple_eod)
244+
245+
# Example 2: Get quotes for MSFT
246+
if run_microsoft_quotes_example:
247+
microsoft_quotes = historical_data.get_quotes(
248+
"MSFT", "20240101", "20240131", interval="3600000"
249+
)
250+
if microsoft_quotes is None:
251+
historical_data.logger.warning("Failed to get Microsoft Quotes")
252+
elif microsoft_quotes.empty:
253+
historical_data.logger.warning("Microsoft Quotes data is empty")
254+
else:
255+
historical_data.logger.info("Microsoft Quotes received")
256+
print("\nMicrosoft Quotes:")
257+
print(microsoft_quotes)
258+
259+
# Example 3: Get OHLC for GOOGL
260+
if run_google_ohlc_example:
261+
google_ohlc = historical_data.get_ohlc(
262+
"GOOGL", "20240101", "20240131", interval="3600000"
263+
)
264+
if google_ohlc is None:
265+
historical_data.logger.warning("Failed to get Google OHLC data")
266+
elif google_ohlc.empty:
267+
historical_data.logger.warning("Google OHLC data is empty")
268+
else:
269+
historical_data.logger.info("Google OHLC data received")
270+
print("\nGoogle OHLC data:")
271+
print(google_ohlc)
272+
273+
# Example 4: Get trades for TSLA
274+
if run_tesla_trades_example:
275+
tesla_trades = historical_data.get_trades("TSLA", "20240101", "20240102")
276+
if tesla_trades is None:
277+
historical_data.logger.warning("Failed to get Tesla Trades data")
278+
elif tesla_trades.empty:
279+
historical_data.logger.warning("Tesla Trades data is empty")
280+
else:
281+
historical_data.logger.info("Tesla Trades data received")
282+
print("\nTesla Trades data:")
283+
print(tesla_trades)
284+
285+
# Example 5: Get trade quotes for AMZN
286+
if run_amazon_trade_quote_example:
287+
amazon_trade_quotes = historical_data.get_trade_quote("AMZN", "20240101", "20240102")
288+
if amazon_trade_quotes is None:
289+
historical_data.logger.warning("Failed to get Amazon Trade Quotes data")
290+
elif amazon_trade_quotes.empty:
291+
historical_data.logger.warning("Amazon Trade Quotes data is empty")
292+
else:
293+
historical_data.logger.info("Amazon Trade Quotes data received")
294+
print("\nAmazon Trade Quotes data:")
295+
print(amazon_trade_quotes)
296+
297+
# Example 6: Get splits for NVDA
298+
if run_nvidia_splits_example:
299+
nvidia_splits = historical_data.get_splits("NVDA", "20230101", "20240131")
300+
if nvidia_splits is None:
301+
historical_data.logger.warning("Failed to get NVIDIA Splits data")
302+
elif nvidia_splits.empty:
303+
historical_data.logger.warning("NVIDIA Splits data is empty")
304+
else:
305+
historical_data.logger.info("NVIDIA Splits data received")
306+
print("\nNVIDIA Splits data:")
307+
print(nvidia_splits)
308+
309+
# Example 7: Get dividends for INTC
310+
if run_intel_dividends_example:
311+
intel_dividends = historical_data.get_dividends("INTC", "20230101", "20240131")
312+
if intel_dividends is None:
313+
historical_data.logger.warning("Failed to get Intel Dividends data")
314+
elif intel_dividends.empty:
315+
historical_data.logger.warning("Intel Dividends data is empty")
316+
else:
317+
historical_data.logger.info("Intel Dividends data received")
318+
print("\nIntel Dividends data:")
319+
print(intel_dividends)

0 commit comments

Comments
 (0)