Skip to content

Commit 5965df3

Browse files
committed
More updates to agentic framework
1 parent 2cdfd9c commit 5965df3

9 files changed

Lines changed: 209 additions & 28 deletions

File tree

brain/agentic.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,30 @@
1616
MAX_EXPLORATION_COUNT = 3
1717

1818

19+
def plateau_condition(state: GraphState) -> bool:
20+
"""Check if the exploration has plateaued."""
21+
storage = state["storage"]
22+
best_alpha = storage.best_alpha
23+
old_best_alpha = state.get("old_best_alpha")
24+
25+
if best_alpha is None or old_best_alpha is None:
26+
return False
27+
28+
# Check if the score has not improved significantly
29+
score_diff = (
30+
best_alpha.fitness - old_best_alpha.fitness + best_alpha.sharpe - old_best_alpha.sharpe
31+
)
32+
return score_diff < 0.1
33+
34+
1935
def seed_finder_node(state: GraphState) -> GraphState:
2036
"""Find a seed alpha to start the exploration."""
2137
# Iterate databse till we find some decent alpha, or some other seed idea
22-
alpha = invoke_seeder(state)
23-
print(f"Seed alpha: {alpha.prompt_format()}")
38+
alpha_idea, config = invoke_seeder(state)
39+
print(f"Seed alpha: {alpha_idea}")
2440
return {
25-
"alpha_idea": alpha.regular,
26-
"default_config": get_config(alpha),
41+
"alpha_idea": alpha_idea,
42+
"default_config": config,
2743
"node": "plan",
2844
"state": "explore",
2945
"explore_count": 0,
@@ -35,10 +51,11 @@ def seed_finder_node(state: GraphState) -> GraphState:
3551
def planner_node(state: GraphState) -> GraphState:
3652
"""Plan the next steps based on the current alpha and state."""
3753
state["explore_count"] += 1
54+
state["old_best_alpha"] = state["storage"].best_alpha
3855

3956
plan = invoke_planner(state)
4057
if not plan:
41-
return {**state, "node": "seed", "state": "explore"}
58+
return {**state, "node": "seed"}
4259
return {**state, "node": "execute", "plan": plan}
4360

4461

@@ -70,7 +87,7 @@ def explore_test_node(state: GraphState) -> GraphState:
7087
}
7188

7289
# TODO: Test plateau condition, compare previous best, with current best alpha
73-
if state["explore_count"] < MAX_EXPLORATION_COUNT:
90+
if state["explore_count"] < MAX_EXPLORATION_COUNT and plateau_condition(state):
7491
return {**state, "node": "plan", "state": "explore"}
7592

7693
return {**state, "node": "seed", "state": "explore"}
@@ -89,7 +106,7 @@ def submit_test_node(state: GraphState) -> GraphState:
89106
return {**state, "node": "seed", "state": "explore", "explore_count": 0}
90107

91108
# TODO: Test plateau condition, compare previous best, with current best alpha
92-
if state["explore_count"] < MAX_EXPLORATION_COUNT:
109+
if state["explore_count"] < MAX_EXPLORATION_COUNT and plateau_condition(state):
93110
return {**state, "node": "plan", "state": "fine-tune"}
94111

95112
return {**state, "node": "seed", "state": "explore"}

brain/agents/executor.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from brain.tools.operators import describe_operators, operators
1919
from brain.tools.simulation import StopException, submit_alpha
2020

21-
STEPS_PER_INSTRUCTION = 4
21+
STEPS_PER_INSTRUCTION = 2
2222

2323

2424
def prompt(
@@ -70,10 +70,11 @@ class CustomState(AgentState):
7070
)
7171

7272

73-
def create_alpha_simulation(
74-
storage: Storage, plan: list[str], config: dict = DEFAULT_CONFIG
75-
) -> tuple[Response, Alpha]:
73+
def create_alpha_simulation(storage: Storage, state: GraphState) -> tuple[Response, Alpha]:
7674
"""Create a new alpha based on the given ID."""
75+
plan = state.get("plan", [])
76+
config = state.get("default_config", DEFAULT_CONFIG)
77+
7778
top_k = 5
7879

