File size: 18,230 Bytes
35cd772
 
 
 
605026d
35cd772
c7f8147
a919a08
 
 
 
 
35cd772
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1f23e4b
35cd772
 
 
 
 
 
 
 
 
 
 
 
1e96b75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
036e101
35cd772
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5bb2b91
35cd772
 
 
 
 
 
 
 
 
 
 
5bb2b91
35cd772
 
 
 
 
5bb2b91
35cd772
 
 
 
 
 
 
1e96b75
 
 
 
 
 
 
 
5bb2b91
35cd772
5bb2b91
35cd772
5bb2b91
35cd772
 
 
1e96b75
35cd772
 
 
5bb2b91
35cd772
 
 
 
 
 
 
1e96b75
 
35cd772
 
605026d
5bb2b91
 
 
 
066c51a
 
5bb2b91
4a2c8a4
 
 
5bb2b91
ad0055f
5bb2b91
 
ad0055f
5bb2b91
 
ad0055f
5bb2b91
ad0055f
5bb2b91
 
cafa384
017f01d
 
5bb2b91
 
9411d73
f7d0a84
9411d73
 
 
35cd772
f7d0a84
9411d73
 
 
 
 
 
dc363fb
35cd772
 
4f13da0
35cd772
 
1c0b871
9411d73
331592e
 
 
17f9994
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cb33f24
 
17f9994
 
 
 
 
 
 
0bdd1c4
 
 
 
 
cb33f24
 
0bdd1c4
 
 
 
cb33f24
 
17f9994
 
 
 
 
 
cb33f24
 
 
 
17f9994
 
 
 
 
 
 
 
 
35cd772
331592e
e1a4603
35cd772
 
 
17f9994
331592e
 
 
 
17f9994
 
 
 
614511d
35cd772
d41207b
 
29c390a
a5864f4
017f01d
d41207b
ec00f29
 
 
 
77dfae0
 
 
 
d41207b
 
35cd772
17f9994
 
 
 
 
614511d
 
 
17f9994
 
 
 
35cd772
 
17f9994
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
import os
import requests
from typing import Dict, Any
import snowflake.connector
from openai import OpenAI
import gradio as gr
import time
from datetime import datetime
from io import BytesIO
import base64
import matplotlib.pyplot as plt
import pandas as pd

def get_financial_data(ticker: str, api_key: str, period: str = 'annual') -> Dict[str, Any]:
    base_url = "https://financialmodelingprep.com/api/v3/"
    endpoints = {
        "profile": f"profile/{ticker}?apikey={api_key}",
        "balance_sheet": f"balance-sheet-statement/{ticker}?period={period}&apikey={api_key}",
        "income_statement": f"income-statement/{ticker}?period={period}&apikey={api_key}"
    }

    results = {}

    for key, endpoint in endpoints.items():
        url = base_url + endpoint
        response = requests.get(url)
        if response.status_code == 200:
            data = response.json()
            if data:
                if key == "profile":
                    results[key] = data[0]
                elif key == "balance_sheet":
                    results[key] = data[0]  # Get the most recent balance sheet statement
                elif key == "income_statement":
                    results[key] = data[0]  # Get the most recent income statement
            else:
                results[key] = None
        else:
            raise Exception(f"Failed to fetch data from {key} endpoint for ticker {ticker}. HTTP Status Code: {response.status_code}")

    return results

def get_proprietary_indicators(ticker: str) -> str:
    connection = snowflake.connector.connect(
        user=os.getenv('SNOWFLAKE_USER'),
        password=os.getenv('SNOWFLAKE_PASSWORD'),
        account=os.getenv('SNOWFLAKE_ACCOUNT'),
        warehouse=os.getenv('SNOWFLAKE_WAREHOUSE'),
        database=os.getenv('SNOWFLAKE_DATABASE')
    )
    query = f"""
    SELECT prompt
    FROM researchdata.TS_INDICATORS_FOR_GPT_PROMPT
    WHERE SYMBOL = '{ticker}'
    """
    cursor = connection.cursor()
    cursor.execute(query)
    result = cursor.fetchone()
    connection.close()
    
    if result:
        return result[0]
    else:
        return "No proprietary indicators available for this ticker."

