Skip to content

Commit aae2655

Browse files
committed
WIP: New architecture - multi-simulation
1 parent ea9d15c commit aae2655

4 files changed

Lines changed: 186 additions & 22 deletions

File tree

brain/agent.py

Lines changed: 142 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1+
from concurrent.futures import ThreadPoolExecutor, as_completed
2+
13
from langchain_core.messages import AnyMessage
4+
from langchain_core.messages.utils import count_tokens_approximately
25
from langchain_core.runnables import RunnableConfig
6+
from langgraph.checkpoint.memory import InMemorySaver
37
from langgraph.prebuilt import create_react_agent
48
from langgraph.prebuilt.chat_agent_executor import AgentState
9+
from langmem.short_term import SummarizationNode
510

6-
from brain.agent_config import get_universe_config
11+
from brain.agent_config import DEFAULT_CONFIG, get_universe_config
12+
from brain.api import DEFAULT_CONFIG as API_DEFAULT_CONFIG
13+
from brain.api import BrainAPI
714
from brain.model import MODEL
815
from brain.tools.datafields import get_random_datafields, search_datafields
916
from brain.tools.operators import describe_operators, operators
10-
from brain.tools.simulation import simulate_alpha
17+
from brain.tools.simulation import submit_alpha
18+
19+
MAX_WORKERS = 3
20+
21+
from langgraph.graph import END
22+
from langgraph.types import Command
1123

1224

