mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-28 16:07:46 +00:00
re-commit
This commit is contained in:
@@ -153,3 +153,8 @@ git_ignore_folder/
|
||||
|
||||
# DB files
|
||||
*.db
|
||||
|
||||
# Docker
|
||||
env_factor/
|
||||
env_tpl/
|
||||
mlruns/
|
||||
@@ -1,4 +1,5 @@
|
||||
from pydantic_settings import BaseSettings
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class PropSetting(BaseSettings):
|
||||
@@ -22,6 +23,7 @@ class PropSetting(BaseSettings):
|
||||
qlib_model_summarizer: str = "rdagent.scenarios.qlib.task_generator.feedback.QlibModelHypothesisExperiment2Feedback"
|
||||
|
||||
evolving_n: int = 10
|
||||
|
||||
|
||||
|
||||
py_bin: str = "/usr/bin/python"
|
||||
|
||||
PROP_SETTING = PropSetting()
|
||||
|
||||
@@ -27,7 +27,7 @@ hypothesis2experiment: Hypothesis2Experiment = import_class(PROP_SETTING.qlib_fa
|
||||
qlib_factor_coder: TaskGenerator = import_class(PROP_SETTING.qlib_factor_coder)(scen)
|
||||
qlib_factor_runner: TaskGenerator = import_class(PROP_SETTING.qlib_factor_runner)(scen)
|
||||
|
||||
qlib_factor_summarizer: HypothesisExperiment2Feedback = import_class(PROP_SETTING.qlib_factor_summarizer)()
|
||||
qlib_factor_summarizer: HypothesisExperiment2Feedback = import_class(PROP_SETTING.qlib_factor_summarizer)(scen)
|
||||
|
||||
|
||||
trace = Trace(scen=scen)
|
||||
@@ -39,3 +39,20 @@ for _ in range(PROP_SETTING.evolving_n):
|
||||
feedback = qlib_factor_summarizer.generateFeedback(exp, hypothesis, trace)
|
||||
|
||||
trace.hist.append((hypothesis, exp, feedback))
|
||||
|
||||
|
||||
"""
|
||||
trace = Trace(scen=scen)
|
||||
# for _ in range(PROP_SETTING.evolving_n):
|
||||
for _ in range(1):
|
||||
hypothesis = hypothesis_gen.gen(trace)
|
||||
exp = hypothesis2experiment.convert(hypothesis, trace)
|
||||
# exp = qlib_factor_coder.generate(exp)
|
||||
import pickle
|
||||
file_path = '/home/finco/v-yuanteli/RD-Agent/git_ignore_folder/factor_data_output/exp_new.pkl'
|
||||
with open(file_path, 'rb') as file:
|
||||
exp = pickle.load(file)
|
||||
exp = qlib_factor_runner.generate(exp)
|
||||
feedback = qlib_factor_summarizer.generateFeedback(exp, hypothesis, trace)
|
||||
# trace.hist.append((hypothesis, exp, feedback))
|
||||
"""
|
||||
@@ -18,6 +18,10 @@ ASpecificTask = TypeVar("ASpecificTask", bound=Task)
|
||||
|
||||
|
||||
class Implementation(ABC, Generic[ASpecificTask]):
|
||||
# TODO: workspace;
|
||||
# - code or data(optional)
|
||||
# - Execute logic
|
||||
# - `env is not included`. It is a underlying infra
|
||||
def __init__(self, target_task: ASpecificTask) -> None:
|
||||
self.target_task = target_task
|
||||
|
||||
@@ -85,6 +89,7 @@ class FBImplementation(Implementation):
|
||||
typical usage of `*args, **kwargs`:
|
||||
Different methods shares the same data. The data are passed by the arguments.
|
||||
"""
|
||||
# TODO: model and factor prepare;
|
||||
|
||||
def inject_code(self, **files: str):
|
||||
"""
|
||||
@@ -112,12 +117,14 @@ class Experiment(ABC, Generic[ASpecificTask, ASpecificImp]):
|
||||
"""
|
||||
The experiment is a sequence of tasks and the implementations of the tasks after generated by the TaskGenerator.
|
||||
"""
|
||||
result_ws: Optional[FBImplementation]
|
||||
|
||||
def __init__(self, sub_tasks: Sequence[ASpecificTask]) -> None:
|
||||
self.sub_tasks = sub_tasks
|
||||
self.sub_implementations: Sequence[ASpecificImp] = [None for _ in self.sub_tasks]
|
||||
self.based_experiments: Sequence[Experiment] = []
|
||||
self.result: object = None # The result of the experiment, can be different types in different scenarios.
|
||||
self.result_ws = None
|
||||
|
||||
|
||||
TaskOrExperiment = TypeVar("TaskOrExperiment", Task, Experiment)
|
||||
|
||||
@@ -92,9 +92,12 @@ class Hypothesis2Experiment(ABC, Generic[ASpecificExp]):
|
||||
class HypothesisExperiment2Feedback:
|
||||
""" "Generated feedbacks on the hypothesis from **Executed** Implementations of different tasks & their comparisons with previous performances"""
|
||||
|
||||
def generateFeedback(self, ti: Experiment, hypothesis: Hypothesis, trace: Trace) -> HypothesisFeedback:
|
||||
def __init__(self, scen: Scenario):
|
||||
self.scen = scen
|
||||
|
||||
def generateFeedback(self, exp: Experiment, hypothesis: Hypothesis, trace: Trace) -> HypothesisFeedback:
|
||||
"""
|
||||
The `ti` should be executed and the results should be included, as well as the comparison between previous results (done by LLM).
|
||||
The `exp` should be executed and the results should be included, as well as the comparison between previous results (done by LLM).
|
||||
For example: `mlflow` of Qlib will be included.
|
||||
"""
|
||||
raise NotImplementedError("generateFeedback method is not implemented.")
|
||||
|
||||
@@ -47,4 +47,36 @@ model_experiment_output_format: |-
|
||||
"model_type": "type of model 1, Tabular or TimesSeries"
|
||||
}
|
||||
# Don't add ellipsis (...) or any filler text that might cause JSON parsing errors here!
|
||||
}
|
||||
}
|
||||
|
||||
data_feedback_generation:
|
||||
system: |-
|
||||
You are a professional result analysis assistant on data driven R&D.
|
||||
The task is described in the following scenario:
|
||||
{{ scenario }}
|
||||
You will receive a hypothesis, multiple tasks with their factors, and some results.
|
||||
Your feedback should specify whether the current result supports or refutes the hypothesis, compare it with previous results, and suggest improvements or new directions.
|
||||
Please provide detailed and constructive feedback for the future exploration.
|
||||
Please respond in JSON format, and example JSON Structure for Result Analysis:
|
||||
{
|
||||
"Observations": "Your overall observations here",
|
||||
"Feedback for Hypothesis": "Observations related to the hypothesis",
|
||||
"New Hypothesis": "Put your new hypothesis here.",
|
||||
"Reasoning": "Provide reasoning for the hypothesis here.",
|
||||
"Replace Best Result": "yes or no"
|
||||
}
|
||||
user: |-
|
||||
Target hypothesis:
|
||||
{{hypothesis}}
|
||||
Tasks and Factors:
|
||||
{{task_details}}
|
||||
Current Result:
|
||||
{{current_result}}
|
||||
SOTA Result:
|
||||
{{sota_result}}
|
||||
Analyze the current result in the context of its ability to:
|
||||
1. Support or refute the hypothesis.
|
||||
2. Show improvement or deterioration compared to the last experiment.
|
||||
3. Demonstrate positive or negative effects when compared to Alpha158.
|
||||
|
||||
Provide detailed feedback and recommend whether to replace the best result if the new factor proves superior.
|
||||
|
||||
@@ -1,6 +1,28 @@
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
from typing import List
|
||||
import pandas as pd
|
||||
import pickle
|
||||
from rdagent.app.qlib_rd_loop.conf import PROP_SETTING
|
||||
from rdagent.core.task_generator import TaskGenerator
|
||||
from rdagent.utils.env import QTDockerEnv, LocalConf, LocalEnv
|
||||
from rdagent.scenarios.qlib.experiment.factor_experiment import QlibFactorExperiment
|
||||
from rdagent.core.log import RDAgentLog
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
DIRNAME_local = Path.cwd()
|
||||
logger = RDAgentLog()
|
||||
|
||||
# class QlibFactorExpWorkspace:
|
||||
|
||||
# def prepare():
|
||||
# # create a folder;
|
||||
# # copy template
|
||||
# # place data inside the folder `combined_factors`
|
||||
# #
|
||||
# def execute():
|
||||
# de = DockerEnv()
|
||||
# de.run(local_path=self.ws_path, entry="qrun conf.yaml")
|
||||
|
||||
class QlibFactorRunner(TaskGenerator[QlibFactorExperiment]):
|
||||
"""
|
||||
@@ -13,6 +35,183 @@ class QlibFactorRunner(TaskGenerator[QlibFactorExperiment]):
|
||||
|
||||
- TODO: implement a qlib handler
|
||||
"""
|
||||
|
||||
def FetchAlpha158ResultFromDocker(self):
|
||||
"""
|
||||
Run Docker to get alpha158 result.
|
||||
|
||||
This method prepares the Qlib Docker environment, executes the necessary commands to
|
||||
run the backtest, and fetches the results stored in a pickle file.
|
||||
|
||||
Returns:
|
||||
Any: The alpha158 result. If successful, returns a pandas DataFrame. Otherwise, returns None.
|
||||
"""
|
||||
# Initialize and prepare the Qlib Docker environment
|
||||
qtde = QTDockerEnv()
|
||||
qtde.prepare()
|
||||
|
||||
# Clean up any previous run artifacts by deleting the mlruns directory
|
||||
result = qtde.run(local_path=str(DIRNAME / "env_factor"), entry="rm -r mlruns", env={"PYTHONPATH": "./"})
|
||||
|
||||
# Run the Qlib backtest using the configuration file conf.yaml
|
||||
result = qtde.run(local_path=str(DIRNAME / "env_factor"), entry="qrun conf.yaml", env={"PYTHONPATH": "./"})
|
||||
|
||||
# Execute a Python script to extract the experiment results
|
||||
result = qtde.run(local_path=str(DIRNAME / "env_factor"), entry="python read_exp_res.py")
|
||||
|
||||
pkl_path = DIRNAME / 'env_factor/qlib_res.pkl'
|
||||
|
||||
if not pkl_path.exists():
|
||||
logger.error(f"File {pkl_path} does not exist.")
|
||||
return None
|
||||
|
||||
with open(pkl_path, 'rb') as f:
|
||||
result = pickle.load(f)
|
||||
|
||||
# Check if the loaded result is a pandas DataFrame and not empty
|
||||
if isinstance(result, pd.DataFrame):
|
||||
if not result.empty:
|
||||
logger.info("Successfully retrieved alpha158 result.")
|
||||
return result
|
||||
else:
|
||||
logger.error("Result DataFrame is empty.")
|
||||
return None
|
||||
else:
|
||||
logger.error("Data format error.")
|
||||
return None
|
||||
|
||||
|
||||
def generate(self, exp: QlibFactorExperiment) -> QlibFactorExperiment:
|
||||
return exp # TODO IMPLEMENT THIS
|
||||
"""
|
||||
Generate the experiment by processing and combining factor data,
|
||||
then passing the combined data to Docker for backtest results.
|
||||
"""
|
||||
|
||||
SOTA_factor = self.process_factor_data(exp.based_experiments)
|
||||
|
||||
if exp.based_experiments[-1].result is None:
|
||||
exp.based_experiments[-1].result = self.FetchAlpha158ResultFromDocker()
|
||||
|
||||
# Process the new factors data
|
||||
new_factors = self.process_factor_data(exp)
|
||||
|
||||
# Combine the SOTA factor and new factors if SOTA factor exists
|
||||
if SOTA_factor is not None:
|
||||
combined_factors = pd.concat([SOTA_factor, new_factors], axis=1).dropna()
|
||||
else:
|
||||
combined_factors = new_factors
|
||||
|
||||
# Sort and nest the combined factors under 'feature'
|
||||
combined_factors = combined_factors.sort_index()
|
||||
new_columns = pd.MultiIndex.from_product([['feature'], combined_factors.columns])
|
||||
combined_factors.columns = new_columns
|
||||
|
||||
# Save the combined factors to a pickle file
|
||||
combined_factors_path = DIRNAME / 'env_factor/combined_factors_df.pkl'
|
||||
with open(combined_factors_path, 'wb') as f:
|
||||
pickle.dump(combined_factors, f)
|
||||
|
||||
""" Docker run
|
||||
# Call Docker, pass the combined factors to Docker, and generate backtest results
|
||||
qtde = QTDockerEnv()
|
||||
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_combined.yaml", env={"PYTHONPATH": "./"})
|
||||
|
||||
result = qtde.run(local_path=str(DIRNAME / "env_factor"), entry="python read_exp_res.py")
|
||||
|
||||
pkl_path = DIRNAME / 'env_factor/qlib_res.pkl'
|
||||
|
||||
if not pkl_path.exists():
|
||||
logger.error(f"File {pkl_path} does not exist.")
|
||||
return None
|
||||
|
||||
with open(pkl_path, 'rb') as f:
|
||||
result = pickle.load(f)
|
||||
"""
|
||||
|
||||
# Local run
|
||||
# Clean up any previous run artifacts by deleting the mlruns directory
|
||||
mlruns_path = DIRNAME_local / 'mlruns' / '1'
|
||||
if mlruns_path.exists() and mlruns_path.is_dir():
|
||||
shutil.rmtree(mlruns_path)
|
||||
|
||||
# Prepare local Qlib environment
|
||||
local_conf = LocalConf(
|
||||
py_bin=PROP_SETTING.py_bin,
|
||||
default_entry="qrun conf_combined.yaml",
|
||||
)
|
||||
qle = LocalEnv(conf=local_conf)
|
||||
qle.prepare()
|
||||
conf_path = str(DIRNAME / "env_factor" / "conf_combined.yaml")
|
||||
qle.run(entry="qrun " + conf_path)
|
||||
|
||||
# Verify if the new folder is created
|
||||
mlrun_p = DIRNAME_local / 'mlruns' / '1'
|
||||
assert mlrun_p.exists(), f"Expected output file {mlrun_p} not found"
|
||||
|
||||
# Locate the newly generated folder in mlruns/1/
|
||||
new_folders = [folder for folder in mlrun_p.iterdir() if folder.is_dir()]
|
||||
if not new_folders:
|
||||
raise FileNotFoundError("No new folders found in 'mlruns/1/'.")
|
||||
|
||||
new_folder = new_folders[0] # Assuming there's only one new folder
|
||||
pickle_file = new_folder / 'artifacts' / 'portfolio_analysis' / 'port_analysis_1day.pkl'
|
||||
assert pickle_file.exists(), f"Expected pickle file {pickle_file} not found"
|
||||
|
||||
with open(pickle_file, 'rb') as f:
|
||||
result = pickle.load(f)
|
||||
|
||||
exp.result = result
|
||||
|
||||
# Check if the result is valid and is a DataFrame
|
||||
if isinstance(result, pd.DataFrame):
|
||||
if not result.empty:
|
||||
logger.info("Successfully retrieved experiment result.")
|
||||
return exp
|
||||
else:
|
||||
logger.error("Result DataFrame is empty.")
|
||||
return None
|
||||
else:
|
||||
logger.error("Data format error.")
|
||||
return None
|
||||
|
||||
def process_factor_data(self, exp_or_list: List[QlibFactorExperiment] | QlibFactorExperiment) -> pd.DataFrame:
|
||||
"""
|
||||
Process and combine factor data from experiment implementations.
|
||||
|
||||
Args:
|
||||
exp (ASpecificExp): The experiment containing factor data.
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: Combined factor data without NaN values.
|
||||
"""
|
||||
if isinstance(exp_or_list, QlibFactorExperiment):
|
||||
exp_or_list = [exp_or_list]
|
||||
factor_dfs = []
|
||||
|
||||
# Collect all exp's dataframes
|
||||
for exp in exp_or_list:
|
||||
# Iterate over sub-implementations and execute them to get each factor data
|
||||
for implementation in exp.sub_implementations:
|
||||
message, df = implementation.execute()
|
||||
|
||||
# Check if factor generation was successful
|
||||
if 'Execution succeeded without error.\nExpected output file found.' in message:
|
||||
factor_dfs.append(df)
|
||||
|
||||
# Combine all successful factor data
|
||||
if factor_dfs:
|
||||
combined_factors = pd.concat(factor_dfs, axis=1)
|
||||
|
||||
# Remove rows with NaN values
|
||||
combined_factors = combined_factors.dropna()
|
||||
|
||||
# print(combined_factors)
|
||||
return combined_factors
|
||||
else:
|
||||
logger.error("No valid factor data found to merge.")
|
||||
return pd.DataFrame() # Return an empty DataFrame if no valid data
|
||||
|
||||
@@ -1,10 +1,95 @@
|
||||
# TODO:
|
||||
# Implement to feedback.
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from jinja2 import Environment, StrictUndefined
|
||||
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
|
||||
from rdagent.utils.env import QTDockerEnv
|
||||
from rdagent.core.log import RDAgentLog
|
||||
import json
|
||||
import pandas as pd
|
||||
import pickle
|
||||
|
||||
|
||||
class QlibFactorHypothesisExperiment2Feedback(HypothesisExperiment2Feedback): ...
|
||||
|
||||
feedback_prompts = Prompts(file_path=Path(__file__).parent.parent / "prompts.yaml")
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
logger = RDAgentLog()
|
||||
|
||||
class QlibModelHypothesisExperiment2Feedback(HypothesisExperiment2Feedback): ...
|
||||
|
||||
class QlibFactorHypothesisExperiment2Feedback(HypothesisExperiment2Feedback):
|
||||
def generateFeedback(self, exp: Experiment, hypothesis: Hypothesis, trace: Trace) -> HypothesisFeedback:
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
logger.info("Generating feedback...")
|
||||
hypothesis_text = hypothesis.hypothesis
|
||||
current_result = exp.result
|
||||
tasks_factors = [task.get_factor_information() for task in exp.sub_tasks]
|
||||
|
||||
sota_result = exp.based_experiments[-1].result
|
||||
|
||||
# Generate the system prompt
|
||||
sys_prompt = Environment(undefined=StrictUndefined).from_string(feedback_prompts["data_feedback_generation"]["system"]).render(scenario=self.scen.get_scenario_all_desc())
|
||||
|
||||
|
||||
# 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 = Environment(undefined=StrictUndefined).from_string(feedback_prompts["data_feedback_generation"]["user"]).format(
|
||||
hypothesis_text=hypothesis_text,
|
||||
task_details=task_details,
|
||||
current_result=current_result,
|
||||
sota_result=sota_result
|
||||
)
|
||||
|
||||
# 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
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"Generated Hypothesis Feedback:\n"
|
||||
f"Observations: {observations}\n"
|
||||
f"Feedback for Hypothesis: {hypothesis_evaluation}\n"
|
||||
f"New Hypothesis: {new_hypothesis}\n"
|
||||
f"Reason: {reason}\n"
|
||||
f"Replace Best Result: {'Yes' if decision else 'No'}"
|
||||
)
|
||||
|
||||
return hypothesis_feedback
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
# 🐳 Run Docker & Qlib
|
||||
---
|
||||
|
||||
## 📄 Description
|
||||
This guide explains how to run the Qlib Docker test file located at `test/utils/test_env.py` in the RD-Agent repository.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Running Instructions
|
||||
|
||||
### 1. Install the required Python libraries
|
||||
- Ensure that the `docker` Python library is installed:
|
||||
```sh
|
||||
pip install docker
|
||||
```
|
||||
|
||||
### 2. Run the test script
|
||||
- Execute the test script to verify the Docker environment setup:
|
||||
```sh
|
||||
python test/utils/test_env.py
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
- **PermissionError: [Errno 13] Permission denied.**
|
||||
> This error occurs when the current user does not have the necessary permissions to access the Docker socket. To resolve this issue, follow these steps:
|
||||
|
||||
1. **Add the current user to the `docker` group**
|
||||
Docker requires root or `docker` group user permissions to access the Docker socket. Add the current user to the `docker` group:
|
||||
```sh
|
||||
sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
2. **Refresh group changes**
|
||||
To apply the group changes, log out and log back in, or use the following command:
|
||||
```sh
|
||||
newgrp docker
|
||||
```
|
||||
|
||||
3. **Verify Docker access**
|
||||
Run the following command to ensure that Docker can be accessed:
|
||||
```sh
|
||||
docker run hello-world
|
||||
```
|
||||
|
||||
4. **Rerun the test script**
|
||||
After completing these steps, rerun the test script:
|
||||
```sh
|
||||
python test/utils/test_env.py
|
||||
```
|
||||
---
|
||||
## 🛠️ Detailed Qlib Docker Function Framework
|
||||
|
||||
Here, we provide an overview of the specific functions within the Qlib Docker framework, their purposes, and examples of how to call them.
|
||||
|
||||
### QTDockerEnv Class in `env.py`
|
||||
|
||||
The `QTDockerEnv` class is responsible for setting up and running Docker environments for Qlib experiments.
|
||||
|
||||
#### Methods:
|
||||
|
||||
1. **prepare()**
|
||||
- **Purpose**: Prepares the Docker environment for running experiments. This includes building the Docker image if necessary.
|
||||
- **Example**:
|
||||
```python
|
||||
qtde = QTDockerEnv()
|
||||
qtde.prepare()
|
||||
```
|
||||
|
||||
2. **run(local_path: str, entry: str) -> str**
|
||||
- **Purpose**: Runs a specified entry point (e.g., a configuration file) in the prepared Docker environment.
|
||||
- **Parameters**:
|
||||
- `local_path`: Path to the local directory to mount into the Docker container.
|
||||
- `entry`: Command or entry point to run inside the Docker container.
|
||||
- **Returns**: The stdout output from the Docker container.
|
||||
- **Example**:
|
||||
```python
|
||||
result = qtde.run(local_path="/path/to/env_tpl", entry="qrun conf.yaml")
|
||||
```
|
||||
---
|
||||
### 📊 Expected Output
|
||||
|
||||
Upon successful execution, the test script will produce analysis results of benchmark returns and various risk metrics. The expected output should be similar to:
|
||||
|
||||
```
|
||||
'The following are analysis results of benchmark return (1 day).'
|
||||
risk
|
||||
mean 0.000477
|
||||
std 0.012295
|
||||
annualized_return 0.113561
|
||||
information_ratio 0.598699
|
||||
max_drawdown -0.370479
|
||||
|
||||
'The following are analysis results of the excess return without cost (1 day).'
|
||||
risk
|
||||
mean 0.000530
|
||||
std 0.005718
|
||||
annualized_return 0.126029
|
||||
information_ratio 1.428574
|
||||
max_drawdown -0.072310
|
||||
|
||||
'The following are analysis results of the excess return with cost (1 day).'
|
||||
risk
|
||||
mean 0.000339
|
||||
std 0.005717
|
||||
annualized_return 0.080654
|
||||
information_ratio 0.914486
|
||||
max_drawdown -0.086083
|
||||
|
||||
'The following are analysis results of indicators (1 day).'
|
||||
value
|
||||
ffr 1.0
|
||||
pa 0.0
|
||||
pos 0.0
|
||||
```
|
||||
|
||||
By following these steps and using the provided functions, you should be able to run the Qlib Docker tests and obtain the expected analysis results.
|
||||
+13
-13
@@ -23,18 +23,17 @@ class EnvUtils(unittest.TestCase):
|
||||
|
||||
# NOTE: Since I don't know the exact environment in which it will be used, here's just an example.
|
||||
# NOTE: Because you need to download the data during the prepare process. So you need to have pyqlib in your environment.
|
||||
# def test_local(self):
|
||||
# local_conf = LocalConf(
|
||||
# py_bin="/home/v-linlanglv/miniconda3/envs/RD-Agent-310/bin",
|
||||
# default_entry="qrun conf.yaml",
|
||||
# )
|
||||
# qle = LocalEnv(conf=local_conf)
|
||||
# qle.prepare()
|
||||
# exe_path = str(DIRNAME / "env_tpl")
|
||||
# conf_path = str(DIRNAME / "env_tpl" / "conf.yaml")
|
||||
# qle.run(entry="qrun " + conf_path, local_path=exe_path)
|
||||
# mlrun_p = DIRNAME / "env_tpl" / "mlruns"
|
||||
# self.assertTrue(mlrun_p.exists(), f"Expected output file {mlrun_p} not found")
|
||||
def test_local(self):
|
||||
local_conf = LocalConf(
|
||||
py_bin="/home/v-linlanglv/miniconda3/envs/RD-Agent-310/bin",
|
||||
default_entry="qrun conf.yaml",
|
||||
)
|
||||
qle = LocalEnv(conf=local_conf)
|
||||
qle.prepare()
|
||||
conf_path = str(DIRNAME / "env_tpl" / "conf.yaml")
|
||||
qle.run(entry="qrun " + conf_path)
|
||||
mlrun_p = DIRNAME / "env_tpl" / "mlruns"
|
||||
self.assertTrue(mlrun_p.exists(), f"Expected output file {mlrun_p} not found")
|
||||
|
||||
def test_docker(self):
|
||||
"""
|
||||
@@ -51,7 +50,8 @@ class EnvUtils(unittest.TestCase):
|
||||
self.assertTrue(mlrun_p.exists(), f"Expected output file {mlrun_p} not found")
|
||||
|
||||
# read experiment
|
||||
result = qtde.run(local_path=str(DIRNAME / "env_tpl"), entry="python read_exp.py")
|
||||
result = qtde.run(local_path=str(DIRNAME / "env_tpl"), entry="python read_exp_res.py")
|
||||
print("here")
|
||||
print(result)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
sys.path.append(str(Path(__file__).resolve().parent.parent))
|
||||
from rdagent.utils.env import QTDockerEnv, LocalEnv, LocalConf
|
||||
import shutil
|
||||
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
|
||||
class EnvUtils(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_docker(self):
|
||||
"""
|
||||
We will mount `env_tpl` into the docker image.
|
||||
And run the docker image with `qrun conf.yaml`
|
||||
"""
|
||||
qtde = QTDockerEnv()
|
||||
qtde.prepare()
|
||||
qtde.prepare() # you can prepare for multiple times. It is expected to handle it correctly
|
||||
# the stdout are returned as result
|
||||
result = qtde.run(local_path=str(DIRNAME / "env_tpl"), entry="qrun conf2.yaml")
|
||||
|
||||
mlrun_p = DIRNAME / "env_tpl" / "mlruns"
|
||||
self.assertTrue(mlrun_p.exists(), f"Expected output file {mlrun_p} not found")
|
||||
|
||||
# read experiment
|
||||
result = qtde.run(local_path=str(DIRNAME / "env_tpl"), entry="python read_exp_res.py")
|
||||
print("here")
|
||||
# print(result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user