def fetch_news(ticker: str, api_key: str) -> str:
    url = f'https://financialmodelingprep.com/api/v3/stock_news?page=0&tickers={ticker}&apikey={api_key}'
    response = requests.get(url)
    if response.status_code == 200:
        news_data = response.json()
        if news_data:
            news_summary = '\n\nRecent News Articles:\n'
            for article in news_data[:5]:  # Limit to the 5 most recent articles
                news_summary += f"Publish Date: {article['publishedDate']}\nTitle: {article['title']}\nSummary:{article['text']}\n\n"
            return news_summary
        else:
            return "No recent news articles found."
    else:
        return "Failed to fetch news articles."

def format_for_gpt_prompt(financial_data: Dict[str, Any], proprietary_indicators: str, news_data: str) -> str:
    profile = financial_data.get('profile', {})
    balance_sheet = financial_data.get('balance_sheet', {})
    income_statement = financial_data.get('income_statement', {})

    company_name = profile.get('companyName', 'N/A')
    company_description = profile.get('description', 'N/A')
    ticker = profile.get('symbol', 'N/A')
    market_cap = profile.get('mktCap', 'N/A')
    ceo = profile.get('ceo', 'N/A')
    beta = profile.get('beta', 'N/A')

    financial_data_str = f"""
Balance Sheet:
Total Assets: {balance_sheet.get('totalAssets', 'N/A')}
Total Liabilities: {balance_sheet.get('totalLiabilities', 'N/A')}
Total Equity: {balance_sheet.get('totalEquity', 'N/A')}
Cash and Cash Equivalents: {balance_sheet.get('cashAndCashEquivalents', 'N/A')}
Cash and Short Term Investments: {balance_sheet.get('cashAndShortTermInvestments', 'N/A')}
Inventory: {balance_sheet.get('inventory', 'N/A')}
Net Receivables: {balance_sheet.get('netReceivables', 'N/A')}
Total Current Assets: {balance_sheet.get('totalCurrentAssets', 'N/A')}
Total Current Liabilities: {balance_sheet.get('totalCurrentLiabilities', 'N/A')}
Long Term Debt: {balance_sheet.get('longTermDebt', 'N/A')}
Short Term Debt: {balance_sheet.get('shortTermDebt', 'N/A')}
Total Debt: {balance_sheet.get('totalDebt', 'N/A')}
Property, Plant, and Equipment Net: {balance_sheet.get('propertyPlantEquipmentNet', 'N/A')}
Deferred Revenue: {balance_sheet.get('deferredRevenue', 'N/A')}
Accumulated Other Comprehensive Income Loss: {balance_sheet.get('accumulatedOtherComprehensiveIncomeLoss', 'N/A')}
Retained Earnings: {balance_sheet.get('retainedEarnings', 'N/A')}
Total Non-Current Assets: {balance_sheet.get('totalNonCurrentAssets', 'N/A')}
Total Non-Current Liabilities: {balance_sheet.get('totalNonCurrentLiabilities', 'N/A')}
Goodwill and Intangible Assets: {balance_sheet.get('goodwillAndIntangibleAssets', 'N/A')}

Income Statement:
Revenue: {income_statement.get('revenue', 'N/A')}
Cost of Revenue: {income_statement.get('costOfRevenue', 'N/A')}
Gross Profit: {income_statement.get('grossProfit', 'N/A')}
Gross Profit Ratio: {income_statement.get('grossProfitRatio', 'N/A')}
Operating Income: {income_statement.get('operatingIncome', 'N/A')}
Operating Income Ratio: {income_statement.get('operatingIncomeRatio', 'N/A')}
Net Income: {income_statement.get('netIncome', 'N/A')}
Net Income Ratio: {income_statement.get('netIncomeRatio', 'N/A')}
EBITDA: {income_statement.get('ebitda', 'N/A')}
EBITDA Ratio: {income_statement.get('ebitdaratio', 'N/A')}
Earnings Per Share (EPS): {income_statement.get('eps', 'N/A')}
Diluted EPS: {income_statement.get('epsdiluted', 'N/A')}
Cost and Expenses: {income_statement.get('costAndExpenses', 'N/A')}
Depreciation and Amortization: {income_statement.get('depreciationAndAmortization', 'N/A')}
Income Before Tax: {income_statement.get('incomeBeforeTax', 'N/A')}
Income Before Tax Ratio: {income_statement.get('incomeBeforeTaxRatio', 'N/A')}
Income Tax Expense: {income_statement.get('incomeTaxExpense', 'N/A')}
Operating Expenses: {income_statement.get('operatingExpenses', 'N/A')}
Research and Development Expenses: {income_statement.get('researchAndDevelopmentExpenses', 'N/A')}
Selling, General, and Administrative Expenses: {income_statement.get('sellingGeneralAndAdministrativeExpenses', 'N/A')}
Total Other Income Expenses Net: {income_statement.get('totalOtherIncomeExpensesNet', 'N/A')}
Weighted Average Shares Outstanding: {income_statement.get('weightedAverageShsOut', 'N/A')}
Weighted Average Shares Outstanding Diluted: {income_statement.get('weightedAverageShsOutDil', 'N/A')}
"""

    prompt = f"""
Here is the profile of the company you will be analyzing:

<company>
Name: {company_name} ({ticker})
Description: {company_description}
Market Cap: {market_cap}
CEO: {ceo}
Beta: {beta}
</company>

Please carefully review this background information on the company.

Next, here is the most recent quarter's balance sheet and income statement data for the company:

<financial_data>
{financial_data_str}
</financial_data>

Next, here are some of the proprietary TradeSmith indicators for the company:

<proprietary_indicators>

{proprietary_indicators}

</proprietary_indicators>

Next, here are some recent news articles about the company:

<news_articles>

{news_data}

</news_articles>

<analysis>

Provide your analysis of the company profile, financial data, proprietary TradeSmith indicators, and recent news. Show your analytical thought process.

</analysis>

<prediction>

Considering everything, what prediction would you make for this stock's return over the next 21 trading days? If TradeSmith's Predictive Alpha projection was told to you, use that as your prediction, and make sure your reasoning aligns with the prediction. Otherwise, make your own prediction based on the information you analyzed.

</prediction>

Remember, your role is to be an expert stock analyst. Explain your reasoning so that investors can understand the key factors driving your recommendation. Assume you are talking to a group of readers, not to an individual, to comply with legal's requirement of not giving out personalized advice. At the end, always disclaim something along the lines of "investing and trading is risky..."
"""

    return prompt

