diff --git a/docs/docs/integrations/tools/polygon.ipynb b/docs/docs/integrations/tools/polygon.ipynb index 151791254ed..7d2e98b34d9 100644 --- a/docs/docs/integrations/tools/polygon.ipynb +++ b/docs/docs/integrations/tools/polygon.ipynb @@ -41,7 +41,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 2, "id": "ac4910f8", "metadata": { "id": "ac4910f8", @@ -49,6 +49,7 @@ }, "outputs": [], "source": [ + "from langchain_community.tools.polygon.aggregates import PolygonAggregates\n", "from langchain_community.tools.polygon.financials import PolygonFinancials\n", "from langchain_community.tools.polygon.last_quote import PolygonLastQuote\n", "from langchain_community.tools.polygon.ticker_news import PolygonTickerNews\n", @@ -86,7 +87,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Tool output: {\"P\": 181.18, \"S\": 7, \"T\": \"AAPL\", \"X\": 11, \"i\": [604], \"p\": 181.13, \"q\": 704855, \"s\": 3, \"t\": 1709213573791533513, \"x\": 8, \"y\": 1709213573791191178, \"z\": 3}\n" + "Tool output: {\"P\": 170.5, \"S\": 2, \"T\": \"AAPL\", \"X\": 11, \"i\": [604], \"p\": 170.48, \"q\": 106666224, \"s\": 1, \"t\": 1709945992614283138, \"x\": 12, \"y\": 1709945992614268948, \"z\": 3}\n" ] } ], @@ -116,7 +117,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "id": "174e2556-eb3e-48a4-bde6-9a3309fae9c9", "metadata": {}, "outputs": [ @@ -124,7 +125,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Latest price for AAPL is $181.13\n" + "Latest price for AAPL is $170.48\n" ] } ], @@ -134,6 +135,57 @@ "print(f\"Latest price for {ticker} is ${latest_price}\")" ] }, + { + "cell_type": "markdown", + "id": "1f478364-f41b-47f2-ac4b-d3154f1c7faa", + "metadata": {}, + "source": [ + "### Get aggregates (historical prices) for ticker" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "5e14e091-3150-4bd5-bfd3-de17caa75ee1", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_community.tools.polygon.aggregates import PolygonAggregatesSchema\n", + "\n", + "# Define param\n", + "params = PolygonAggregatesSchema(\n", + " ticker=ticker,\n", + " timespan=\"day\",\n", + " timespan_multiplier=1,\n", + " from_date=\"2024-03-01\",\n", + " to_date=\"2024-03-08\",\n", + ")\n", + "# Get aggregates for ticker\n", + "aggregates_tool = PolygonAggregates(api_wrapper=api_wrapper)\n", + "aggregates = aggregates_tool.run(tool_input=params.dict())\n", + "aggregates_json = json.loads(aggregates)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "a01f3888-d233-400d-b8c4-298d27c8f793", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total aggregates: 6\n", + "Aggregates: [{'v': 73450582.0, 'vw': 179.0322, 'o': 179.55, 'c': 179.66, 'h': 180.53, 'l': 177.38, 't': 1709269200000, 'n': 911077}, {'v': 81505451.0, 'vw': 174.8938, 'o': 176.15, 'c': 175.1, 'h': 176.9, 'l': 173.79, 't': 1709528400000, 'n': 1167166}, {'v': 94702355.0, 'vw': 170.3234, 'o': 170.76, 'c': 170.12, 'h': 172.04, 'l': 169.62, 't': 1709614800000, 'n': 1108820}, {'v': 68568907.0, 'vw': 169.5506, 'o': 171.06, 'c': 169.12, 'h': 171.24, 'l': 168.68, 't': 1709701200000, 'n': 896297}, {'v': 71763761.0, 'vw': 169.3619, 'o': 169.15, 'c': 169, 'h': 170.73, 'l': 168.49, 't': 1709787600000, 'n': 825405}, {'v': 76267041.0, 'vw': 171.5322, 'o': 169, 'c': 170.73, 'h': 173.7, 'l': 168.94, 't': 1709874000000, 'n': 925213}]\n" + ] + } + ], + "source": [ + "print(f\"Total aggregates: {len(aggregates_json)}\")\n", + "print(f\"Aggregates: {aggregates_json}\")" + ] + }, { "cell_type": "markdown", "id": "04f1b612-f91f-471c-8264-9cc8c14bdaef", diff --git a/libs/community/langchain_community/agent_toolkits/polygon/toolkit.py b/libs/community/langchain_community/agent_toolkits/polygon/toolkit.py index a9d39b3fb65..a41555b7dc5 100644 --- a/libs/community/langchain_community/agent_toolkits/polygon/toolkit.py +++ b/libs/community/langchain_community/agent_toolkits/polygon/toolkit.py @@ -3,6 +3,7 @@ from typing import List from langchain_community.agent_toolkits.base import BaseToolkit from langchain_community.tools import BaseTool from langchain_community.tools.polygon import ( + PolygonAggregates, PolygonFinancials, PolygonLastQuote, PolygonTickerNews, @@ -20,6 +21,9 @@ class PolygonToolkit(BaseToolkit): cls, polygon_api_wrapper: PolygonAPIWrapper ) -> "PolygonToolkit": tools = [ + PolygonAggregates( + api_wrapper=polygon_api_wrapper, + ), PolygonLastQuote( api_wrapper=polygon_api_wrapper, ), diff --git a/libs/community/langchain_community/tools/__init__.py b/libs/community/langchain_community/tools/__init__.py index 96791b7db01..1a2252c38b3 100644 --- a/libs/community/langchain_community/tools/__init__.py +++ b/libs/community/langchain_community/tools/__init__.py @@ -504,6 +504,12 @@ def _import_plugin() -> Any: return AIPluginTool +def _import_polygon_tool_PolygonAggregates() -> Any: + from langchain_community.tools.polygon.aggregates import PolygonAggregates + + return PolygonAggregates + + def _import_polygon_tool_PolygonFinancials() -> Any: from langchain_community.tools.polygon.financials import PolygonFinancials @@ -973,6 +979,8 @@ def __getattr__(name: str) -> Any: return _import_playwright_NavigateTool() elif name == "AIPluginTool": return _import_plugin() + elif name == "PolygonAggregates": + return _import_polygon_tool_PolygonAggregates() elif name == "PolygonFinancials": return _import_polygon_tool_PolygonFinancials() elif name == "PolygonLastQuote": @@ -1164,6 +1172,7 @@ __all__ = [ "OpenAPISpec", "OpenWeatherMapQueryRun", "PubmedQueryRun", + "PolygonAggregates", "PolygonFinancials", "PolygonLastQuote", "PolygonTickerNews", diff --git a/libs/community/langchain_community/tools/polygon/__init__.py b/libs/community/langchain_community/tools/polygon/__init__.py index c748968a202..87a9c1c1357 100644 --- a/libs/community/langchain_community/tools/polygon/__init__.py +++ b/libs/community/langchain_community/tools/polygon/__init__.py @@ -1,10 +1,12 @@ """Polygon IO tools.""" +from langchain_community.tools.polygon.aggregates import PolygonAggregates from langchain_community.tools.polygon.financials import PolygonFinancials from langchain_community.tools.polygon.last_quote import PolygonLastQuote from langchain_community.tools.polygon.ticker_news import PolygonTickerNews __all__ = [ + "PolygonAggregates", "PolygonFinancials", "PolygonLastQuote", "PolygonTickerNews", diff --git a/libs/community/langchain_community/tools/polygon/aggregates.py b/libs/community/langchain_community/tools/polygon/aggregates.py new file mode 100644 index 00000000000..e4c07761727 --- /dev/null +++ b/libs/community/langchain_community/tools/polygon/aggregates.py @@ -0,0 +1,77 @@ +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.polygon import PolygonAPIWrapper + + +class PolygonAggregatesSchema(BaseModel): + """Input for PolygonAggregates.""" + + ticker: str = Field( + description="The ticker symbol to fetch aggregates for.", + ) + timespan: str = Field( + description="The size of the time window. " + "Possible values are: " + "second, minute, hour, day, week, month, quarter, year. " + "Default is 'day'", + ) + timespan_multiplier: int = Field( + description="The number of timespans to aggregate. " + "For example, if timespan is 'day' and " + "timespan_multiplier is 1, the result will be daily bars. " + "If timespan is 'day' and timespan_multiplier is 5, " + "the result will be weekly bars. " + "Default is 1.", + ) + from_date: str = Field( + description="The start of the aggregate time window. " + "Either a date with the format YYYY-MM-DD or " + "a millisecond timestamp.", + ) + to_date: str = Field( + description="The end of the aggregate time window. " + "Either a date with the format YYYY-MM-DD or " + "a millisecond timestamp.", + ) + + +class PolygonAggregates(BaseTool): + """ + Tool that gets aggregate bars (stock prices) over a + given date range for a given ticker from Polygon. + """ + + mode: str = "get_aggregates" + name: str = "polygon_aggregates" + description: str = ( + "A wrapper around Polygon's Aggregates API. " + "This tool is useful for fetching aggregate bars (stock prices) for a ticker. " + "Input should be the ticker, date range, timespan, and timespan multiplier" + " that you want to get the aggregate bars for." + ) + args_schema: Type[PolygonAggregatesSchema] = PolygonAggregatesSchema + + api_wrapper: PolygonAPIWrapper + + def _run( + self, + ticker: str, + timespan: str, + timespan_multiplier: int, + from_date: str, + to_date: str, + run_manager: Optional[CallbackManagerForToolRun] = None, + ) -> str: + """Use the Polygon API tool.""" + return self.api_wrapper.run( + mode=self.mode, + ticker=ticker, + timespan=timespan, + timespan_multiplier=timespan_multiplier, + from_date=from_date, + to_date=to_date, + ) diff --git a/libs/community/langchain_community/utilities/polygon.py b/libs/community/langchain_community/utilities/polygon.py index a9ddb43a4b7..e98d772e364 100644 --- a/libs/community/langchain_community/utilities/polygon.py +++ b/libs/community/langchain_community/utilities/polygon.py @@ -3,7 +3,7 @@ Util that calls several of Polygon's stock market REST APIs. Docs: https://polygon.io/docs/stocks/getting-started """ import json -from typing import Dict, Optional +from typing import Any, Dict, Optional import requests from langchain_core.pydantic_v1 import BaseModel, root_validator @@ -31,6 +31,8 @@ class PolygonAPIWrapper(BaseModel): """ Get fundamental financial data, which is found in balance sheets, income statements, and cash flow statements for a given ticker. + + /vX/reference/financials """ url = ( f"{POLYGON_BASE_URL}vX/reference/financials?" @@ -47,7 +49,11 @@ class PolygonAPIWrapper(BaseModel): return data.get("results", None) def get_last_quote(self, ticker: str) -> Optional[dict]: - """Get the most recent National Best Bid and Offer (Quote) for a ticker.""" + """ + Get the most recent National Best Bid and Offer (Quote) for a ticker. + + /v2/last/nbbo/{ticker} + """ url = f"{POLYGON_BASE_URL}v2/last/nbbo/{ticker}?apiKey={self.polygon_api_key}" response = requests.get(url) data = response.json() @@ -62,6 +68,8 @@ class PolygonAPIWrapper(BaseModel): """ Get the most recent news articles relating to a stock ticker symbol, including a summary of the article and a link to the original source. + + /v2/reference/news """ url = ( f"{POLYGON_BASE_URL}v2/reference/news?" @@ -77,12 +85,48 @@ class PolygonAPIWrapper(BaseModel): return data.get("results", None) - def run(self, mode: str, ticker: str) -> str: + def get_aggregates(self, ticker: str, **kwargs: Any) -> Optional[dict]: + """ + Get aggregate bars for a stock over a given date range + in custom time window sizes. + + /v2/aggs/ticker/{ticker}/range/{multiplier}/{timespan}/{from_date}/{to_date} + """ + timespan = kwargs.get("timespan", "day") + multiplier = kwargs.get("timespan_multiplier", 1) + from_date = kwargs.get("from_date", None) + to_date = kwargs.get("to_date", None) + adjusted = kwargs.get("adjusted", True) + sort = kwargs.get("sort", "asc") + + url = ( + f"{POLYGON_BASE_URL}v2/aggs" + f"/ticker/{ticker}" + f"/range/{multiplier}" + f"/{timespan}" + f"/{from_date}" + f"/{to_date}" + f"?apiKey={self.polygon_api_key}" + f"&adjusted={adjusted}" + f"&sort={sort}" + ) + response = requests.get(url) + data = response.json() + + status = data.get("status", None) + if status != "OK": + raise ValueError(f"API Error: {data}") + + return data.get("results", None) + + def run(self, mode: str, ticker: str, **kwargs: Any) -> str: if mode == "get_financials": return json.dumps(self.get_financials(ticker)) elif mode == "get_last_quote": return json.dumps(self.get_last_quote(ticker)) elif mode == "get_ticker_news": return json.dumps(self.get_ticker_news(ticker)) + elif mode == "get_aggregates": + return json.dumps(self.get_aggregates(ticker, **kwargs)) else: raise ValueError(f"Invalid mode {mode} for Polygon API.") diff --git a/libs/community/tests/unit_tests/tools/test_imports.py b/libs/community/tests/unit_tests/tools/test_imports.py index 9f455ba0483..740903dbfc2 100644 --- a/libs/community/tests/unit_tests/tools/test_imports.py +++ b/libs/community/tests/unit_tests/tools/test_imports.py @@ -84,6 +84,7 @@ EXPECTED_ALL = [ "OpenAPISpec", "OpenWeatherMapQueryRun", "PubmedQueryRun", + "PolygonAggregates", "PolygonFinancials", "PolygonLastQuote", "PolygonTickerNews", diff --git a/libs/community/tests/unit_tests/tools/test_public_api.py b/libs/community/tests/unit_tests/tools/test_public_api.py index 18a951b20ae..173e58844fc 100644 --- a/libs/community/tests/unit_tests/tools/test_public_api.py +++ b/libs/community/tests/unit_tests/tools/test_public_api.py @@ -86,6 +86,7 @@ _EXPECTED = [ "OpenAPISpec", "OpenWeatherMapQueryRun", "PubmedQueryRun", + "PolygonAggregates", "PolygonFinancials", "PolygonLastQuote", "PolygonTickerNews",