sanjeevl10 commited on
Commit
38b6b6d
·
0 Parent(s):

add aap.py and added sentiment analysis

Browse files
.chainlit/config.toml ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ # Whether to enable telemetry (default: true). No personal data is collected.
3
+ enable_telemetry = true
4
+
5
+ # List of environment variables to be provided by each user to use the app.
6
+ user_env = []
7
+
8
+ # Duration (in seconds) during which the session is saved when the connection is lost
9
+ session_timeout = 3600
10
+
11
+ # Enable third parties caching (e.g LangChain cache)
12
+ cache = false
13
+
14
+ # Follow symlink for asset mount (see https://github.com/Chainlit/chainlit/issues/317)
15
+ # follow_symlink = false
16
+
17
+ [features]
18
+ # Show the prompt playground
19
+ prompt_playground = true
20
+
21
+ # Process and display HTML in messages. This can be a security risk (see https://stackoverflow.com/questions/19603097/why-is-it-dangerous-to-render-user-generated-html-or-javascript)
22
+ unsafe_allow_html = false
23
+
24
+ # Process and display mathematical expressions. This can clash with "$" characters in messages.
25
+ latex = false
26
+
27
+ # Authorize users to upload files with messages
28
+ multi_modal = true
29
+
30
+ # Allows user to use speech to text
31
+ [features.speech_to_text]
32
+ enabled = false
33
+ # See all languages here https://github.com/JamesBrill/react-speech-recognition/blob/HEAD/docs/API.md#language-string
34
+ # language = "en-US"
35
+
36
+ [UI]
37
+ # Name of the app and chatbot.
38
+ name = "Chatbot"
39
+
40
+ # Show the readme while the conversation is empty.
41
+ show_readme_as_default = true
42
+
43
+ # Description of the app and chatbot. This is used for HTML tags.
44
+ # description = ""
45
+
46
+ # Large size content are by default collapsed for a cleaner ui
47
+ default_collapse_content = true
48
+
49
+ # The default value for the expand messages settings.
50
+ default_expand_messages = false
51
+
52
+ # Hide the chain of thought details from the user in the UI.
53
+ hide_cot = false
54
+
55
+ # Link to your github repo. This will add a github button in the UI's header.
56
+ # github = ""
57
+
58
+ # Specify a CSS file that can be used to customize the user interface.
59
+ # The CSS file can be served from the public directory or via an external link.
60
+ # custom_css = "/public/test.css"
61
+
62
+ # Override default MUI light theme. (Check theme.ts)
63
+ [UI.theme.light]
64
+ #background = "#FAFAFA"
65
+ #paper = "#FFFFFF"
66
+
67
+ [UI.theme.light.primary]
68
+ #main = "#F80061"
69
+ #dark = "#980039"
70
+ #light = "#FFE7EB"
71
+
72
+ # Override default MUI dark theme. (Check theme.ts)
73
+ [UI.theme.dark]
74
+ #background = "#FAFAFA"
75
+ #paper = "#FFFFFF"
76
+
77
+ [UI.theme.dark.primary]
78
+ #main = "#F80061"
79
+ #dark = "#980039"
80
+ #light = "#FFE7EB"
81
+
82
+
83
+ [meta]
84
+ generated_by = "0.7.700"
Dockerfile ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11
2
+ RUN useradd -m -u 1000 user
3
+ USER user
4
+ ENV HOME=/home/user \
5
+ PATH=/home/user/.local/bin:$PATH
6
+ WORKDIR $HOME/app
7
+ COPY --chown=user . $HOME/app/
8
+ RUN chown user -R ${HOME}/app/data
9
+ COPY ./requirements.txt ~/app/requirements.txt
10
+ RUN pip install -r requirements.txt
11
+ COPY . .
12
+ CMD ["chainlit", "run", "app.py", "--port", "7860"]
README.md ADDED
@@ -0,0 +1 @@
 
 
1
+ # StockSavvy
__pycache__/app_test3_memory1.cpython-310.pyc ADDED
Binary file (9.38 kB). View file
 
__pycache__/utils.cpython-310.pyc ADDED
Binary file (5.11 kB). View file
 
