add financial statements cron job
This commit is contained in:
parent
6fd82a0f4a
commit
cf324c849d
73
app/cron_financial_statements.py
Normal file
73
app/cron_financial_statements.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import os
|
||||||
|
import ujson
|
||||||
|
import random
|
||||||
|
import asyncio
|
||||||
|
import aiohttp
|
||||||
|
import sqlite3
|
||||||
|
from tqdm import tqdm
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
api_key = os.getenv('FMP_API_KEY')
|
||||||
|
|
||||||
|
# Configurations
|
||||||
|
include_current_quarter = False
|
||||||
|
max_concurrent_requests = 100 # Limit concurrent requests
|
||||||
|
|
||||||
|
async def fetch_data(session, url, symbol, attempt=0):
|
||||||
|
try:
|
||||||
|
async with session.get(url) as response:
|
||||||
|
if response.status == 200:
|
||||||
|
data = await response.json()
|
||||||
|
return data
|
||||||
|
else:
|
||||||
|
print(f"Error fetching data for {symbol}: HTTP {response.status}")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Exception during fetching data for {symbol}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def save_json(symbol, period, data_type, data):
|
||||||
|
os.makedirs(f"json/financial-statements/{data_type}/{period}/", exist_ok=True)
|
||||||
|
with open(f"json/financial-statements/{data_type}/{period}/{symbol}.json", 'w') as file:
|
||||||
|
ujson.dump(data, file)
|
||||||
|
|
||||||
|
async def get_financial_statements(session, symbol, semaphore, request_counter):
|
||||||
|
base_url = "https://financialmodelingprep.com/api/v3"
|
||||||
|
periods = ['quarter', 'annual']
|
||||||
|
data_types = ['income-statement', 'balance-sheet-statement', 'cash-flow-statement', 'ratios']
|
||||||
|
|
||||||
|
async with semaphore:
|
||||||
|
for period in periods:
|
||||||
|
for data_type in data_types:
|
||||||
|
url = f"{base_url}/{data_type}/{symbol}?period={period}&apikey={api_key}"
|
||||||
|
data = await fetch_data(session, url, symbol)
|
||||||
|
if data:
|
||||||
|
await save_json(symbol, period, data_type, data)
|
||||||
|
|
||||||
|
request_counter[0] += 1 # Increment the request counter
|
||||||
|
if request_counter[0] >= 1000:
|
||||||
|
await asyncio.sleep(60) # Pause for 60 seconds
|
||||||
|
request_counter[0] = 0 # Reset the request counter after the pause
|
||||||
|
|
||||||
|
async def run():
|
||||||
|
con = sqlite3.connect('stocks.db')
|
||||||
|
cursor = con.cursor()
|
||||||
|
cursor.execute("PRAGMA journal_mode = wal")
|
||||||
|
cursor.execute("SELECT DISTINCT symbol FROM stocks WHERE symbol NOT LIKE '%.%'")
|
||||||
|
symbols = [row[0] for row in cursor.fetchall()]
|
||||||
|
con.close()
|
||||||
|
|
||||||
|
semaphore = asyncio.Semaphore(max_concurrent_requests)
|
||||||
|
request_counter = [0] # Using a list to keep a mutable counter across async tasks
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
tasks = []
|
||||||
|
for symbol in tqdm(symbols):
|
||||||
|
task = asyncio.create_task(get_financial_statements(session, symbol, semaphore, request_counter))
|
||||||
|
tasks.append(task)
|
||||||
|
|
||||||
|
await asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(run())
|
||||||
191
app/main.py
191
app/main.py
@ -710,33 +710,39 @@ async def stock_income(data: TickerData, api_key: str = Security(get_api_key)):
|
|||||||
|
|
||||||
cache_key = f"stock-income-{ticker}"
|
cache_key = f"stock-income-{ticker}"
|
||||||
cached_result = redis_client.get(cache_key)
|
cached_result = redis_client.get(cache_key)
|
||||||
|
|
||||||
if cached_result:
|
if cached_result:
|
||||||
return orjson.loads(cached_result)
|
return StreamingResponse(
|
||||||
|
io.BytesIO(cached_result),
|
||||||
|
media_type="application/json",
|
||||||
|
headers={"Content-Encoding": "gzip"}
|
||||||
|
)
|
||||||
|
|
||||||
query_template = """
|
|
||||||
SELECT
|
|
||||||
income, income_growth
|
|
||||||
FROM
|
|
||||||
stocks
|
|
||||||
WHERE
|
|
||||||
symbol = ?
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
with db_connection(STOCK_DB) as cursor:
|
with open(f"json/financial-statements/income-statement/quarter/{ticker}.json", 'rb') as file:
|
||||||
cursor.execute(query_template, (ticker,))
|
quarter_res = orjson.loads(file.read())
|
||||||
result = cursor.fetchone()
|
|
||||||
if result:
|
|
||||||
income_statement = orjson.loads(result[0])
|
|
||||||
income_statement_growth = orjson.loads(result[1])
|
|
||||||
res = clean_financial_data(income_statement, income_statement_growth)
|
|
||||||
else:
|
|
||||||
res = []
|
|
||||||
except:
|
except:
|
||||||
res = []
|
quarter_res = []
|
||||||
|
|
||||||
redis_client.set(cache_key, orjson.dumps(res))
|
try:
|
||||||
redis_client.expire(cache_key, 3600*3600) # Set cache expiration time to 1 hour
|
with open(f"json/financial-statements/income-statement/annual/{ticker}.json", 'rb') as file:
|
||||||
return res
|
annual_res = orjson.loads(file.read())
|
||||||
|
except:
|
||||||
|
annual_res = []
|
||||||
|
|
||||||
|
res = {'quarter': quarter_res, 'annual': annual_res}
|
||||||
|
|
||||||
|
res = orjson.dumps(res)
|
||||||
|
compressed_data = gzip.compress(res)
|
||||||
|
|
||||||
|
redis_client.set(cache_key, compressed_data)
|
||||||
|
redis_client.expire(cache_key, 3600 * 24) # Set cache expiration time to 1 day
|
||||||
|
|
||||||
|
return StreamingResponse(
|
||||||
|
io.BytesIO(compressed_data),
|
||||||
|
media_type="application/json",
|
||||||
|
headers={"Content-Encoding": "gzip"}
|
||||||
|
)
|
||||||
|
|
||||||
@app.post("/stock-balance-sheet")
|
@app.post("/stock-balance-sheet")
|
||||||
async def stock_balance_sheet(data: TickerData, api_key: str = Security(get_api_key)):
|
async def stock_balance_sheet(data: TickerData, api_key: str = Security(get_api_key)):
|
||||||
@ -745,33 +751,39 @@ async def stock_balance_sheet(data: TickerData, api_key: str = Security(get_api_
|
|||||||
|
|
||||||
cache_key = f"stock-balance-sheet-{ticker}"
|
cache_key = f"stock-balance-sheet-{ticker}"
|
||||||
cached_result = redis_client.get(cache_key)
|
cached_result = redis_client.get(cache_key)
|
||||||
|
|
||||||
if cached_result:
|
if cached_result:
|
||||||
return orjson.loads(cached_result)
|
return StreamingResponse(
|
||||||
|
io.BytesIO(cached_result),
|
||||||
|
media_type="application/json",
|
||||||
|
headers={"Content-Encoding": "gzip"}
|
||||||
|
)
|
||||||
|
|
||||||
query_template = """
|
|
||||||
SELECT
|
|
||||||
balance, balance_growth
|
|
||||||
FROM
|
|
||||||
stocks
|
|
||||||
WHERE
|
|
||||||
symbol = ?
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
with db_connection(STOCK_DB) as cursor:
|
with open(f"json/financial-statements/balance-sheet-statement/quarter/{ticker}.json", 'rb') as file:
|
||||||
cursor.execute(query_template, (ticker,))
|
quarter_res = orjson.loads(file.read())
|
||||||
result = cursor.fetchone()
|
|
||||||
if result:
|
|
||||||
balance_statement = orjson.loads(result[0])
|
|
||||||
balance_statement_growth = orjson.loads(result[1])
|
|
||||||
res = clean_financial_data(balance_statement, balance_statement_growth)
|
|
||||||
else:
|
|
||||||
res = []
|
|
||||||
except:
|
except:
|
||||||
res = []
|
quarter_res = []
|
||||||
|
|
||||||
redis_client.set(cache_key, orjson.dumps(res))
|
try:
|
||||||
redis_client.expire(cache_key, 3600*3600)
|
with open(f"json/financial-statements/balance-sheet-statement/annual/{ticker}.json", 'rb') as file:
|
||||||
return res
|
annual_res = orjson.loads(file.read())
|
||||||
|
except:
|
||||||
|
annual_res = []
|
||||||
|
|
||||||
|
res = {'quarter': quarter_res, 'annual': annual_res}
|
||||||
|
|
||||||
|
res = orjson.dumps(res)
|
||||||
|
compressed_data = gzip.compress(res)
|
||||||
|
|
||||||
|
redis_client.set(cache_key, compressed_data)
|
||||||
|
redis_client.expire(cache_key, 3600 * 24) # Set cache expiration time to 1 day
|
||||||
|
|
||||||
|
return StreamingResponse(
|
||||||
|
io.BytesIO(compressed_data),
|
||||||
|
media_type="application/json",
|
||||||
|
headers={"Content-Encoding": "gzip"}
|
||||||
|
)
|
||||||
|
|
||||||
@app.post("/stock-ratios")
|
@app.post("/stock-ratios")
|
||||||
async def stock_ratios(data: TickerData, api_key: str = Security(get_api_key)):
|
async def stock_ratios(data: TickerData, api_key: str = Security(get_api_key)):
|
||||||
@ -780,27 +792,39 @@ async def stock_ratios(data: TickerData, api_key: str = Security(get_api_key)):
|
|||||||
|
|
||||||
cache_key = f"stock-ratios-{ticker}"
|
cache_key = f"stock-ratios-{ticker}"
|
||||||
cached_result = redis_client.get(cache_key)
|
cached_result = redis_client.get(cache_key)
|
||||||
if cached_result:
|
|
||||||
return orjson.loads(cached_result)
|
|
||||||
|
|
||||||
query_template = """
|
if cached_result:
|
||||||
SELECT
|
return StreamingResponse(
|
||||||
ratios
|
io.BytesIO(cached_result),
|
||||||
FROM
|
media_type="application/json",
|
||||||
stocks
|
headers={"Content-Encoding": "gzip"}
|
||||||
WHERE
|
)
|
||||||
symbol = ?
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
df = pd.read_sql_query(query_template,con, params=(ticker,))
|
with open(f"json/financial-statements/ratios/quarter/{ticker}.json", 'rb') as file:
|
||||||
res = orjson.loads(df['ratios'].iloc[0])
|
quarter_res = orjson.loads(file.read())
|
||||||
except:
|
except:
|
||||||
res = []
|
quarter_res = []
|
||||||
|
|
||||||
redis_client.set(cache_key, orjson.dumps(res))
|
try:
|
||||||
redis_client.expire(cache_key, 3600*3600) # Set cache expiration time to 1 hour
|
with open(f"json/financial-statements/ratios/annual/{ticker}.json", 'rb') as file:
|
||||||
return res
|
annual_res = orjson.loads(file.read())
|
||||||
|
except:
|
||||||
|
annual_res = []
|
||||||
|
|
||||||
|
res = {'quarter': quarter_res, 'annual': annual_res}
|
||||||
|
|
||||||
|
res = orjson.dumps(res)
|
||||||
|
compressed_data = gzip.compress(res)
|
||||||
|
|
||||||
|
redis_client.set(cache_key, compressed_data)
|
||||||
|
redis_client.expire(cache_key, 3600 * 24) # Set cache expiration time to 1 day
|
||||||
|
|
||||||
|
return StreamingResponse(
|
||||||
|
io.BytesIO(compressed_data),
|
||||||
|
media_type="application/json",
|
||||||
|
headers={"Content-Encoding": "gzip"}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.post("/stock-cash-flow")
|
@app.post("/stock-cash-flow")
|
||||||
@ -810,29 +834,40 @@ async def stock_cash_flow(data: TickerData, api_key: str = Security(get_api_key)
|
|||||||
|
|
||||||
cache_key = f"stock-cash-flow-{ticker}"
|
cache_key = f"stock-cash-flow-{ticker}"
|
||||||
cached_result = redis_client.get(cache_key)
|
cached_result = redis_client.get(cache_key)
|
||||||
if cached_result:
|
|
||||||
return orjson.loads(cached_result)
|
|
||||||
|
|
||||||
query_template = """
|
if cached_result:
|
||||||
SELECT
|
return StreamingResponse(
|
||||||
cashflow, cashflow_growth
|
io.BytesIO(cached_result),
|
||||||
FROM
|
media_type="application/json",
|
||||||
stocks
|
headers={"Content-Encoding": "gzip"}
|
||||||
WHERE
|
)
|
||||||
symbol = ?
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
df = pd.read_sql_query(query_template,con, params=(ticker,))
|
with open(f"json/financial-statements/cash-flow-statement/quarter/{ticker}.json", 'rb') as file:
|
||||||
cash_flow_statement = orjson.loads(df['cashflow'].iloc[0])
|
quarter_res = orjson.loads(file.read())
|
||||||
cash_flow_statement_growth = orjson.loads(df['cashflow_growth'].iloc[0])
|
|
||||||
res = clean_financial_data(cash_flow_statement,cash_flow_statement_growth)
|
|
||||||
except:
|
except:
|
||||||
res = []
|
quarter_res = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(f"json/financial-statements/cash-flow-statement/annual/{ticker}.json", 'rb') as file:
|
||||||
|
annual_res = orjson.loads(file.read())
|
||||||
|
except:
|
||||||
|
annual_res = []
|
||||||
|
|
||||||
|
res = {'quarter': quarter_res, 'annual': annual_res}
|
||||||
|
|
||||||
|
res = orjson.dumps(res)
|
||||||
|
compressed_data = gzip.compress(res)
|
||||||
|
|
||||||
|
redis_client.set(cache_key, compressed_data)
|
||||||
|
redis_client.expire(cache_key, 3600 * 24) # Set cache expiration time to 1 day
|
||||||
|
|
||||||
|
return StreamingResponse(
|
||||||
|
io.BytesIO(compressed_data),
|
||||||
|
media_type="application/json",
|
||||||
|
headers={"Content-Encoding": "gzip"}
|
||||||
|
)
|
||||||
|
|
||||||
redis_client.set(cache_key, orjson.dumps(res))
|
|
||||||
redis_client.expire(cache_key, 3600*3600) # Set cache expiration time to 1 hour
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -464,6 +464,14 @@ def run_tracker():
|
|||||||
command = base_command + [source, f"root@{useast_ip_address}:{dest}"]
|
command = base_command + [source, f"root@{useast_ip_address}:{dest}"]
|
||||||
run_command(command)
|
run_command(command)
|
||||||
|
|
||||||
|
def run_financial_statements():
|
||||||
|
run_command(["python3", "cron_financial_statements.py"])
|
||||||
|
command = [
|
||||||
|
"sudo", "rsync", "-avz", "-e", "ssh",
|
||||||
|
"/root/backend/app/json/financial-statements",
|
||||||
|
f"root@{useast_ip_address}:/root/backend/app/json"
|
||||||
|
]
|
||||||
|
run_command(command)
|
||||||
|
|
||||||
# Create functions to run each schedule in a separate thread
|
# Create functions to run each schedule in a separate thread
|
||||||
def run_threaded(job_func):
|
def run_threaded(job_func):
|
||||||
@ -481,6 +489,8 @@ schedule.every().day.at("06:30").do(run_threaded, run_pocketbase).tag('pocketbas
|
|||||||
|
|
||||||
schedule.every().day.at("07:00").do(run_threaded, run_ta_rating).tag('ta_rating_job')
|
schedule.every().day.at("07:00").do(run_threaded, run_ta_rating).tag('ta_rating_job')
|
||||||
schedule.every().day.at("07:30").do(run_threaded, run_government_contract).tag('government_contract_job')
|
schedule.every().day.at("07:30").do(run_threaded, run_government_contract).tag('government_contract_job')
|
||||||
|
schedule.every().day.at("07:30").do(run_threaded, run_financial_statements).tag('financial_statements_job')
|
||||||
|
|
||||||
|
|
||||||
schedule.every().day.at("08:00").do(run_threaded, run_cron_insider_trading).tag('insider_trading_job')
|
schedule.every().day.at("08:00").do(run_threaded, run_cron_insider_trading).tag('insider_trading_job')
|
||||||
schedule.every().day.at("09:00").do(run_threaded, run_congress_trading).tag('congress_job')
|
schedule.every().day.at("09:00").do(run_threaded, run_congress_trading).tag('congress_job')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user