Spaces:
Runtime error
Runtime error
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
|