-
-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor QlibFactorExperiment to process and combine factor data, dyn…
…amically handle SOTA factors, and integrate Docker&Qlib-based backtest results.
- Loading branch information
1 parent
44e5610
commit b8d119c
Showing
7 changed files
with
393 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -153,3 +153,6 @@ git_ignore_folder/ | |
|
||
# DB files | ||
*.db | ||
|
||
# Docker | ||
env_factor/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,144 @@ | ||
# TODO: | ||
# Implement to feedback. | ||
|
||
from pathlib import Path | ||
from rdagent.core.prompts import Prompts | ||
from rdagent.core.proposal import HypothesisExperiment2Feedback | ||
from rdagent.core.proposal import Trace | ||
from rdagent.core.experiment import Experiment | ||
from rdagent.core.proposal import Hypothesis, HypothesisFeedback | ||
from rdagent.oai.llm_utils import APIBackend | ||
import json | ||
import pandas as pd | ||
import pickle | ||
from rdagent.utils.env import QTDockerEnv | ||
|
||
feedback_prompts = Prompts(file_path=Path(__file__).parent.parent / "prompts.yaml") | ||
|
||
class QlibFactorHypothesisExperiment2Feedback(HypothesisExperiment2Feedback): ... | ||
DIRNAME = Path(__file__).absolute().resolve().parent | ||
MLRUNS_DIR = Path("/home/finco/RDAgent_MS/RD-Agent/rdagent/scenarios/qlib/task_generator/env_factor/mlruns/1") | ||
|
||
|
||
class QlibFactorHypothesisExperiment2Feedback(HypothesisExperiment2Feedback): | ||
def generateFeedback(self, exp, hypothesis, trace): | ||
""" | ||
Generate feedback for the given experiment and hypothesis. | ||
Args: | ||
exp (QlibFactorExperiment): The experiment to generate feedback for. | ||
hypothesis (QlibFactorHypothesis): The hypothesis to generate feedback for. | ||
trace (Trace): The trace of the experiment. | ||
Returns: | ||
Any: The feedback generated for the given experiment and hypothesis. | ||
""" | ||
print("Generating feedback...") | ||
hypothesis_text = hypothesis.hypothesis | ||
current_result = exp.result | ||
tasks_factors = [(task.factor_name, task.factor_description) for task in exp.sub_tasks] | ||
|
||
# Check if based_experiments is empty | ||
if not exp.based_experiments: | ||
# Run Docker to get alpha158 result as SOTA | ||
sota_result = self.FetchAlpha158ResultFromDocker() | ||
else: | ||
sota_result = exp.based_experiments[-1].result | ||
|
||
# Generate the system prompt | ||
sys_prompt = feedback_prompts["data_feedback_generation"]["system"] | ||
|
||
# Prepare task details | ||
task_details = "\n".join([f"Task: {factor_name}, Factor: {factor_description}" for factor_name, factor_description in tasks_factors]) | ||
|
||
# Generate the user prompt | ||
usr_prompt_template = feedback_prompts["data_feedback_generation"]["user"] | ||
usr_prompt = usr_prompt_template.format( | ||
hypothesis_text=hypothesis_text, | ||
task_details=task_details, | ||
current_result=current_result, | ||
sota_result=sota_result | ||
) | ||
|
||
try: | ||
# Call the APIBackend to generate the response for hypothesis feedback | ||
response = APIBackend().build_messages_and_create_chat_completion( | ||
user_prompt=usr_prompt, | ||
system_prompt=sys_prompt, | ||
json_mode=True, | ||
) | ||
|
||
# Parse the JSON response to extract the feedback | ||
response_json = json.loads(response) | ||
|
||
# Extract fields from JSON response | ||
observations = response_json.get("Observations", "No observations provided") | ||
hypothesis_evaluation = response_json.get("Feedback for Hypothesis", "No feedback provided") | ||
new_hypothesis = response_json.get("New Hypothesis", "No new hypothesis provided") | ||
reason = response_json.get("Reasoning", "No reasoning provided") | ||
decision = response_json.get("Replace Best Result", "no").lower() == "yes" | ||
|
||
# Create HypothesisFeedback object | ||
hypothesis_feedback = HypothesisFeedback( | ||
observations=observations, | ||
hypothesis_evaluation=hypothesis_evaluation, | ||
new_hypothesis=new_hypothesis, | ||
reason=reason, | ||
decision=decision | ||
) | ||
|
||
print("Generated Hypothesis Feedback:") | ||
print(f"Observations: {observations}") | ||
print(f"Feedback for Hypothesis: {hypothesis_evaluation}") | ||
print(f"New Hypothesis: {new_hypothesis}") | ||
print(f"Reason: {reason}") | ||
print(f"Replace Best Result: {'Yes' if decision else 'No'}") | ||
|
||
return hypothesis_feedback | ||
|
||
except json.JSONDecodeError as e: | ||
print("Error parsing JSON response from LLM for hypothesis feedback:", e) | ||
except Exception as e: | ||
print("An unexpected error occurred while generating hypothesis feedback:", e) | ||
return HypothesisFeedback(observations="", hypothesis_evaluation="", new_hypothesis="", reason="", decision=False) | ||
|
||
|
||
def FetchAlpha158ResultFromDocker(self): | ||
""" | ||
Run Docker to get alpha158 result. | ||
Returns: | ||
Any: The alpha158 result. | ||
""" | ||
# TODO: Implement the Docker call to get alpha158 result | ||
qtde = QTDockerEnv() | ||
qtde.prepare() # Preparing the environment | ||
qtde.prepare() | ||
|
||
# Run the Docker command | ||
result = qtde.run(local_path=str(DIRNAME / "env_factor"), entry="rm -r mlruns", env={"PYTHONPATH": "./"}) | ||
# Run the Qlib backtest | ||
result = qtde.run(local_path=str(DIRNAME / "env_factor"), entry="qrun conf.yaml", env={"PYTHONPATH": "./"}) | ||
|
||
# Check for new directories in MLRUNS_DIR | ||
new_dir = {d.name for d in MLRUNS_DIR.iterdir() if d.is_dir()} | ||
new_dir_name = new_dir.pop() | ||
pkl_path = MLRUNS_DIR / new_dir_name / 'artifacts/portfolio_analysis/port_analysis_1day.pkl' | ||
|
||
if not pkl_path.exists(): | ||
print(f"File {pkl_path} does not exist.") | ||
return None | ||
|
||
with open(pkl_path, 'rb') as f: | ||
result = pickle.load(f) | ||
|
||
# Check if the result is valid and is a DataFrame | ||
if isinstance(result, pd.DataFrame): | ||
if not result.empty: | ||
print("Successfully retrieved alpha158 result.") | ||
return result | ||
else: | ||
print("Result DataFrame is empty.") | ||
return None | ||
else: | ||
print("Data format error.") | ||
return None |
Oops, something went wrong.