7980
alphas = {
@@ -93,6 +94,9 @@ def create_alpha_simulation(
9394
Create a new alpha based on the best alpha according to the following instructions:
9495
{plan[(storage.counter // STEPS_PER_INSTRUCTION) % len(plan)]}
9596
97+
ALPHA IDEA
98+
----------
99+
{state.get("alpha_idea", "None")}
96100
97101
BEST ALPHA
98102
----------
@@ -126,7 +130,7 @@ def create_alpha_simulation(
126130
]
127131
},
128132
config={
129-
"recursion_limit": 50,
133+
"recursion_limit": 30,
130134
"configurable": {
131135
**config,
132136
"alphas": alphas_store,
@@ -158,7 +162,7 @@ def invoke(state: GraphState) -> GraphState:
158162
config = get_config(storage.best_alpha)
159163
else:
160164
config = state.get("default_config", DEFAULT_CONFIG)
161-
create_alpha = partial(create_alpha_simulation, plan=plan, config=config)
165+
create_alpha = partial(create_alpha_simulation, state=state)
162166

163167
storage.reset_counter()
164168

brain/agents/planner.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
from brain.graph_state import GraphState
55
from brain.model import MODEL
6+
from brain.tools.datafields import get_random_datafields, search_datafields
7+
from brain.tools.ideas import get_random_idea
68
from brain.tools.operators import operators
79

810

@@ -14,24 +16,25 @@ class ResponseFormat(BaseModel):
1416

1517
agent = create_react_agent(
1618
model=MODEL,
17-
tools=[],
19+
tools=[get_random_idea, search_datafields, get_random_datafields],
1820
response_format=ResponseFormat,
1921
prompt=f"""
2022
You are a senior quantitative researcher working with the World Quant Brain platform to create alphas. Based on an initial alpha idea provided (either in written form or as an expression), outline specific steps to implement concrete changes to the alpha using the Fast Expression Language, which includes various operators.
2123
2224
Allowed operators are: {operators.all()}
2325
24-
1. Analyze the initial alpha idea to identify key components such as variables, conditions, and expected outcomes.
25-
2. Propose specific changes to the alpha expression, detailing how each change can enhance its performance. For example:
26+
1. Execute the original alpha to obtain a baseline performance.
27+
2. Analyze the initial alpha idea to identify key components such as variables, conditions, and expected outcomes.
28+
3. Propose specific changes to the alpha expression, detailing how each change can enhance its performance. For example:
2629
<example>
2730
- Change the parameter value of 'X' from 0.5 to 0.7 to test sensitivity.
2831
- Introduce a new operator, such as 'rank', to refine the conditions under which trades are executed.
2932
- Suggest using a different data field, like 'volume', instead of 'price' to capture market dynamics more accurately.
3033
- Suggest type of data field to search for, e.g. 'momentum', 'trend', 'news' etc.
3134
- Add a group neutralization to the alpha expression to reduce sector bias.
3235
</example>
33-
3. For each proposed change, provide a rationale explaining why it may improve the alpha's effectiveness.
34-
4. Ensure that each change is concrete and directly related to the alpha expression, focusing on parameter adjustments, operator modifications, or data field substitutions.
36+
4. For each proposed change, provide a rationale explaining why it may improve the alpha's effectiveness.
37+
5. Ensure that each change is concrete and directly related to the alpha expression, focusing on parameter adjustments, operator modifications, or data field substitutions.
3538
3639
List only the steps related to changing the alpha expression. Do NOT include steps such as evaluation, backtesting, documentation, or communication.
3740
@@ -49,7 +52,7 @@ def invoke(state: GraphState) -> ResponseFormat:
4952
"You are exploring a new alphas based on the new idea.\n"
5053
f"{state.get('alpha_idea', '')}\n"
5154
f"Best alpha so far:\n {best_alpha.prompt_format() if best_alpha else 'None'}\n"
52-
"Create a plan to explore this idea, start with simple alpha expression.\n"
55+
"Create a plan to explore this idea.\n"
5356
)
5457
elif state.get("state", "explore") == "fine_tune" and best_alpha is not None:
5558
message = (

brain/agents/seeder.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
1+
import json
12
import random
3+
from pathlib import Path
24

5+
from brain.agent_config import get_config
36
from brain.alpha_class import Alpha
47
from brain.database import Database
58
from brain.graph_state import GraphState
69

10+
TEMPLATES = json.load((Path(__file__).parent.parent / "tools/data/alpha_templates.json").open())
711

8-
def invoke(state: GraphState) -> Alpha:
12+
13+
def invoke(state: GraphState) -> tuple[str, dict]:
914
"""Invoke seeder agent to generate alphas."""
10-
page = random.randint(1, 100)
15+
if random.random() < 0.3:
16+
template = random.choice(TEMPLATES)["template"]
17+
# TODO: Add random config
18+
return (
19+
f"Use following template, fix syntax and try replacing different datafields\n```\n{template}\n```",
20+
{},
21+
)
22+
23+
page = 0
1124
while True:
1225
database = Database()
1326
alphas = database.get_alphas_page(page)
@@ -17,7 +30,10 @@ def invoke(state: GraphState) -> Alpha:
1730
corr = alpha.update_self_correlation()
1831
database.upsert_alpha(alpha)
1932

20-
if score > 0 and corr < 0.5:
21-
return alpha
33+
if score > 0 and corr < 0.7 and alpha.sharpe > 1:
34+
print(
35+
f"Found alpha: {alpha.regular} with score {score} and self-correlation {corr}"
36+
)
37+
return f"Use following alpha\n```\n{alpha.regular}\n```", get_config(alpha)
2238

2339
page += 1

brain/agents/tester.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
def invoke(state: GraphState) -> GraphState:
11-
"""Invoke fine-tuning agent."""
11+
"""Invoke tester agent, returns False if any test fails."""
1212
storage = state.get("storage")
1313

1414
best_alpha = storage.best_alpha
@@ -31,8 +31,8 @@ def invoke(state: GraphState) -> GraphState:
3131

3232
if (
3333
alpha.fitness < best_alpha.fitness * 0.5
34-
and alpha.sharpe < best_alpha.sharpe * 0.5
35-
and alpha.long_count + alpha.short_count < 400
34+
or alpha.sharpe < best_alpha.sharpe * 0.5
35+
or alpha.long_count + alpha.short_count < 400
3636
):
3737
return False
3838

brain/database.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,10 @@ def get_alphas_page(
8686
FROM alphas
8787
WHERE self_correlation < %s
8888
AND (score IS NULL OR score > %s)
89+
AND sharpe > 1.0
90+
AND fitness > 0.9
8991
AND (long_count IS NULL OR long_count > 100)
90-
ORDER BY sharpe DESC, fitness DESC
92+
ORDER BY score DESC NULLS LAST, sharpe DESC, fitness DESC
9193
LIMIT %s
9294
OFFSET %s
9395
"""

brain/graph_state.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from typing import TypedDict
22

3+
from brain.alpha_class import Alpha
34
from brain.alpha_storage import Storage
45

56

67
class GraphState(TypedDict, total=False):
78
alpha_idea: str
89
storage: Storage
10+
old_best_alpha: Alpha | None
911
# Number of times the alpha idea has been explored
1012
explore_count: int
1113
# Static fine-tuning

0 commit comments

Comments
 (0)