community: Add stock market tools from financialdatasets.ai (#25025)

**Description:**
In this PR, I am adding three stock market tools from
financialdatasets.ai (my API!):
- get balance sheets
- get cash flow statements
- get income statements

Twitter handle: [@virattt](https://twitter.com/virattt)

---------

Co-authored-by: Erick Friis <erick@langchain.dev>
This commit is contained in:
Virat Singh
2024-08-06 14:28:12 -04:00
committed by GitHub
parent 267855b3c1
commit 264ab96980
10 changed files with 715 additions and 0 deletions

View File

@@ -0,0 +1 @@
"""financial datasets toolkit."""

View File

@@ -0,0 +1,45 @@
from __future__ import annotations
from typing import List
from langchain_core.pydantic_v1 import Field
from langchain_core.tools import BaseToolkit
from langchain_community.tools import BaseTool
from langchain_community.tools.financial_datasets.balance_sheets import BalanceSheets
from langchain_community.tools.financial_datasets.cash_flow_statements import (
CashFlowStatements,
)
from langchain_community.tools.financial_datasets.income_statements import (
IncomeStatements,
)
from langchain_community.utilities.financial_datasets import FinancialDatasetsAPIWrapper
class FinancialDatasetsToolkit(BaseToolkit):
"""Toolkit for interacting with financialdatasets.ai.
Parameters:
api_wrapper: The FinancialDatasets API Wrapper.
"""
api_wrapper: FinancialDatasetsAPIWrapper = Field(
default_factory=FinancialDatasetsAPIWrapper
)
def __init__(self, api_wrapper: FinancialDatasetsAPIWrapper):
super().__init__()
self.api_wrapper = api_wrapper
class Config:
"""Pydantic config."""
arbitrary_types_allowed = True
def get_tools(self) -> List[BaseTool]:
"""Get the tools in the toolkit."""
return [
BalanceSheets(api_wrapper=self.api_wrapper),
CashFlowStatements(api_wrapper=self.api_wrapper),
IncomeStatements(api_wrapper=self.api_wrapper),
]

View File

@@ -120,6 +120,15 @@ if TYPE_CHECKING:
ReadFileTool,
WriteFileTool,
)
from langchain_community.tools.financial_datasets.balance_sheets import (
BalanceSheets,
)
from langchain_community.tools.financial_datasets.cash_flow_statements import (
CashFlowStatements,
)
from langchain_community.tools.financial_datasets.income_statements import (
IncomeStatements,
)
from langchain_community.tools.gmail import (
GmailCreateDraft,
GmailGetMessage,
@@ -348,6 +357,7 @@ __all__ = [
"AzureCogsSpeech2TextTool",
"AzureCogsText2SpeechTool",
"AzureCogsTextAnalyticsHealthTool",
"BalanceSheets",
"BaseGraphQLTool",
"BaseRequestsTool",
"BaseSQLDatabaseTool",
@@ -357,6 +367,7 @@ __all__ = [
"BingSearchResults",
"BingSearchRun",
"BraveSearch",
"CashFlowStatements",
"ClickTool",
"CogniswitchKnowledgeRequest",
"CogniswitchKnowledgeSourceFile",
@@ -396,6 +407,7 @@ __all__ = [
"GoogleSerperRun",
"HumanInputRun",
"IFTTTWebhook",
"IncomeStatements",
"InfoPowerBITool",
"InfoSQLDatabaseTool",
"InfoSparkSQLTool",
@@ -498,6 +510,7 @@ _module_lookup = {
"AzureCogsSpeech2TextTool": "langchain_community.tools.azure_cognitive_services",
"AzureCogsText2SpeechTool": "langchain_community.tools.azure_cognitive_services",
"AzureCogsTextAnalyticsHealthTool": "langchain_community.tools.azure_cognitive_services", # noqa: E501
"BalanceSheets": "langchain_community.tools.financial_datasets.balance_sheets",
"BaseGraphQLTool": "langchain_community.tools.graphql.tool",
"BaseRequestsTool": "langchain_community.tools.requests.tool",
"BaseSQLDatabaseTool": "langchain_community.tools.sql_database.tool",
@@ -507,6 +520,7 @@ _module_lookup = {
"BingSearchResults": "langchain_community.tools.bing_search.tool",
"BingSearchRun": "langchain_community.tools.bing_search.tool",
"BraveSearch": "langchain_community.tools.brave_search.tool",
"CashFlowStatements": "langchain_community.tools.financial_datasets.cash_flow_statements", # noqa: E501
"ClickTool": "langchain_community.tools.playwright",
"CogniswitchKnowledgeRequest": "langchain_community.tools.cogniswitch.tool",
"CogniswitchKnowledgeSourceFile": "langchain_community.tools.cogniswitch.tool",
@@ -547,6 +561,7 @@ _module_lookup = {
"GoogleSerperRun": "langchain_community.tools.google_serper.tool",
"HumanInputRun": "langchain_community.tools.human.tool",
"IFTTTWebhook": "langchain_community.tools.ifttt",
"IncomeStatements": "langchain_community.tools.financial_datasets.income_statements", # noqa: E501
"InfoPowerBITool": "langchain_community.tools.powerbi.tool",
"InfoSQLDatabaseTool": "langchain_community.tools.sql_database.tool",
"InfoSparkSQLTool": "langchain_community.tools.spark_sql.tool",

View File

@@ -0,0 +1,17 @@
"""financial datasets tools."""
from langchain_community.tools.financial_datasets.balance_sheets import (
BalanceSheets,
)
from langchain_community.tools.financial_datasets.cash_flow_statements import (
CashFlowStatements,
)
from langchain_community.tools.financial_datasets.income_statements import (
IncomeStatements,
)
__all__ = [
"BalanceSheets",
"CashFlowStatements",
"IncomeStatements",
]

View File

@@ -0,0 +1,62 @@
from typing import Optional, Type
from langchain_core.callbacks import CallbackManagerForToolRun
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.tools import BaseTool
from langchain_community.utilities.financial_datasets import FinancialDatasetsAPIWrapper
class BalanceSheetsSchema(BaseModel):
"""Input for BalanceSheets."""
ticker: str = Field(
description="The ticker symbol to fetch balance sheets for.",
)
period: str = Field(
description="The period of the balance sheets. "
"Possible values are: "
"annual, quarterly, ttm. "
"Default is 'annual'.",
)
limit: int = Field(
description="The number of balance sheets to return. " "Default is 10.",
)
class BalanceSheets(BaseTool):
"""
Tool that gets balance sheets for a given ticker over a given period.
"""
mode: str = "get_balance_sheets"
name: str = "balance_sheets"
description: str = (
"A wrapper around financial datasets's Balance Sheets API. "
"This tool is useful for fetching balance sheets for a given ticker."
"The tool fetches balance sheets for a given ticker over a given period."
"The period can be annual, quarterly, or trailing twelve months (ttm)."
"The number of balance sheets to return can also be "
"specified using the limit parameter."
)
args_schema: Type[BalanceSheetsSchema] = BalanceSheetsSchema
api_wrapper: FinancialDatasetsAPIWrapper = Field(..., exclude=True)
def __init__(self, api_wrapper: FinancialDatasetsAPIWrapper):
super().__init__(api_wrapper=api_wrapper)
def _run(
self,
ticker: str,
period: str,
limit: Optional[int],
run_manager: Optional[CallbackManagerForToolRun] = None,
) -> str:
"""Use the Balance Sheets API tool."""
return self.api_wrapper.run(
mode=self.mode,
ticker=ticker,
period=period,
limit=limit,
)

View File

@@ -0,0 +1,62 @@
from typing import Optional, Type
from langchain_core.callbacks import CallbackManagerForToolRun
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.tools import BaseTool
from langchain_community.utilities.financial_datasets import FinancialDatasetsAPIWrapper
class CashFlowStatementsSchema(BaseModel):
"""Input for CashFlowStatements."""
ticker: str = Field(
description="The ticker symbol to fetch cash flow statements for.",
)
period: str = Field(
description="The period of the cash flow statement. "
"Possible values are: "
"annual, quarterly, ttm. "
"Default is 'annual'.",
)
limit: int = Field(
description="The number of cash flow statements to return. " "Default is 10.",
)
class CashFlowStatements(BaseTool):
"""
Tool that gets cash flow statements for a given ticker over a given period.
"""
mode: str = "get_cash_flow_statements"
name: str = "cash_flow_statements"
description: str = (
"A wrapper around financial datasets's Cash Flow Statements API. "
"This tool is useful for fetching cash flow statements for a given ticker."
"The tool fetches cash flow statements for a given ticker over a given period."
"The period can be annual, quarterly, or trailing twelve months (ttm)."
"The number of cash flow statements to return can also be "
"specified using the limit parameter."
)
args_schema: Type[CashFlowStatementsSchema] = CashFlowStatementsSchema
api_wrapper: FinancialDatasetsAPIWrapper = Field(..., exclude=True)
def __init__(self, api_wrapper: FinancialDatasetsAPIWrapper):
super().__init__(api_wrapper=api_wrapper)
def _run(
self,
ticker: str,
period: str,
limit: Optional[int],
run_manager: Optional[CallbackManagerForToolRun] = None,
) -> str:
"""Use the Cash Flow Statements API tool."""
return self.api_wrapper.run(
mode=self.mode,
ticker=ticker,
period=period,
limit=limit,
)

View File

@@ -0,0 +1,62 @@
from typing import Optional, Type
from langchain_core.callbacks import CallbackManagerForToolRun
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.tools import BaseTool
from langchain_community.utilities.financial_datasets import FinancialDatasetsAPIWrapper
class IncomeStatementsSchema(BaseModel):
"""Input for IncomeStatements."""
ticker: str = Field(
description="The ticker symbol to fetch income statements for.",
)
period: str = Field(
description="The period of the income statement. "
"Possible values are: "
"annual, quarterly, ttm. "
"Default is 'annual'.",
)
limit: int = Field(
description="The number of income statements to return. " "Default is 10.",
)
class IncomeStatements(BaseTool):
"""
Tool that gets income statements for a given ticker over a given period.
"""
mode: str = "get_income_statements"
name: str = "income_statements"
description: str = (
"A wrapper around financial datasets's Income Statements API. "
"This tool is useful for fetching income statements for a given ticker."
"The tool fetches income statements for a given ticker over a given period."
"The period can be annual, quarterly, or trailing twelve months (ttm)."
"The number of income statements to return can also be "
"specified using the limit parameter."
)
args_schema: Type[IncomeStatementsSchema] = IncomeStatementsSchema
api_wrapper: FinancialDatasetsAPIWrapper = Field(..., exclude=True)
def __init__(self, api_wrapper: FinancialDatasetsAPIWrapper):
super().__init__(api_wrapper=api_wrapper)
def _run(
self,
ticker: str,
period: str,
limit: Optional[int],
run_manager: Optional[CallbackManagerForToolRun] = None,
) -> str:
"""Use the Income Statements API tool."""
return self.api_wrapper.run(
mode=self.mode,
ticker=ticker,
period=period,
limit=limit,
)

View File

@@ -0,0 +1,135 @@
"""
Util that calls several of financial datasets stock market REST APIs.
Docs: https://docs.financialdatasets.ai/
"""
import json
from typing import Any, List, Optional
import requests
from langchain_core.pydantic_v1 import BaseModel
from langchain_core.utils import get_from_dict_or_env
FINANCIAL_DATASETS_BASE_URL = "https://api.financialdatasets.ai/"
class FinancialDatasetsAPIWrapper(BaseModel):
"""Wrapper for financial datasets API."""
financial_datasets_api_key: Optional[str] = None
def __init__(self, **data: Any):
super().__init__(**data)
self.financial_datasets_api_key = get_from_dict_or_env(
data, "financial_datasets_api_key", "FINANCIAL_DATASETS_API_KEY"
)
def get_income_statements(
self,
ticker: str,
period: str,
limit: Optional[int],
) -> Optional[dict]:
"""
Get the income statements for a stock `ticker` over a `period` of time.
:param ticker: the stock ticker
:param period: the period of time to get the balance sheets for.
Possible values are: annual, quarterly, ttm.
:param limit: the number of results to return, default is 10
:return: a list of income statements
"""
url = (
f"{FINANCIAL_DATASETS_BASE_URL}financials/income-statements/"
f"?ticker={ticker}"
f"&period={period}"
f"&limit={limit if limit else 10}"
)
# Add the api key to the headers
headers = {"X-API-KEY": self.financial_datasets_api_key}
# Execute the request
response = requests.get(url, headers=headers)
data = response.json()
return data.get("income_statements", None)
def get_balance_sheets(
self,
ticker: str,
period: str,
limit: Optional[int],
) -> List[dict]:
"""
Get the balance sheets for a stock `ticker` over a `period` of time.
:param ticker: the stock ticker
:param period: the period of time to get the balance sheets for.
Possible values are: annual, quarterly, ttm.
:param limit: the number of results to return, default is 10
:return: a list of balance sheets
"""
url = (
f"{FINANCIAL_DATASETS_BASE_URL}financials/balance-sheets/"
f"?ticker={ticker}"
f"&period={period}"
f"&limit={limit if limit else 10}"
)
# Add the api key to the headers
headers = {"X-API-KEY": self.financial_datasets_api_key}
# Execute the request
response = requests.get(url, headers=headers)
data = response.json()
return data.get("balance_sheets", None)
def get_cash_flow_statements(
self,
ticker: str,
period: str,
limit: Optional[int],
) -> List[dict]:
"""
Get the cash flow statements for a stock `ticker` over a `period` of time.
:param ticker: the stock ticker
:param period: the period of time to get the balance sheets for.
Possible values are: annual, quarterly, ttm.
:param limit: the number of results to return, default is 10
:return: a list of cash flow statements
"""
url = (
f"{FINANCIAL_DATASETS_BASE_URL}financials/cash-flow-statements/"
f"?ticker={ticker}"
f"&period={period}"
f"&limit={limit if limit else 10}"
)
# Add the api key to the headers
headers = {"X-API-KEY": self.financial_datasets_api_key}
# Execute the request
response = requests.get(url, headers=headers)
data = response.json()
return data.get("cash_flow_statements", None)
def run(self, mode: str, ticker: str, **kwargs: Any) -> str:
if mode == "get_income_statements":
period = kwargs.get("period", "annual")
limit = kwargs.get("limit", 10)
return json.dumps(self.get_income_statements(ticker, period, limit))
elif mode == "get_balance_sheets":
period = kwargs.get("period", "annual")
limit = kwargs.get("limit", 10)
return json.dumps(self.get_balance_sheets(ticker, period, limit))
elif mode == "get_cash_flow_statements":
period = kwargs.get("period", "annual")
limit = kwargs.get("limit", 10)
return json.dumps(self.get_cash_flow_statements(ticker, period, limit))
else:
raise ValueError(f"Invalid mode {mode} for financial datasets API.")

View File

@@ -20,6 +20,7 @@ EXPECTED_ALL = [
"AzureCogsSpeech2TextTool",
"AzureCogsText2SpeechTool",
"AzureCogsTextAnalyticsHealthTool",
"BalanceSheets",
"BaseGraphQLTool",
"BaseRequestsTool",
"BaseSQLDatabaseTool",
@@ -29,6 +30,7 @@ EXPECTED_ALL = [
"BingSearchResults",
"BingSearchRun",
"BraveSearch",
"CashFlowStatements",
"ClickTool",
"CogniswitchKnowledgeSourceFile",
"CogniswitchKnowledgeSourceURL",
@@ -68,6 +70,7 @@ EXPECTED_ALL = [
"GoogleSerperRun",
"HumanInputRun",
"IFTTTWebhook",
"IncomeStatements",
"InfoPowerBITool",
"InfoSQLDatabaseTool",
"InfoSparkSQLTool",