Spaces:
Sleeping
Sleeping
sanjeevl10
commited on
Commit
·
38b6b6d
0
Parent(s):
add aap.py and added sentiment analysis
Browse files- .chainlit/config.toml +84 -0
- Dockerfile +12 -0
- README.md +1 -0
- __pycache__/app_test3_memory1.cpython-310.pyc +0 -0
- __pycache__/utils.cpython-310.pyc +0 -0
- app.py +318 -0
- chainlit.md +1 -0
- df_history.csv +63 -0
- requirements.txt +24 -0
- tools/.chainlit/config.toml +84 -0
- tools/__pycache__/data_analyst.cpython-310.pyc +0 -0
- tools/__pycache__/evaluator.cpython-310.pyc +0 -0
- tools/__pycache__/forecasting_expert_arima.cpython-310.pyc +0 -0
- tools/__pycache__/forecasting_expert_rf.cpython-310.pyc +0 -0
- tools/__pycache__/investment_advisor.cpython-310.pyc +0 -0
- tools/__pycache__/stock_sentiment_analysis_util.cpython-310.pyc +0 -0
- tools/__pycache__/stock_sentiment_evalutor.cpython-310.pyc +0 -0
- tools/chart_expert.py +206 -0
- tools/chart_expert1.py +155 -0
- tools/data_analyst.py +74 -0
- tools/df_history.csv +63 -0
- tools/evaluator.py +80 -0
- tools/forecasting_expert_arima.py +80 -0
- tools/forecasting_expert_rf.py +105 -0
- tools/investment_advisor.py +68 -0
- tools/stock_sentiment_analysis_util.py +192 -0
- tools/stock_sentiment_evalutor.py +261 -0
- utils.py +178 -0
.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
|