Overview
DebtStack provides a native LangChain toolkit with 7 tools covering all credit data primitives. Build agents that can autonomously search companies, analyze bonds, traverse corporate structures, and research SEC filings.Installation
Copy
pip install debtstack-ai[langchain] langchain-openai
Quick Start
The SDK includes a ready-to-useDebtStackToolkit — no manual tool definitions needed:
Copy
from debtstack.langchain import DebtStackToolkit
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain_openai import ChatOpenAI
from langchain import hub
# Initialize toolkit with your API key
toolkit = DebtStackToolkit(api_key="ds_xxxxx")
tools = toolkit.get_tools()
# Create an agent with GPT-4 (or any LangChain-compatible LLM)
llm = ChatOpenAI(temperature=0, model="gpt-4")
prompt = hub.pull("hwchase17/openai-functions-agent")
agent = create_openai_functions_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# Ask natural language questions about corporate credit
result = agent_executor.invoke({
"input": "Which MAG7 company has the highest leverage?"
})
print(result["output"])
Available Tools
The toolkit provides 7 tools covering all DebtStack API primitives:| Tool | Description |
|---|---|
debtstack_search_companies | Screen companies by leverage, sector, coverage ratios, and risk flags |
debtstack_search_bonds | Filter bonds by yield, spread, seniority, maturity, and pricing |
debtstack_resolve_bond | Look up bonds by CUSIP, ISIN, or description (e.g., “RIG 8% 2027”) |
debtstack_traverse_entities | Follow guarantor chains, map corporate structure, trace ownership |
debtstack_search_pricing | Get FINRA TRACE bond prices, YTM, and spreads |
debtstack_search_documents | Full-text search across credit agreements, indentures, and SEC filings |
debtstack_get_changes | Track debt structure changes over time (new issuances, maturities, leverage) |
Example Queries
The agent can handle complex, multi-step credit analysis:Copy
# Leverage comparison
result = agent_executor.invoke({
"input": "Compare the leverage of RIG, VAL, and DO"
})
# High-yield screening
result = agent_executor.invoke({
"input": "Find bonds yielding more than 9% with senior secured status"
})
# Structural analysis
result = agent_executor.invoke({
"input": "Who guarantees Transocean's 8.75% 2030 notes? How many entities are in the chain?"
})
# Covenant research
result = agent_executor.invoke({
"input": "Search for maintenance covenant language in Charter's credit agreements"
})
# Change tracking
result = agent_executor.invoke({
"input": "What changed in RIG's debt structure since January 2025?"
})
# Corporate structure comparison
result = agent_executor.invoke({
"input": "Compare Charter and Altice's corporate structures - which has more structural subordination risk?"
})
# Distressed bond hunting
result = agent_executor.invoke({
"input": "Find distressed bonds trading below 80 cents on the dollar"
})
Custom Tool Definitions
If you prefer to define tools manually instead of using the SDK toolkit, here’s how to build them withStructuredTool:
Copy
from langchain.tools import StructuredTool
from pydantic import BaseModel, Field
import requests
API_KEY = "ds_xxxxx"
HEADERS = {"X-API-Key": API_KEY}
BASE_URL = "https://api.debtstack.ai/v1"
# === Schemas ===
class SearchCompaniesInput(BaseModel):
ticker: str = Field(default=None, description="Comma-separated tickers (e.g., 'AAPL,MSFT')")
sector: str = Field(default=None, description="Business sector (e.g., 'Technology')")
min_leverage: float = Field(default=None, description="Minimum leverage ratio")
max_leverage: float = Field(default=None, description="Maximum leverage ratio")
has_structural_sub: bool = Field(default=None, description="Filter for structural subordination")
fields: str = Field(default="ticker,name,sector,net_leverage_ratio,total_debt")
class SearchBondsInput(BaseModel):
ticker: str = Field(default=None, description="Company ticker")
min_ytm: float = Field(default=None, description="Minimum YTM (%)")
seniority: str = Field(default=None, description="senior_secured, senior_unsecured, subordinated")
maturity_before: str = Field(default=None, description="Maturity before date (YYYY-MM-DD)")
fields: str = Field(default="name,cusip,company_ticker,coupon_rate,maturity_date,seniority,pricing")
class ResolveBondInput(BaseModel):
query: str = Field(description="Bond description (e.g., 'RIG 8% 2027') or CUSIP")
class TraverseEntitiesInput(BaseModel):
start_type: str = Field(description="Start node type: 'bond', 'company', or 'entity'")
start_id: str = Field(description="Start node ID: CUSIP, ticker, or entity UUID")
relationships: str = Field(default="guarantees", description="Relationship type: guarantees, subsidiaries, parent")
direction: str = Field(default="inbound", description="Traversal direction: inbound, outbound, both")
class SearchPricingInput(BaseModel):
ticker: str = Field(default=None, description="Company ticker")
cusip: str = Field(default=None, description="Bond CUSIP")
min_ytm: float = Field(default=None, description="Minimum YTM (%)")
class SearchDocumentsInput(BaseModel):
query: str = Field(description="Search terms (e.g., 'leverage covenant')")
ticker: str = Field(default=None, description="Company ticker to filter by")
section_type: str = Field(default=None, description="debt_footnote, credit_agreement, indenture, covenants, mda_liquidity")
class GetChangesInput(BaseModel):
ticker: str = Field(description="Company ticker")
since: str = Field(description="Date to compare from (YYYY-MM-DD)")
# === Functions ===
def search_companies(**kwargs):
params = {k: v for k, v in kwargs.items() if v is not None}
return requests.get(f"{BASE_URL}/companies", params=params, headers=HEADERS).json()["data"]
def search_bonds(**kwargs):
params = {k: v for k, v in kwargs.items() if v is not None}
if params.get("min_ytm"):
params["has_pricing"] = True
return requests.get(f"{BASE_URL}/bonds", params=params, headers=HEADERS).json()["data"]
def resolve_bond(query: str):
matches = requests.get(f"{BASE_URL}/bonds/resolve", params={"q": query}, headers=HEADERS).json()["data"]["matches"]
return matches[0]["bond"] if matches else {"error": "No match found"}
def traverse_entities(start_type: str, start_id: str, relationships: str = "guarantees", direction: str = "inbound"):
return requests.post(f"{BASE_URL}/entities/traverse", headers={**HEADERS, "Content-Type": "application/json"},
json={"start": {"type": start_type, "id": start_id}, "relationships": [relationships], "direction": direction}
).json()["data"]
def search_pricing(**kwargs):
params = {k: v for k, v in kwargs.items() if v is not None}
params["has_pricing"] = True
return requests.get(f"{BASE_URL}/bonds", params=params, headers=HEADERS).json()["data"]
def search_documents(query: str, ticker: str = None, section_type: str = None):
params = {"q": query}
if ticker: params["ticker"] = ticker
if section_type: params["section_type"] = section_type
return requests.get(f"{BASE_URL}/documents/search", params=params, headers=HEADERS).json()["data"]
def get_changes(ticker: str, since: str):
return requests.get(f"{BASE_URL}/companies/{ticker}/changes", params={"since": since}, headers=HEADERS).json()["data"]
# === Create Tools ===
debtstack_tools = [
StructuredTool.from_function(func=search_companies, name="debtstack_search_companies",
description="Search companies by leverage, sector, debt metrics. Use for company screening and comparisons.",
args_schema=SearchCompaniesInput),
StructuredTool.from_function(func=search_bonds, name="debtstack_search_bonds",
description="Search bonds by yield, seniority, maturity. Use for bond screening and yield hunting.",
args_schema=SearchBondsInput),
StructuredTool.from_function(func=resolve_bond, name="debtstack_resolve_bond",
description="Convert bond description to CUSIP and details. Use when user mentions a bond by name.",
args_schema=ResolveBondInput),
StructuredTool.from_function(func=traverse_entities, name="debtstack_traverse_entities",
description="Follow guarantor chains and map corporate structure. Use for structural subordination analysis.",
args_schema=TraverseEntitiesInput),
StructuredTool.from_function(func=search_pricing, name="debtstack_search_pricing",
description="Get FINRA TRACE bond prices, YTM, and spreads. Use for relative value and distressed analysis.",
args_schema=SearchPricingInput),
StructuredTool.from_function(func=search_documents, name="debtstack_search_documents",
description="Search SEC filings for covenant language, credit agreement terms, debt descriptions.",
args_schema=SearchDocumentsInput),
StructuredTool.from_function(func=get_changes, name="debtstack_get_changes",
description="Track changes in debt structure since a date. Use for monitoring issuances and maturities.",
args_schema=GetChangesInput),
]
LangChain Expression Language (LCEL)
Copy
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
from debtstack.langchain import DebtStackToolkit
toolkit = DebtStackToolkit(api_key="ds_xxxxx")
tools = toolkit.get_tools()
prompt = ChatPromptTemplate.from_messages([
("system", """You are a credit analyst assistant with access to DebtStack tools.
When analyzing companies, always consider:
1. Leverage levels and trends
2. Debt structure (secured vs unsecured)
3. Maturity profile
4. Structural subordination risk"""),
("human", "{question}")
])
llm = ChatOpenAI(model="gpt-4").bind_tools(tools)
chain = prompt | llm | StrOutputParser()