app.py ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_experimental.agents import create_pandas_dataframe_agent
2
+ from langchain.llms import OpenAI
3
+ import chainlit as cl
4
+ from plotly.subplots import make_subplots
5
+ import utils as u
6
+ from langchain.agents import AgentExecutor, create_openai_tools_agent
7
+ from langchain_core.messages import BaseMessage, HumanMessage
8
+ from langchain_openai import ChatOpenAI
9
+ from langchain_core.output_parsers.openai_functions import JsonOutputFunctionsParser
10
+ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
11
+ from tools import data_analyst
12
+ from tools import stock_sentiment_analysis_util
13
+ import functools
14
+ from typing import Annotated
15
+ import operator
16
+ from typing import Sequence, TypedDict
17
+ from langchain.agents import initialize_agent, Tool
18
+ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
19
+ from langgraph.graph import END, StateGraph
20
+ import numpy as np
21
+ import pandas as pd
22
+ from dotenv import load_dotenv
23
+ import os
24
+ import yfinance as yf
25
+ import functools
26
+ from typing import Annotated
27
+ import operator
28
+ from typing import Sequence, TypedDict
29
+ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
30
+ from langgraph.graph import END, StateGraph
31
+ from tools import data_analyst, forecasting_expert_arima, forecasting_expert_rf, evaluator, investment_advisor
32
+ from chainlit.input_widget import Select
33
+ import matplotlib.pyplot as plt
34
+ from langgraph.checkpoint.memory import MemorySaver
35
+
36
+ load_dotenv()
37
+ OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
38
+
39
+ from GoogleNews import GoogleNews
40
+
41
+ def search_news(stockticker):
42
+ """Useful to search the internet for news about a given topic and return relevant results."""
43
+ # Set the number of top news results to return
44
+ googlenews = GoogleNews()
45
+ googlenews.set_period('7d')
46
+ googlenews.get_news(stockticker)
47
+ result_string=googlenews.get_texts()
48
+
49
+ return result_string
50
+
51
+
52
+ def create_agent(llm: ChatOpenAI, tools: list, system_prompt: str):
53
+ # Each worker node will be given a name and some tools.
54
+ prompt = ChatPromptTemplate.from_messages(
55
+ [
56
+ (
57
+ "system",
58
+ system_prompt,
59
+ ),
60
+ MessagesPlaceholder(variable_name="messages"),
61
+ MessagesPlaceholder(variable_name="agent_scratchpad"),
62
+ ]
63
+ )
64
+ agent = create_openai_tools_agent(llm, tools, prompt)
65
+ executor = AgentExecutor(agent=agent, tools=tools)
66
+ return executor
67
+
68
+
69
+ def agent_node(state, agent, name):
70
+ result = agent.invoke(state)
71
+ return {"messages": [HumanMessage(content=result["output"], name=name)]}
72
+
73
+ llm = ChatOpenAI(model="gpt-3.5-turbo")
74
+
75
+ #======================== AGENTS ==================================
76
+ # The agent state is the input to each node in the graph
77
+ class AgentState(TypedDict):
78
+ # The annotation tells the graph that new messages will always
79
+ # be added to the current states
80
+ messages: Annotated[Sequence[BaseMessage], operator.add]
81
+ # The 'next' field indicates where to route to next
82
+ next: str
83
+
84
+ # DATA ANALYST
85
+ prompt_data_analyst="You are a stock data analyst.\
86
+ Provide correct stock ticker from Yahoo Finance.\
87
+ Expected output: stocticker.\
88
+ Provide it in the following format: >>stockticker>> \
89
+ for example: >>AAPL>>"
90
+
91
+ tools_data_analyst=data_analyst.data_analyst_tools()
92
+ data_agent = create_agent(
93
+ llm,
94
+ tools_data_analyst,
95
+ prompt_data_analyst)
96
+ get_historical_prices = functools.partial(agent_node, agent=data_agent, name="Data_analyst")
97
+
98
+ #ARIMA Forecasting expert
99
+ prompt_forecasting_expert_arima="""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
100
+ You are stock prediction expert, \
101
+ take historical stock data from message and train the ARIMA model from statsmodels Python library on the last week,then provide prediction for the 'Close' price for the next day.\
102
+ Give the value for mae_arima to Evaluator.\
103
+ Expected output:list of predicted prices with predicted dates for a selected stock ticker and mae_arima value.\n
104
+ <|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
105
+
106
+ tools_forecasting_expert_arima=forecasting_expert_arima.forecasting_expert_arima_tools()
107
+ code_forecasting_arima = create_agent(
108
+ llm,
109
+ tools_forecasting_expert_arima,
110
+ prompt_forecasting_expert_arima,
111
+ )
112
+ predict_future_prices_arima = functools.partial(agent_node, agent=code_forecasting_arima, name="Forecasting_expert_ARIMA")
113
+
114
+ # RF Forecasting expert
115
+ prompt_forecasting_expert_random_forest="""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
116
+ You are stock prediction expert, \
117
+ take historical stock data from message and train the Random forest model from statsmodels Python library on the last week,then provide prediction for the 'Close' price for the next day.\
118
+ Give the value for mae_rf to Evaluator.\
119
+ Expected output:list of predicted prices with predicted dates for a selected stock ticker and mae_rf value.\n
120
+ <|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
121
+
122
+ tools_forecasting_expert_random_forest=forecasting_expert_rf.forecasting_expert_rf_tools()
123
+ code_forecasting_random_forest = create_agent(
124
+ llm,
125
+ tools_forecasting_expert_random_forest,
126
+ prompt_forecasting_expert_random_forest,
127
+ )
128
+ predict_future_prices_random_forest = functools.partial(agent_node, agent=code_forecasting_random_forest, name="Forecasting_expert_random_forest")
129
+
130
+ # EVALUATOR
131
+ prompt_evaluator="""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
132
+ You are an evaluator retrieve arima_prediction and arima mean average error from forecasting expert arima and rf_prediction and mean average error for random forest from forecasting expert random forest\
133
+ print final prediction number.
134
+ Next, compare prediction price and current price to provide reccommendation if he should buy/sell/hold the stock. \
135
+ Expected output: one value for the prediction, explain why you have selected this value, reccommendation buy or sell stock and why.\
136
+ <|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
137
+
138
+ tools_evaluate=evaluator.evaluator_tools()
139
+ code_evaluate = create_agent(
140
+ llm,
141
+ tools_evaluate,
142
+ prompt_evaluator,
143
+ )
144
+ evaluate = functools.partial(agent_node, agent=code_evaluate, name="Evaluator")
145
+
146
+ # Investment advisor
147
+ prompt_inv_advisor="""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
148
+ Provide personalized investment advice and recommendations and analyze historical stock prices if asked.\
149
+ Consider user input message for the latest news on the stock.\
150
+ Provide overall sentiment of the news Positive/Negative/Neutral, and recommend if the user should invest in such stock.\
151
+ <|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
152
+
153
+ tools_reccommend=investment_advisor.investment_advisor_tools()
154
+
155
+ code_inv_advisor = create_agent(
156
+ llm,
157
+ tools_reccommend,
158
+ prompt_inv_advisor,
159
+ )
160
+
161
+ reccommend = functools.partial(agent_node, agent=code_inv_advisor, name="Investment_advisor")
162
+
163
+ workflow_data = StateGraph(AgentState)
164
+ workflow_data.add_node("Data_analyst", get_historical_prices)
165
+ workflow_data.set_entry_point("Data_analyst")
166
+ graph_data=workflow_data.compile()
167
+
168
+ workflow = StateGraph(AgentState)
169
+ #workflow.add_node("Data_analyst", get_historical_prices)
170
+ workflow.add_node("Forecasting_expert_random_forest", predict_future_prices_random_forest)
171
+ workflow.add_node("Forecasting_expert_ARIMA", predict_future_prices_arima)
172
+ workflow.add_node("Evaluator", evaluate)
173
+
174
+
175
+ # Finally, add entrypoint
176
+ workflow.set_entry_point("Forecasting_expert_random_forest")
177
+ workflow.add_edge("Forecasting_expert_random_forest","Forecasting_expert_ARIMA")
178
+ workflow.add_edge("Forecasting_expert_ARIMA","Evaluator")
179
+ workflow.add_edge("Evaluator",END)
180
+ graph = workflow.compile()
181
+
182
+ #Print graph
183
+ #graph.get_graph().print_ascii()
184
+
185
+ """ memory = MemorySaver()
186
+ workflow_news = StateGraph(AgentState)
187
+ workflow_news.add_node("Investment_advisor", reccommend)
188
+ workflow_news.set_entry_point("Investment_advisor")
189
+ workflow_news.add_edge("Investment_advisor",END)
190
+ graph_news = workflow_news.compile(checkpointer=memory) """
191
+
192
+ from langchain_core.runnables import RunnableConfig
193
+ from chainlit import AskUserMessage
194
+ @cl.on_chat_start
195
+ async def on_chat_start():
196
+ cl.user_session.set("counter", 0)
197
+ # Sending an image with the local file path
198
+ elements = [
199
+ cl.Image(name="image1", display="inline", path="./stock_image1.png",size="large")
200
+ ]
201
+ await cl.Message(content="Hello there, Welcome to ##StockSavyy!", elements=elements).send()
202
+ await cl.Message(content="Tell me the stockticker you want me to analyze.").send()
203
+
204
+ @cl.on_message
205
+ async def main(message: cl.Message):
206
+ #"what is the weather in sf"
207
+ counter = cl.user_session.get("counter")
208
+ counter += 1
209
+ cl.user_session.set("counter", counter)
210
+ await cl.Message(content=f"You sent {counter} message(s)!").send()
211
+ #if counter==1:
212
+ inputs = {"messages": [HumanMessage(content=message.content)]}
213
+ res_data = graph_data.invoke(inputs, config=RunnableConfig(callbacks=[
214
+ cl.LangchainCallbackHandler(
215
+ to_ignore=["ChannelRead", "RunnableLambda", "ChannelWrite", "__start__", "_execute"]
216
+ # can add more into the to_ignore: "agent:edges", "call_model"
217
+ # to_keep=
218
+
219
+ )]))
220
+ #print(res_data)
221
+ await cl.Message(content=res_data["messages"][-1].content).send()
222
+ #print('ticker',str(res_data).split(">>"))
223
+ if len(str(res_data).split(">>")[1])<10:
224
+ stockticker=(str(res_data).split(">>")[1])
225
+ else:
226
+ stockticker=(str(res_data).split(">>")[0])
227
+ #print('ticker1',stockticker)
228
+ print('here')
229
+ df=u.get_stock_price(stockticker)
230
+ df_history=u.historical_stock_prices(stockticker,90)
231
+ df_history_to_msg1=eval(str(list((pd.DataFrame(df_history['Close'].values.reshape(1, -1)[0]).T).iloc[0,:])))
232
+
233
+ inputs_all = {"messages": [HumanMessage(content=(f"Predict {stockticker}, historical prices are: {df_history_to_msg1}."))]}
234
+ df_history=pd.DataFrame(df_history)
235
+ df_history['stockticker']=np.repeat(stockticker,len(df_history))
236
+ df_history.to_csv('df_history.csv')
237
+ #df_history.to_csv('./tools/df_history.csv')
238
+
239
+ print ("Running forecasting models on historical prices")
240
+ res = graph.invoke(inputs_all, config=RunnableConfig(callbacks=[
241
+ cl.LangchainCallbackHandler(
242
+ to_ignore=["ChannelRead", "RunnableLambda", "ChannelWrite", "__start__", "_execute"]
243
+ # can add more into the to_ignore: "agent:edges", "call_model"
244
+ # to_keep=
245
+
246
+ )]))
247
+ await cl.Message(content= res["messages"][-2].content + '\n\n' + res["messages"][-1].content).send()
248
+
249
+ #Plotting the graph
250
+ df=u.historical_stock_prices(stockticker,90)
251
+ df=u.calculate_MACD(df, fast_period=12, slow_period=26, signal_period=9)
252
+ #df values
253
+ #Index(['Open', 'High', 'Low', 'Close', 'Volume', 'Dividends', 'Stock Splits','EMA_fast', 'EMA_slow', 'MACD', 'Signal_Line', 'MACD_Histogram']
254
+ fig = u.plot_macd2(df)
255
+
256
+ if fig:
257
+ elements = [cl.Pyplot(name="plot", figure=fig, display="inline",size="large"),
258
+ ]
259
+ await cl.Message(
260
+ content="Here is the MACD plot",
261
+ elements=elements,
262
+ ).send()
263
+ else:
264
+ await cl.Message(
265
+ content="Failed to generate the MACD plot."
266
+ ).send()
267
+
268
+
269
+ #Perform sentiment analysis on the stock news & predict dominant sentiment along with plotting the sentiment breakdown chart
270
+ news_articles = stock_sentiment_analysis_util.fetch_news(stockticker)
271
+
272
+ analysis_results = []
273
+
274
+ #Perform sentiment analysis for each product review
275
+ for article in news_articles:
276
+ sentiment_analysis_result = stock_sentiment_analysis_util.analyze_sentiment(article['News_Article'])
277
+
278
+ # Display sentiment analysis results
279
+ #print(f'News Article: {sentiment_analysis_result["News_Article"]} : Sentiment: {sentiment_analysis_result["Sentiment"]}', '\n')
280
+
281
+ result = {
282
+ 'News_Article': sentiment_analysis_result["News_Article"],
283
+ 'Sentiment': sentiment_analysis_result["Sentiment"][0]['label']
284
+ }
285
+
286
+ analysis_results.append(result)
287
+
288
+
289
+ #Retrieve dominant sentiment based on sentiment analysis data of reviews
290
+ dominant_sentiment = stock_sentiment_analysis_util.get_dominant_sentiment(analysis_results)
291
+ await cl.Message(
292
+ content="Dominant sentiment of the stock based on last 7 days of news is : " + dominant_sentiment
293
+ ).send()
294
+
295
+ #Plot sentiment breakdown chart
296
+
297
+ fig = stock_sentiment_analysis_util.plot_sentiment_graph(analysis_results)
298
+ if fig:
299
+ elements = [cl.Pyplot(name="plot", figure=fig, display="inline",size="large"),
300
+ ]
301
+ await cl.Message(
302
+ content="Sentiment breakdown plot",
303
+ elements=elements,
304
+ ).send()
305
+ else:
306
+ await cl.Message(
307
+ content="Failed to generate the MACD plot."
308
+ ).send()
309
+
310
+ #Generate summarized message rationalize dominant sentiment
311
+ summary = stock_sentiment_analysis_util.generate_summary_of_sentiment(analysis_results, dominant_sentiment)
312
+ await cl.Message(
313
+ content= summary
314
+ ).send()
315
+
316
+
317
+
318
+
chainlit.md ADDED
@@ -0,0 +1 @@
 
 
1
+ # Welcome to AskAnyQuery Bot!!
df_history.csv ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Date,Open,High,Low,Close,Volume,Dividends,Stock Splits,stockticker
2
+ 2024-04-22 00:00:00-04:00,399.3596471827562,402.1246793258712,395.03745670124425,400.2380676269531,20286900,0.0,0.0,MSFT
3
+ 2024-04-23 00:00:00-04:00,403.51216021293357,407.4650522060062,402.33429210205253,406.836181640625,15734500,0.0,0.0,MSFT
4
+ 2024-04-24 00:00:00-04:00,408.82258607970806,411.72735028943725,406.0475926794115,408.323486328125,15065300,0.0,0.0,MSFT
5
+ 2024-04-25 00:00:00-04:00,393.32054400787314,399.1700088625431,387.3313470651067,398.321533203125,40586500,0.0,0.0,MSFT
6
+ 2024-04-26 00:00:00-04:00,411.4279132786848,412.25640548421114,405.02945064216703,405.58843994140625,29694700,0.0,0.0,MSFT
7
+ 2024-04-29 00:00:00-04:00,404.52035539531056,405.5884361925186,398.4712687423875,401.5257568359375,19582100,0.0,0.0,MSFT
8
+ 2024-04-30 00:00:00-04:00,400.76710737423014,401.4359144425664,388.4693126899027,388.6289978027344,28781400,0.0,0.0,MSFT
9
+ 2024-05-01 00:00:00-04:00,391.9030904630616,400.9967037344784,389.6072438016868,394.2289123535156,23562500,0.0,0.0,MSFT
10
+ 2024-05-02 00:00:00-04:00,396.94401914412265,399.20992105581087,393.9394288835304,397.1236877441406,17709400,0.0,0.0,MSFT
11
+ 2024-05-03 00:00:00-04:00,401.55570709720826,406.4169339510819,401.13644988960164,405.9278259277344,17446700,0.0,0.0,MSFT
12
+ 2024-05-06 00:00:00-04:00,408.024048156178,413.1847226485525,405.63833666603693,412.7954406738281,16996600,0.0,0.0,MSFT
13
+ 2024-05-07 00:00:00-04:00,413.91342570011614,413.92341744357753,408.35344694069664,408.6029968261719,20018200,0.0,0.0,MSFT
14
+ 2024-05-08 00:00:00-04:00,407.4351142805277,411.48780192255407,405.97772103822234,409.80084228515625,11792300,0.0,0.0,MSFT
15
+ 2024-05-09 00:00:00-04:00,409.8307875446534,411.97691043744567,408.363433019907,411.57763671875,14689700,0.0,0.0,MSFT
16
+ 2024-05-10 00:00:00-04:00,412.1965086797442,414.6321179246016,411.05854661467066,413.9932556152344,13402300,0.0,0.0,MSFT
17
+ 2024-05-13 00:00:00-04:00,417.2573820335048,417.5967662074119,410.08032520369875,412.97509765625,15440200,0.0,0.0,MSFT
18
+ 2024-05-14 00:00:00-04:00,411.2781631216723,416.73831581889846,410.8090081198034,415.80999755859375,15109300,0.0,0.0,MSFT
19
+ 2024-05-15 00:00:00-04:00,417.8999938964844,423.80999755859375,417.2699890136719,423.0799865722656,22239500,0.75,0.0,MSFT
20
+ 2024-05-16 00:00:00-04:00,421.79998779296875,425.4200134277344,420.3500061035156,420.989990234375,17530100,0.0,0.0,MSFT
21
+ 2024-05-17 00:00:00-04:00,422.5400085449219,422.9200134277344,418.0299987792969,420.2099914550781,15352200,0.0,0.0,MSFT
22
+ 2024-05-20 00:00:00-04:00,420.2099914550781,426.7699890136719,419.989990234375,425.3399963378906,16272100,0.0,0.0,MSFT
23
+ 2024-05-21 00:00:00-04:00,426.8299865722656,432.9700012207031,424.8500061035156,429.0400085449219,21453300,0.0,0.0,MSFT
24
+ 2024-05-22 00:00:00-04:00,430.0899963378906,432.4100036621094,427.1300048828125,430.5199890136719,18073700,0.0,0.0,MSFT
25
+ 2024-05-23 00:00:00-04:00,432.9700012207031,433.6000061035156,425.4200134277344,427.0,17211700,0.0,0.0,MSFT
26
+ 2024-05-24 00:00:00-04:00,427.19000244140625,431.05999755859375,424.4100036621094,430.1600036621094,11845800,0.0,0.0,MSFT
27
+ 2024-05-28 00:00:00-04:00,429.6300048828125,430.82000732421875,426.6000061035156,430.32000732421875,15718000,0.0,0.0,MSFT
28
+ 2024-05-29 00:00:00-04:00,425.69000244140625,430.94000244140625,425.69000244140625,429.1700134277344,15517100,0.0,0.0,MSFT
29
+ 2024-05-30 00:00:00-04:00,424.29998779296875,424.29998779296875,414.239990234375,414.6700134277344,28424800,0.0,0.0,MSFT
30
+ 2024-05-31 00:00:00-04:00,416.75,416.75,404.510009765625,415.1300048828125,47995300,0.0,0.0,MSFT
31
+ 2024-06-03 00:00:00-04:00,415.5299987792969,416.42999267578125,408.9200134277344,413.5199890136719,17484700,0.0,0.0,MSFT
32
+ 2024-06-04 00:00:00-04:00,412.42999267578125,416.44000244140625,409.67999267578125,416.07000732421875,14348900,0.0,0.0,MSFT
33
+ 2024-06-05 00:00:00-04:00,417.80999755859375,424.0799865722656,416.29998779296875,424.010009765625,16988000,0.0,0.0,MSFT
34
+ 2024-06-06 00:00:00-04:00,424.010009765625,425.30999755859375,420.5799865722656,424.5199890136719,14861300,0.0,0.0,MSFT
35
+ 2024-06-07 00:00:00-04:00,426.20001220703125,426.2799987792969,423.0,423.8500061035156,13621700,0.0,0.0,MSFT
36
+ 2024-06-10 00:00:00-04:00,424.70001220703125,428.0799865722656,423.8900146484375,427.8699951171875,14003000,0.0,0.0,MSFT
37
+ 2024-06-11 00:00:00-04:00,425.4800109863281,432.82000732421875,425.25,432.67999267578125,14551100,0.0,0.0,MSFT
38
+ 2024-06-12 00:00:00-04:00,435.32000732421875,443.3999938964844,433.25,441.05999755859375,22366200,0.0,0.0,MSFT
39
+ 2024-06-13 00:00:00-04:00,440.8500061035156,443.3900146484375,439.3699951171875,441.5799865722656,15960600,0.0,0.0,MSFT
40
+ 2024-06-14 00:00:00-04:00,438.2799987792969,443.1400146484375,436.7200012207031,442.57000732421875,13582000,0.0,0.0,MSFT
41
+ 2024-06-17 00:00:00-04:00,442.5899963378906,450.94000244140625,440.7200012207031,448.3699951171875,20790000,0.0,0.0,MSFT
42
+ 2024-06-18 00:00:00-04:00,449.7099914550781,450.1400146484375,444.8900146484375,446.3399963378906,17112500,0.0,0.0,MSFT
43
+ 2024-06-20 00:00:00-04:00,446.29998779296875,446.5299987792969,441.2699890136719,445.70001220703125,19877400,0.0,0.0,MSFT
44
+ 2024-06-21 00:00:00-04:00,447.3800048828125,450.5799865722656,446.510009765625,449.7799987792969,34486200,0.0,0.0,MSFT
45
+ 2024-06-24 00:00:00-04:00,449.79998779296875,452.75,446.4100036621094,447.6700134277344,15913700,0.0,0.0,MSFT
46
+ 2024-06-25 00:00:00-04:00,448.25,451.4200134277344,446.75,450.95001220703125,16747500,0.0,0.0,MSFT
47
+ 2024-06-26 00:00:00-04:00,449.0,453.6000061035156,448.19000244140625,452.1600036621094,16507000,0.0,0.0,MSFT
48
+ 2024-06-27 00:00:00-04:00,452.17999267578125,456.1700134277344,451.7699890136719,452.8500061035156,14806300,0.0,0.0,MSFT
49
+ 2024-06-28 00:00:00-04:00,453.07000732421875,455.3800048828125,446.4100036621094,446.95001220703125,28362300,0.0,0.0,MSFT
50
+ 2024-07-01 00:00:00-04:00,448.6600036621094,457.3699951171875,445.6600036621094,456.7300109863281,17662800,0.0,0.0,MSFT
51
+ 2024-07-02 00:00:00-04:00,453.20001220703125,459.5899963378906,453.1099853515625,459.2799987792969,13979800,0.0,0.0,MSFT
52
+ 2024-07-03 00:00:00-04:00,458.19000244140625,461.0199890136719,457.8800048828125,460.7699890136719,9932800,0.0,0.0,MSFT
53
+ 2024-07-05 00:00:00-04:00,459.6099853515625,468.3500061035156,458.9700012207031,467.55999755859375,16000300,0.0,0.0,MSFT
54
+ 2024-07-08 00:00:00-04:00,466.54998779296875,467.70001220703125,464.4599914550781,466.239990234375,12962300,0.0,0.0,MSFT
55
+ 2024-07-09 00:00:00-04:00,467.0,467.3299865722656,458.0,459.5400085449219,17207200,0.0,0.0,MSFT
56
+ 2024-07-10 00:00:00-04:00,461.2200012207031,466.4599914550781,458.8599853515625,466.25,18196100,0.0,0.0,MSFT
57
+ 2024-07-11 00:00:00-04:00,462.9800109863281,464.7799987792969,451.54998779296875,454.70001220703125,23111200,0.0,0.0,MSFT
58
+ 2024-07-12 00:00:00-04:00,454.3299865722656,456.3599853515625,450.6499938964844,453.54998779296875,16324300,0.0,0.0,MSFT
59
+ 2024-07-15 00:00:00-04:00,453.29998779296875,457.260009765625,451.42999267578125,453.9599914550781,14429400,0.0,0.0,MSFT
60
+ 2024-07-16 00:00:00-04:00,454.2200012207031,454.29998779296875,446.6600036621094,449.5199890136719,17175700,0.0,0.0,MSFT
61
+ 2024-07-17 00:00:00-04:00,442.5899963378906,444.8500061035156,439.17999267578125,443.5199890136719,21778000,0.0,0.0,MSFT
62
+ 2024-07-18 00:00:00-04:00,444.3399963378906,444.6499938964844,434.3999938964844,440.3699951171875,20794800,0.0,0.0,MSFT
63
+ 2024-07-19 00:00:00-04:00,433.1000061035156,441.1400146484375,432.0,437.1099853515625,20862400,0.0,0.0,MSFT
requirements.txt ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #langchain
2
+ #chainlit
3
+ #tabulate
4
+ #pandas
5
+ chainlit==0.7.700
6
+ langchain-experimental==0.0.62
7
+ langchain==0.2.7
8
+ langchain_community==0.2.7
9
+ langchain_core==0.2.18
10
+ plotly==5.22.0
11
+ pandas==2.2.2
12
+ yfinance==0.2.40
13
+ langchain-openai==0.1.16
14
+ langgraph==0.1.8
15
+ pydantic==2.8.2
16
+ langchain.tools==0.1.34
17
+ statsmodels==0.14.2
18
+ matplotlib==3.9.1
19
+ python-dotenv==1.0.1
20
+ alpaca_trade_api
21
+ transformers
22
+ pandas
23
+ GoogleNews
24
+ streamlit
tools/.chainlit/config.toml ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ # Whether to enable telemetry (default: true). No personal data is collected.
3
+ enable_telemetry = true
4
+
5
+ # List of environment variables to be provided by each user to use the app.
6
+ user_env = []
7
+
8
+ # Duration (in seconds) during which the session is saved when the connection is lost
9
+ session_timeout = 3600
10
+
11
+ # Enable third parties caching (e.g LangChain cache)
12
+ cache = false
13
+
14
+ # Follow symlink for asset mount (see https://github.com/Chainlit/chainlit/issues/317)
15
+ # follow_symlink = false
16
+
17
+ [features]
18
+ # Show the prompt playground
19
+ prompt_playground = true
20
+
21
+ # Process and display HTML in messages. This can be a security risk (see https://stackoverflow.com/questions/19603097/why-is-it-dangerous-to-render-user-generated-html-or-javascript)
22
+ unsafe_allow_html = false
23
+
24
+ # Process and display mathematical expressions. This can clash with "$" characters in messages.
25
+ latex = false
26
+
27
+ # Authorize users to upload files with messages
28
+ multi_modal = true
29
+
30
+ # Allows user to use speech to text
31
+ [features.speech_to_text]
32
+ enabled = false
33
+ # See all languages here https://github.com/JamesBrill/react-speech-recognition/blob/HEAD/docs/API.md#language-string
34
+ # language = "en-US"
35
+
36
+ [UI]
37
+ # Name of the app and chatbot.
38
+ name = "Chatbot"
39
+
40
+ # Show the readme while the conversation is empty.
41
+ show_readme_as_default = true
42
+
43
+ # Description of the app and chatbot. This is used for HTML tags.
44
+ # description = ""
45
+
46
+ # Large size content are by default collapsed for a cleaner ui
47
+ default_collapse_content = true
48
+
49
+ # The default value for the expand messages settings.
50
+ default_expand_messages = false
51
+
52
+ # Hide the chain of thought details from the user in the UI.
53
+ hide_cot = false
54
+
55
+ # Link to your github repo. This will add a github button in the UI's header.
56
+ # github = ""
57
+
58
+ # Specify a CSS file that can be used to customize the user interface.
59
+ # The CSS file can be served from the public directory or via an external link.
60
+ # custom_css = "/public/test.css"
61
+
62
+ # Override default MUI light theme. (Check theme.ts)
63
+ [UI.theme.light]
64
+ #background = "#FAFAFA"
65
+ #paper = "#FFFFFF"
66
+
67
+ [UI.theme.light.primary]
68
+ #main = "#F80061"
69
+ #dark = "#980039"
70
+ #light = "#FFE7EB"
71
+
72
+ # Override default MUI dark theme. (Check theme.ts)
73
+ [UI.theme.dark]
74
+ #background = "#FAFAFA"
75
+ #paper = "#FFFFFF"
76
+
77
+ [UI.theme.dark.primary]
78
+ #main = "#F80061"
79
+ #dark = "#980039"
80
+ #light = "#FFE7EB"
81
+
82
+
83
+ [meta]
84
+ generated_by = "0.7.700"
tools/__pycache__/data_analyst.cpython-310.pyc ADDED
Binary file (3.89 kB). View file
 