def generate_gpt_prompt(ticker: str, api_key: str) -> str:
    financial_data = get_financial_data(ticker, api_key)
    proprietary_indicators = get_proprietary_indicators(ticker)
    news_data = fetch_news(ticker, api_key)
    return format_for_gpt_prompt(financial_data, proprietary_indicators, news_data)

def run_gpt_model(prompt: str, api_key: str) -> str:
    openai_client = OpenAI(api_key = api_key)

    system_prompt = """
You are a highly experienced stock analyst with deep knowledge of TradeSmith and InvestorPlace products and services. You assist subscribers of these platforms by providing comprehensive stock analyses. Ensure your output is clear, concise, and formatted for easy reading with line breaks after each sentence. The analysis should be structured as follows:

[Company Description]:
Provide a brief overview of the company in 2-3 sentences.

[TradeSmith Indicator Analysis]:
Incorporate relevant TradeSmith indicators, explaining what they suggest about the stock's performance and potential. Use proprietary indicators effectively to add value to the analysis. Use bullet points, and for each point, explain why it's worth noting. If any billionaires hold the stock, name them. If the Predictive Alpha prediction is told to you, don't talk about it here, use it as your prediction in the following Prediction & Analysis section.

[News Analysis]:
Summarize recent news articles or reports related to the company. Discuss any significant events or developments that could impact the stock's performance. Use bullet points, and for each point, explain why it's worth noting.

[Income Statement Analysis]:
Analyze key figures from the company's income statement, such as revenue, expenses, and net income. Highlight trends and significant changes. Use bullet points, and for each point, explain why it's worth noting.

[Balance Sheet Analysis]:
Examine important elements from the balance sheet, focusing on assets, liabilities, and shareholders' equity. Discuss the company's financial health and stability. Use bullet points, and for each point, explain why it's worth noting.

[Prediction & Reasoning]:
Provide a well-reasoned prediction for the stock's future 21 day performance. If a Predictive Alpha prediction was provided, use that as your prediction, supported by your analysis. Offer actionable recommendations based on your insights.

Make sure each section is thorough yet concise, providing valuable insights for the subscriber. Only touch on the details you deem important. Your output should look professionally formatted and work well with gradio's markup output.  

Finally, it is critically important that you comply with the legal guideline of not providing personalized financial advice to individuals.
"""


    system = {
      "role":"system",
      "content": (system_prompt)
      }

    user = {
      "role":"user",
      "content":prompt
      }

    response = openai_client.chat.completions.create(
        model="gpt-4o",  # or another model
        messages = [system,user],
        max_tokens=1500,
        temperature=0.7,
        stream=True
    )

    collected_chunks = ""
    for chunk in response:
        chunk_content = chunk.choices[0].delta.content or ""
        if chunk_content:
            yield chunk_content
            #time.sleep(0.01)

