diff --git a/rdagent/app/qlib_rd_loop/factor_w_sc.py b/rdagent/app/qlib_rd_loop/factor_w_sc.py index 1e6f8220..79087162 100644 --- a/rdagent/app/qlib_rd_loop/factor_w_sc.py +++ b/rdagent/app/qlib_rd_loop/factor_w_sc.py @@ -30,9 +30,4 @@ def main(path=None, step_n=None): if __name__ == "__main__": - fire.Fire(main) - - - -if __name__ == "__main__": - fire.Fire(main) + fire.Fire(main) \ No newline at end of file diff --git a/rdagent/components/benchmark/eval_method.py b/rdagent/components/benchmark/eval_method.py index c8f2aec8..193d1ce2 100644 --- a/rdagent/components/benchmark/eval_method.py +++ b/rdagent/components/benchmark/eval_method.py @@ -197,4 +197,3 @@ def summarize_res(res: EVAL_RES) -> pd.DataFrame: sum_res[key] = val return pd.DataFrame(sum_res) - diff --git a/rdagent/components/coder/factor_coder/CoSTEER/evolving_agent.py b/rdagent/components/coder/factor_coder/CoSTEER/evolving_agent.py index e6ad776b..75661805 100644 --- a/rdagent/components/coder/factor_coder/CoSTEER/evolving_agent.py +++ b/rdagent/components/coder/factor_coder/CoSTEER/evolving_agent.py @@ -14,6 +14,6 @@ def filter_evolvable_subjects_by_feedback(self, evo: EvolvableSubjects, feedback assert len(evo.sub_workspace_list) == len(feedback) for index in range(len(evo.sub_workspace_list)): - if feedback[index] is not None and not feedback[index].final_decision: + if feedback[index] and not feedback[index].final_decision: evo.sub_workspace_list[index].clear() return evo diff --git a/rdagent/core/utils.py b/rdagent/core/utils.py index a01f2a64..949d4a37 100644 --- a/rdagent/core/utils.py +++ b/rdagent/core/utils.py @@ -26,7 +26,8 @@ def __new__(cls, *args: Any, **kwargs: Any) -> Any: # TODO: this restriction can be solved. exception_message = "Please only use kwargs in Singleton to avoid misunderstanding." raise RDAgentException(exception_message) - kwargs_hash = hash(tuple(sorted(kwargs.items()))) + all_args = [(-1, f"{cls.__module__}.{cls.__name__}")] + [(i, args[i]) for i in args] + list(sorted(kwargs.items())) + kwargs_hash = hash(tuple(all_args)) if kwargs_hash not in cls._instance_dict: cls._instance_dict[kwargs_hash] = super().__new__(cls) # Corrected call cls._instance_dict[kwargs_hash].__init__(**kwargs) # Ensure __init__ is called diff --git a/rdagent/scenarios/qlib/prompts.yaml b/rdagent/scenarios/qlib/prompts.yaml index 139ebd57..5e3239d9 100644 --- a/rdagent/scenarios/qlib/prompts.yaml +++ b/rdagent/scenarios/qlib/prompts.yaml @@ -111,10 +111,20 @@ factor_hypothesis_specification: |- - Explore sophisticated combinations or new types of factors, e.g., "Develop a composite factor integrating ESG scores with traditional financial metrics." Important Note: - - If a hypothesis achieves the desired results, start a new direction while preserving the effective factors from previous hypotheses. - - New evaluations should combine the newly proposed factors with previously successful factors that surpassed SOTA. - - If the previous hypothesis factor exceeds SOTA, you can write another factor in the same direction, otherwise, explore new directions. - - The final maintained SOTA is the continuous accumulation of factors that surpass each iteration. + Logic Explanation: + - If the previous hypothesis factor exceeds SOTA, the SOTA factor library will include this factor. + - The new experiment will generate new factors, which will be combined with the factors in the SOTA library. + - These combined factors will be backtested and compared against the current SOTA to iterate continuously. + Development Directions: + - New Direction: + - Propose a new factor direction for exploration and construction. + - Optimization of Existing Direction: + - If the previous experiment's factor replaced SOTA, you can further improve upon that factor. + - Clearly specify the differences in name and improvements compared to the previous factor. + - Continued Research: + - If the previous experiment's factor did not replace SOTA, continue researching how to optimize and construct factors in this direction. + Final Goal: + - The final maintained SOTA should be the continuous accumulation of factors that surpass each iteration. factor_experiment_output_format: |- The output should follow JSON format. The schema is as follows: diff --git a/rdagent/scenarios/qlib/proposal/factor_proposal.py b/rdagent/scenarios/qlib/proposal/factor_proposal.py index b1dadc66..eea5fa5e 100644 --- a/rdagent/scenarios/qlib/proposal/factor_proposal.py +++ b/rdagent/scenarios/qlib/proposal/factor_proposal.py @@ -39,7 +39,7 @@ def prepare_context(self, trace: Trace) -> Tuple[dict, bool]: def convert_response(self, response: str) -> FactorHypothesis: response_dict = json.loads(response) - hypothesis = QlibFactorHypothesis(hypothesis=response_dict["hypothesis"], reason=response_dict["reason"]) + hypothesis = QlibFactorHypothesis(hypothesis=response_dict["hypothesis"], reason=response_dict["reason"], concise_reason=response_dict["concise_reason"]) return hypothesis @@ -72,13 +72,32 @@ def prepare_context(self, hypothesis: Hypothesis, trace: Trace) -> Tuple[dict | def convert_response(self, response: str, trace: Trace) -> FactorExperiment: response_dict = json.loads(response) tasks = [] + for factor_name in response_dict: description = response_dict[factor_name]["description"] formulation = response_dict[factor_name]["formulation"] variables = response_dict[factor_name]["variables"] tasks.append(FactorTask(factor_name, description, formulation, variables)) + exp = QlibFactorExperiment(tasks) exp.based_experiments = [t[1] for t in trace.hist if t[2]] + if len(exp.based_experiments) == 0: exp.based_experiments.append(QlibFactorExperiment(sub_tasks=[])) + + unique_tasks = [] + + for task in tasks: + duplicate = False + for based_exp in exp.based_experiments: + for sub_task in based_exp.sub_tasks: + if task.factor_name == sub_task.factor_name: + duplicate = True + break + if duplicate: + break + if not duplicate: + unique_tasks.append(task) + + exp.tasks = unique_tasks return exp