tools/__pycache__/evaluator.cpython-310.pyc ADDED
Binary file (4.04 kB). View file
 
tools/__pycache__/forecasting_expert_arima.cpython-310.pyc ADDED
Binary file (3.26 kB). View file
 
tools/__pycache__/forecasting_expert_rf.cpython-310.pyc ADDED
Binary file (4.05 kB). View file
 
tools/__pycache__/investment_advisor.cpython-310.pyc ADDED
Binary file (3.42 kB). View file
 
tools/__pycache__/stock_sentiment_analysis_util.cpython-310.pyc ADDED
Binary file (4.42 kB). View file
 
tools/__pycache__/stock_sentiment_evalutor.cpython-310.pyc ADDED
Binary file (8.81 kB). View file
 
tools/chart_expert.py ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic.v1 import BaseModel, Field
2
+ from langchain.tools import BaseTool
3
+ from typing import Optional, Type
4
+ from langchain.tools import StructuredTool
5
+ import yfinance as yf
6
+ from typing import List
7
+ from datetime import datetime,timedelta
8
+ import matplotlib.pyplot as plt
9
+ import chainlit as cl
10
+ import plotly.graph_objects as go
11
+ import pandas as pd
12
+ import yfinance as yf
13
+ from plotly.subplots import make_subplots
14
+
15
+ def chart_expert_tools():
16
+
17
+ def historical_stock_prices(stockticker, days_ago):
18
+ """Upload accurate data to accurate dates from yahoo finance.
19
+ Receive data on the last week and give them to forecasting experts.
20
+ Receive data on the last 90 days and give them to visualization expert."""
21
+ ticker = yf.Ticker(stockticker)
22
+ end_date = datetime.now()
23
+ start_date = end_date - timedelta(days=days_ago)
24
+ start_date = start_date.strftime('%Y-%m-%d')
25
+ end_date = end_date.strftime('%Y-%m-%d')
26
+ historical_data = ticker.history(start=start_date, end=end_date)
27
+ return historical_data
28
+
29
+ class HistoricalStockPricesInput(BaseModel):
30
+ """Input for Stock ticker check."""
31
+
32
+ stockticker: str = Field(..., description="Ticker symbol for stock or index")
33
+ days_ago: int = Field(..., description="Int number of days to look back")
34
+
35
+ class HistoricalStockPricesTool(BaseTool):
36
+ name = "historical_stock_prices"
37
+ description = "Useful for when you need to find out the historical stock prices. Use Yahoo Finance API to find the correct stockticker."
38
+
39
+ def _run(self, stockticker: str, days_ago: int):
40
+ historical_prices = historical_stock_prices(stockticker, days_ago)
41
+
42
+ return {"historical prices": historical_prices}
43
+
44
+ def _arun(self, stockticker: str, days_ago: int):
45
+ raise NotImplementedError("This tool does not support async")
46
+
47
+ args_schema: Optional[Type[BaseModel]] = HistoricalStockPricesInput
48
+
49
+ def calculate_MACD(historical_data, fast_period=12, slow_period=26, signal_period=9):
50
+ """
51
+ Calculates the MACD (Moving Average Convergence Divergence) and related indicators.
52
+
53
+ Parameters:
54
+ df (DataFrame): A pandas DataFrame containing at least a 'Close' column with closing prices.
55
+ fast_period (int): The period for the fast EMA (default is 12).
56
+ slow_period (int): The period for the slow EMA (default is 26).
57
+ signal_period (int): The period for the signal line EMA (default is 9).
58
+
59
+ Returns:
60
+ DataFrame: A pandas DataFrame with the original data and added columns for MACD, Signal Line, and MACD Histogram.
61
+ """
62
+ df=historical_data[['Close','Open','High','Low']]
63
+ df['EMA_fast'] = df['Close'].ewm(span=fast_period, adjust=False).mean()
64
+ df['EMA_slow'] = df['Close'].ewm(span=slow_period, adjust=False).mean()
65
+ df['MACD'] = df['EMA_fast'] - df['EMA_slow']
66
+
67
+ df['Signal_Line'] = df['MACD'].ewm(span=signal_period, adjust=False).mean()
68
+ df['MACD_Histogram'] = df['MACD'] - df['Signal_Line']
69
+ return df
70
+
71
+
72
+ class MACDCalculateInput(BaseModel):
73
+ """Input for Stock ticker check."""
74
+ stockticker: str = Field(..., description="Ticker symbol for stock or index")
75
+
76
+ class MACDCalculateTool(BaseTool):
77
+ name = "macd_calculate"
78
+ description = "Useful for calculating MACD as input for MACD plot."
79
+
80
+ def _run(self, stockticker: str, historical_data: float):
81
+ df = calculate_MACD(historical_data)
82
+
83
+ return df
84
+
85
+ def _arun(self, stockticker: str, historical_data: float):
86
+ raise NotImplementedError("This tool does not support async")
87
+
88
+ args_schema: Optional[Type[BaseModel]] = MACDCalculateInput
89
+
90
+ def plot_macd(df):
91
+
92
+ # Create Figure
93
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, row_heights=[0.7, 0.3],
94
+ vertical_spacing=0.15, # Adjust vertical spacing between subplots
95
+ subplot_titles=("Candlestick Chart", "MACD")) # Add subplot titles
96
+
97
+
98
+ # Subplot 1: Plot candlestick chart
99
+ fig.add_trace(go.Candlestick(
100
+ x=df.index,
101
+ open=df['Open'],
102
+ high=df['High'],
103
+ low=df['Low'],
104
+ close=df['Close'],
105
+ increasing_line_color='#00cc96', # Green for increasing
106
+ decreasing_line_color='#ff3e3e', # Red for decreasing
107
+ showlegend=False
108
+ ), row=1, col=1) # Specify row and column indices
109
+
110
+
111
+ # Subplot 2: Plot MACD
112
+ fig.add_trace(
113
+ go.Scatter(
114
+ x=df.index,
115
+ y=df['MACD'],
116
+ mode='lines',
117
+ name='MACD',
118
+ line=dict(color='blue')
119
+ ),
120
+ row=2, col=1
121
+ )
122
+
123
+ fig.add_trace(
124
+ go.Scatter(
125
+ x=df.index,
126
+ y=df['Signal_Line'],
127
+ mode='lines',
128
+ name='Signal Line',
129
+ line=dict(color='red')
130
+ ),
131
+ row=2, col=1
132
+ )
133
+
134
+ # Plot MACD Histogram with different colors for positive and negative values
135
+ histogram_colors = ['green' if val >= 0 else 'red' for val in df['MACD_Histogram']]
136
+
137
+ fig.add_trace(
138
+ go.Bar(
139
+ x=df.index,
140
+ y=df['MACD_Histogram'],
141
+ name='MACD Histogram',
142
+ marker_color=histogram_colors
143
+ ),
144
+ row=2, col=1
145
+ )
146
+
147
+ # Update layout with zoom and pan tools enabled
148
+ layout = go.Layout(
149
+ title='MSFT Candlestick Chart and MACD Subplots',
150
+ title_font=dict(size=25), # Adjust title font size
151
+ plot_bgcolor='#f2f2f2', # Light gray background
152
+ height=800,
153
+ width=1500,
154
+ xaxis_rangeslider=dict(visible=True, thickness=0.03),
155
+ )
156
+
157
+ # Update the layout of the entire figure
158
+ fig.update_layout(layout)
159
+ fig.update_yaxes(fixedrange=False, row=1, col=1)
160
+ fig.update_yaxes(fixedrange=True, row=2, col=1)
161
+ fig.update_xaxes(type='category', row=1, col=1)
162
+ fig.update_xaxes(type='category', nticks=10, row=2, col=1)
163
+
164
+ fig.show()
165
+
166
+ class PlotMACDInput(BaseModel):
167
+ """Input for Stock ticker check."""
168
+
169
+ stockticker: str = Field(..., description="Ticker symbol for stock or index")
170
+ df: List = Field(..., description="List of historical price values")
171
+ days_ago: int = Field(..., description="Int number of days to look back")
172
+
173
+ class PlotMACDTool(BaseTool):
174
+ name = "plot_macd"
175
+ description = "Useful for creating beautiful candle stick plot for MACD for a stock price."
176
+
177
+ def _run(self, df: List[float]):
178
+ historical_prices = plot_macd(df)
179
+
180
+ return {"historical prices": historical_prices}
181
+
182
+ def _arun(self, df: List[float]):
183
+ raise NotImplementedError("This tool does not support async")
184
+
185
+ args_schema: Optional[Type[BaseModel]] = PlotMACDInput
186
+
187
+
188
+
189
+ tools_chart_expert = [StructuredTool.from_function(
190
+ func=HistoricalStockPricesTool,
191
+ args_schema=HistoricalStockPricesInput,
192
+ description="Function to get historical stock prices.",
193
+ ),
194
+ StructuredTool.from_function(
195
+ func=MACDCalculateTool,
196
+ args_schema=MACDCalculateInput,
197
+ description="Calculate MACD as input for MACD plot.",
198
+ ),
199
+ StructuredTool.from_function(
200
+ func=PlotMACDTool,
201
+ args_schema=PlotMACDInput,
202
+ description="Plot MACD.",
203
+ ),
204
+
205
+ ]
206
+ return tools_chart_expert
tools/chart_expert1.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic.v1 import BaseModel, Field
2
+ from langchain.tools import BaseTool
3
+ from typing import Optional, Type
4
+ from langchain.tools import StructuredTool
5
+ import yfinance as yf
6
+ from typing import List
7
+ from datetime import datetime,timedelta
8
+ import matplotlib.pyplot as plt
9
+ import chainlit as cl
10
+ import plotly.graph_objects as go
11
+ import pandas as pd
12
+ import yfinance as yf
13
+ from plotly.subplots import make_subplots
14
+ import chainlit as cl
15
+
16
+ class chart_expert_tools():
17
+
18
+ def plot_macd(stockticker, days_ago):
19
+ """Upload accurate data to accurate dates from yahoo finance.
20
+ Receive data on the last week and give them to forecasting experts.
21
+ Receive data on the last 90 days and give them to visualization expert."""
22
+ ticker = yf.Ticker(stockticker)
23
+ end_date = datetime.now()
24
+ start_date = end_date - timedelta(days=days_ago)
25
+ start_date = start_date.strftime('%Y-%m-%d')
26
+ end_date = end_date.strftime('%Y-%m-%d')
27
+ historical_data = ticker.history(start=start_date, end=end_date)
28
+
29
+ fast_period=12
30
+ slow_period=26
31
+ signal_period=9
32
+
33
+ df=historical_data[['Close','Open','High','Low']]
34
+ df['EMA_fast'] = df['Close'].ewm(span=fast_period, adjust=False).mean()
35
+ df['EMA_slow'] = df['Close'].ewm(span=slow_period, adjust=False).mean()
36
+ df['MACD'] = df['EMA_fast'] - df['EMA_slow']
37
+
38
+ df['Signal_Line'] = df['MACD'].ewm(span=signal_period, adjust=False).mean()
39
+ df['MACD_Histogram'] = df['MACD'] - df['Signal_Line']
40
+
41
+ # Create Figure
42
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, row_heights=[0.7, 0.3],
43
+ vertical_spacing=0.15, # Adjust vertical spacing between subplots
44
+ subplot_titles=("Candlestick Chart", "MACD")) # Add subplot titles
45
+
46
+
47
+ # Subplot 1: Plot candlestick chart
48
+ fig.add_trace(go.Candlestick(
49
+ x=df.index,
50
+ open=df['Open'],
51
+ high=df['High'],
52
+ low=df['Low'],
53
+ close=df['Close'],
54
+ increasing_line_color='#00cc96', # Green for increasing
55
+ decreasing_line_color='#ff3e3e', # Red for decreasing
56
+ showlegend=False
57
+ ), row=1, col=1) # Specify row and column indices
58
+
59
+
60
+ # Subplot 2: Plot MACD
61
+ fig.add_trace(
62
+ go.Scatter(
63
+ x=df.index,
64
+ y=df['MACD'],
65
+ mode='lines',
66
+ name='MACD',
67
+ line=dict(color='blue')
68
+ ),
69
+ row=2, col=1
70
+ )
71
+
72
+ fig.add_trace(
73
+ go.Scatter(
74
+ x=df.index,
75
+ y=df['Signal_Line'],
76
+ mode='lines',
77
+ name='Signal Line',
78
+ line=dict(color='red')
79
+ ),
80
+ row=2, col=1
81
+ )
82
+
83
+ # Plot MACD Histogram with different colors for positive and negative values
84
+ histogram_colors = ['green' if val >= 0 else 'red' for val in df['MACD_Histogram']]
85
+
86
+ fig.add_trace(
87
+ go.Bar(
88
+ x=df.index,
89
+ y=df['MACD_Histogram'],
90
+ name='MACD Histogram',
91
+ marker_color=histogram_colors
92
+ ),
93
+ row=2, col=1
94
+ )
95
+
96
+ # Update layout with zoom and pan tools enabled
97
+ layout = go.Layout(
98
+ title='MSFT Candlestick Chart and MACD Subplots',
99
+ title_font=dict(size=25), # Adjust title font size
100
+ plot_bgcolor='#f2f2f2', # Light gray background
101
+ height=800,
102
+ width=1500,
103
+ xaxis_rangeslider=dict(visible=True, thickness=0.03),
104
+ )
105
+
106
+ # Update the layout of the entire figure
107
+ fig.update_layout(layout)
108
+ fig.update_yaxes(fixedrange=False, row=1, col=1)
109
+ fig.update_yaxes(fixedrange=True, row=2, col=1)
110
+ fig.update_xaxes(type='category', row=1, col=1)
111
+ fig.update_xaxes(type='category', nticks=10, row=2, col=1)
112
+
113
+ fig.show()
114
+ # elements=[
115
+ # cl.Pyplot(name="plot", figure=fig, display="inline"),
116
+ # ]
117
+
118
+ # cl.Message(
119
+ # content="Ask me anything about stocks.",
120
+ # elements=elements,
121
+ # ).send()
122
+ # return elements
123
+
124
+
125
+ # class PlotMACDInput(BaseModel):
126
+ # """Input for Stock ticker check."""
127
+
128
+ # stockticker: str = Field(..., description="Ticker symbol for stock or index")
129
+ # days_ago: int = Field(..., description="Int number of days to look back")
130
+
131
+ # class PlotMACDTool(BaseTool):
132
+ # name = "plot_macd"
133
+ # description = "Useful for creating beautiful candle stick plot for MACD for a stock price."
134
+
135
+ # def _run(self, df: List[float]):
136
+ # historical_prices = plot_macd(df)
137
+
138
+ # return {"historical prices": historical_prices}
139
+
140
+ # def _arun(self, df: List[float]):
141
+ # raise NotImplementedError("This tool does not support async")
142
+
143
+ # args_schema: Optional[Type[BaseModel]] = PlotMACDInput
144
+
145
+
146
+
147
+ # tools_chart_expert = [
148
+ # StructuredTool.from_function(
149
+ # func=PlotMACDTool,
150
+ # args_schema=PlotMACDInput,
151
+ # description="Plot MACD.",
152
+ # ),
153
+
154
+ # ]
155
+ #return tools_chart_expert
tools/data_analyst.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic.v1 import BaseModel, Field
2
+ from langchain.tools import BaseTool
3
+ from typing import Optional, Type
4
+ from langchain.tools import StructuredTool
5
+ import yfinance as yf
6
+ from typing import List
7
+ from datetime import datetime,timedelta
8
+
9
+ def data_analyst_tools():
10
+ def get_stock_price(stockticker: str) -> str:
11
+ ticker = yf.Ticker(stockticker)
12
+ todays_data = ticker.history(period='1d')
13
+ return str(round(todays_data['Close'][0], 2))
14
+
15
+ class StockPriceCheckInput(BaseModel):
16
+ """Input for Stock price check."""
17
+ stockticker: str = Field(..., description="Ticker symbol for stock or index")
18
+
19
+ class StockPriceTool(BaseTool):
20
+ name = "get_stock_ticker_price"
21
+ description = "Useful for when you need to find out the price of stock. You should input the stock ticker used on the yfinance API"
22
+ """Input for Stock price check."""
23
+ stockticker: str = Field(..., description="Ticker symbol for stock or index")
24
+ def _run(self, stockticker: str):
25
+ # print("i'm running")
26
+ price_response = get_stock_price(stockticker)
27
+
28
+ return str(price_response)
29
+
30
+ def _arun(self, stockticker: str):
31
+ raise NotImplementedError("This tool does not support async")
32
+ args_schema: Optional[Type[BaseModel]] = StockPriceCheckInput
33
+
34
+ def historical_stock_prices(stockticker, days_ago):
35
+ ticker = yf.Ticker(stockticker)
36
+ end_date = datetime.now()
37
+ start_date = end_date - timedelta(days=days_ago)
38
+ start_date = start_date.strftime('%Y-%m-%d')
39
+ end_date = end_date.strftime('%Y-%m-%d')
40
+ historical_data = ticker.history(start=start_date, end=end_date)
41
+ return historical_data
42
+
43
+ class HistoricalStockPricesInput(BaseModel):
44
+ """Input for Stock ticker check."""
45
+
46
+ stockticker: str = Field(..., description="Ticker symbol for stock or index")
47
+ days_ago: int = Field(..., description="Int number of days to look back")
48
+
49
+ class HistoricalStockPricesTool(BaseTool):
50
+ name = "historical_stock_prices"
51
+ description = "Useful for when you need to find out the historical stock prices. Use Yahoo Finance API to find the correct stockticker."
52
+
53
+ def _run(self, stockticker: str, days_ago: int):
54
+ historical_prices = historical_stock_prices(stockticker, days_ago)
55
+
56
+ return {"historical prices": historical_prices}
57
+
58
+ def _arun(self, stockticker: str, days_ago: int):
59
+ raise NotImplementedError("This tool does not support async")
60
+
61
+ args_schema: Optional[Type[BaseModel]] = HistoricalStockPricesInput
62
+
63
+ tools_data_analyst = [StructuredTool.from_function(
64
+ func=StockPriceTool,
65
+ args_schema=StockPriceCheckInput,
66
+ description="Function to get current stock prices.",
67
+ ),
68
+ # StructuredTool.from_function(
69
+ # func=HistoricalStockPricesTool,
70
+ # args_schema=HistoricalStockPricesInput,
71
+ # description="Function to get historical stock prices.",
72
+ # )
73
+ ]
74
+ return tools_data_analyst
tools/df_history.csv ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Date,Open,High,Low,Close,Volume,Dividends,Stock Splits,stockticker
2
+ 2024-04-22 00:00:00-04:00,399.3596471827562,402.1246793258712,395.03745670124425,400.2380676269531,20286900,0.0,0.0,MSFT
3
+ 2024-04-23 00:00:00-04:00,403.51216021293357,407.4650522060062,402.33429210205253,406.836181640625,15734500,0.0,0.0,MSFT
4
+ 2024-04-24 00:00:00-04:00,408.82258607970806,411.72735028943725,406.0475926794115,408.323486328125,15065300,0.0,0.0,MSFT
5
+ 2024-04-25 00:00:00-04:00,393.32054400787314,399.1700088625431,387.3313470651067,398.321533203125,40586500,0.0,0.0,MSFT
6
+ 2024-04-26 00:00:00-04:00,411.4279132786848,412.25640548421114,405.02945064216703,405.58843994140625,29694700,0.0,0.0,MSFT
7
+ 2024-04-29 00:00:00-04:00,404.52035539531056,405.5884361925186,398.4712687423875,401.5257568359375,19582100,0.0,0.0,MSFT
8
+ 2024-04-30 00:00:00-04:00,400.76710737423014,401.4359144425664,388.4693126899027,388.6289978027344,28781400,0.0,0.0,MSFT
9
+ 2024-05-01 00:00:00-04:00,391.9030904630616,400.9967037344784,389.6072438016868,394.2289123535156,23562500,0.0,0.0,MSFT
10
+ 2024-05-02 00:00:00-04:00,396.94401914412265,399.20992105581087,393.9394288835304,397.1236877441406,17709400,0.0,0.0,MSFT
11
+ 2024-05-03 00:00:00-04:00,401.55570709720826,406.4169339510819,401.13644988960164,405.9278259277344,17446700,0.0,0.0,MSFT
12
+ 2024-05-06 00:00:00-04:00,408.024048156178,413.1847226485525,405.63833666603693,412.7954406738281,16996600,0.0,0.0,MSFT
13
+ 2024-05-07 00:00:00-04:00,413.91342570011614,413.92341744357753,408.35344694069664,408.6029968261719,20018200,0.0,0.0,MSFT
14
+ 2024-05-08 00:00:00-04:00,407.4351142805277,411.48780192255407,405.97772103822234,409.80084228515625,11792300,0.0,0.0,MSFT
15
+ 2024-05-09 00:00:00-04:00,409.8307875446534,411.97691043744567,408.363433019907,411.57763671875,14689700,0.0,0.0,MSFT
16
+ 2024-05-10 00:00:00-04:00,412.1965086797442,414.6321179246016,411.05854661467066,413.9932556152344,13402300,0.0,0.0,MSFT
17
+ 2024-05-13 00:00:00-04:00,417.2573820335048,417.5967662074119,410.08032520369875,412.97509765625,15440200,0.0,0.0,MSFT
18
+ 2024-05-14 00:00:00-04:00,411.2781631216723,416.73831581889846,410.8090081198034,415.80999755859375,15109300,0.0,0.0,MSFT
19
+ 2024-05-15 00:00:00-04:00,417.8999938964844,423.80999755859375,417.2699890136719,423.0799865722656,22239500,0.75,0.0,MSFT
20
+ 2024-05-16 00:00:00-04:00,421.79998779296875,425.4200134277344,420.3500061035156,420.989990234375,17530100,0.0,0.0,MSFT
21
+ 2024-05-17 00:00:00-04:00,422.5400085449219,422.9200134277344,418.0299987792969,420.2099914550781,15352200,0.0,0.0,MSFT
22
+ 2024-05-20 00:00:00-04:00,420.2099914550781,426.7699890136719,419.989990234375,425.3399963378906,16272100,0.0,0.0,MSFT
23
+ 2024-05-21 00:00:00-04:00,426.8299865722656,432.9700012207031,424.8500061035156,429.0400085449219,21453300,0.0,0.0,MSFT
24
+ 2024-05-22 00:00:00-04:00,430.0899963378906,432.4100036621094,427.1300048828125,430.5199890136719,18073700,0.0,0.0,MSFT
25
+ 2024-05-23 00:00:00-04:00,432.9700012207031,433.6000061035156,425.4200134277344,427.0,17211700,0.0,0.0,MSFT
26
+ 2024-05-24 00:00:00-04:00,427.19000244140625,431.05999755859375,424.4100036621094,430.1600036621094,11845800,0.0,0.0,MSFT
27
+ 2024-05-28 00:00:00-04:00,429.6300048828125,430.82000732421875,426.6000061035156,430.32000732421875,15718000,0.0,0.0,MSFT
28
+ 2024-05-29 00:00:00-04:00,425.69000244140625,430.94000244140625,425.69000244140625,429.1700134277344,15517100,0.0,0.0,MSFT
29
+ 2024-05-30 00:00:00-04:00,424.29998779296875,424.29998779296875,414.239990234375,414.6700134277344,28424800,0.0,0.0,MSFT
30
+ 2024-05-31 00:00:00-04:00,416.75,416.75,404.510009765625,415.1300048828125,47995300,0.0,0.0,MSFT
31
+ 2024-06-03 00:00:00-04:00,415.5299987792969,416.42999267578125,408.9200134277344,413.5199890136719,17484700,0.0,0.0,MSFT
32
+ 2024-06-04 00:00:00-04:00,412.42999267578125,416.44000244140625,409.67999267578125,416.07000732421875,14348900,0.0,0.0,MSFT
33
+ 2024-06-05 00:00:00-04:00,417.80999755859375,424.0799865722656,416.29998779296875,424.010009765625,16988000,0.0,0.0,MSFT
34
+ 2024-06-06 00:00:00-04:00,424.010009765625,425.30999755859375,420.5799865722656,424.5199890136719,14861300,0.0,0.0,MSFT
35
+ 2024-06-07 00:00:00-04:00,426.20001220703125,426.2799987792969,423.0,423.8500061035156,13621700,0.0,0.0,MSFT
36
+ 2024-06-10 00:00:00-04:00,424.70001220703125,428.0799865722656,423.8900146484375,427.8699951171875,14003000,0.0,0.0,MSFT
37
+ 2024-06-11 00:00:00-04:00,425.4800109863281,432.82000732421875,425.25,432.67999267578125,14551100,0.0,0.0,MSFT
38
+ 2024-06-12 00:00:00-04:00,435.32000732421875,443.3999938964844,433.25,441.05999755859375,22366200,0.0,0.0,MSFT
39
+ 2024-06-13 00:00:00-04:00,440.8500061035156,443.3900146484375,439.3699951171875,441.5799865722656,15960600,0.0,0.0,MSFT
40
+ 2024-06-14 00:00:00-04:00,438.2799987792969,443.1400146484375,436.7200012207031,442.57000732421875,13582000,0.0,0.0,MSFT
41
+ 2024-06-17 00:00:00-04:00,442.5899963378906,450.94000244140625,440.7200012207031,448.3699951171875,20790000,0.0,0.0,MSFT
42
+ 2024-06-18 00:00:00-04:00,449.7099914550781,450.1400146484375,444.8900146484375,446.3399963378906,17112500,0.0,0.0,MSFT
43
+ 2024-06-20 00:00:00-04:00,446.29998779296875,446.5299987792969,441.2699890136719,445.70001220703125,19877400,0.0,0.0,MSFT
44
+ 2024-06-21 00:00:00-04:00,447.3800048828125,450.5799865722656,446.510009765625,449.7799987792969,34486200,0.0,0.0,MSFT
45
+ 2024-06-24 00:00:00-04:00,449.79998779296875,452.75,446.4100036621094,447.6700134277344,15913700,0.0,0.0,MSFT
46
+ 2024-06-25 00:00:00-04:00,448.25,451.4200134277344,446.75,450.95001220703125,16747500,0.0,0.0,MSFT
47
+ 2024-06-26 00:00:00-04:00,449.0,453.6000061035156,448.19000244140625,452.1600036621094,16507000,0.0,0.0,MSFT
48
+ 2024-06-27 00:00:00-04:00,452.17999267578125,456.1700134277344,451.7699890136719,452.8500061035156,14806300,0.0,0.0,MSFT
49
+ 2024-06-28 00:00:00-04:00,453.07000732421875,455.3800048828125,446.4100036621094,446.95001220703125,28362300,0.0,0.0,MSFT
50
+ 2024-07-01 00:00:00-04:00,448.6600036621094,457.3699951171875,445.6600036621094,456.7300109863281,17662800,0.0,0.0,MSFT
51
+ 2024-07-02 00:00:00-04:00,453.20001220703125,459.5899963378906,453.1099853515625,459.2799987792969,13979800,0.0,0.0,MSFT
52
+ 2024-07-03 00:00:00-04:00,458.19000244140625,461.0199890136719,457.8800048828125,460.7699890136719,9932800,0.0,0.0,MSFT
53
+ 2024-07-05 00:00:00-04:00,459.6099853515625,468.3500061035156,458.9700012207031,467.55999755859375,16000300,0.0,0.0,MSFT
54
+ 2024-07-08 00:00:00-04:00,466.54998779296875,467.70001220703125,464.4599914550781,466.239990234375,12962300,0.0,0.0,MSFT
55
+ 2024-07-09 00:00:00-04:00,467.0,467.3299865722656,458.0,459.5400085449219,17207200,0.0,0.0,MSFT
56
+ 2024-07-10 00:00:00-04:00,461.2200012207031,466.4599914550781,458.8599853515625,466.25,18196100,0.0,0.0,MSFT
57
+ 2024-07-11 00:00:00-04:00,462.9800109863281,464.7799987792969,451.54998779296875,454.70001220703125,23111200,0.0,0.0,MSFT
58
+ 2024-07-12 00:00:00-04:00,454.3299865722656,456.3599853515625,450.6499938964844,453.54998779296875,16311300,0.0,0.0,MSFT
59
+ 2024-07-15 00:00:00-04:00,453.29998779296875,457.260009765625,451.42999267578125,453.9599914550781,14429400,0.0,0.0,MSFT
60
+ 2024-07-16 00:00:00-04:00,454.2200012207031,454.29998779296875,446.6600036621094,449.5199890136719,17175700,0.0,0.0,MSFT
61
+ 2024-07-17 00:00:00-04:00,442.5899963378906,444.8500061035156,439.17999267578125,443.5199890136719,21778000,0.0,0.0,MSFT
62
+ 2024-07-18 00:00:00-04:00,444.3399963378906,444.6499938964844,434.3999938964844,440.3699951171875,20794800,0.0,0.0,MSFT
63
+ 2024-07-19 00:00:00-04:00,433.1000061035156,441.1400146484375,432.0,437.1099853515625,20862400,0.0,0.0,MSFT
tools/evaluator.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVALUATOR
2
+ import yfinance as yf
3
+ from datetime import datetime, timedelta
4
+ import pandas as pd
5
+ from sklearn.metrics import mean_absolute_error
6
+ from sklearn.model_selection import train_test_split
7
+ import matplotlib.pyplot as plt
8
+ import pandas as pd
9
+ from pydantic.v1 import BaseModel, Field
10
+ from langchain.tools import BaseTool
11
+ from typing import Optional, Type
12
+ from langchain.tools import StructuredTool
13
+
14
+ def evaluator_tools():
15
+ def compare_prediction(mae_rf, mae_arima,prediction_rf,prediction_arima):
16
+ if mae_rf>mae_arima:
17
+ result=prediction_arima
18
+ else:
19
+ result=prediction_rf
20
+ return {"final_predicted_outcome": result}#,"mae_rf": mae_rf}
21
+
22
+ class compare_predictionInput(BaseModel):
23
+ """Input for printing final prediction number."""
24
+ mae_rf: int = Field(..., description="Mean average error for random forest")
25
+ mae_arima: int = Field(..., description="Mean average error for ARIMA")
26
+
27
+ prediction_rf: int = Field(..., description="Price prediction using random forest")
28
+ prediction_arima: int = Field(..., description="Price prediction using ARIMA")
29
+
30
+ class compare_predictionTool(BaseTool):
31
+ name = "Comparing rf and arima predictions"
32
+ description = "Useful for showing which predicted outcome is the final result."
33
+
34
+ def _run(self, mae_rf=int,mae_arima=int,prediction_rf=int,prediction_arima=int):
35
+ result = compare_prediction(mae_rf,mae_arima,prediction_rf,prediction_arima)
36
+ return {"final_predicted_outcome": result}
37
+
38
+ def _arun(self, mae_rf=int,mae_arima=int,prediction_rf=int,prediction_arima=int):
39
+ raise NotImplementedError("This tool does not support async")
40
+
41
+ args_schema: Optional[Type[BaseModel]] = compare_predictionInput
42
+
43
+ def buy_or_sell(current_price: float, prediction:float) -> str:
44
+ if current_price>prediction:
45
+ position="sell"
46
+ else:
47
+ position="buy"
48
+ return str(position)
49
+
50
+ class buy_or_sellInput(BaseModel):
51
+ """Input for printing final prediction number."""
52
+ current_price: float = Field(..., description="Current stock price")
53
+ prediction: float = Field(..., description="Final price prediction from Evaluator")
54
+
55
+ class buy_or_sellTool(BaseTool):
56
+ name = "Comparing current price with prediction"
57
+ description = """Useful for deciding if to buy/sell stocks based on the prediction result."""
58
+
59
+ def _run(self, current_price=float,prediction=float):
60
+ position = buy_or_sell(current_price,prediction)
61
+ return {"position": position}
62
+
63
+ def _arun(self,current_price=float,prediction=float):
64
+ raise NotImplementedError("This tool does not support async")
65
+
66
+ args_schema: Optional[Type[BaseModel]] = buy_or_sellInput
67
+
68
+ tools_evaluate = [
69
+ StructuredTool.from_function(
70
+ func=compare_predictionTool,
71
+ args_schema=compare_predictionInput,
72
+ description="Function to evaluate predicted stock prices and print final result.",
73
+ ),
74
+ StructuredTool.from_function(
75
+ func=buy_or_sellTool,
76
+ args_schema=buy_or_sellInput,
77
+ description="Function to evaluate client stock position.",
78
+ ),
79
+ ]
80
+ return tools_evaluate
tools/forecasting_expert_arima.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FORECASTING EXPERT ARIMA TOOLS
2
+
3
+ from datetime import datetime, timedelta
4
+ import pandas as pd
5
+ from statsmodels.tsa.arima.model import ARIMA
6
+ from sklearn.metrics import mean_absolute_error
7
+ from pydantic.v1 import BaseModel, Field
8
+ from langchain.tools import BaseTool
9
+ from typing import Optional, Type
10
+ from langchain.tools import StructuredTool
11
+
12
+ def forecasting_expert_arima_tools():
13
+ def ARIMA_forecast(symbol,historical_data, train_days_ago, forecast_days):
14
+ """Useful for forecasting a variable using ARIMA model.
15
+ Use historical 'Close' stock prices and get prediction.
16
+ Give prediction output to the client.
17
+ Give mae_arima from the model to Evaluator.
18
+ """
19
+
20
+ df=historical_data[['Close']]
21
+ df.index=pd.to_datetime(df.index)
22
+ model = ARIMA(df.dropna(), order=(2,0,2))
23
+ model_fit = model.fit()
24
+
25
+ # Split the data into training and testing sets
26
+ train_size = int(len(df) * 0.8)
27
+ train, test = df.iloc[:train_size], df.iloc[train_size:]
28
+
29
+ # Fit the ARIMA model on the training set
30
+ model = ARIMA(train.dropna(), order=(2, 0, 2))
31
+ model_fit = model.fit()
32
+
33
+ # Make predictions
34
+ predictions = model_fit.forecast(steps=len(test))
35
+ #test['Predicted'] = predictions
36
+
37
+ # Calculate the MAE
38
+ mae_arima = mean_absolute_error(test['Close'], predictions)
39
+ # plt.plot(y_test, label='Actual')
40
+ # plt.plot(y_pred, label='Predicted')
41
+ # plt.legend()
42
+ # plt.show()
43
+ forecast = model_fit.get_forecast(forecast_days).predicted_mean
44
+ arima_prediction=forecast
45
+ return {"arima_prediction": arima_prediction,"mae_arima": mae_arima}
46
+
47
+ class PredictStocksARIMAInput(BaseModel):
48
+ """Input for Stock ticker check."""
49
+
50
+ stockticker: str = Field(..., description="Ticker symbol for stock or index")
51
+ days_ago: int = Field(..., description="Int number of days to look back")
52
+
53
+ class PredictStocksARIMATool(BaseTool):
54
+ name = "ARIMA_forecast"
55
+ description = "Useful for forecasting stock prices using ARIMA model."
56
+
57
+ def _run(self, stockticker: str, days_ago: int,historical_data: float, train_days_ago=int, forecast_days=int):
58
+ arima_prediction = ARIMA_forecast(stockticker,historical_data, train_days_ago, forecast_days).predicted_price
59
+ mae_arima== ARIMA_forecast(stockticker,historical_data, train_days_ago, forecast_days).mae_arima
60
+
61
+ return {"arima_prediction":arima_prediction,"mae_arima":mae_arima}
62
+
63
+ def _arun(self, stockticker: str, days_ago: int,historical_data: float, train_days_ago=int, forecast_days=int):
64
+ raise NotImplementedError("This tool does not support async")
65
+
66
+ args_schema: Optional[Type[BaseModel]] = PredictStocksARIMAInput
67
+
68
+ tools_forecasting_expert_arima = [
69
+ StructuredTool.from_function(
70
+ func=PredictStocksARIMATool,
71
+ args_schema=PredictStocksARIMAInput,
72
+ description="Function to predict stock prices with ARIMA model and to get mae_arima for the model.",
73
+ ),
74
+ StructuredTool.from_function(
75
+ func=PredictStocksARIMATool,
76
+ args_schema=PredictStocksARIMAInput,
77
+ description="Function to predict stock prices with ARIMA model and to get mae_arima for the model.",
78
+ )
79
+ ]
80
+ return tools_forecasting_expert_arima
tools/forecasting_expert_rf.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FORECASTING EXPERT RF TOOLS
2
+
3
+ from datetime import datetime, timedelta
4
+ from sklearn.metrics import mean_absolute_error
5
+ from sklearn.model_selection import train_test_split
6
+ import pandas as pd
7
+ from sklearn.ensemble import RandomForestRegressor
8
+ from pydantic.v1 import BaseModel, Field
9
+ from langchain.tools import BaseTool
10
+ from typing import Optional, Type
11
+ from langchain.tools import StructuredTool
12
+
13
+ def forecasting_expert_rf_tools():
14
+ def RF_forecast(symbol,historical_data, train_days_ago, forecast_days):
15
+ """Useful for forecasting a variable using ARIMA model.
16
+ Use historical 'Close' stock prices and get prediction.
17
+ Give prediction output.
18
+ Send mae_rf from the model to Evaluator.
19
+ """
20
+ df=historical_data[['Close']]
21
+ df.index=pd.to_datetime(df.index)
22
+ df.index.names=['date']
23
+ end_date = datetime.now()
24
+
25
+ df=df.reset_index()
26
+ # Feature Engineering
27
+ df['day'] = df['date'].dt.day
28
+ df['month'] = df['date'].dt.month
29
+ df['year'] = df['date'].dt.year
30
+ df['lag1'] = df['Close'].shift(1)
31
+ df['lag2'] = df['Close'].shift(2)
32
+ df = df.dropna()
33
+
34
+ # Prepare the data
35
+ features = ['day','month', 'year', 'lag1', 'lag2']
36
+ X = df[features]
37
+ y = df['Close']
38
+
39
+ # Split the data into training and testing sets
40
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)
41
+
42
+ # Initialize and train the model
43
+ model = RandomForestRegressor(n_estimators=100, random_state=42)
44
+ model.fit(X_train, y_train)
45
+
46
+ # Make predictions
47
+ y_pred = model.predict(X_test)
48
+
49
+ # Evaluate the model
50
+ mae_rf = mean_absolute_error(y_test, y_pred)
51
+ print(f'Mean Absolute Error: {mae_rf}')
52
+
53
+ # Forecast future values (next 12 months)
54
+ future_dates = pd.date_range(start=pd.to_datetime(end_date), end=pd.to_datetime(end_date)+ timedelta(days=forecast_days), freq='D')
55
+ future_df = pd.DataFrame(future_dates, columns=['date'])
56
+ future_df['day'] = future_df['date'].dt.day
57
+ future_df['month'] = future_df['date'].dt.month
58
+ future_df['year'] = future_df['date'].dt.year
59
+ future_df['lag1'] = df['Close'].iloc[-1]
60
+ future_df['lag2'] = df['Close'].iloc[-2]
61
+
62
+ # Use the last observed values for lag features
63
+ for i in range(1, len(future_df)):
64
+ future_df.loc[future_df.index[i], 'lag1'] = future_df.loc[future_df.index[i-1], 'Close'] if 'Close' in future_df.columns else future_df.loc[future_df.index[i-1], 'lag1']
65
+ future_df.loc[future_df.index[i], 'lag2'] = future_df.loc[future_df.index[i-1], 'lag1']
66
+
67
+ future_X = future_df[features]
68
+ future_df['Close'] = model.predict(future_X)
69
+ rf_prediction=future_df['Close']
70
+ # Print the forecasted values
71
+ return {"predicted_price": rf_prediction,"mae_rf": mae_rf}
72
+
73
+ class PredictStocksRFInput(BaseModel):
74
+ """Input for Stock ticker check."""
75
+
76
+ stockticker: str = Field(..., description="Ticker symbol for stock or index")
77
+ days_ago: int = Field(..., description="Int number of days to look back")
78
+
79
+ class PredictStocksRFTool(BaseTool):
80
+ name = "Random_forest_forecast"
81
+ description = "Useful for forecasting stock prices using Random forest model."
82
+
83
+ def _run(self, stockticker: str, days_ago: int,historical_data: float, train_days_ago=int, forecast_days=int):
84
+ predicted_prices = RF_forecast(stockticker,historical_data, train_days_ago, forecast_days).predict_price
85
+ mae_rf= RF_forecast(stockticker,historical_data, train_days_ago, forecast_days).mae_rf
86
+ return {"rf_prediction":rf_prediction,"mae_rf":mae_rf}
87
+
88
+ def _arun(self, stockticker: str, days_ago: int,historical_data: float, train_days_ago=int, forecast_days=int):
89
+ raise NotImplementedError("This tool does not support async")
90
+
91
+ args_schema: Optional[Type[BaseModel]] = PredictStocksRFInput
92
+
93
+ tools_forecasting_expert_random_forest = [
94
+ StructuredTool.from_function(
95
+ func=PredictStocksRFTool,
96
+ args_schema=PredictStocksRFInput,
97
+ description="Function to predict stock prices with random forest model and to get mae_rf for the model.",
98
+ ),
99
+ StructuredTool.from_function(
100
+ func=PredictStocksRFTool,
101
+ args_schema=PredictStocksRFInput,
102
+ description="Function to predict stock prices with random forest model and to get mae_rf for the model.",
103
+ ),
104
+ ]
105
+ return tools_forecasting_expert_random_forest
tools/investment_advisor.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic.v1 import BaseModel, Field
2
+ from langchain.tools import BaseTool
3
+ from typing import Optional, Type
4
+ from langchain.tools import StructuredTool
5
+ import yfinance as yf
6
+ from typing import List
7
+ from datetime import datetime,timedelta
8
+ import pandas as pd
9
+
10
+ def investment_advisor_tools():
11
+
12
+
13
+ def news_summary(df_search):
14
+ "Take df_search from the user input message. Summarize news on the selected stockticker and provide Sentiment: positive/negative/neutral to the user."
15
+ return eval(df_search)
16
+
17
+ class newsSummaryInput(BaseModel):
18
+ """Input for summarizing articles."""
19
+ df_search: str = Field(..., description="News articles.")
20
+
21
+ class newsSummaryTool(BaseTool):
22
+ name = "Summarize news on the stockticker"
23
+ description = """Useful for summarizing the newest article on a selected stockticker."""
24
+
25
+ def _run(self, df_search=str):
26
+ position = news_summary(df_search)
27
+ return {"position": position}
28
+
29
+ def _arun(self,df_search=str):
30
+ raise NotImplementedError("This tool does not support async")
31
+
32
+ args_schema: Optional[Type[BaseModel]] = newsSummaryInput
33
+
34
+ def analyze_prices():
35
+ """Take historical prices, analyze them and answer user's questions."""
36
+ df_prices=pd.read_csv('../df_history.csv')
37
+ return df_prices
38
+
39
+ class pricesInput(BaseModel):
40
+ """Input for summarizing articles."""
41
+ stockticker: str = Field(..., description="stockticker name")
42
+
43
+ class pricesTool(BaseTool):
44
+ name = "Get prices from csv file analyze them and answer questions"
45
+ description = """Useful for analyzing historical stock prices."""
46
+
47
+ def _run(self, stockticker=str):
48
+ df_prices = analyze_prices()
49
+ return {"prices": df_prices}
50
+
51
+ def _arun(self, stockticker=str):
52
+ raise NotImplementedError("This tool does not support async")
53
+
54
+ args_schema: Optional[Type[BaseModel]] = pricesInput
55
+
56
+ tools_reccommend = [
57
+ StructuredTool.from_function(
58
+ func=newsSummaryTool,
59
+ args_schema=newsSummaryInput,
60
+ description="Summarize articles.",
61
+ ),
62
+ StructuredTool.from_function(
63
+ func=pricesTool,
64
+ args_schema=pricesInput,
65
+ description="Analyze stock prices.",
66
+ )
67
+ ]
68
+ return tools_reccommend
tools/stock_sentiment_analysis_util.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import json
3
+ import os
4
+ from dotenv import load_dotenv
5
+ from transformers import pipeline
6
+ import os
7
+ import pandas as pd
8
+ from collections import defaultdict
9
+ from datetime import date
10
+ import matplotlib.pyplot as plt
11
+ import http.client, urllib.parse
12
+ from GoogleNews import GoogleNews
13
+ from langchain_openai import ChatOpenAI
14
+
15
+ def fetch_news(stockticker):
16
+
17
+ """ Fetches news articles for a given stock symbol within a specified date range.
18
+
19
+ Args:
20
+ - stockticker (str): Symbol of a particular stock
21
+
22
+ Returns:
23
+ - list: A list of dictionaries containing stock news. """
24
+
25
+ load_dotenv()
26
+ days_to_fetch_news = os.environ["DAYS_TO_FETCH_NEWS"]
27
+
28
+ googlenews = GoogleNews()
29
+ googlenews.set_period(days_to_fetch_news)
30
+ googlenews.get_news(stockticker)
31
+ news_json=googlenews.get_texts()
32
+
33
+
34
+ no_of_news_articles_to_fetch = os.environ["NO_OF_NEWS_ARTICLES_TO_FETCH"]
35
+ news_article_list = []
36
+ counter = 0
37
+ for article in news_json:
38
+
39
+ if(counter >= int(no_of_news_articles_to_fetch)):
40
+ break
41
+
42
+ relevant_info = {
43
+ 'News_Article': article
44
+ }
45
+ news_article_list.append(relevant_info)
46
+ counter+=1
47
+
48
+ return news_article_list
49
+
50
+
51
+
52
+ def analyze_sentiment(article):
53
+ """
54
+ Analyzes the sentiment of a given news article.
55
+
56
+ Args:
57
+ - news_article (dict): Dictionary containing 'summary', 'headline', and 'created_at' keys.
58
+
59
+ Returns:
60
+ - dict: A dictionary containing sentiment analysis results.
61
+ """
62
+
63
+ #Analyze sentiment using default model
64
+ #classifier = pipeline('sentiment-analysis')
65
+
66
+ #Analyze sentiment using specific model
67
+ classifier = pipeline(model='mrm8488/distilroberta-finetuned-financial-news-sentiment-analysis')
68
+ sentiment_result = classifier(str(article))
69
+
70
+ analysis_result = {
71
+ 'News_Article': article,
72
+ 'Sentiment': sentiment_result
73
+ }
74
+
75
+ return analysis_result
76
+
77
+
78
+ def generate_summary_of_sentiment(sentiment_analysis_results, dominant_sentiment):
79
+
80
+
81
+ news_article_sentiment = str(sentiment_analysis_results)
82
+ print("News article sentiment : " + news_article_sentiment)
83
+
84
+
85
+ os.environ["OPENAI_API_KEY"] = os.environ["OPENAI_API_KEY"]
86
+ model = ChatOpenAI(
87
+ model="gpt-4o",
88
+ temperature=0,
89
+ max_tokens=None,
90
+ timeout=None,
91
+ max_retries=2,
92
+ # api_key="...", # if you prefer to pass api key in directly instaed of using env vars
93
+ # base_url="...",
94
+ # organization="...",
95
+ # other params...
96
+ )
97
+
98
+ messages=[
99
+ {"role": "system", "content": "You are a helpful assistant that looks at all news articles, their sentiment, along with domainant sentiment and generates a summary rationalizing dominant sentiment "},
100
+ {"role": "user", "content": f"News articles and their sentiments: {news_article_sentiment}, and dominant sentiment is: {dominant_sentiment}"}
101
+ ]
102
+ response = model.invoke(messages)
103
+
104
+
105
+ summary = response.content
106
+ print ("+++++++++++++++++++++++++++++++++++++++++++++++")
107
+ print(summary)
108
+ print ("+++++++++++++++++++++++++++++++++++++++++++++++")
109
+ return summary
110
+
111
+
112
+ def plot_sentiment_graph(sentiment_analysis_results):
113
+ """
114
+ Plots a sentiment analysis graph
115
+
116
+ Args:
117
+ - sentiment_analysis_result): (dict): Dictionary containing 'Review Title : Summary', 'Rating', and 'Sentiment' keys.
118
+
119
+ Returns:
120
+ - dict: A dictionary containing sentiment analysis results.
121
+ """
122
+ df = pd.DataFrame(sentiment_analysis_results)
123
+ print(df)
124
+
125
+ #Group by Rating, sentiment value count
126
+ grouped = df['Sentiment'].value_counts()
127
+
128
+ sentiment_counts = df['Sentiment'].value_counts()
129
+
130
+ # Plotting pie chart
131
+ fig = plt.figure(figsize=(8, 8))
132
+ plt.pie(sentiment_counts, labels=sentiment_counts.index, autopct='%1.1f%%', startangle=140)
133
+ plt.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle.
134
+
135
+ #Open below when u running this program locally and c
136
+ #plt.show()
137
+
138
+ return fig
139
+
140
+
141
+ def get_dominant_sentiment (sentiment_analysis_results):
142
+ """
143
+ Returns overall sentiment, negative or positive or neutral depending on the count of negative sentiment vs positive sentiment
144
+
145
+ Args:
146
+ - sentiment_analysis_result): (dict): Dictionary containing 'summary', 'headline', and 'created_at' keys.
147
+
148
+ Returns:
149
+ - dict: A dictionary containing sentiment analysis results.
150
+ """
151
+ df = pd.DataFrame(sentiment_analysis_results)
152
+
153
+ # Group by the 'sentiment' column and count the occurrences of each sentiment value
154
+ sentiment_counts = df['Sentiment'].value_counts().reset_index()
155
+ sentiment_counts.columns = ['sentiment', 'count']
156
+ print(sentiment_counts)
157
+
158
+ # Find the sentiment with the highest count
159
+ dominant_sentiment = sentiment_counts.loc[sentiment_counts['count'].idxmax()]
160
+
161
+ return dominant_sentiment['sentiment']
162
+
163
+ #starting point of the program
164
+ if __name__ == '__main__':
165
+
166
+ #fetch stock news
167
+ news_articles = fetch_news('AAPL')
168
+
169
+ analysis_results = []
170
+
171
+ #Perform sentiment analysis for each product review
172
+ for article in news_articles:
173
+ sentiment_analysis_result = analyze_sentiment(article['News_Article'])
174
+
175
+ # Display sentiment analysis results
176
+ print(f'News Article: {sentiment_analysis_result["News_Article"]} : Sentiment: {sentiment_analysis_result["Sentiment"]}', '\n')
177
+
178
+ result = {
179
+ 'News_Article': sentiment_analysis_result["News_Article"],
180
+ 'Sentiment': sentiment_analysis_result["Sentiment"][0]['label']
181
+ }
182
+
183
+ analysis_results.append(result)
184
+
185
+
186
+ #Graph dominant sentiment based on sentiment analysis data of reviews
187
+ dominant_sentiment = get_dominant_sentiment(analysis_results)
188
+ print(dominant_sentiment)
189
+
190
+ #Plot graph
191
+ plot_sentiment_graph(analysis_results)
192
+
tools/stock_sentiment_evalutor.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import pipeline
2
+ from alpaca_trade_api import REST
3
+ import os
4
+ from dotenv import load_dotenv
5
+ from datetime import datetime
6
+ import pandas as pd
7
+ import matplotlib.pyplot as plt
8
+ from datetime import date, timedelta
9
+ from pydantic.v1 import BaseModel, Field
10
+ from langchain.tools import BaseTool
11
+ from typing import Optional, Type
12
+ from langchain.tools import StructuredTool
13
+
14
+
15
+ def sentimental_analysis_tools():
16
+
17
+ class AlpacaNewsFetcher:
18
+ """
19
+ A class for fetching news articles related to a specific stock from Alpaca API.
20
+
21
+ Attributes:
22
+ - api_key (str): Alpaca API key for authentication.
23
+ - api_secret (str): Alpaca API secret for authentication.
24
+ - rest_client (alpaca_trade_api.REST): Alpaca REST API client.
25
+ """
26
+
27
+ def __init__(self):
28
+ """
29
+ Initializes the AlpacaNewsFetcher object.
30
+
31
+ Args:
32
+ - api_key (str): Alpaca API key for authentication.
33
+ - api_secret (str): Alpaca API secret for authentication.
34
+ """
35
+ load_dotenv()
36
+ self.api_key = os.environ["ALPACA_API_KEY"]
37
+ self.api_secret = os.environ["ALPACA_SECRET"]
38
+ self.rest_client = REST(self.api_key, self.api_secret)
39
+
40
+ #No of news articles to fetch for the input stock ticker.
41
+ self.no_of_newsarticles_to_fetch = os.environ["NO_OF_NEWSARTICLES_TO_FETCH"]
42
+
43
+ #No of days to fetch news articles for
44
+ self.no_of_days = os.environ["NO_OF_DAYS_TO_FETCH_NEWS_ARTICLES"]
45
+
46
+
47
+ def fetch_news(self, stockticker):
48
+ """
49
+ Fetches news articles for a given stock symbol within a specified date range.
50
+
51
+ Args:
52
+ - stockticker (str): Stock symbol for which news articles are to be fetched (e.g., "AAPL").
53
+
54
+ Returns:
55
+ - list: A list of dictionaries containing relevant information for each news article.
56
+ """
57
+
58
+ #Date range for which to get the news
59
+ start_date = date.today()
60
+ end_date = date.today() - timedelta(self.no_of_days)
61
+
62
+ news_articles = self.rest_client.get_news(stockticker, start_date, end_date, limit=self.no_of_newsarticles_to_fetch )
63
+ formatted_news = []
64
+
65
+ for article in news_articles:
66
+ summary = article.summary
67
+ title = article.headline
68
+ timestamp = article.created_at
69
+
70
+ relevant_info = {
71
+ 'timestamp': timestamp,
72
+ 'title': title,
73
+ 'summary': summary
74
+ }
75
+
76
+ formatted_news.append(relevant_info)
77
+
78
+ return formatted_news
79
+
80
+
81
+ class NewsSentimentAnalysis:
82
+ """
83
+ A class for sentiment analysis of news articles using the Transformers library.
84
+
85
+ Attributes:
86
+ - classifier (pipeline): Sentiment analysis pipeline from Transformers.
87
+ """
88
+
89
+ def __init__(self):
90
+ """
91
+ Initializes the NewsSentimentAnalysis object.
92
+ """
93
+ self.classifier = pipeline('sentiment-analysis')
94
+
95
+
96
+ def analyze_sentiment(self, news_article):
97
+ """
98
+ Analyzes the sentiment of a given news article.
99
+
100
+ Args:
101
+ - news_article (dict): Dictionary containing 'summary', 'headline', and 'created_at' keys.
102
+
103
+ Returns:
104
+ - dict: A dictionary containing sentiment analysis results.
105
+ """
106
+ summary = news_article['summary']
107
+ title = news_article['title']
108
+ timestamp = news_article['timestamp']
109
+
110
+ relevant_text = summary + title
111
+ sentiment_result = self.classifier(relevant_text)
112
+
113
+ analysis_result = {
114
+ 'timestamp': timestamp,
115
+ 'title': title,
116
+ 'summary': summary,
117
+ 'sentiment': sentiment_result
118
+ }
119
+
120
+ return analysis_result
121
+
122
+ def plot_sentiment_graph(self, sentiment_analysis_result):
123
+ """
124
+ Plots a sentiment analysis graph
125
+
126
+ Args:
127
+ - sentiment_analysis_result): (dict): Dictionary containing 'summary', 'headline', and 'created_at' keys.
128
+
129
+ Returns:
130
+ - dict: A dictionary containing sentiment analysis results.
131
+ """
132
+ df = pd.DataFrame(sentiment_analysis_result)
133
+ df['Timestamp'] = pd.to_datetime(df['Timestamp'])
134
+ df['Date'] = df['Timestamp'].dt.date
135
+
136
+ #Group by Date, sentiment value count
137
+ grouped = df.groupby(by='Date')['Sentiment'].value_counts()
138
+
139
+ grouped.plot.pie()
140
+
141
+ def get_dominant_sentiment (self, sentiment_analysis_result):
142
+ """
143
+ Returns overall sentiment, negative or positive or neutral depending on the count of negative sentiment vs positive sentiment
144
+
145
+ Args:
146
+ - sentiment_analysis_result): (dict): Dictionary containing 'summary', 'headline', and 'created_at' keys.
147
+
148
+ Returns:
149
+ - dict: A dictionary containing sentiment analysis results.
150
+ """
151
+ df = pd.DataFrame(sentiment_analysis_result)
152
+ df['Timestamp'] = pd.to_datetime(df['Timestamp'])
153
+ df['Date'] = df['Timestamp'].dt.date
154
+
155
+ #Group by Date, sentiment value count
156
+ grouped = df.groupby(by='Date')['Sentiment'].value_counts()
157
+ df = pd.DataFrame(list(grouped.items()), columns=['Sentiment', 'count'])
158
+ df['date'] = df['Sentiment'].apply(lambda x: x[0])
159
+ df['sentiment'] = df['Sentiment'].apply(lambda x: x[1])
160
+ df.drop('Sentiment', axis=1, inplace=True)
161
+ result = df.groupby('sentiment')['count'].sum().reset_index()
162
+
163
+ # Determine the sentiment with the most count
164
+ dominant_sentiment = result.loc[result['count'].idxmax()]
165
+
166
+ return dominant_sentiment
167
+
168
+
169
+ #Function to get the stock sentiment
170
+ def get_stock_sentiment(stockticker: str):
171
+
172
+ #Initialize AlpacaNewsFetcher, a class for fetching news articles related to a specific stock from Alpaca API.
173
+ news_fetcher = AlpacaNewsFetcher()
174
+
175
+
176
+ # Fetch news (contains - title of the news, timestamp and summary) for specified stocksticker
177
+ news_data = news_fetcher.fetch_news(stockticker)
178
+
179
+ # Initialize the NewsSentimentAnalysis object
180
+ news_sentiment_analyzer = NewsSentimentAnalysis()
181
+ analysis_result = []
182
+
183
+ # Assume 'news_data' is a list of news articles (each as a dictionary), analyze sentiment of each news
184
+ for article in news_data:
185
+ sentiment_analysis_result = news_sentiment_analyzer.analyze_sentiment(article)
186
+
187
+ # Display sentiment analysis results
188
+ print(f'Timestamp: {sentiment_analysis_result["timestamp"]}, '
189
+ f'Title: {sentiment_analysis_result["title"]}, '
190
+ f'Summary: {sentiment_analysis_result["summary"]}')
191
+
192
+ print(f'Sentiment: {sentiment_analysis_result["sentiment"]}', '\n')
193
+
194
+ result = {
195
+ 'Timestamp': sentiment_analysis_result["timestamp"],
196
+ 'News- Title:Summar': sentiment_analysis_result["title"] + sentiment_analysis_result["summary"],
197
+ 'Sentiment': sentiment_analysis_result["sentiment"][0]['label']
198
+ }
199
+ analysis_result.append(result)
200
+
201
+ #Extracting timestamp of article and sentiment of article for graphing
202
+ """ result_for_graph = {
203
+ 'Timestamp': sentiment_analysis_result["timestamp"],
204
+ 'Sentiment': sentiment_analysis_result["sentiment"][0]['label']
205
+ }
206
+
207
+ analysis_result.append(result_for_graph)
208
+ """
209
+
210
+ #Get dominant sentiment
211
+ dominant_sentiment = news_sentiment_analyzer.get_dominant_sentiment(sentiment_analysis_result)
212
+
213
+ #Build response string for news sentiment
214
+ output_string = ""
215
+ for result in analysis_result:
216
+ output_string = output_string + f'{result["Timestamp"]} : {result["News- Title:Summary"]} : {result["Sentiment"]}' + '\n'
217
+
218
+ final_result = {
219
+ 'Sentiment-analysis-result' : output_string,
220
+ 'Dominant-sentiment' : dominant_sentiment['sentiment']
221
+ }
222
+
223
+ return final_result
224
+
225
+
226
+ class StockSentimentCheckInput(BaseModel):
227
+ """Input for Stock price check."""
228
+ stockticker: str = Field(..., description="Ticker symbol for stock or index")
229
+
230
+ class StockSentimentAnalysisTool(BaseTool):
231
+ name = "get_stock_sentiment"
232
+ description = """Useful for finding sentiment of stock, based on published news articles.
233
+ Fetches configured number of news items for the sentiment,
234
+ determines sentiment of each news items and then returns
235
+ List of sentiment analysit result & domainant sentiment of the news
236
+ """
237
+
238
+ """Input for Stock sentiment analysis."""
239
+ stockticker: str = Field(..., description="Ticker symbol for stock or index")
240
+ def _run(self, stockticker: str):
241
+ # print("i'm running")
242
+ sentiment_response = get_stock_sentiment(stockticker)
243
+ print("++++++++++++++++++++++++++++++++++++++++++++++++++++++")
244
+ print(str(sentiment_response))
245
+ print("++++++++++++++++++++++++++++++++++++++++++++++++++++++")
246
+
247
+ return sentiment_response
248
+
249
+ def _arun(self, stockticker: str):
250
+ raise NotImplementedError("This tool does not support async")
251
+
252
+ args_schema: Optional[Type[BaseModel]] = StockSentimentCheckInput
253
+
254
+
255
+ tools_sentiment_analyst = [StructuredTool.from_function(
256
+ func=StockSentimentAnalysisTool,
257
+ args_schema=StockSentimentCheckInput,
258
+ description="Function to get stock sentiment.",
259
+ )
260
+ ]
261
+ return tools_sentiment_analyst
utils.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import matplotlib.pyplot as plt
2
+ import chainlit as cl
3
+ import plotly.graph_objects as go
4
+ import pandas as pd
5
+ import numpy as np
6
+ from datetime import datetime, timedelta
7
+ import yfinance as yf
8
+ from plotly.subplots import make_subplots
9
+
10
+ def get_stock_price(stockticker: str) -> str:
11
+ ticker = yf.Ticker(stockticker)
12
+ todays_data = ticker.history(period='1d')
13
+ return str(round(todays_data['Close'][0], 2))
14
+
15
+ def plot_candlestick_stock_price(historical_data):
16
+ """Useful for plotting candlestick plot for stock prices.
17
+ Use historical stock price data from yahoo finance for the week and plot them."""
18
+ df=historical_data[['Close','Open','High','Low']]
19
+ df.index=pd.to_datetime(df.index)
20
+ df.index.names=['Date']
21
+ df=df.reset_index()
22
+
23
+ fig = go.Figure(data=[go.Candlestick(x=df['Date'],
24
+ open=df['Open'],
25
+ high=df['High'],
26
+ low=df['Low'],
27
+ close=df['Close'])])
28
+ fig.show()
29
+
30
+ def historical_stock_prices(stockticker, days_ago):
31
+ """Upload accurate data to accurate dates from yahoo finance."""
32
+ ticker = yf.Ticker(stockticker)
33
+ end_date = datetime.now()
34
+ start_date = end_date - timedelta(days=days_ago)
35
+ start_date = start_date.strftime('%Y-%m-%d')
36
+ end_date = end_date.strftime('%Y-%m-%d')
37
+ historical_data = ticker.history(start=start_date, end=end_date)
38
+ return historical_data
39
+
40
+ def plot_macd2(df):
41
+ try:
42
+ # Debugging: Print the dataframe columns and a few rows
43
+ print("DataFrame columns:", df.columns)
44
+ print("DataFrame head:\n", df.head())
45
+
46
+ # Convert DataFrame index and columns to numpy arrays
47
+ index = df.index.to_numpy()
48
+ close_prices = df['Close'].to_numpy()
49
+ macd = df['MACD'].to_numpy()
50
+ signal_line = df['Signal_Line'].to_numpy()
51
+ macd_histogram = df['MACD_Histogram'].to_numpy()
52
+
53
+ fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(10, 8), gridspec_kw={'height_ratios': [3, 1]})
54
+
55
+ # Subplot 1: Candlestick chart
56
+ ax1.plot(index, close_prices, label='Close', color='black')
57
+ ax1.set_title("Candlestick Chart")
58
+ ax1.set_ylabel("Price")
59
+ ax1.legend()
60
+
61
+ # Subplot 2: MACD
62
+ ax2.plot(index, macd, label='MACD', color='blue')
63
+ ax2.plot(index, signal_line, label='Signal Line', color='red')
64
+
65
+ histogram_colors = np.where(macd_histogram >= 0, 'green', 'red')
66
+ ax2.bar(index, macd_histogram, color=histogram_colors, alpha=0.6)
67
+
68
+ ax2.set_title("MACD")
69
+ ax2.set_ylabel("MACD Value")
70
+ ax2.legend()
71
+
72
+ plt.xlabel("Date")
73
+ plt.tight_layout()
74
+
75
+ return fig
76
+ except Exception as e:
77
+ print(f"Error in plot_macd: {e}")
78
+ return None
79
+
80
+ def plot_macd(df):
81
+
82
+ # Create Figure
83
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, row_heights=[0.2, 0.1],
84
+ vertical_spacing=0.15, # Adjust vertical spacing between subplots
85
+ subplot_titles=("Candlestick Chart", "MACD")) # Add subplot titles
86
+
87
+
88
+ # Subplot 1: Plot candlestick chart
89
+ fig.add_trace(go.Candlestick(
90
+ x=df.index,
91
+ open=df['Open'],
92
+ high=df['High'],
93
+ low=df['Low'],
94
+ close=df['Close'],
95
+ increasing_line_color='#00cc96', # Green for increasing
96
+ decreasing_line_color='#ff3e3e', # Red for decreasing
97
+ showlegend=False
98
+ ), row=1, col=1) # Specify row and column indices
99
+
100
+
101
+ # Subplot 2: Plot MACD
102
+ fig.add_trace(
103
+ go.Scatter(
104
+ x=df.index,
105
+ y=df['MACD'],
106
+ mode='lines',
107
+ name='MACD',
108
+ line=dict(color='blue')
109
+ ),
110
+ row=2, col=1
111
+ )
112
+
113
+ fig.add_trace(
114
+ go.Scatter(
115
+ x=df.index,
116
+ y=df['Signal_Line'],
117
+ mode='lines',
118
+ name='Signal Line',
119
+ line=dict(color='red')
120
+ ),
121
+ row=2, col=1
122
+ )
123
+
124
+ # Plot MACD Histogram with different colors for positive and negative values
125
+ histogram_colors = ['green' if val >= 0 else 'red' for val in df['MACD_Histogram']]
126
+
127
+ fig.add_trace(
128
+ go.Bar(
129
+ x=df.index,
130
+ y=df['MACD_Histogram'],
131
+ name='MACD Histogram',
132
+ marker_color=histogram_colors
133
+ ),
134
+ row=2, col=1
135
+ )
136
+
137
+ # Update layout with zoom and pan tools enabled
138
+ layout = go.Layout(
139
+ title='MSFT Candlestick Chart and MACD Subplots',
140
+ title_font=dict(size=12), # Adjust title font size
141
+ plot_bgcolor='#f2f2f2', # Light gray background
142
+ height=600,
143
+ width=1200,
144
+ xaxis_rangeslider=dict(visible=True, thickness=0.03),
145
+ )
146
+
147
+ # Update the layout of the entire figure
148
+ fig.update_layout(layout)
149
+ fig.update_yaxes(fixedrange=False, row=1, col=1)
150
+ fig.update_yaxes(fixedrange=True, row=2, col=1)
151
+ fig.update_xaxes(type='category', row=1, col=1)
152
+ fig.update_xaxes(type='category', nticks=10, row=2, col=1)
153
+
154
+ fig.show()
155
+ #return fig
156
+
157
+ def calculate_MACD(df, fast_period=12, slow_period=26, signal_period=9):
158
+ """
159
+ Calculates the MACD (Moving Average Convergence Divergence) and related indicators.
160
+
161
+ Parameters:
162
+ df (DataFrame): A pandas DataFrame containing at least a 'Close' column with closing prices.
163
+ fast_period (int): The period for the fast EMA (default is 12).
164
+ slow_period (int): The period for the slow EMA (default is 26).
165
+ signal_period (int): The period for the signal line EMA (default is 9).
166
+
167
+ Returns:
168
+ DataFrame: A pandas DataFrame with the original data and added columns for MACD, Signal Line, and MACD Histogram.
169
+ """
170
+
171
+ df['EMA_fast'] = df['Close'].ewm(span=fast_period, adjust=False).mean()
172
+ df['EMA_slow'] = df['Close'].ewm(span=slow_period, adjust=False).mean()
173
+ df['MACD'] = df['EMA_fast'] - df['EMA_slow']
174
+
175
+ df['Signal_Line'] = df['MACD'].ewm(span=signal_period, adjust=False).mean()
176
+ df['MACD_Histogram'] = df['MACD'] - df['Signal_Line']
177
+
178
+ return df