def plot_price_chart(ticker: str):
    # Establishing connection to Snowflake
    connection = snowflake.connector.connect(
        user=os.getenv('SNOWFLAKE_USER'),
        password=os.getenv('SNOWFLAKE_PASSWORD'),
        account=os.getenv('SNOWFLAKE_ACCOUNT'),
        warehouse=os.getenv('SNOWFLAKE_WAREHOUSE'),
        database=os.getenv('SNOWFLAKE_DATABASE')
    )

    cursor = connection.cursor()

    # Function to get historical price data
    def get_historical_price_data(ticker):
        query = f"""
        select s.symbol,
               sd.TRADEDATE,
               sd.TRADEADJCLOSE
            from HISTORICALDATANEW.SYMBOL_SVIEW s
        join HISTORICALDATANEW.SYMBOLTYPES_SVIEW st on s.SYMBOLTYPEID = st.SYMBOLTYPEID
        join HISTORICALDATANEW.STOCKDATA_SVIEW sd on s.SYMBOLID = sd.SYMBOLID
        where st.name in ('Common stock','ETF')
        and case when s.ISDUPLICATE then 1 else 0 end = 0
        and s.EXCHANGEID in (1,8)
        and s.SYMBOL = '{ticker}'
        qualify row_number() over (partition by s.SYMBOLID order by TRADEDATE desc) <= 50
        order by TRADEDATE;
        """
        historical_price_data = cursor.execute(query).fetch_pandas_all()
        return historical_price_data

    # Function to get predicted return data
    def get_predicted_return_data(ticker):
        query = f"""
        select s.SYMBOL,
               pa.PREDICTEDTRADEDATE,
               pa.PREDICTEDRETURN
        from RESEARCHDATA.PREDICTIVEALPHAHISTORICALVALUES_SVIEW pa
        join HISTORICALDATANEW.SYMBOL_SVIEW s on s.SYMBOLID = pa.SYMBOLID
        where pa.PREDICTEDPERIOD = 21
        and s.SYMBOL = '{ticker}'
        qualify row_number() over (partition by pa.SYMBOLID order by TRADEDATE desc) = 1;
        """
        predicted_return_data = cursor.execute(query).fetch_pandas_all()
        return predicted_return_data

    historical_data = get_historical_price_data(ticker)
    predicted_data = get_predicted_return_data(ticker)
    
    # Extract the relevant data
    historical_dates = pd.to_datetime(historical_data['TRADEDATE'])
    historical_prices = historical_data['TRADEADJCLOSE'].astype(float)
    
    last_known_date = historical_dates.iloc[-1]
    last_known_price = historical_prices.iloc[-1]
    
    predicted_date = pd.to_datetime(predicted_data['PREDICTEDTRADEDATE'].iloc[0])
    predicted_return = float(predicted_data['PREDICTEDRETURN'].iloc[0])
    
    predicted_price = last_known_price * (1 + predicted_return)
    
    # Plot the data
    plt.figure(figsize=(12, 6))  # Increase figure size to give more space for annotations
    plt.plot(historical_dates, historical_prices, label='Historical Prices', marker='o')
    plt.axvline(x=last_known_date, color='gray', linestyle='--', label='Last Known Date')
    plt.plot([last_known_date, predicted_date], [last_known_price, predicted_price], 'r--', label='Predicted Price')
    
    # Adding gap for 21 trading days (assuming 5 trading days per week)
    gap_dates = pd.date_range(start=last_known_date, periods=22, freq='B')[-1]
    plt.axvline(x=gap_dates, color='blue', linestyle='--', label='21 Trading Days Gap')

    # Annotations
    plt.annotate(f'Last Known Price: {last_known_price:.2f}', 
                 xy=(last_known_date, last_known_price), 
                 xytext=(last_known_date, last_known_price * 1.05),
                 arrowprops=dict(facecolor='black', arrowstyle='->'),
                 bbox=dict(boxstyle='round,pad=0.5', edgecolor='black', facecolor='white'))
    
    plt.annotate(f'Predicted Price: {predicted_price:.2f}\nPredicted Return: {predicted_return:.2%}', 
                 xy=(predicted_date, predicted_price), 
                 xytext=(predicted_date, predicted_price * 1.05),
                 arrowprops=dict(facecolor='red', arrowstyle='->'),
                 bbox=dict(boxstyle='round,pad=0.5', edgecolor='red', facecolor='white'))
    
    plt.xlabel('Date')
    plt.ylabel('Price')
    plt.title(f'{ticker} Price Chart with Predicted Price')
    plt.legend()
    plt.grid(True)
    
    # Adjust limits to ensure annotations fit
    plt.ylim(bottom=min(historical_prices) * 0.95, top=max(historical_prices) * 1.1)
    plt.xlim(left=min(historical_dates), right=predicted_date + pd.Timedelta(days=10))

    # Save plot to a bytes buffer
    buf = BytesIO()
    plt.savefig(buf, format="png")
    buf.seek(0)
    img_str = base64.b64encode(buf.read()).decode("utf-8")
    buf.close()
    
    return f'<img src="data:image/png;base64,{img_str}" alt="Price Chart">'

