Skip to content

Commit 6b61437

Browse files
Merge pull request #20 from C00ldudeNoonan/dagster-plus
update dagster_cloud.yaml file
2 parents 49b74f3 + 8d1fbda commit 6b61437

23 files changed

Lines changed: 2670 additions & 1733 deletions
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ locations:
22
- location_name: macro_agents
33
code_source:
44
module_name: macro_agents.definitions
5+
working_directory: ./macro_agents
56
build:
6-
directory: .
7+
directory: ./macro_agents
78

macro_agents/pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ dependencies = [
3131
dev = [
3232
"dagster-webserver",
3333
"pytest",
34+
"pytest-cov",
35+
"pytest-xdist",
36+
"mypy",
3437
"ruff",
3538
]
3639

macro_agents/src/macro_agents/definitions.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
from macro_agents.defs.transformation.dbt import dbt_cli_resource
88
from macro_agents.defs.agents.analysis_agent import EconomicAnalyzer
99
from macro_agents.defs.agents.economic_cycle_analyzer import EconomicCycleAnalyzer
10-
from macro_agents.defs.agents.enhanced_economic_cycle_analyzer import EnhancedEconomicCycleAnalyzer
10+
from macro_agents.defs.agents.enhanced_economic_cycle_analyzer import (
11+
EnhancedEconomicCycleAnalyzer,
12+
)
1113
from macro_agents.defs.agents.asset_allocation_analyzer import AssetAllocationAnalyzer
1214
from macro_agents.defs.agents.dspy_evaluation import FinancialEvaluator, PromptOptimizer
1315
from macro_agents.defs.agents.backtesting import BacktestingEngine

macro_agents/src/macro_agents/defs/agents/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,3 @@
99
from .economic_dashboard import *
1010
from .backtesting_visualization import *
1111
from .model_improvement_pipeline import *
12-

macro_agents/src/macro_agents/defs/agents/analysis_agent.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
import duckdb
2-
import polars as pl
31
import dspy
42
from typing import Optional, Dict, Any, List
5-
import io
6-
import json
73
from datetime import datetime
8-
from pathlib import Path
94
import dagster as dg
105
from pydantic import Field
116

macro_agents/src/macro_agents/defs/agents/asset_allocation_analyzer.py

Lines changed: 63 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import dspy
2-
import polars as pl
3-
from typing import Optional, Dict, Any, List, Tuple
4-
import json
2+
from typing import Optional, Dict, Any, Tuple
53
from datetime import datetime
64
import dagster as dg
75
from pydantic import Field
@@ -12,19 +10,19 @@
1210

1311
class AssetAllocationSignature(dspy.Signature):
1412
"""Generate specific asset allocation recommendations based on economic cycle analysis and market trends."""
15-
13+
1614
economic_cycle_analysis: str = dspy.InputField(
1715
desc="Economic cycle analysis including current cycle position and key indicators"
1816
)
19-
17+
2018
market_trend_analysis: str = dspy.InputField(
2119
desc="Market trend analysis including sector performance and momentum indicators"
2220
)
23-
21+
2422
current_portfolio_context: str = dspy.InputField(
2523
desc="Current portfolio context and constraints (optional)"
2624
)
27-
25+
2826
allocation_recommendations: str = dspy.OutputField(
2927
desc="""Detailed asset allocation recommendations including:
3028
1. Portfolio Allocation by Asset Class:
@@ -60,32 +58,37 @@ class AssetAllocationModule(dspy.Module):
6058
def __init__(self):
6159
super().__init__()
6260
self.analyze_allocation = dspy.ChainOfThought(AssetAllocationSignature)
63-
64-
def forward(self, economic_cycle_analysis: str, market_trend_analysis: str, current_portfolio_context: str = ""):
61+
62+
def forward(
63+
self,
64+
economic_cycle_analysis: str,
65+
market_trend_analysis: str,
66+
current_portfolio_context: str = "",
67+
):
6568
return self.analyze_allocation(
6669
economic_cycle_analysis=economic_cycle_analysis,
6770
market_trend_analysis=market_trend_analysis,
68-
current_portfolio_context=current_portfolio_context
71+
current_portfolio_context=current_portfolio_context,
6972
)
7073

7174

7275
class AssetAllocationAnalyzer(dg.ConfigurableResource):
7376
"""Asset allocation analyzer that provides specific investment recommendations."""
74-
77+
7578
model_name: str = Field(
7679
default="gpt-4-turbo-preview", description="LLM model to use for analysis"
7780
)
7881
openai_api_key: str = Field(description="OpenAI API key for DSPy")
79-
82+
8083
def setup_for_execution(self, context) -> None:
8184
"""Initialize DSPy when the resource is used."""
8285
# Initialize DSPy
8386
lm = dspy.LM(model=self.model_name, api_key=self.openai_api_key)
8487
dspy.settings.configure(lm=lm)
85-
88+
8689
# Initialize analyzer
8790
self._allocation_analyzer = AssetAllocationModule()
88-
91+
8992
@property
9093
def allocation_analyzer(self):
9194
"""Get asset allocation analyzer."""
@@ -102,46 +105,48 @@ def get_latest_analysis(self, md_resource: MotherDuckResource) -> Tuple[str, str
102105
)
103106
ORDER BY analysis_type
104107
"""
105-
108+
106109
df = md_resource.execute_query(query, read_only=True)
107-
110+
108111
cycle_analysis = ""
109112
trend_analysis = ""
110-
113+
111114
for row in df.iter_rows(named=True):
112115
if row["analysis_type"] == "economic_cycle":
113116
cycle_analysis = row["analysis_content"]
114117
elif row["analysis_type"] == "market_trends":
115118
trend_analysis = row["analysis_content"]
116-
119+
117120
return cycle_analysis, trend_analysis
118121

119122
def generate_allocation_recommendations(
120123
self,
121124
md_resource: MotherDuckResource,
122125
portfolio_context: str = "",
123-
context: Optional[dg.AssetExecutionContext] = None
126+
context: Optional[dg.AssetExecutionContext] = None,
124127
) -> Dict[str, Any]:
125128
"""Generate asset allocation recommendations based on latest analysis."""
126129
if context:
127130
context.log.info("Retrieving latest economic and market analysis...")
128-
131+
129132
# Get latest analysis
130133
cycle_analysis, trend_analysis = self.get_latest_analysis(md_resource)
131-
134+
132135
if not cycle_analysis or not trend_analysis:
133-
raise ValueError("No recent economic cycle or market trend analysis found. Please run economic_cycle_analysis first.")
134-
136+
raise ValueError(
137+
"No recent economic cycle or market trend analysis found. Please run economic_cycle_analysis first."
138+
)
139+
135140
if context:
136141
context.log.info("Generating asset allocation recommendations...")
137-
142+
138143
# Generate recommendations
139144
allocation_result = self.allocation_analyzer(
140145
economic_cycle_analysis=cycle_analysis,
141146
market_trend_analysis=trend_analysis,
142-
current_portfolio_context=portfolio_context
147+
current_portfolio_context=portfolio_context,
143148
)
144-
149+
145150
# Format results
146151
analysis_timestamp = datetime.now()
147152
result = {
@@ -151,9 +156,11 @@ def generate_allocation_recommendations(
151156
"model_name": self.model_name,
152157
"allocation_recommendations": allocation_result.allocation_recommendations,
153158
"portfolio_context": portfolio_context,
154-
"source_analysis_timestamp": self._get_latest_analysis_timestamp(md_resource)
159+
"source_analysis_timestamp": self._get_latest_analysis_timestamp(
160+
md_resource
161+
),
155162
}
156-
163+
157164
return result
158165

159166
def _get_latest_analysis_timestamp(self, md_resource: MotherDuckResource) -> str:
@@ -162,14 +169,14 @@ def _get_latest_analysis_timestamp(self, md_resource: MotherDuckResource) -> str
162169
SELECT MAX(analysis_timestamp) as latest_timestamp
163170
FROM economic_cycle_analysis
164171
"""
165-
172+
166173
df = md_resource.execute_query(query, read_only=True)
167174
return df[0, "latest_timestamp"] if not df.is_empty() else ""
168175

169176
def format_allocation_as_json(
170177
self,
171178
allocation_result: Dict[str, Any],
172-
metadata: Optional[Dict[str, Any]] = None
179+
metadata: Optional[Dict[str, Any]] = None,
173180
) -> Dict[str, Any]:
174181
"""Format allocation recommendations as JSON record."""
175182
json_result = {
@@ -180,13 +187,13 @@ def format_allocation_as_json(
180187
"analysis_time": allocation_result["analysis_time"],
181188
"model_name": allocation_result["model_name"],
182189
"portfolio_context": allocation_result["portfolio_context"],
183-
"source_analysis_timestamp": allocation_result["source_analysis_timestamp"]
190+
"source_analysis_timestamp": allocation_result["source_analysis_timestamp"],
184191
}
185-
192+
186193
# Add metadata if provided
187194
if metadata:
188195
json_result.update(metadata)
189-
196+
190197
return json_result
191198

192199
def write_allocation_to_table(
@@ -195,24 +202,24 @@ def write_allocation_to_table(
195202
allocation_result: Dict[str, Any],
196203
output_table: str = "asset_allocation_recommendations",
197204
if_exists: str = "append",
198-
context: Optional[dg.AssetExecutionContext] = None
205+
context: Optional[dg.AssetExecutionContext] = None,
199206
) -> None:
200207
"""Write allocation recommendations to database."""
201208
# Format results as JSON
202209
json_result = self.format_allocation_as_json(
203210
allocation_result,
204211
metadata={
205212
"dagster_run_id": context.run_id if context else None,
206-
"dagster_asset_key": str(context.asset_key) if context else None
207-
}
213+
"dagster_asset_key": str(context.asset_key) if context else None,
214+
},
208215
)
209-
216+
210217
# Write to database
211218
md_resource.write_results_to_table(
212219
[json_result],
213220
output_table=output_table,
214221
if_exists=if_exists,
215-
context=context
222+
context=context,
216223
)
217224

218225

@@ -229,39 +236,39 @@ def asset_allocation_recommendations(
229236
) -> Dict[str, Any]:
230237
"""
231238
Asset that generates specific asset allocation recommendations based on economic analysis.
232-
239+
233240
Returns:
234241
Dictionary with allocation recommendations and metadata
235242
"""
236243
context.log.info("Starting asset allocation analysis...")
237-
244+
238245
# Generate recommendations
239246
allocation_result = allocation_analyzer.generate_allocation_recommendations(
240247
md_resource=md,
241248
portfolio_context="General portfolio - no specific constraints",
242-
context=context
249+
context=context,
243250
)
244-
251+
245252
# Write results to database
246253
context.log.info("Writing allocation recommendations to database...")
247254
allocation_analyzer.write_allocation_to_table(
248255
md_resource=md,
249256
allocation_result=allocation_result,
250257
output_table="asset_allocation_recommendations",
251258
if_exists="append",
252-
context=context
259+
context=context,
253260
)
254-
261+
255262
# Return metadata
256263
result_metadata = {
257264
"analysis_completed": True,
258265
"analysis_timestamp": allocation_result["analysis_timestamp"],
259266
"model_name": allocation_result["model_name"],
260267
"output_table": "asset_allocation_recommendations",
261268
"records_written": 1,
262-
"source_analysis_timestamp": allocation_result["source_analysis_timestamp"]
269+
"source_analysis_timestamp": allocation_result["source_analysis_timestamp"],
263270
}
264-
271+
265272
context.log.info(f"Asset allocation analysis complete: {result_metadata}")
266273
return result_metadata
267274

@@ -279,14 +286,14 @@ def custom_asset_allocation(
279286
) -> Dict[str, Any]:
280287
"""
281288
Asset that generates custom asset allocation recommendations with specific portfolio context.
282-
289+
283290
This asset can be customized with different portfolio contexts by modifying the portfolio_context parameter.
284-
291+
285292
Returns:
286293
Dictionary with custom allocation recommendations and metadata
287294
"""
288295
context.log.info("Starting custom asset allocation analysis...")
289-
296+
290297
# Define custom portfolio context
291298
custom_context = """
292299
Portfolio Context:
@@ -298,24 +305,22 @@ def custom_asset_allocation(
298305
- Liquidity needs: Moderate (some funds may be needed within 2-3 years)
299306
- Tax considerations: Taxable account, prefer tax-efficient strategies
300307
"""
301-
308+
302309
# Generate custom recommendations
303310
allocation_result = allocation_analyzer.generate_allocation_recommendations(
304-
md_resource=md,
305-
portfolio_context=custom_context,
306-
context=context
311+
md_resource=md, portfolio_context=custom_context, context=context
307312
)
308-
313+
309314
# Write results to database
310315
context.log.info("Writing custom allocation recommendations to database...")
311316
allocation_analyzer.write_allocation_to_table(
312317
md_resource=md,
313318
allocation_result=allocation_result,
314319
output_table="custom_asset_allocation_recommendations",
315320
if_exists="append",
316-
context=context
321+
context=context,
317322
)
318-
323+
319324
# Return metadata
320325
result_metadata = {
321326
"analysis_completed": True,
@@ -324,8 +329,8 @@ def custom_asset_allocation(
324329
"output_table": "custom_asset_allocation_recommendations",
325330
"records_written": 1,
326331
"portfolio_context": "Conservative retirement-focused",
327-
"source_analysis_timestamp": allocation_result["source_analysis_timestamp"]
332+
"source_analysis_timestamp": allocation_result["source_analysis_timestamp"],
328333
}
329-
334+
330335
context.log.info(f"Custom asset allocation analysis complete: {result_metadata}")
331336
return result_metadata

0 commit comments

Comments
 (0)