Skip to content

Commit e2f65b9

Browse files
committed
Updated adr to make it more atomic. Implemented ADR specialist aI agent
1 parent df75f25 commit e2f65b9

9 files changed

+201
-91
lines changed

dev_tools/adr_agent.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import os
2+
import yaml
3+
from typing import List, Dict
4+
from langchain_community.vectorstores import Chroma
5+
from langchain_ollama import OllamaEmbeddings, ChatOllama
6+
from langchain.chains import RetrievalQA
7+
from langchain.schema import Document
8+
9+
class ADRAgent:
10+
def __init__(self, adr_directory: str, model_name: str = "mistral"):
11+
"""
12+
Initializes the ADR Agent by loading all ADR files from the given directory.
13+
"""
14+
self.adr_directory = adr_directory
15+
self.model_name = model_name
16+
self.adr_vectorstore = self.index_adrs()
17+
self.llm_retrieval = ChatOllama(model=self.model_name) # AI model for processing ADRs
18+
self.design_qa = RetrievalQA.from_chain_type(self.llm_retrieval, retriever=self.adr_vectorstore.as_retriever())
19+
20+
def load_adrs(self) -> List[Document]:
21+
"""
22+
Loads all ADRs from the specified directory, parses them, and converts them into Documents.
23+
"""
24+
documents = []
25+
for file in os.listdir(self.adr_directory):
26+
if file.endswith(".yaml") and file.startswith("adr-"):
27+
file_path = os.path.join(self.adr_directory, file)
28+
with open(file_path, "r", encoding="utf-8") as f:
29+
adr_data = yaml.safe_load(f)
30+
31+
adr_text = f"{adr_data['title']}\n\n{adr_data['context']}\n\n{adr_data['decision']}"
32+
metadata = {
33+
"adr_id": adr_data["adr_id"],
34+
"title": adr_data["title"],
35+
"filename": file_path
36+
}
37+
documents.append(Document(page_content=adr_text, metadata=metadata))
38+
return documents
39+
40+
def index_adrs(self):
41+
"""
42+
Indexes ADRs in ChromaDB for semantic search.
43+
"""
44+
adr_documents = self.load_adrs()
45+
embedding_model = OllamaEmbeddings(model=self.model_name) # Use specified embedding model
46+
return Chroma.from_documents(adr_documents, embedding=embedding_model)
47+
48+
def retrieve_relevant_adrs(self, feature_request: str, k=3) -> List[Document]:
49+
"""
50+
Fetches the most relevant ADRs using vector similarity search.
51+
"""
52+
retriever = self.adr_vectorstore.as_retriever(search_kwargs={"k": k})
53+
results = retriever.invoke(feature_request)
54+
return results["documents"]
55+
56+
def generate_high_level_steps(self, feature_request: str) -> List[str]:
57+
"""
58+
Uses AI-powered RetrievalQA to generate structured high-level implementation steps.
59+
"""
60+
prompt = f"""
61+
You are an AI agent helping a developer implement a feature request in Hedgehog2.jl, a Julia derivatives pricing library.
62+
Your task is to retrieve relevant design decisions (ADRs) and provide structured implementation steps.
63+
64+
Feature Request: {feature_request}
65+
66+
Respond with clear, step-by-step guidance based on existing design decisions.
67+
"""
68+
response = self.design_qa.invoke(prompt)
69+
return response["result"]
70+
71+
# Main execution
72+
if __name__ == "__main__":
73+
adr_agent = ADRAgent("./docs/adr", model_name="mistral") # Replace with your ADR directory path and preferred model
74+
feature_request = "Implement put options pricing using Black-Scholes analytical formulas"
75+
steps = adr_agent.generate_high_level_steps(feature_request)
76+
77+
print(steps)

docs/adr/adr-001-payoff.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
adr_id: 001
2+
title: "Payoff Object Design"
3+
status: Accepted
4+
date: 2025-03-11
5+
context: |
6+
The pricing framework must support multiple payoffs while maintaining type safety and modularity.
7+
A `payoff` should represent the contract terms and intrinsic valuation function independently of market inputs or pricing models.
8+
9+
decision: |
10+
- Define a `payoff` type that subtypes `AbstractPayoff`.
11+
- Each payoff should encapsulate contract details (e.g., strike, expiry) and provide an intrinsic value function.
12+
- Payoff objects should not contain market assumptions or pricing logic.
13+
14+
consequences:
15+
positive:
16+
- "Encapsulates contract-specific logic separately from pricing and market assumptions."
17+
- "Allows defining new payoffs without modifying existing pricing structures."
18+
negative:
19+
- "Requires defining explicit struct types for each new payoff format."
20+
21+
alternatives:
22+
- name: "Store pricing logic inside the `AbstractPayoff` subtypes"
23+
pros: "Simplifies structure since each payoff knows how to price itself."
24+
cons: "Reduces flexibility—pricing depends on the payoff rather than being modular."
25+
26+
references:

docs/adr/adr-001-structuring-pricing.yaml

Lines changed: 0 additions & 44 deletions
This file was deleted.

docs/adr/adr-002-market-inputs.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
adr_id: 002
2+
title: "Market Inputs Structure"
3+
status: Accepted
4+
date: 2025-03-11
5+
context: |
6+
Market data, such as volatility surfaces and discount curves, must be structured separately from pricing methods.
7+
`marketInputs` should represent external market conditions relevant for pricing.
8+
9+
decision: |
10+
- Define a `marketInputs` type that subtypes `AbstractMarketInputs`.
11+
- This struct should contain market parameters (e.g., spot price, volatility, interest rate) but not pricing logic.
12+
- Market inputs should be designed for easy extension to support additional factors.
13+
14+
consequences:
15+
positive:
16+
- "Keeps market data independent of pricing models, allowing flexible combinations."
17+
- "New market models can be added without modifying existing pricing methods."
18+
negative:
19+
- "Requires explicitly defining each market input struct."
20+
21+
alternatives:
22+
- name: "Pass market data as separate function arguments"
23+
pros: "Avoids struct overhead and keeps the API simple."
24+
cons: "Less structured and harder to extend for new market conditions."
25+
26+
references:

docs/adr/adr-002-pricer-callable-struct.yaml

Lines changed: 0 additions & 42 deletions
This file was deleted.

docs/adr/adr-003-pricing-methods.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
adr_id: 003
2+
title: "Pricing Method Structure"
3+
status: Accepted
4+
date: 2025-03-11
5+
context: |
6+
Pricing models require specific numerical techniques and assumptions. To maintain modularity, these methods should be encapsulated in separate objects.
7+
8+
decision: |
9+
- Define a `pricingMethod` type that subtypes `AbstractPricingMethod`.
10+
- This struct should contain configuration details for the pricing technique (e.g., finite difference grid size, Monte Carlo paths) but should not include the pricing algorithm itself.
11+
- Pricing functions should be implemented separately and dispatched based on `(payoff, marketInputs, pricingMethod)`.
12+
13+
consequences:
14+
positive:
15+
- "Allows pricing techniques to be swapped independently of payoffs and market inputs."
16+
- "Enables efficient method specialization using Julia's multiple dispatch."
17+
negative:
18+
- "Requires defining a struct for each pricing method instead of using a simple function call."
19+
20+
alternatives:
21+
- name: "Embed pricing logic inside market inputs or payoffs"
22+
pros: "Removes the need for separate pricing method objects."
23+
cons: "Mixes concerns and reduces flexibility when changing pricing approaches."
24+
25+
references:
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
adr_id: 004
2+
title: "Pricing Method Dispatch via Pricer{A, B, C}"
3+
status: Accepted
4+
date: 2025-03-11
5+
context: |
6+
The Hedgehog2.jl pricing framework must support modular pricing techniques that integrate with multiple payoffs, market data configurations, and pricing methodologies.
7+
To ensure flexibility and extensibility, pricing calculations must be structured using Julia’s multiple dispatch based on the combination of a `payoff`, `marketInputs`, and `pricingMethod`.
8+
9+
decision: |
10+
- Every new pricing method must be implemented by defining a new multiple dispatch method of `Pricer{A, B, C}`.
11+
- `Pricer` should not contain a universal pricing function—each `(payoff, marketInputs, pricingMethod)` combination must define its own dispatch specialization.
12+
- The pricing logic should **never** be embedded inside `pricingMethod` or `payoff` structs.
13+
- Example implementation:
14+
```julia
15+
function (pricer::Pricer{VanillaEuropeanCall, BlackScholesInputs, BlackScholesMethod})()
16+
S, K, r, σ, T = pricer.marketInputs.spot, pricer.payoff.strike, pricer.marketInputs.rate, pricer.marketInputs.sigma, pricer.payoff.time
17+
d1 = (log(S / K) + (r + 0.5 * σ^2) * T) / (σ * sqrt(T))
18+
d2 = d1 - σ * sqrt(T)
19+
return S * cdf(Normal(), d1) - K * exp(-r * T) * cdf(Normal(), d2)
20+
end
21+
```
22+
23+
consequences:
24+
positive:
25+
- "Ensures modularity: Each pricing method can be added separately using Julia’s multiple dispatch."
26+
- "Explicitly defines how new pricing methods must be structured."
27+
- "Avoids incorrect designs where pricing logic is inside structs."
28+
negative:
29+
- "Requires defining a `Pricer{A, B, C}` method explicitly for each combination of payoff, market inputs, and pricing method."
30+
- "Less flexible than embedding pricing in the struct, but ensures modularity."
31+
32+
alternatives:
33+
- name: "Store pricing logic inside the `AbstractPricingMethod` subtype"
34+
pros: "Simplifies structure since each pricing method contains its logic."
35+
cons: "Breaks modularity—pricing logic would be tied to the struct rather than using multiple dispatch."
36+
37+
references:
38+
- adr-001-payoff.yaml
39+
- adr-002-market-inputs.yaml
40+
- adr-003-pricing-methods.yaml

docs/adr/adr-003-greeks-calculation-design.yaml renamed to docs/adr/adr-005-greeks-calculation-design.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: "Greeks Calculation Design"
33
status: Accepted
44
date: 2025-03-11
55
context: |
6-
In ADR-002, we introduced `Pricer{P, M, S}` as a callable struct that computes a derivative’s price
6+
In ADR-004, we introduced `Pricer{P, M, S}` as a callable struct that computes a derivative’s price
77
using a combination of:
88
- A `payoff` that subtypes `AbstractPayoff`
99
- Market data (`marketInputs`) that subtypes `AbstractMarketInputs`
@@ -51,4 +51,4 @@ alternatives:
5151
cons: "Less modular, Greeks logic is scattered across functions."
5252

5353
references:
54-
- adr-002-pricer-callable-struct.yaml
54+
- adr-004-pricer-callable-struct.yaml

docs/adr/index.yaml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
adr_index:
2-
- "adr-001-structuring-pricing.yaml"
3-
- "adr-002-pricer-callable-struct.yaml"
4-
- "adr-003-greeks-calculation-design.yaml"
2+
- "adr-001-payoff.yaml"
3+
- "adr-002-market-inputs.yaml"
4+
- "adr-003-pricing-methods.yaml"
5+
- "adr-004-pricer-callable-struct.yaml"
6+
- "adr-005-greeks-calculation-design.yaml"

0 commit comments

Comments
 (0)