def analyze_stock(ticker: str):
    ticker = ticker.upper()
    fmp_api_key = os.getenv('FMP_API_KEY')
    openai_api_key = os.getenv('OPENAI_API_KEY')
    prompt = generate_gpt_prompt(ticker, fmp_api_key)
    yield prompt, "", ""  # Initially yield the prompt with empty analysis and chart response
    
    analysis_chunks = []
    for analysis_chunk in run_gpt_model(prompt, openai_api_key):
        analysis_chunks.append(analysis_chunk)
        yield "".join(analysis_chunks), prompt, ""  # Stream the accumulated analysis chunks
    
    # Generate the price chart after the analysis
    price_chart_html = plot_price_chart(ticker)
    yield "".join(analysis_chunks),price_chart_html, prompt

description = """We gave Ann-E a name, but not a personality. It's time to change that!

               Imagine entering a ticker on the TradeSmith site, going to the Predictive Alpha widget, and getting a stock analysis from Ann-E to go along with the prediction.
               
               Currently, the analysis is based on company profile information, recent news, the most recent quarter's balance sheet and income statements, and TradeSmith proprietary indicators.
               
               This demonstration shows just a simple use case for building out a proprietary Alta Large Language Model (LLM). 
               
               Future vision includes interactive model, fine-tuned on Alta content and indicators, that help our subscribers and keep them engaged on the site. 
               
               The possibilities are endless!
               
               Note: We can control outputs to meet legal requirements.
               """
              

# Gradio Interface
iface = gr.Interface(
    fn=analyze_stock, 
    inputs=gr.Textbox(label='Enter a Ticker', value='AAPL'), 
    outputs=[
        gr.Markdown(label='Ann-E Response'),
        gr.HTML(label='Price Chart'),
        gr.Textbox(label='Prompt for Model (behind the scenes).')
        
    ], 
    title="Demo: Predictive Alpha + Stock Analysis GPT Model",
    description=description
)

if __name__ == "__main__":
    iface.launch()