1325
def prompt(
@@ -19,11 +31,24 @@ def prompt(
1931
# - store best alphas so far
2032
conf = get_universe_config(config)
2133

34+
if state.get("end", False):
35+
state["is_last_step"] = True
36+
return "END (do not continue)"
37+
2238
prompt = f"""
23-
You are a quant researcher using World Quant Brain platform.
24-
You trying to find best alpha which passes all the checks.
25-
You can simulate alpha expressions and get results.
26-
Return alpha passing all the checks.
39+
You are a quant researcher using World Quant Brain platform. Based on provided strategy, create
40+
a new alpha.
41+
42+
Steps:
43+
1. Propose the alpha and run the alpha simulation.
44+
2. Check the resulting metrics of alpha (try to maximize fitness)
45+
3. Repeat the process until the alpha is satisfactory (passing all the checks).
46+
47+
- Try applying different functions and changing parameter values.
48+
- Do NOT nest functions unless necessary, create simple alphas that are easy to understand and explain using basic operations like +, -, *, /.
49+
- Use only one operator in the alpha expression.
50+
- Try combining multiple data fields to create a new alpha.
51+
- Change alpha completely if you think it is not good enough.
2752
2853
Working with equity in universe: {conf['universe']}, region: {conf['region']}
2954
and delay {conf['delay']}.
@@ -34,8 +59,118 @@ def prompt(
3459
return [{"role": "system", "content": prompt}] + state["messages"]
3560

3661

62+
summarization_node = SummarizationNode(
63+
token_counter=count_tokens_approximately,
64+
model=MODEL,
65+
max_tokens=2000,
66+
max_summary_tokens=1000,
67+
output_messages_key="llm_input_messages",
68+
)
69+
70+
71+
class CustomState(AgentState):
72+
end: bool = False
73+
74+
3775
agent = create_react_agent(
3876
model=MODEL,
39-
tools=[simulate_alpha, describe_operators, search_datafields, get_random_datafields],
77+
tools=[submit_alpha, describe_operators, search_datafields, get_random_datafields],
4078
prompt=prompt,
79+
state_schema=CustomState,
4180
)
81+
82+
83+
def create_alpha_simulation(alphas_dict, alphas_categories):
84+
"""Create a new alpha based on the given ID."""
85+
alphas = []
86+
agent.invoke(
87+
{
88+
"messages": [
89+
{
90+
"role": "user",
91+
"content": """
92+
Your task is to create a new alpha. Below is possible suggestion for the alpha.
93+
94+
/*
95+
HYPOTHESIS: "Fair value accounting" aims to measure and report on-going valuations of assets and liabilities. A recent increase in the fair value of liabilities may indicate a higher cost to be borne by the company in the future. This may deteriorate the company's financial health, potentially leading to lower profitability or financial distress.
96+
97+
IMPLEMENTATION: Using ts_rank() operator, sell stocks when there is an increase in the fair value of liabilities within a year using fundamental data.
98+
99+
HINT TO IMPROVE THE ALPHA: Could observing the increase over a shorter period improve accuracy?
100+
*/
101+
102+
-ts_rank(fn_liab_fair_val_l1_a,252)
103+
104+
""",
105+
}
106+
]
107+
},
108+
config={
109+
"configurable": {
110+
**DEFAULT_CONFIG,
111+
"alphas": alphas,
112+
}
113+
},
114+
)
115+
116+
print("Alpha", alphas[-1])
117+
return alphas[-1]
118+
119+
120+
def monitor_alpha(response, alpha_config):
121+
"""Monitor the alpha simulation."""
122+
simulation_result = BrainAPI().simulation_progress(response)
123+
if not simulation_result["completed"]:
124+
return {
125+
"alpha_id": None,
126+
"simulate_data": alpha_config,
127+
"error": simulation_result["error"],
128+
}
129+
130+
BrainAPI().set_alpha_properties(simulation_result["result"]["id"])
131+
132+
return BrainAPI().get_specified_alpha_stats(
133+
simulation_result["result"]["id"], alpha_config, **API_DEFAULT_CONFIG
134+
)
135+
136+
137+
print("Creating alpha...")
138+
response, config = create_alpha_simulation({}, [])
139+
res = monitor_alpha(response, config)
140+
print("RES", res)
141+
quit()
142+
143+
144+
def main():
145+
"""Main function to run the agent."""
146+
alphas_dict = {}
147+
alphas_categories = {
148+
"passing": [],
149+
"failing": [],
150+
"pending": [],
151+
}
152+
153+
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as pool:
154+
# map Future → URL so we know which request just finished
155+
for _ in range(MAX_WORKERS):
156+
response, alpha_config = generate_alpha()
157+
live_jobs = {pool.submit(monitor_alpha, alpha_id): alpha_id}
158+
159+
while live_jobs:
160+
for job in as_completed(live_jobs):
161+
url = live_jobs.pop(job) # remove from “running” set
162+
163+
try:
164+
body = job.result()
165+
print(f"[OK] {url}")
166+
167+
except Exception as exc:
168+
print(f"[ERR] {url}{exc}")
169+
break # go back to outer loop
170+
171+
# MAIN‑THREAD logic: decide next jobs based on *this* response
172+
for nxt in extract_follow_up(body):
173+
if nxt not in seen: # avoid loops / dupes
174+
seen.add(nxt)
175+
live_jobs[pool.submit(fetch, nxt)] = nxt
176+
break

brain/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ def _request(self, method: str, endpoint: str, max_retries: int = 200, **kwargs)
564564
else:
565565
return response
566566

567-
raise Exception(f"Max retries exceeded for {method} request to {endpoint}")
567+
raise TimeoutError(f"Max retries exceeded for {method} request to {endpoint}")
568568

569569
def _authenticate(self):
570570
"""Authenticate user."""

brain/main.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,27 @@ def main():
1717
"messages": [
1818
{
1919
"role": "user",
20-
"content": (
21-
"Come up with a new alpha expression, start by exploring possible data"
22-
"fields and then test it."
23-
),
20+
"content": """
21+
Your task is to create a new alpha. Below is possible suggestion for the alpha.
22+
23+
/*
24+
HYPOTHESIS: "Fair value accounting" aims to measure and report on-going valuations of assets and liabilities. A recent increase in the fair value of liabilities may indicate a higher cost to be borne by the company in the future. This may deteriorate the company's financial health, potentially leading to lower profitability or financial distress.
25+
26+
IMPLEMENTATION: Using ts_rank() operator, sell stocks when there is an increase in the fair value of liabilities within a year using fundamental data.
27+
28+
HINT TO IMPROVE THE ALPHA: Could observing the increase over a shorter period improve accuracy?
29+
*/
30+
31+
-ts_rank(fn_liab_fair_val_l1_a,252)
32+
33+
""",
2434
}
2535
]
2636
},
27-
config={"configurable": DEFAULT_CONFIG},
37+
config={"configurable": {**DEFAULT_CONFIG, "thread_id": "1"}},
2838
)
29-
result.pretty_print()
39+
for msg in result["messages"]:
40+
print(f"{type(msg)}, {msg}")
3041

3142

3243
if __name__ == "__main__":

brain/tools/simulation.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,53 @@
11
"""Agent tool for simulating alpha expressions and obtaining output."""
22

3+
import time
4+
from typing import Annotated
5+
36
import pandas as pd
7+
from langchain_core.messages import ToolMessage
48
from langchain_core.runnables import RunnableConfig
9+
from langchain_core.tools import InjectedToolCallId, tool
10+
from langgraph.graph import END
11+
from langgraph.types import Command
512

613
from brain.agent_config import get_universe_config
714
from brain.alpha_helpers import generate_alpha
815
from brain.api import BrainAPI
916

1017

11-
def simulate_alpha(alpha: str, config: RunnableConfig) -> str:
18+
def submit_alpha(
19+
alpha: str, tool_call_id: Annotated[str, InjectedToolCallId], config: RunnableConfig
20+
) -> Command:
1221
"""Execute alpha expression."""
1322
conf = get_universe_config(config)
1423
alpha_config = generate_alpha(alpha, **conf)
1524

16-
print("Alpha configuration:")
17-
print(alpha_config)
25+
print(f"Alpha config: {alpha_config}")
1826

27+
result, response = {}, ""
1928
try:
20-
result = BrainAPI().simulate_alpha_list([alpha_config])[0]
29+
response = BrainAPI().start_simulation(alpha_config)
30+
endpoint = response.headers["Location"]
31+
time.sleep(5)
32+
result = BrainAPI().s.request("GET", endpoint).json()
2133
except Exception as e:
2234
print(f"Error during simulation: {e}")
2335
quit()
2436

25-
if error := result.get("error"):
37+
if result.get("status", "") == "ERROR":
38+
error = result.get("message", "Unknown error")
2639
print(f"Error occured: {error}")
2740
# TODO: More instructions on how to fix (operators, etc.)
2841
return f"Incorrect alpha. Fix following error and simulate it again: {error}"
2942

30-
response = format_alpha_prompt(result)
31-
print(response)
32-
return response
43+
config["configurable"]["alphas"].append((response, alpha_config))
44+
45+
return Command(
46+
update={
47+
"end": True,
48+
"messages": [ToolMessage(content=END, tool_call_id=tool_call_id)],
49+
},
50+
)
3351

3452

3553
def format_alpha_prompt(alpha_data):

0 commit comments

Comments
 (0)