mirror of
https://github.com/microsoft/qlib.git
synced 2026-07-17 09:24:34 +08:00
Connect TrainTask & Rolling & DDG-DA (#1599)
* Connect train task to ddg-da & rolling * Pylint & black formatting * Formatting
This commit is contained in:
@@ -16,3 +16,5 @@ MAX_RETRY=120
|
|||||||
|
|
||||||
CONTINOUS_MODE=True
|
CONTINOUS_MODE=True
|
||||||
DEBUG_MODE=True
|
DEBUG_MODE=True
|
||||||
|
|
||||||
|
# TEMPERATURE=
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# TODO: use pydantic for other modules in Qlib
|
# TODO: use pydantic for other modules in Qlib
|
||||||
from pydantic import BaseSettings
|
# from pydantic_settings import BaseSettings
|
||||||
from qlib.finco.utils import SingletonBaseClass
|
from qlib.finco.utils import SingletonBaseClass
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|||||||
97
qlib/finco/context.py
Normal file
97
qlib/finco/context.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
|
import copy
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional, List
|
||||||
|
from qlib.finco.log import FinCoLog
|
||||||
|
from qlib.typehint import Literal
|
||||||
|
|
||||||
|
from qlib.finco.utils import similarity
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Design:
|
||||||
|
plan: str
|
||||||
|
classes: str
|
||||||
|
decision: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Exp:
|
||||||
|
"""Experiment"""
|
||||||
|
|
||||||
|
# compoments
|
||||||
|
dataset: Optional[Design] = None
|
||||||
|
datahandler: Optional[Design] = None
|
||||||
|
model: Optional[Design] = None
|
||||||
|
record: Optional[Design] = None
|
||||||
|
strategy: Optional[Design] = None
|
||||||
|
backtest: Optional[Design] = None
|
||||||
|
|
||||||
|
# basic
|
||||||
|
template: Optional[Path] = None
|
||||||
|
|
||||||
|
# rolling strategy. None indicates no rolling
|
||||||
|
rolling: Optional[Literal["base", "ddgda"]] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class StructContext:
|
||||||
|
"""Part of the context have clear meaning and structure, so they will be saved here and can be easily retrieved and understood"""
|
||||||
|
|
||||||
|
# TODO: move more content in WorkflowContextManager.context to here
|
||||||
|
workspace: Path
|
||||||
|
exp_list: List[Exp] = field(default_factory=list) # the planned experiments
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowContextManager:
|
||||||
|
"""Context Manager stores the context of the workflow"""
|
||||||
|
|
||||||
|
"""All context are key value pairs which saves the input, output and status of the whole workflow"""
|
||||||
|
|
||||||
|
def __init__(self, workspace: Path) -> None:
|
||||||
|
self.context = {}
|
||||||
|
self.logger = FinCoLog()
|
||||||
|
# this context is public
|
||||||
|
self.struct_context = StructContext(workspace) # TODO: move more content in context to here
|
||||||
|
self.set_context("workspace", workspace) # TODO: remove me
|
||||||
|
|
||||||
|
def set_context(self, key, value):
|
||||||
|
if key in self.context:
|
||||||
|
self.logger.warning("The key already exists in the context, the value will be overwritten")
|
||||||
|
self.context[key] = value
|
||||||
|
|
||||||
|
def get_context(self, key):
|
||||||
|
# NOTE: if the key doesn't exist, return None. In the future, we may raise an error to detect abnormal behavior
|
||||||
|
if key not in self.context:
|
||||||
|
self.logger.warning("The key doesn't exist in the context")
|
||||||
|
return None
|
||||||
|
return self.context[key]
|
||||||
|
|
||||||
|
def update_context(self, key, new_value):
|
||||||
|
# NOTE: if the key doesn't exist, return None. In the future, we may raise an error to detect abnormal behavior
|
||||||
|
if key not in self.context:
|
||||||
|
self.logger.warning("The key doesn't exist in the context")
|
||||||
|
self.context.update({key: new_value})
|
||||||
|
|
||||||
|
def get_all_context(self):
|
||||||
|
"""return a deep copy of the context"""
|
||||||
|
"""TODO: do we need to return a deep copy?"""
|
||||||
|
return copy.deepcopy(self.context)
|
||||||
|
|
||||||
|
def retrieve(self, query: str) -> dict:
|
||||||
|
if query in self.context.keys():
|
||||||
|
return {query: self.context.get(query)}
|
||||||
|
|
||||||
|
# Note: retrieve information from context by string similarity maybe abandon in future
|
||||||
|
scores = {}
|
||||||
|
for k, v in self.context.items():
|
||||||
|
scores.update({k: max(similarity(query, k), similarity(query, v))})
|
||||||
|
max_score_key = max(scores, key=scores.get)
|
||||||
|
return {max_score_key: self.context.get(max_score_key)}
|
||||||
|
|
||||||
|
def clear(self, reserve: list = None):
|
||||||
|
if reserve is None:
|
||||||
|
reserve = []
|
||||||
|
|
||||||
|
_context = {k: self.get_context(k) for k in reserve}
|
||||||
|
self.context = _context
|
||||||
@@ -65,6 +65,7 @@ class YamlStorage(Storage):
|
|||||||
This class is responsible for storage and loading of Knowledge related data in yaml format.
|
This class is responsible for storage and loading of Knowledge related data in yaml format.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DEFAULT_NAME = "storage.yml"
|
DEFAULT_NAME = "storage.yml"
|
||||||
|
|
||||||
def __init__(self, path: Union[str, Path]):
|
def __init__(self, path: Union[str, Path]):
|
||||||
@@ -82,7 +83,7 @@ class YamlStorage(Storage):
|
|||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
"""use pickle as the default save method"""
|
"""use pickle as the default save method"""
|
||||||
Path.mkdir(self.path.parent, exist_ok=True)
|
Path.mkdir(self.path.parent, exist_ok=True)
|
||||||
with open(self.path, 'w') as f:
|
with open(self.path, "w") as f:
|
||||||
yaml.dump(self.documents, f)
|
yaml.dump(self.documents, f)
|
||||||
|
|
||||||
|
|
||||||
@@ -190,9 +191,14 @@ class ExperimentKnowledge(Knowledge):
|
|||||||
def brief(self):
|
def brief(self):
|
||||||
docs = []
|
docs = []
|
||||||
for recorder in self.storage.recs:
|
for recorder in self.storage.recs:
|
||||||
docs.append({"exp_name": self.storage.exp.name, "record_info": recorder.info,
|
docs.append(
|
||||||
"config": recorder.load_object("config"),
|
{
|
||||||
"context_summary": recorder.load_object("context_summary")})
|
"exp_name": self.storage.exp.name,
|
||||||
|
"record_info": recorder.info,
|
||||||
|
"config": recorder.load_object("config"),
|
||||||
|
"context_summary": recorder.load_object("context_summary"),
|
||||||
|
}
|
||||||
|
)
|
||||||
return docs
|
return docs
|
||||||
|
|
||||||
|
|
||||||
@@ -295,7 +301,7 @@ class InfrastructureKnowledge(Knowledge):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
functions = []
|
functions = []
|
||||||
for py_file_path in Path(directory).rglob('*.py'):
|
for py_file_path in Path(directory).rglob("*.py"):
|
||||||
for _functions in self.get_functions_with_docstrings(py_file_path):
|
for _functions in self.get_functions_with_docstrings(py_file_path):
|
||||||
functions.append(_functions)
|
functions.append(_functions)
|
||||||
|
|
||||||
@@ -314,7 +320,7 @@ class InfrastructureKnowledge(Knowledge):
|
|||||||
docstring = None
|
docstring = None
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if line.strip().startswith("def ") or line.strip().startswith("class "):
|
if line.strip().startswith("def ") or line.strip().startswith("class "):
|
||||||
func = line.strip().split(' ')[1].split('(')[0]
|
func = line.strip().split(" ")[1].split("(")[0]
|
||||||
if func.startswith("__"):
|
if func.startswith("__"):
|
||||||
continue
|
continue
|
||||||
if current_func is not None:
|
if current_func is not None:
|
||||||
@@ -336,7 +342,6 @@ class InfrastructureKnowledge(Knowledge):
|
|||||||
|
|
||||||
|
|
||||||
class Topic:
|
class Topic:
|
||||||
|
|
||||||
def __init__(self, name: str, describe: Template):
|
def __init__(self, name: str, describe: Template):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.describe = describe
|
self.describe = describe
|
||||||
@@ -347,9 +352,7 @@ class Topic:
|
|||||||
def summarize(self, docs: list):
|
def summarize(self, docs: list):
|
||||||
self.logger.info(f"Summarize topic: \nname: {self.name}\ndescribe: {self.describe.module}")
|
self.logger.info(f"Summarize topic: \nname: {self.name}\ndescribe: {self.describe.module}")
|
||||||
prompt_workflow_selection = self.describe.render(docs=docs)
|
prompt_workflow_selection = self.describe.render(docs=docs)
|
||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
response = APIBackend().build_messages_and_create_chat_completion(user_prompt=prompt_workflow_selection)
|
||||||
user_prompt=prompt_workflow_selection
|
|
||||||
)
|
|
||||||
|
|
||||||
self.knowledge = response
|
self.knowledge = response
|
||||||
self.docs = docs
|
self.docs = docs
|
||||||
@@ -395,22 +398,26 @@ class KnowledgeBase:
|
|||||||
|
|
||||||
def load_practice_knowledge(self, path: Path) -> PracticeKnowledge:
|
def load_practice_knowledge(self, path: Path) -> PracticeKnowledge:
|
||||||
self.practice_knowledge = PracticeKnowledge(
|
self.practice_knowledge = PracticeKnowledge(
|
||||||
YamlStorage(path.joinpath(f"{self.KT_PRACTICE}/{YamlStorage.DEFAULT_NAME}")))
|
YamlStorage(path.joinpath(f"{self.KT_PRACTICE}/{YamlStorage.DEFAULT_NAME}"))
|
||||||
|
)
|
||||||
return self.practice_knowledge
|
return self.practice_knowledge
|
||||||
|
|
||||||
def load_execute_knowledge(self, path: Path) -> ExecuteKnowledge:
|
def load_execute_knowledge(self, path: Path) -> ExecuteKnowledge:
|
||||||
self.execute_knowledge = ExecuteKnowledge(
|
self.execute_knowledge = ExecuteKnowledge(
|
||||||
YamlStorage(path.joinpath(f"{self.KT_EXECUTE}/{YamlStorage.DEFAULT_NAME}")))
|
YamlStorage(path.joinpath(f"{self.KT_EXECUTE}/{YamlStorage.DEFAULT_NAME}"))
|
||||||
|
)
|
||||||
return self.execute_knowledge
|
return self.execute_knowledge
|
||||||
|
|
||||||
def load_finance_knowledge(self, path: Path) -> FinanceKnowledge:
|
def load_finance_knowledge(self, path: Path) -> FinanceKnowledge:
|
||||||
self.finance_knowledge = FinanceKnowledge(
|
self.finance_knowledge = FinanceKnowledge(
|
||||||
YamlStorage(path.joinpath(f"{self.KT_FINANCE}/{YamlStorage.DEFAULT_NAME}")))
|
YamlStorage(path.joinpath(f"{self.KT_FINANCE}/{YamlStorage.DEFAULT_NAME}"))
|
||||||
|
)
|
||||||
return self.finance_knowledge
|
return self.finance_knowledge
|
||||||
|
|
||||||
def load_infrastructure_knowledge(self, path: Path) -> InfrastructureKnowledge:
|
def load_infrastructure_knowledge(self, path: Path) -> InfrastructureKnowledge:
|
||||||
self.infrastructure_knowledge = InfrastructureKnowledge(
|
self.infrastructure_knowledge = InfrastructureKnowledge(
|
||||||
YamlStorage(path.joinpath(f"{self.KT_INFRASTRUCTURE}/{YamlStorage.DEFAULT_NAME}")))
|
YamlStorage(path.joinpath(f"{self.KT_INFRASTRUCTURE}/{YamlStorage.DEFAULT_NAME}"))
|
||||||
|
)
|
||||||
return self.infrastructure_knowledge
|
return self.infrastructure_knowledge
|
||||||
|
|
||||||
def get_knowledge(self, knowledge_type: str = None):
|
def get_knowledge(self, knowledge_type: str = None):
|
||||||
@@ -423,8 +430,12 @@ class KnowledgeBase:
|
|||||||
elif knowledge_type == self.KT_INFRASTRUCTURE:
|
elif knowledge_type == self.KT_INFRASTRUCTURE:
|
||||||
knowledge = self.infrastructure_knowledge.knowledge
|
knowledge = self.infrastructure_knowledge.knowledge
|
||||||
else:
|
else:
|
||||||
knowledge = self.execute_knowledge.knowledge + self.practice_knowledge.knowledge \
|
knowledge = (
|
||||||
+ self.finance_knowledge.knowledge + self.infrastructure_knowledge.knowledge
|
self.execute_knowledge.knowledge
|
||||||
|
+ self.practice_knowledge.knowledge
|
||||||
|
+ self.finance_knowledge.knowledge
|
||||||
|
+ self.infrastructure_knowledge.knowledge
|
||||||
|
)
|
||||||
return knowledge
|
return knowledge
|
||||||
|
|
||||||
def query(self, knowledge_type: str = None, content: str = None, n: int = 5):
|
def query(self, knowledge_type: str = None, content: str = None, n: int = 5):
|
||||||
@@ -451,7 +462,8 @@ class KnowledgeBase:
|
|||||||
|
|
||||||
prompt = Template(
|
prompt = Template(
|
||||||
"""find the most relevant doc with this query: '{{content}}' from docs='{{docs}}'.
|
"""find the most relevant doc with this query: '{{content}}' from docs='{{docs}}'.
|
||||||
Just return the most relevant item I provided, no more explain. For example: {'function': 'config.resolve_path', 'docstring': None}""")
|
Just return the most relevant item I provided, no more explain. For example: {'function': 'config.resolve_path', 'docstring': None}"""
|
||||||
|
)
|
||||||
prompt_workflow_selection = prompt.render(content=content, docs=similar_n_docs)
|
prompt_workflow_selection = prompt.render(content=content, docs=similar_n_docs)
|
||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
response = APIBackend().build_messages_and_create_chat_completion(
|
||||||
user_prompt=prompt_workflow_selection, system_prompt="You are an excellent assistant."
|
user_prompt=prompt_workflow_selection, system_prompt="You are an excellent assistant."
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class APIBackend(SingletonBaseClass):
|
|||||||
"content": system_prompt,
|
"content": system_prompt,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
messages.extend(former_messages[-1*cfg.max_past_message_include:])
|
messages.extend(former_messages[-1 * cfg.max_past_message_include :])
|
||||||
messages.append(
|
messages.append(
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
@@ -82,7 +82,6 @@ class APIBackend(SingletonBaseClass):
|
|||||||
temperature: float = None,
|
temperature: float = None,
|
||||||
max_tokens: Optional[int] = None,
|
max_tokens: Optional[int] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
|
||||||
if self.debug_mode:
|
if self.debug_mode:
|
||||||
key = json.dumps(messages)
|
key = json.dumps(messages)
|
||||||
if key in self.cache:
|
if key in self.cache:
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class LogColors:
|
|||||||
"""
|
"""
|
||||||
ANSI color codes for use in console output.
|
ANSI color codes for use in console output.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
RED = "\033[91m"
|
RED = "\033[91m"
|
||||||
GREEN = "\033[92m"
|
GREEN = "\033[92m"
|
||||||
YELLOW = "\033[93m"
|
YELLOW = "\033[93m"
|
||||||
@@ -58,8 +59,11 @@ def formatting_log(logger, title="Info"):
|
|||||||
a context manager, print liens before and after a function
|
a context manager, print liens before and after a function
|
||||||
"""
|
"""
|
||||||
length = {"Start": 120, "Task": 120, "Info": 60, "Interact": 60, "End": 120}.get(title, 60)
|
length = {"Start": 120, "Task": 120, "Info": 60, "Interact": 60, "End": 120}.get(title, 60)
|
||||||
color, bold = (LogColors.YELLOW, LogColors.BOLD) \
|
color, bold = (
|
||||||
if title in ["Start", "Task", "Info", "Interact", "End"] else (LogColors.CYAN, "")
|
(LogColors.YELLOW, LogColors.BOLD)
|
||||||
|
if title in ["Start", "Task", "Info", "Interact", "End"]
|
||||||
|
else (LogColors.CYAN, "")
|
||||||
|
)
|
||||||
logger.info("")
|
logger.info("")
|
||||||
logger.info(f"{color}{bold}{'-'} {title} {'-' * (length - len(title))}{LogColors.END}")
|
logger.info(f"{color}{bold}{'-'} {title} {'-' * (length - len(title))}{LogColors.END}")
|
||||||
yield
|
yield
|
||||||
@@ -99,12 +103,12 @@ class FinCoLog(SingletonBaseClass):
|
|||||||
f"{LogColors.MAGENTA}{LogColors.BOLD}Role:{LogColors.END} "
|
f"{LogColors.MAGENTA}{LogColors.BOLD}Role:{LogColors.END} "
|
||||||
f"{LogColors.CYAN}{m['role']}{LogColors.END}\n"
|
f"{LogColors.CYAN}{m['role']}{LogColors.END}\n"
|
||||||
+ f"{LogColors.MAGENTA}{LogColors.BOLD}Content:{LogColors.END} "
|
+ f"{LogColors.MAGENTA}{LogColors.BOLD}Content:{LogColors.END} "
|
||||||
f"{LogColors.CYAN}{m['content']}{LogColors.END}\n")
|
f"{LogColors.CYAN}{m['content']}{LogColors.END}\n"
|
||||||
|
)
|
||||||
|
|
||||||
def log_response(self, response: str):
|
def log_response(self, response: str):
|
||||||
with formatting_log(self.logger, "GPT Response"):
|
with formatting_log(self.logger, "GPT Response"):
|
||||||
self.logger.info(
|
self.logger.info(f"{LogColors.CYAN}{response}{LogColors.END}\n")
|
||||||
f"{LogColors.CYAN}{response}{LogColors.END}\n")
|
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# It looks wierd if we only have logger
|
# It looks wierd if we only have logger
|
||||||
@@ -118,14 +122,13 @@ class FinCoLog(SingletonBaseClass):
|
|||||||
def plain_info(self, *args):
|
def plain_info(self, *args):
|
||||||
for arg in args:
|
for arg in args:
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"{LogColors.YELLOW}{LogColors.BOLD}Info:{LogColors.END}{LogColors.WHITE}{arg}{LogColors.END}")
|
f"{LogColors.YELLOW}{LogColors.BOLD}Info:{LogColors.END}{LogColors.WHITE}{arg}{LogColors.END}"
|
||||||
|
)
|
||||||
|
|
||||||
def warning(self, *args):
|
def warning(self, *args):
|
||||||
for arg in args:
|
for arg in args:
|
||||||
self.logger.warning(
|
self.logger.warning(f"{LogColors.BLUE}{LogColors.BOLD}Warning:{LogColors.END}{arg}")
|
||||||
f"{LogColors.BLUE}{LogColors.BOLD}Warning:{LogColors.END}{arg}")
|
|
||||||
|
|
||||||
def error(self, *args):
|
def error(self, *args):
|
||||||
for arg in args:
|
for arg in args:
|
||||||
self.logger.error(
|
self.logger.error(f"{LogColors.RED}{LogColors.BOLD}Error:{LogColors.END}{arg}")
|
||||||
f"{LogColors.RED}{LogColors.BOLD}Error:{LogColors.END}{arg}")
|
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ from qlib.finco import get_finco_path
|
|||||||
class PromptTemplate(SingletonBaseClass):
|
class PromptTemplate(SingletonBaseClass):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
_template = yaml.load(open(Path.joinpath(get_finco_path(), "prompt_template.yaml"), "r"),
|
_template = yaml.load(
|
||||||
Loader=yaml.FullLoader)
|
open(Path.joinpath(get_finco_path(), "prompt_template.yaml"), "r"), Loader=yaml.FullLoader
|
||||||
|
)
|
||||||
for k, v in _template.items():
|
for k, v in _template.items():
|
||||||
if k == "mods":
|
if k == "mods":
|
||||||
continue
|
continue
|
||||||
@@ -28,5 +29,5 @@ class PromptTemplate(SingletonBaseClass):
|
|||||||
file_path = Path(file_path)
|
file_path = Path(file_path)
|
||||||
Path.mkdir(file_path.parent, exist_ok=True)
|
Path.mkdir(file_path.parent, exist_ok=True)
|
||||||
|
|
||||||
with open(file_path, 'w') as f:
|
with open(file_path, "w") as f:
|
||||||
yaml.dump(self.__dict__, f)
|
yaml.dump(self.__dict__, f)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import os
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import io
|
import io
|
||||||
from typing import Any, List, Union
|
from typing import Any, List, Optional, Union
|
||||||
import ruamel.yaml as yaml
|
import ruamel.yaml as yaml
|
||||||
import abc
|
import abc
|
||||||
import re
|
import re
|
||||||
@@ -20,6 +20,8 @@ from qlib.finco.log import FinCoLog, LogColors
|
|||||||
from qlib.finco.conf import Config
|
from qlib.finco.conf import Config
|
||||||
from qlib.finco.knowledge import KnowledgeBase, Topic
|
from qlib.finco.knowledge import KnowledgeBase, Topic
|
||||||
|
|
||||||
|
from qlib.finco.context import Design, Exp, WorkflowContextManager
|
||||||
|
|
||||||
COMPONENT_LIST = ["Dataset", "DataHandler", "Model", "Record", "Strategy", "Backtest"]
|
COMPONENT_LIST = ["Dataset", "DataHandler", "Model", "Record", "Strategy", "Backtest"]
|
||||||
|
|
||||||
|
|
||||||
@@ -40,9 +42,19 @@ class Task:
|
|||||||
- Edit Task: it is supposed to edit the code base directly.
|
- Edit Task: it is supposed to edit the code base directly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
_context_manager: WorkflowContextManager
|
||||||
self._context_manager = None
|
|
||||||
|
def __init__(self, tpl_ver: Optional[str] = None) -> None:
|
||||||
|
"""
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
tpl_ver : Optional[str]
|
||||||
|
The Version of the template.
|
||||||
|
If the previous results will greatly affect the next QA. We may use different version instead of combine everything in the same one.
|
||||||
|
"""
|
||||||
self.prompt_template = PromptTemplate()
|
self.prompt_template = PromptTemplate()
|
||||||
|
self.tlp_ver = tpl_ver
|
||||||
self.executed = False
|
self.executed = False
|
||||||
self.continuous = Config().continuous_mode
|
self.continuous = Config().continuous_mode
|
||||||
self.logger = FinCoLog()
|
self.logger = FinCoLog()
|
||||||
@@ -77,9 +89,9 @@ class Task:
|
|||||||
|
|
||||||
def interact(self, prompt: str, **kwargs) -> Any:
|
def interact(self, prompt: str, **kwargs) -> Any:
|
||||||
"""
|
"""
|
||||||
The user can interact with the task. This method only handle business in current task. It will return True
|
The user can interact with the task. This method only handle business in current task. It will return True
|
||||||
while continuous is True. This method will return user input if input cannot be parsed as 'yes' or 'no'.
|
while continuous is True. This method will return user input if input cannot be parsed as 'yes' or 'no'.
|
||||||
@return True, False, str
|
@return True, False, str
|
||||||
"""
|
"""
|
||||||
self.logger.info(title="Interact")
|
self.logger.info(title="Interact")
|
||||||
if self.continuous:
|
if self.continuous:
|
||||||
@@ -100,11 +112,17 @@ class Task:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def system(self):
|
def system(self):
|
||||||
return self.prompt_template.get(self.__class__.__name__ + "_system")
|
key = self.__class__.__name__ + "_system"
|
||||||
|
if self.tlp_ver is not None:
|
||||||
|
key = key + "." + self.tlp_ver
|
||||||
|
return self.prompt_template.get(key)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user(self):
|
def user(self):
|
||||||
return self.prompt_template.get(self.__class__.__name__ + "_user")
|
key = self.__class__.__name__ + "_user"
|
||||||
|
if self.tlp_ver is not None:
|
||||||
|
key = key + "." + self.tlp_ver
|
||||||
|
return self.prompt_template.get(key)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.__class__.__name__
|
return self.__class__.__name__
|
||||||
@@ -123,9 +141,7 @@ class WorkflowTask(Task):
|
|||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
response = APIBackend().build_messages_and_create_chat_completion(
|
||||||
prompt_workflow_selection, self.system.render()
|
prompt_workflow_selection, self.system.render()
|
||||||
)
|
)
|
||||||
self.save_chat_history_to_context_manager(
|
self.save_chat_history_to_context_manager(prompt_workflow_selection, response, self.system.render())
|
||||||
prompt_workflow_selection, response, self.system.render()
|
|
||||||
)
|
|
||||||
workflow = response.split(":")[1].strip().lower()
|
workflow = response.split(":")[1].strip().lower()
|
||||||
self.executed = True
|
self.executed = True
|
||||||
self._context_manager.set_context("workflow", workflow)
|
self._context_manager.set_context("workflow", workflow)
|
||||||
@@ -156,13 +172,21 @@ class HighLevelPlanTask(PlanTask):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
|
|
||||||
self._context_manager.set_context("target", "minimizing the maximum drawdown")
|
self._context_manager.set_context("target", "minimizing the maximum drawdown")
|
||||||
self._context_manager.set_context("deliverable", "a daily quantitative investment strategy in A-share stock market. A model will be included in the strategy.")
|
self._context_manager.set_context(
|
||||||
self._context_manager.set_context("user_intention", "build an A-share stock market daily portfolio in quantitative investment and minimize the maximum drawdown.")
|
"deliverable",
|
||||||
|
"a daily quantitative investment strategy in A-share stock market. A model will be included in the strategy.",
|
||||||
|
)
|
||||||
|
self._context_manager.set_context(
|
||||||
|
"user_intention",
|
||||||
|
"build an A-share stock market daily portfolio in quantitative investment and minimize the maximum drawdown.",
|
||||||
|
)
|
||||||
self._context_manager.set_context("business_level", "Controller(e.g. Rolling retrain), Data")
|
self._context_manager.set_context("business_level", "Controller(e.g. Rolling retrain), Data")
|
||||||
self._context_manager.set_context("algorithm_level", "supervised learning")
|
self._context_manager.set_context("algorithm_level", "supervised learning")
|
||||||
self._context_manager.set_context("thinking_detail", "We want to leverage more recent data than outdated data. So we have to compare with or without rolling training process of the model like a meta controller. When with a rolling training process, data will be different at each time.")
|
self._context_manager.set_context(
|
||||||
|
"thinking_detail",
|
||||||
|
"We want to leverage more recent data than outdated data. So we have to compare with or without rolling training process of the model like a meta controller. When with a rolling training process, data will be different at each time.",
|
||||||
|
)
|
||||||
|
|
||||||
target = self._context_manager.get_context("target")
|
target = self._context_manager.get_context("target")
|
||||||
deliverable = self._context_manager.get_context("deliverable")
|
deliverable = self._context_manager.get_context("deliverable")
|
||||||
@@ -182,24 +206,26 @@ class HighLevelPlanTask(PlanTask):
|
|||||||
finance_knowledge = KnowledgeBase().query(knowledge_type=KnowledgeBase.KT_FINANCE, content=user_intention)
|
finance_knowledge = KnowledgeBase().query(knowledge_type=KnowledgeBase.KT_FINANCE, content=user_intention)
|
||||||
|
|
||||||
system_prompt = self.system.render()
|
system_prompt = self.system.render()
|
||||||
user_prompt = self.user.render(target=target, deliverable=deliverable, business_level=business_level,
|
user_prompt = self.user.render(
|
||||||
algorithm_level=algorithm_level, thinking_detail=thinking_detail,
|
target=target,
|
||||||
practice_knowledge=practice_knowledge, finance_knowledge=finance_knowledge,
|
deliverable=deliverable,
|
||||||
user_intention=user_intention)
|
business_level=business_level,
|
||||||
|
algorithm_level=algorithm_level,
|
||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
thinking_detail=thinking_detail,
|
||||||
user_prompt, system_prompt
|
practice_knowledge=practice_knowledge,
|
||||||
|
finance_knowledge=finance_knowledge,
|
||||||
|
user_intention=user_intention,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.save_chat_history_to_context_manager(
|
response = APIBackend().build_messages_and_create_chat_completion(user_prompt, system_prompt)
|
||||||
user_prompt, response, system_prompt
|
|
||||||
)
|
response = APIBackend().build_messages_and_create_chat_completion(user_prompt, system_prompt)
|
||||||
|
|
||||||
|
self.save_chat_history_to_context_manager(user_prompt, response, system_prompt)
|
||||||
|
|
||||||
assert response is not None, "The response is None"
|
assert response is not None, "The response is None"
|
||||||
|
|
||||||
res = re.search(
|
res = re.search(r"Workflow:(.*)Experiments:(.*)Metrics:(.*)", response, re.S)
|
||||||
r"Workflow:(.*)Experiments:(.*)Metrics:(.*)", response, re.S
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
res is not None and len(res.groups()) == 3
|
res is not None and len(res.groups()) == 3
|
||||||
@@ -225,7 +251,7 @@ class SLPlanTask(PlanTask):
|
|||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
workflow = self._context_manager.get_context("high_level_workflow")
|
workflow = self._context_manager.get_context("high_level_workflow")
|
||||||
assert (workflow.lower() == "supervised learning"), "The workflow is not supervised learning"
|
assert workflow.lower() == "supervised learning", "The workflow is not supervised learning"
|
||||||
|
|
||||||
target = self._context_manager.get_context("target")
|
target = self._context_manager.get_context("target")
|
||||||
deliverable = self._context_manager.get_context("deliverable")
|
deliverable = self._context_manager.get_context("deliverable")
|
||||||
@@ -237,30 +263,35 @@ class SLPlanTask(PlanTask):
|
|||||||
|
|
||||||
experiment_count = max([i for i in range(10) if f"{i}." in experiments])
|
experiment_count = max([i for i in range(10) if f"{i}." in experiments])
|
||||||
|
|
||||||
infrastructure_knowledge = KnowledgeBase().query(knowledge_type=KnowledgeBase.KT_INFRASTRUCTURE,
|
infrastructure_knowledge = KnowledgeBase().query(
|
||||||
content=experiments)
|
knowledge_type=KnowledgeBase.KT_INFRASTRUCTURE, content=experiments
|
||||||
|
)
|
||||||
|
|
||||||
system_prompt = self.system.render()
|
system_prompt = self.system.render()
|
||||||
user_prompt = self.user.render(target=target, deliverable=deliverable, business_level=business_level,
|
user_prompt = self.user.render(
|
||||||
algorithm_level=algorithm_level, thinking_detail=thinking_detail,
|
target=target,
|
||||||
infrastructure_knowledge=infrastructure_knowledge,
|
deliverable=deliverable,
|
||||||
user_intention=user_intention, experiments=experiments)
|
business_level=business_level,
|
||||||
|
algorithm_level=algorithm_level,
|
||||||
|
thinking_detail=thinking_detail,
|
||||||
|
infrastructure_knowledge=infrastructure_knowledge,
|
||||||
|
user_intention=user_intention,
|
||||||
|
experiments=experiments,
|
||||||
|
)
|
||||||
|
|
||||||
former_messages = []
|
former_messages = []
|
||||||
if self.replan:
|
if self.replan:
|
||||||
user_prompt = f"your choice of predefined classes cannot be initialized.\nPlease rewrite the plan and answer with exact required format in system prompt and reply with no more explainations.\nThe error message: {self.error}. Please correct the former answer accordingly."
|
user_prompt = f"your choice of predefined classes cannot be initialized.\nPlease rewrite the plan and answer with exact required format in system prompt and reply with no more explainations.\nThe error message: {self.error}. Please correct the former answer accordingly."
|
||||||
former_messages = self._context_manager.get_context("chat_history")[self.__class__.__name__]['None'][1:]
|
former_messages = self._context_manager.get_context("chat_history")[self.__class__.__name__]["None"][1:]
|
||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
response = APIBackend().build_messages_and_create_chat_completion(
|
||||||
user_prompt, system_prompt, former_messages=former_messages
|
user_prompt, system_prompt, former_messages=former_messages
|
||||||
)
|
)
|
||||||
self.save_chat_history_to_context_manager(
|
self.save_chat_history_to_context_manager(user_prompt, system_prompt, self.system.render())
|
||||||
user_prompt, system_prompt, self.system.render()
|
|
||||||
)
|
|
||||||
for i in range(1, experiment_count + 1):
|
for i in range(1, experiment_count + 1):
|
||||||
assert f"Experiment {i}" in response, f"The experiment {i} is not found in the response"
|
assert f"Experiment {i}" in response, f"The experiment {i} is not found in the response"
|
||||||
self._context_manager.set_context("experiment_count", experiment_count)
|
self._context_manager.set_context("experiment_count", experiment_count)
|
||||||
|
|
||||||
decision_pattern = re.compile("\((.*?)\)")
|
decision_pattern = re.compile(r"\((.*?)\)")
|
||||||
class_pattern = re.compile("{(.*?)}-{(.*?)}")
|
class_pattern = re.compile("{(.*?)}-{(.*?)}")
|
||||||
new_task = []
|
new_task = []
|
||||||
|
|
||||||
@@ -270,8 +301,10 @@ class SLPlanTask(PlanTask):
|
|||||||
re_pattern = re_pattern + "Difference:(.*)"
|
re_pattern = re_pattern + "Difference:(.*)"
|
||||||
re_pattern = re.compile(re_pattern, re.S)
|
re_pattern = re.compile(re_pattern, re.S)
|
||||||
# 1) CURD on the workspace
|
# 1) CURD on the workspace
|
||||||
|
self._context_manager
|
||||||
match_res = re.search(re_pattern, response)
|
match_res = re.search(re_pattern, response)
|
||||||
for experiment_id in range(1, experiment_count + 1):
|
for experiment_id in range(1, experiment_count + 1):
|
||||||
|
exp = Exp()
|
||||||
for name in COMPONENT_LIST:
|
for name in COMPONENT_LIST:
|
||||||
target_line = [line for line in match_res.group(experiment_id).split("\n") if f"{name}:" in line]
|
target_line = [line for line in match_res.group(experiment_id).split("\n") if f"{name}:" in line]
|
||||||
assert len(target_line) == 1, f"The {name} is not found in the response"
|
assert len(target_line) == 1, f"The {name} is not found in the response"
|
||||||
@@ -289,27 +322,42 @@ class SLPlanTask(PlanTask):
|
|||||||
self._context_manager.set_context(f"{name}_experiment_{experiment_id}_decision", decision)
|
self._context_manager.set_context(f"{name}_experiment_{experiment_id}_decision", decision)
|
||||||
self._context_manager.set_context(f"{name}_experiment_{experiment_id}_classes", classes)
|
self._context_manager.set_context(f"{name}_experiment_{experiment_id}_classes", classes)
|
||||||
self._context_manager.set_context(f"{name}_experiment_{experiment_id}_plan", target_line)
|
self._context_manager.set_context(f"{name}_experiment_{experiment_id}_plan", target_line)
|
||||||
|
setattr(exp, name.lower(), Design(plan=target_line, classes=classes, decision=decision))
|
||||||
assert decision in ["Default", "Personized"], f"The decision of {name} is not correct"
|
assert decision in ["Default", "Personized"], f"The decision of {name} is not correct"
|
||||||
|
# TODO: the strctured experiments should replace
|
||||||
|
self._context_manager.struct_context.exp_list.append(exp)
|
||||||
|
|
||||||
# 1) create a workspace
|
# 1) create a workspace
|
||||||
# TODO: we have to make choice between `sl` and `sl-cfg`
|
# TODO: we have to make choice between `sl` and `sl-cfg`
|
||||||
new_task.append(
|
# new_task.append(
|
||||||
ConfigSearchTask()
|
# # ConfigSearchTask(get_tpl_path()), # select template from the tpl folder directly. The prompt does not align with the task
|
||||||
)
|
# ConfigSearchTask(), # select template from the baselines.
|
||||||
|
# )
|
||||||
|
|
||||||
|
# Because selecting template is not that stable. We try to start with
|
||||||
|
cfg_tpl = get_tpl_path() / "sl" / "workflow_config.yaml"
|
||||||
|
new_task.append(CMDTask(f"make a directory in the '{self._context_manager.struct_context.workspace}'"))
|
||||||
|
for i, exp in enumerate(self._context_manager.struct_context.exp_list, 1):
|
||||||
|
exp.template = cfg_tpl
|
||||||
|
new_task.append(
|
||||||
|
CMDTask(
|
||||||
|
f"copy the file '{cfg_tpl}' to '{self._context_manager.struct_context.workspace}' and rename to experiment_{i}.yaml"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# for name in COMPONENT_LIST:
|
# for name in COMPONENT_LIST:
|
||||||
# if decision == "Default":
|
# if decision == "Default":
|
||||||
new_task.extend([HyperparameterFinetuneActionTask()])
|
new_task.extend([HyperparameterFinetuneActionTask()])
|
||||||
# elif decision == "Personized":
|
# elif decision == "Personized":
|
||||||
# # TODO open ImplementActionTask to let GPT write code
|
# # TODO open ImplementActionTask to let GPT write code
|
||||||
# new_task.extend([HyperparameterActionTask(name), ConfigActionTask(name), YamlEditTask(name)])
|
# new_task.extend([HyperparameterActionTask(name), ConfigActionTask(name), YamlEditTask(name)])
|
||||||
# # new_task.extend([HyperparameterActionTask(name), ConfigActionTask(name), ImplementActionTask(name), CodeDumpTask(name), YamlEditTask(name)])
|
# # new_task.extend([HyperparameterActionTask(name), ConfigActionTask(name), ImplementActionTask(name), CodeDumpTask(name), YamlEditTask(name)])
|
||||||
return new_task
|
return new_task
|
||||||
|
|
||||||
|
|
||||||
class RLPlanTask(PlanTask):
|
class RLPlanTask(PlanTask):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.logger.error("The RL task is not implemented yet")
|
self.logger.error("The RL task is not implemented yet")
|
||||||
@@ -328,7 +376,7 @@ class TrainTask(Task):
|
|||||||
This train task is responsible for training model configure by yaml file.
|
This train task is responsible for training model configure by yaml file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, experiment_index, rolling = False, ddgda=False, **kwargs) -> None:
|
def __init__(self, experiment_index, rolling=False, ddgda=False, **kwargs) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._output = None
|
self._output = None
|
||||||
self._experiment_index = experiment_index
|
self._experiment_index = experiment_index
|
||||||
@@ -344,41 +392,74 @@ class TrainTask(Task):
|
|||||||
workflow = yaml.safe_load(f)
|
workflow = yaml.safe_load(f)
|
||||||
self._context_manager.set_context(f"workflow_{self._experiment_index}_yaml", workflow)
|
self._context_manager.set_context(f"workflow_{self._experiment_index}_yaml", workflow)
|
||||||
|
|
||||||
confirm = self.interact(f"I select this workflow file: "
|
confirm = self.interact(
|
||||||
f"{LogColors().render(workflow_path, color=LogColors.YELLOW, style=LogColors.BOLD)}\n"
|
f"I select this workflow file: "
|
||||||
f"{yaml.dump(workflow, default_flow_style=False)}"
|
f"{LogColors().render(workflow_path, color=LogColors.YELLOW, style=LogColors.BOLD)}\n"
|
||||||
f"Are you sure you want to use? yes(Y/y), no(N/n):")
|
f"{yaml.dump(workflow, default_flow_style=False)}"
|
||||||
|
f"Are you sure you want to use? yes(Y/y), no(N/n):"
|
||||||
|
)
|
||||||
if confirm is False:
|
if confirm is False:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
command = ["qrun", str(workflow_path)]
|
if not self._rolling:
|
||||||
try:
|
command = ["qrun", str(workflow_path)]
|
||||||
# Run the command and capture the output
|
try:
|
||||||
workspace = self._context_manager.get_context("workspace")
|
# Run the command and capture the output
|
||||||
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True,
|
workspace = self._context_manager.get_context("workspace")
|
||||||
text=True, encoding="utf8", cwd=str(workspace))
|
_ = subprocess.run(
|
||||||
|
command,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
check=True,
|
||||||
|
text=True,
|
||||||
|
encoding="utf8",
|
||||||
|
cwd=str(workspace),
|
||||||
|
)
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
print(f"An error occurred while running the subprocess: {e.stderr} {e.stdout}")
|
print(f"An error occurred while running the subprocess: {e.stderr} {e.stdout}")
|
||||||
real_error = e.stderr+e.stdout
|
real_error = e.stderr + e.stdout
|
||||||
KnowledgeBase().execute_knowledge.add([real_error])
|
KnowledgeBase().execute_knowledge.add([real_error])
|
||||||
|
|
||||||
if "data" in e.stdout.lower() or "handler" in e.stdout.lower():
|
if "data" in e.stdout.lower() or "handler" in e.stdout.lower():
|
||||||
return [HyperparameterActionTask("Dataset", regenerate=True, error=real_error),
|
return [
|
||||||
|
HyperparameterActionTask("Dataset", regenerate=True, error=real_error),
|
||||||
HyperparameterActionTask("DataHandler", regenerate=True, error=real_error),
|
HyperparameterActionTask("DataHandler", regenerate=True, error=real_error),
|
||||||
ConfigActionTask("Dataset"), ConfigActionTask("DataHandler"), YamlEditTask("Dataset"),
|
ConfigActionTask("Dataset"),
|
||||||
YamlEditTask("DataHandler"), TrainTask()]
|
ConfigActionTask("DataHandler"),
|
||||||
elif "model" in e.stdout.lower():
|
YamlEditTask("Dataset"),
|
||||||
return [HyperparameterActionTask("Model", regenerate=True, error=real_error),
|
YamlEditTask("DataHandler"),
|
||||||
ConfigActionTask("Model"), YamlEditTask("Model"), TrainTask()]
|
TrainTask(),
|
||||||
else:
|
]
|
||||||
ret_list = []
|
elif "model" in e.stdout.lower():
|
||||||
for component in COMPONENT_LIST:
|
return [
|
||||||
ret_list.append(HyperparameterActionTask(component, regenerate=True, error=real_error))
|
HyperparameterActionTask("Model", regenerate=True, error=real_error),
|
||||||
ret_list.append(ConfigActionTask(component))
|
ConfigActionTask("Model"),
|
||||||
ret_list.append(YamlEditTask(component))
|
YamlEditTask("Model"),
|
||||||
ret_list.append(TrainTask())
|
TrainTask(),
|
||||||
return ret_list
|
]
|
||||||
|
else:
|
||||||
|
ret_list = []
|
||||||
|
for component in COMPONENT_LIST:
|
||||||
|
ret_list.append(HyperparameterActionTask(component, regenerate=True, error=real_error))
|
||||||
|
ret_list.append(ConfigActionTask(component))
|
||||||
|
ret_list.append(YamlEditTask(component))
|
||||||
|
ret_list.append(TrainTask())
|
||||||
|
return ret_list
|
||||||
|
elif not self._ddgda:
|
||||||
|
command = f"python -m qlib.contrib.rolling base --conf_path {workflow_path} run"
|
||||||
|
# Run the command and capture the output
|
||||||
|
workspace = self._context_manager.struct_context.workspace
|
||||||
|
subprocess.run(
|
||||||
|
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text=True, cwd=str(workspace)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
command = f"python -m qlib.contrib.rolling ddgda --conf_path {workflow_path} run"
|
||||||
|
# Run the command and capture the output
|
||||||
|
workspace = self._context_manager.struct_context.workspace
|
||||||
|
subprocess.run(
|
||||||
|
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text=True, cwd=str(workspace)
|
||||||
|
)
|
||||||
|
|
||||||
return [AnalysisTask()]
|
return [AnalysisTask()]
|
||||||
|
|
||||||
@@ -386,9 +467,7 @@ class TrainTask(Task):
|
|||||||
if self._output is not None:
|
if self._output is not None:
|
||||||
# TODO: it will be overrides by later commands
|
# TODO: it will be overrides by later commands
|
||||||
# utf8 can't decode normally on Windows
|
# utf8 can't decode normally on Windows
|
||||||
self._context_manager.set_context(
|
self._context_manager.set_context(self.__class__.__name__, self._output.decode("ANSI"))
|
||||||
self.__class__.__name__, self._output.decode("ANSI")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AnalysisTask(Task):
|
class AnalysisTask(Task):
|
||||||
@@ -415,9 +494,7 @@ class AnalysisTask(Task):
|
|||||||
self._context_manager.set_context(k, v)
|
self._context_manager.set_context(k, v)
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
prompt = self.user.render(
|
prompt = self.user.render(user_prompt=self._context_manager.get_context("user_prompt"))
|
||||||
user_prompt=self._context_manager.get_context("user_prompt")
|
|
||||||
)
|
|
||||||
be = APIBackend()
|
be = APIBackend()
|
||||||
be.debug_mode = False
|
be.debug_mode = False
|
||||||
|
|
||||||
@@ -430,8 +507,9 @@ class AnalysisTask(Task):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
analysers = response.replace(" ", "").split(",")
|
analysers = response.replace(" ", "").split(",")
|
||||||
confirm = self.interact(f"I select these analysers: {analysers}\n"
|
confirm = self.interact(
|
||||||
f"Are you sure you want to use? yes(Y/y), no(N/n) or prompt:")
|
f"I select these analysers: {analysers}\n" f"Are you sure you want to use? yes(Y/y), no(N/n) or prompt:"
|
||||||
|
)
|
||||||
if confirm is False:
|
if confirm is False:
|
||||||
analysers = []
|
analysers = []
|
||||||
break
|
break
|
||||||
@@ -452,15 +530,14 @@ class AnalysisTask(Task):
|
|||||||
|
|
||||||
# todo: analysis multi experiment(get recorder by id)
|
# todo: analysis multi experiment(get recorder by id)
|
||||||
experiment_name = "workflow"
|
experiment_name = "workflow"
|
||||||
R.set_uri(Path.joinpath(workspace, 'mlruns').as_uri())
|
R.set_uri(Path.joinpath(workspace, "mlruns").as_uri())
|
||||||
|
|
||||||
tasks = []
|
tasks = []
|
||||||
for analyser in analysers:
|
for analyser in analysers:
|
||||||
if analyser in self.__ANALYZERS_PROJECT.keys():
|
if analyser in self.__ANALYZERS_PROJECT.keys():
|
||||||
tasks.append(
|
tasks.append(
|
||||||
self.__ANALYZERS_PROJECT.get(analyser)(
|
self.__ANALYZERS_PROJECT.get(analyser)(
|
||||||
recorder=R.get_recorder(experiment_name=experiment_name),
|
recorder=R.get_recorder(experiment_name=experiment_name), output_dir=workspace
|
||||||
output_dir=workspace
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -475,12 +552,21 @@ class ActionTask(Task):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigSearchTask(ActionTask):
|
class ConfigSearchTask(ActionTask):
|
||||||
def __init__(self):
|
"""Find a template path that user can start with."""
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
def crawl_the_folder(self, folder_path : Path):
|
def __init__(self, conf_path: Optional[Union[Path, str]] = None):
|
||||||
|
super().__init__()
|
||||||
|
if conf_path is None:
|
||||||
|
# If no path provided, find path from the templates.
|
||||||
|
import qlib
|
||||||
|
|
||||||
|
conf_path = Path(os.path.abspath(inspect.getfile(qlib))).parent.parent / "examples" / "benchmarks"
|
||||||
|
if isinstance(conf_path, str):
|
||||||
|
conf_path = Path(conf_path)
|
||||||
|
self.conf_path = conf_path
|
||||||
|
|
||||||
|
def crawl_the_folder(self, folder_path: Path):
|
||||||
yaml_files = []
|
yaml_files = []
|
||||||
for root, _, files in os.walk(folder_path.as_posix()):
|
for root, _, files in os.walk(folder_path.as_posix()):
|
||||||
for file in files:
|
for file in files:
|
||||||
@@ -505,23 +591,18 @@ class ConfigSearchTask(ActionTask):
|
|||||||
|
|
||||||
experiments.append((experiment_id, dataset_class, datahandler_class, model_class))
|
experiments.append((experiment_id, dataset_class, datahandler_class, model_class))
|
||||||
|
|
||||||
import qlib
|
# TODO: each config should contains some descriptions to provide information to make the choice.
|
||||||
benchmarks_root_path = Path(os.path.abspath(inspect.getfile(qlib))).parent.parent / "examples" / "benchmarks"
|
yaml_config_list = self.crawl_the_folder(self.conf_path)
|
||||||
yaml_config_list = self.crawl_the_folder(benchmarks_root_path)
|
|
||||||
|
|
||||||
system_prompt = self.system.render(yaml_config_list=yaml_config_list)
|
system_prompt = self.system.render(yaml_config_list=yaml_config_list)
|
||||||
user_prompt = self.user.render(experiments=experiments)
|
user_prompt = self.user.render(experiments=experiments)
|
||||||
|
|
||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
response = APIBackend().build_messages_and_create_chat_completion(user_prompt, system_prompt)
|
||||||
user_prompt, system_prompt
|
|
||||||
)
|
|
||||||
former_messages = []
|
former_messages = []
|
||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
response = APIBackend().build_messages_and_create_chat_completion(
|
||||||
user_prompt, self.system.render(), former_messages=former_messages
|
user_prompt, self.system.render(), former_messages=former_messages
|
||||||
)
|
)
|
||||||
self.save_chat_history_to_context_manager(
|
self.save_chat_history_to_context_manager(user_prompt, response, self.system.render())
|
||||||
user_prompt, response, self.system.render()
|
|
||||||
)
|
|
||||||
|
|
||||||
experiment_count = self._context_manager.get_context("experiment_count")
|
experiment_count = self._context_manager.get_context("experiment_count")
|
||||||
|
|
||||||
@@ -532,13 +613,22 @@ class ConfigSearchTask(ActionTask):
|
|||||||
|
|
||||||
config_search_result = config_search_pattern.search(response)
|
config_search_result = config_search_pattern.search(response)
|
||||||
|
|
||||||
return_task = [CMDTask(f"make a directory in the {self._context_manager.get_context('workspace')}"), ]
|
return_task = [
|
||||||
|
CMDTask(f"make a directory in the {self._context_manager.get_context('workspace')}"),
|
||||||
|
]
|
||||||
for experiment_id in range(1, experiment_count + 1):
|
for experiment_id in range(1, experiment_count + 1):
|
||||||
self._context_manager.set_context(f"experiment_{experiment_id}_template_config", config_search_result.group(experiment_id).strip('\n'))
|
self._context_manager.set_context(
|
||||||
config_location = benchmarks_root_path / config_search_result.group(experiment_id)
|
f"experiment_{experiment_id}_template_config", config_search_result.group(experiment_id).strip("\n")
|
||||||
return_task.append(CMDTask(f"copy file in {config_location} to {self._context_manager.get_context('workspace')} and rename to experiment_{experiment_id}.yaml"))
|
)
|
||||||
|
config_location = self.conf_path / config_search_result.group(experiment_id)
|
||||||
|
return_task.append(
|
||||||
|
CMDTask(
|
||||||
|
f"copy file in {config_location} to {self._context_manager.get_context('workspace')} and rename to experiment_{experiment_id}.yaml"
|
||||||
|
)
|
||||||
|
)
|
||||||
return return_task
|
return return_task
|
||||||
|
|
||||||
|
|
||||||
class CMDTask(ActionTask):
|
class CMDTask(ActionTask):
|
||||||
"""
|
"""
|
||||||
This CMD task is responsible for ensuring compatibility across different operating systems.
|
This CMD task is responsible for ensuring compatibility across different operating systems.
|
||||||
@@ -551,12 +641,8 @@ class CMDTask(ActionTask):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
prompt = self.user.render(
|
prompt = self.user.render(cmd_intention=self.cmd_intention, user_os=platform.system())
|
||||||
cmd_intention=self.cmd_intention, user_os=platform.system()
|
response = APIBackend().build_messages_and_create_chat_completion(prompt, self.system.render())
|
||||||
)
|
|
||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
|
||||||
prompt, self.system.render()
|
|
||||||
)
|
|
||||||
self._output = subprocess.check_output(response, shell=True, cwd=self.cwd)
|
self._output = subprocess.check_output(response, shell=True, cwd=self.cwd)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -564,9 +650,7 @@ class CMDTask(ActionTask):
|
|||||||
if self._output is not None:
|
if self._output is not None:
|
||||||
# TODO: it will be overrides by later commands
|
# TODO: it will be overrides by later commands
|
||||||
# utf8 can't decode normally on Windows
|
# utf8 can't decode normally on Windows
|
||||||
self._context_manager.set_context(
|
self._context_manager.set_context(self.__class__.__name__, self._output.decode("ANSI"))
|
||||||
self.__class__.__name__, self._output.decode("ANSI")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class HyperparameterFinetuneActionTask(ActionTask):
|
class HyperparameterFinetuneActionTask(ActionTask):
|
||||||
@@ -600,11 +684,9 @@ class HyperparameterFinetuneActionTask(ActionTask):
|
|||||||
thinking_detail=thinking_detail,
|
thinking_detail=thinking_detail,
|
||||||
user_intention=user_intention,
|
user_intention=user_intention,
|
||||||
experiments=experiments,
|
experiments=experiments,
|
||||||
template_configs=template_configs
|
template_configs=template_configs,
|
||||||
)
|
|
||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
|
||||||
user_prompt, system_prompt
|
|
||||||
)
|
)
|
||||||
|
response = APIBackend().build_messages_and_create_chat_completion(user_prompt, system_prompt)
|
||||||
|
|
||||||
config_search_pattern = ""
|
config_search_pattern = ""
|
||||||
for experiment_id in range(1, experiment_count + 1):
|
for experiment_id in range(1, experiment_count + 1):
|
||||||
@@ -614,13 +696,15 @@ class HyperparameterFinetuneActionTask(ActionTask):
|
|||||||
config_search_result = re.search(config_search_pattern, response)
|
config_search_result = re.search(config_search_pattern, response)
|
||||||
return_tasks = []
|
return_tasks = []
|
||||||
for experiment_id in range(1, experiment_count + 1):
|
for experiment_id in range(1, experiment_count + 1):
|
||||||
rolling_res = config_search_result.group((experiment_id-1) * 4 + 2).strip('\n')
|
rolling_res = config_search_result.group((experiment_id - 1) * 4 + 2).strip("\n")
|
||||||
ddgda_res = config_search_result.group((experiment_id-1) * 4 + 3).strip('\n')
|
ddgda_res = config_search_result.group((experiment_id - 1) * 4 + 3).strip("\n")
|
||||||
reason_res = config_search_result.group((experiment_id-1) * 4 + 4).strip('\n')
|
reason_res = config_search_result.group((experiment_id - 1) * 4 + 4).strip("\n")
|
||||||
if "true" in ddgda_res.lower():
|
if "true" in ddgda_res.lower():
|
||||||
return_tasks.append(TrainTask(experiment_id, rolling=True, ddgda=True))
|
return_tasks.append(TrainTask(experiment_id, rolling=True, ddgda=True))
|
||||||
|
self._context_manager.struct_context.exp_list[experiment_id - 1].rolling = "ddgda"
|
||||||
if "true" in rolling_res.lower():
|
if "true" in rolling_res.lower():
|
||||||
return_tasks.append(TrainTask(experiment_id, rolling=True))
|
return_tasks.append(TrainTask(experiment_id, rolling=True))
|
||||||
|
self._context_manager.struct_context.exp_list[experiment_id - 1].rolling = "base"
|
||||||
else:
|
else:
|
||||||
return_tasks.append(TrainTask(experiment_id))
|
return_tasks.append(TrainTask(experiment_id))
|
||||||
self._context_manager.set_context(f"experiment_{experiment_id}_rolling", rolling_res)
|
self._context_manager.set_context(f"experiment_{experiment_id}_rolling", rolling_res)
|
||||||
@@ -652,24 +736,63 @@ class HyperparameterActionTask(ActionTask):
|
|||||||
assert target_component_plan is not None, "target component plan is not set by plan maker"
|
assert target_component_plan is not None, "target component plan is not set by plan maker"
|
||||||
assert target_component_classes is not None, "target component classes is not set by plan maker"
|
assert target_component_classes is not None, "target component classes is not set by plan maker"
|
||||||
|
|
||||||
system_prompt = self.system.render(target_module=self.target_component, choice=target_component_decision, classes=target_component_classes)
|
system_prompt = self.system.render(
|
||||||
|
target_module=self.target_component, choice=target_component_decision, classes=target_component_classes
|
||||||
|
)
|
||||||
|
|
||||||
target_component_classes_and_hyperparameters = []
|
target_component_classes_and_hyperparameters = []
|
||||||
for module_path, class_name in target_component_classes:
|
for module_path, class_name in target_component_classes:
|
||||||
exec(f"from {module_path} import {class_name}")
|
exec(f"from {module_path} import {class_name}")
|
||||||
hyperparameters = [hyperparameter for hyperparameter in {name: param for name, param in inspect.signature(eval(class_name).__init__).parameters.items() if name != "self" and name != "kwargs"}.keys()]
|
hyperparameters = [
|
||||||
|
hyperparameter
|
||||||
|
for hyperparameter in {
|
||||||
|
name: param
|
||||||
|
for name, param in inspect.signature(eval(class_name).__init__).parameters.items()
|
||||||
|
if name != "self" and name != "kwargs"
|
||||||
|
}.keys()
|
||||||
|
]
|
||||||
if class_name == "LGBModel":
|
if class_name == "LGBModel":
|
||||||
hyperparameters.extend([ "boosting_type", "num_leaves", "max_depth", "learning_rate", "n_estimators", "objective", "class_weight", "min_split_gain", "min_child_weight", "min_child_samples", "subsample", "subsample_freq", "colsample_bytree", "reg_alpha", "reg_lambda", "random_state", "n_jobs", "silent", "importance_type", "early_stopping_round", "metric", "num_class", "is_unbalance", "bagging_seed", "verbosity", ])
|
hyperparameters.extend(
|
||||||
|
[
|
||||||
|
"boosting_type",
|
||||||
|
"num_leaves",
|
||||||
|
"max_depth",
|
||||||
|
"learning_rate",
|
||||||
|
"n_estimators",
|
||||||
|
"objective",
|
||||||
|
"class_weight",
|
||||||
|
"min_split_gain",
|
||||||
|
"min_child_weight",
|
||||||
|
"min_child_samples",
|
||||||
|
"subsample",
|
||||||
|
"subsample_freq",
|
||||||
|
"colsample_bytree",
|
||||||
|
"reg_alpha",
|
||||||
|
"reg_lambda",
|
||||||
|
"random_state",
|
||||||
|
"n_jobs",
|
||||||
|
"silent",
|
||||||
|
"importance_type",
|
||||||
|
"early_stopping_round",
|
||||||
|
"metric",
|
||||||
|
"num_class",
|
||||||
|
"is_unbalance",
|
||||||
|
"bagging_seed",
|
||||||
|
"verbosity",
|
||||||
|
]
|
||||||
|
)
|
||||||
elif class_name == "SignalRecord":
|
elif class_name == "SignalRecord":
|
||||||
hyperparameters.remove("model")
|
hyperparameters.remove("model")
|
||||||
hyperparameters.remove("dataset")
|
hyperparameters.remove("dataset")
|
||||||
hyperparameters.remove("recorder")
|
hyperparameters.remove("recorder")
|
||||||
target_component_classes_and_hyperparameters.append((module_path, class_name, hyperparameters))
|
target_component_classes_and_hyperparameters.append((module_path, class_name, hyperparameters))
|
||||||
|
|
||||||
execute_knowledge = KnowledgeBase().query(knowledge_type=KnowledgeBase.KT_EXECUTE,
|
execute_knowledge = KnowledgeBase().query(
|
||||||
content=target_component_plan)
|
knowledge_type=KnowledgeBase.KT_EXECUTE, content=target_component_plan
|
||||||
infrastructure_knowledge = KnowledgeBase().query(knowledge_type=KnowledgeBase.KT_INFRASTRUCTURE,
|
)
|
||||||
content=target_component_plan)
|
infrastructure_knowledge = KnowledgeBase().query(
|
||||||
|
knowledge_type=KnowledgeBase.KT_INFRASTRUCTURE, content=target_component_plan
|
||||||
|
)
|
||||||
|
|
||||||
user_prompt = self.user.render(
|
user_prompt = self.user.render(
|
||||||
user_requirement=user_prompt,
|
user_requirement=user_prompt,
|
||||||
@@ -677,7 +800,7 @@ class HyperparameterActionTask(ActionTask):
|
|||||||
target_component=self.target_component,
|
target_component=self.target_component,
|
||||||
target_component_classes_and_hyperparameters=target_component_classes_and_hyperparameters,
|
target_component_classes_and_hyperparameters=target_component_classes_and_hyperparameters,
|
||||||
execute_knowledge=execute_knowledge,
|
execute_knowledge=execute_knowledge,
|
||||||
infrastructure_knowledge=infrastructure_knowledge
|
infrastructure_knowledge=infrastructure_knowledge,
|
||||||
)
|
)
|
||||||
former_messages = []
|
former_messages = []
|
||||||
if self.regenerate:
|
if self.regenerate:
|
||||||
@@ -685,16 +808,14 @@ class HyperparameterActionTask(ActionTask):
|
|||||||
user_prompt = f"your yaml config generated from your hyperparameter is not in the right format.\n The Yaml string generated from the hyperparameters is not in the right format.\nPlease rewrite the hyperparameters and answer with exact required format in system prompt and reply with no more explainations.\nThe error message: {self.error}. Please correct the former answer accordingly.\nHyperparameters, Reason and Improve suggestion should always be included."
|
user_prompt = f"your yaml config generated from your hyperparameter is not in the right format.\n The Yaml string generated from the hyperparameters is not in the right format.\nPlease rewrite the hyperparameters and answer with exact required format in system prompt and reply with no more explainations.\nThe error message: {self.error}. Please correct the former answer accordingly.\nHyperparameters, Reason and Improve suggestion should always be included."
|
||||||
else:
|
else:
|
||||||
user_prompt = f"your hyperparameter cannot be initialized, may be caused by wrong format of the value or wrong name or some value is not supported in Qlib.\nPlease rewrite the hyperparameters and answer with exact required format in system prompt and reply with no more explainations.\nThe error message: {self.error}. Please correct the former answer accordingly.\nHyperparameters, Reason and Improve suggestion should always be included."
|
user_prompt = f"your hyperparameter cannot be initialized, may be caused by wrong format of the value or wrong name or some value is not supported in Qlib.\nPlease rewrite the hyperparameters and answer with exact required format in system prompt and reply with no more explainations.\nThe error message: {self.error}. Please correct the former answer accordingly.\nHyperparameters, Reason and Improve suggestion should always be included."
|
||||||
former_messages = self._context_manager.get_context("chat_history")[self.__class__.__name__][self.target_component][1:]
|
former_messages = self._context_manager.get_context("chat_history")[self.__class__.__name__][
|
||||||
|
self.target_component
|
||||||
|
][1:]
|
||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
response = APIBackend().build_messages_and_create_chat_completion(
|
||||||
user_prompt, system_prompt, former_messages=former_messages
|
user_prompt, system_prompt, former_messages=former_messages
|
||||||
)
|
)
|
||||||
self.save_chat_history_to_context_manager(
|
self.save_chat_history_to_context_manager(user_prompt, response, system_prompt, self.target_component)
|
||||||
user_prompt, response, system_prompt, self.target_component
|
res = re.search(r"(?i)Hyperparameters:(.*)Reason:(.*)Improve suggestion:(.*)", response, re.S)
|
||||||
)
|
|
||||||
res = re.search(
|
|
||||||
r"(?i)Hyperparameters:(.*)Reason:(.*)Improve suggestion:(.*)", response, re.S
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
assert (
|
assert (
|
||||||
res is not None and len(res.groups()) == 3
|
res is not None and len(res.groups()) == 3
|
||||||
@@ -713,9 +834,7 @@ class HyperparameterActionTask(ActionTask):
|
|||||||
|
|
||||||
self._context_manager.set_context(f"{self.target_component}_hyperparameters", hyperparameters)
|
self._context_manager.set_context(f"{self.target_component}_hyperparameters", hyperparameters)
|
||||||
self._context_manager.set_context(f"{self.target_component}_reason", reason)
|
self._context_manager.set_context(f"{self.target_component}_reason", reason)
|
||||||
self._context_manager.set_context(
|
self._context_manager.set_context(f"{self.target_component}_improve_suggestion", improve_suggestion)
|
||||||
f"{self.target_component}_improve_suggestion", improve_suggestion
|
|
||||||
)
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
@@ -732,12 +851,14 @@ class ConfigActionTask(ActionTask):
|
|||||||
target_component_classes = self._context_manager.get_context(f"{self.target_component}_classes")
|
target_component_classes = self._context_manager.get_context(f"{self.target_component}_classes")
|
||||||
target_component_hyperparameters = self._context_manager.get_context(f"{self.target_component}_hyperparameters")
|
target_component_hyperparameters = self._context_manager.get_context(f"{self.target_component}_hyperparameters")
|
||||||
|
|
||||||
system_prompt = self.system.render(target_module=self.target_component, choice=target_component_decision, classes=target_component_classes)
|
system_prompt = self.system.render(
|
||||||
|
target_module=self.target_component, choice=target_component_decision, classes=target_component_classes
|
||||||
|
)
|
||||||
user_prompt = self.user.render(
|
user_prompt = self.user.render(
|
||||||
user_requirement=user_prompt,
|
user_requirement=user_prompt,
|
||||||
target_component_plan=target_component_plan,
|
target_component_plan=target_component_plan,
|
||||||
target_component=self.target_component,
|
target_component=self.target_component,
|
||||||
target_component_hyperparameters=target_component_hyperparameters
|
target_component_hyperparameters=target_component_hyperparameters,
|
||||||
)
|
)
|
||||||
former_messages = []
|
former_messages = []
|
||||||
# if self.reconfig and user_prompt == self._context_manager.get_context("chat_history")[self.__class__.__name__][self.target_component][-2]["content"]:
|
# if self.reconfig and user_prompt == self._context_manager.get_context("chat_history")[self.__class__.__name__][self.target_component][-2]["content"]:
|
||||||
@@ -746,35 +867,36 @@ class ConfigActionTask(ActionTask):
|
|||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
response = APIBackend().build_messages_and_create_chat_completion(
|
||||||
user_prompt, system_prompt, former_messages=former_messages
|
user_prompt, system_prompt, former_messages=former_messages
|
||||||
)
|
)
|
||||||
self.save_chat_history_to_context_manager(
|
self.save_chat_history_to_context_manager(user_prompt, response, system_prompt, self.target_component)
|
||||||
user_prompt, response, system_prompt, self.target_component
|
|
||||||
)
|
|
||||||
config = re.search(r"```yaml(.*)```", response, re.S).group(1)
|
config = re.search(r"```yaml(.*)```", response, re.S).group(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yaml_config = yaml.safe_load(io.StringIO(config))
|
yaml_config = yaml.safe_load(io.StringIO(config))
|
||||||
except yaml.YAMLError as e:
|
except yaml.YAMLError as e:
|
||||||
self.logger.info(f"Yaml file is not in the correct format: {e}")
|
self.logger.info(f"Yaml file is not in the correct format: {e}")
|
||||||
return_tasks = [HyperparameterActionTask(self.target_component, regenerate=True, error=str(e), error_type="yaml"), ConfigActionTask(self.target_component)]
|
return_tasks = [
|
||||||
|
HyperparameterActionTask(self.target_component, regenerate=True, error=str(e), error_type="yaml"),
|
||||||
|
ConfigActionTask(self.target_component),
|
||||||
|
]
|
||||||
return return_tasks
|
return return_tasks
|
||||||
|
|
||||||
if self.target_component == "Dataset":
|
if self.target_component == "Dataset":
|
||||||
if 'handler' in yaml_config["dataset"]:
|
if "handler" in yaml_config["dataset"]:
|
||||||
del yaml_config['dataset']['handler']
|
del yaml_config["dataset"]["handler"]
|
||||||
elif self.target_component == "DataHandler":
|
elif self.target_component == "DataHandler":
|
||||||
for processor in yaml_config['handler']['kwargs']['infer_processors']:
|
for processor in yaml_config["handler"]["kwargs"]["infer_processors"]:
|
||||||
if "kwargs" in processor and "fields_group" in processor["kwargs"]:
|
if "kwargs" in processor and "fields_group" in processor["kwargs"]:
|
||||||
del processor["kwargs"]['fields_group']
|
del processor["kwargs"]["fields_group"]
|
||||||
for processor in yaml_config['handler']['kwargs']['learn_processors']:
|
for processor in yaml_config["handler"]["kwargs"]["learn_processors"]:
|
||||||
if "kwargs" in processor and "fields_group" in processor["kwargs"]:
|
if "kwargs" in processor and "fields_group" in processor["kwargs"]:
|
||||||
del processor["kwargs"]['fields_group']
|
del processor["kwargs"]["fields_group"]
|
||||||
|
|
||||||
if 'freq' in yaml_config['handler']['kwargs']:
|
if "freq" in yaml_config["handler"]["kwargs"]:
|
||||||
yaml_config['handler']['kwargs']['freq'] = "day" # TODO hot fix freq because no data
|
yaml_config["handler"]["kwargs"]["freq"] = "day" # TODO hot fix freq because no data
|
||||||
elif self.target_component == "Record":
|
elif self.target_component == "Record":
|
||||||
for record in yaml_config['record']:
|
for record in yaml_config["record"]:
|
||||||
if record['class'] == 'SigAnaRecord' and 'label_col' in record['kwargs']:
|
if record["class"] == "SigAnaRecord" and "label_col" in record["kwargs"]:
|
||||||
del record['kwargs']["label_col"]
|
del record["kwargs"]["label_col"]
|
||||||
|
|
||||||
def remove_default(config):
|
def remove_default(config):
|
||||||
if isinstance(config, dict):
|
if isinstance(config, dict):
|
||||||
@@ -787,6 +909,7 @@ class ConfigActionTask(ActionTask):
|
|||||||
elif isinstance(config, list):
|
elif isinstance(config, list):
|
||||||
for item in config:
|
for item in config:
|
||||||
remove_default(item)
|
remove_default(item)
|
||||||
|
|
||||||
remove_default(yaml_config)
|
remove_default(yaml_config)
|
||||||
|
|
||||||
self._context_manager.set_context(f"{self.target_component}_config", yaml_config)
|
self._context_manager.set_context(f"{self.target_component}_config", yaml_config)
|
||||||
@@ -797,7 +920,9 @@ class ImplementActionTask(ActionTask):
|
|||||||
def __init__(self, target_component, reimplement=False) -> None:
|
def __init__(self, target_component, reimplement=False) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.target_component = target_component
|
self.target_component = target_component
|
||||||
assert COMPONENT_LIST.index(self.target_component) <= 2, "The target component is not in dataset datahandler and model"
|
assert (
|
||||||
|
COMPONENT_LIST.index(self.target_component) <= 2
|
||||||
|
), "The target component is not in dataset datahandler and model"
|
||||||
self.reimplement = reimplement
|
self.reimplement = reimplement
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
@@ -809,16 +934,10 @@ class ImplementActionTask(ActionTask):
|
|||||||
user_prompt = self._context_manager.get_context("user_prompt")
|
user_prompt = self._context_manager.get_context("user_prompt")
|
||||||
prompt_element_dict = dict()
|
prompt_element_dict = dict()
|
||||||
for component in COMPONENT_LIST:
|
for component in COMPONENT_LIST:
|
||||||
prompt_element_dict[
|
prompt_element_dict[f"{component}_decision"] = self._context_manager.get_context(f"{component}_decision")
|
||||||
f"{component}_decision"
|
prompt_element_dict[f"{component}_plan"] = self._context_manager.get_context(f"{component}_plan")
|
||||||
] = self._context_manager.get_context(f"{component}_decision")
|
|
||||||
prompt_element_dict[
|
|
||||||
f"{component}_plan"
|
|
||||||
] = self._context_manager.get_context(f"{component}_plan")
|
|
||||||
|
|
||||||
assert (
|
assert None not in prompt_element_dict.values(), "Some decision or plan is not set by plan maker"
|
||||||
None not in prompt_element_dict.values()
|
|
||||||
), "Some decision or plan is not set by plan maker"
|
|
||||||
config = self._context_manager.get_context(f"{self.target_component}_config")
|
config = self._context_manager.get_context(f"{self.target_component}_config")
|
||||||
|
|
||||||
implement_prompt = self.user.render(
|
implement_prompt = self.user.render(
|
||||||
@@ -830,7 +949,9 @@ class ImplementActionTask(ActionTask):
|
|||||||
former_messages = []
|
former_messages = []
|
||||||
if self.reimplement:
|
if self.reimplement:
|
||||||
implement_prompt = "your code seems wrong, please re-implement it and answer with exact required format and reply with no more explainations.\n"
|
implement_prompt = "your code seems wrong, please re-implement it and answer with exact required format and reply with no more explainations.\n"
|
||||||
former_messages = self._context_manager.get_context("chat_history")[self.__class__.__name__][self.target_component][1:]
|
former_messages = self._context_manager.get_context("chat_history")[self.__class__.__name__][
|
||||||
|
self.target_component
|
||||||
|
][1:]
|
||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
response = APIBackend().build_messages_and_create_chat_completion(
|
||||||
implement_prompt, self.system.render(), former_messages=former_messages
|
implement_prompt, self.system.render(), former_messages=former_messages
|
||||||
)
|
)
|
||||||
@@ -838,17 +959,13 @@ class ImplementActionTask(ActionTask):
|
|||||||
implement_prompt, response, self.system.render(), self.target_component
|
implement_prompt, response, self.system.render(), self.target_component
|
||||||
)
|
)
|
||||||
|
|
||||||
res = re.search(
|
res = re.search(r"Code:(.*)Explanation:(.*)Modified config:(.*)", response, re.S)
|
||||||
r"Code:(.*)Explanation:(.*)Modified config:(.*)", response, re.S
|
|
||||||
)
|
|
||||||
assert (
|
assert (
|
||||||
res is not None and len(res.groups()) == 3
|
res is not None and len(res.groups()) == 3
|
||||||
), f"The response of implement action task of component {self.target_component} is not in the correct format"
|
), f"The response of implement action task of component {self.target_component} is not in the correct format"
|
||||||
|
|
||||||
code = re.search(r"```python(.*)```", res.group(1), re.S)
|
code = re.search(r"```python(.*)```", res.group(1), re.S)
|
||||||
assert (
|
assert code is not None, "The code part of implementation action task response is not in the correct format"
|
||||||
code is not None
|
|
||||||
), "The code part of implementation action task response is not in the correct format"
|
|
||||||
code = code.group(1)
|
code = code.group(1)
|
||||||
explanation = res.group(2)
|
explanation = res.group(2)
|
||||||
modified_config = re.search(r"```yaml(.*)```", res.group(3), re.S)
|
modified_config = re.search(r"```yaml(.*)```", res.group(3), re.S)
|
||||||
@@ -858,12 +975,8 @@ class ImplementActionTask(ActionTask):
|
|||||||
modified_config = modified_config.group(1)
|
modified_config = modified_config.group(1)
|
||||||
|
|
||||||
self._context_manager.set_context(f"{self.target_component}_code", code)
|
self._context_manager.set_context(f"{self.target_component}_code", code)
|
||||||
self._context_manager.set_context(
|
self._context_manager.set_context(f"{self.target_component}_code_explanation", explanation)
|
||||||
f"{self.target_component}_code_explanation", explanation
|
self._context_manager.set_context(f"{self.target_component}_modified_config", modified_config)
|
||||||
)
|
|
||||||
self._context_manager.set_context(
|
|
||||||
f"{self.target_component}_modified_config", modified_config
|
|
||||||
)
|
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -908,15 +1021,16 @@ class YamlEditTask(ActionTask):
|
|||||||
res = res | self.replace_key_value_recursive(item, target_key, new_value)
|
res = res | self.replace_key_value_recursive(item, target_key, new_value)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
# 1) read original and new content
|
# 1) read original and new content
|
||||||
self.original_config_location = Path(os.path.join(self._context_manager.get_context('workspace'), "workflow_config.yaml"))
|
self.original_config_location = Path(
|
||||||
|
os.path.join(self._context_manager.get_context("workspace"), "workflow_config.yaml")
|
||||||
|
)
|
||||||
with self.original_config_location.open("r") as f:
|
with self.original_config_location.open("r") as f:
|
||||||
target_config = yaml.safe_load(f)
|
target_config = yaml.safe_load(f)
|
||||||
update_config = self._context_manager.get_context(f'{self.target_component}_modified_config')
|
update_config = self._context_manager.get_context(f"{self.target_component}_modified_config")
|
||||||
if update_config is None:
|
if update_config is None:
|
||||||
update_config = self._context_manager.get_context(f'{self.target_component}_config')
|
update_config = self._context_manager.get_context(f"{self.target_component}_config")
|
||||||
|
|
||||||
# 2) modify the module_path if code is implemented by finco
|
# 2) modify the module_path if code is implemented by finco
|
||||||
# TODO because we skip code writing part, so we mute this step to avoid error
|
# TODO because we skip code writing part, so we mute this step to avoid error
|
||||||
@@ -930,26 +1044,30 @@ class YamlEditTask(ActionTask):
|
|||||||
if self.target_component == "Dataset":
|
if self.target_component == "Dataset":
|
||||||
return []
|
return []
|
||||||
elif self.target_component == "DataHandler":
|
elif self.target_component == "DataHandler":
|
||||||
dataset_update_config = self._context_manager.get_context(f'Dataset_modified_config')
|
dataset_update_config = self._context_manager.get_context(f"Dataset_modified_config")
|
||||||
if dataset_update_config is None:
|
if dataset_update_config is None:
|
||||||
dataset_update_config = self._context_manager.get_context(f'Dataset_config')
|
dataset_update_config = self._context_manager.get_context(f"Dataset_config")
|
||||||
dataset_update_config['dataset']['kwargs']['handler'] = update_config['handler']
|
dataset_update_config["dataset"]["kwargs"]["handler"] = update_config["handler"]
|
||||||
update_config = dataset_update_config
|
update_config = dataset_update_config
|
||||||
real_target_config_key = "dataset"
|
real_target_config_key = "dataset"
|
||||||
else:
|
else:
|
||||||
real_target_config_key = self.target_config_key
|
real_target_config_key = self.target_config_key
|
||||||
|
|
||||||
# 3) replace the module
|
# 3) replace the module
|
||||||
assert isinstance(update_config, dict) and real_target_config_key in update_config, "The config file is not in the correct format"
|
assert (
|
||||||
assert self.replace_key_value_recursive(target_config, real_target_config_key, update_config[real_target_config_key]), "Replace of the yaml file failed."
|
isinstance(update_config, dict) and real_target_config_key in update_config
|
||||||
|
), "The config file is not in the correct format"
|
||||||
|
assert self.replace_key_value_recursive(
|
||||||
|
target_config, real_target_config_key, update_config[real_target_config_key]
|
||||||
|
), "Replace of the yaml file failed."
|
||||||
|
|
||||||
# TODO hotfix for the bug that the record signalrecord config is not updated
|
# TODO hotfix for the bug that the record signalrecord config is not updated
|
||||||
for record in target_config['task']['record']:
|
for record in target_config["task"]["record"]:
|
||||||
if record['class'] == 'SignalRecord':
|
if record["class"] == "SignalRecord":
|
||||||
if 'kwargs' in record and 'model' in record['kwargs']:
|
if "kwargs" in record and "model" in record["kwargs"]:
|
||||||
del record['kwargs']["model"]
|
del record["kwargs"]["model"]
|
||||||
if 'kwargs' in record and 'dataset' in record['kwargs']:
|
if "kwargs" in record and "dataset" in record["kwargs"]:
|
||||||
del record['kwargs']["dataset"]
|
del record["kwargs"]["dataset"]
|
||||||
|
|
||||||
# 4) save the config file
|
# 4) save the config file
|
||||||
with self.original_config_location.open("w") as f:
|
with self.original_config_location.open("w") as f:
|
||||||
@@ -957,20 +1075,25 @@ class YamlEditTask(ActionTask):
|
|||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
class CodeDumpTask(ActionTask):
|
class CodeDumpTask(ActionTask):
|
||||||
def __init__(self, target_component) -> None:
|
def __init__(self, target_component) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.target_component = target_component
|
self.target_component = target_component
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
code = self._context_manager.get_context(f'{self.target_component}_code')
|
code = self._context_manager.get_context(f"{self.target_component}_code")
|
||||||
assert code is not None, "The code is not set"
|
assert code is not None, "The code is not set"
|
||||||
|
|
||||||
with open(os.path.join(self._context_manager.get_context('workspace'), f'{self.target_component}_code.py'), 'w') as f:
|
with open(
|
||||||
|
os.path.join(self._context_manager.get_context("workspace"), f"{self.target_component}_code.py"), "w"
|
||||||
|
) as f:
|
||||||
f.write(code)
|
f.write(code)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
exec(f"from qlib.finco.{os.path.basename(self._context_manager.get_context('workspace'))}.{self.target_component}_code import *")
|
exec(
|
||||||
|
f"from qlib.finco.{os.path.basename(self._context_manager.get_context('workspace'))}.{self.target_component}_code import *"
|
||||||
|
)
|
||||||
except (ImportError, AttributeError, SyntaxError):
|
except (ImportError, AttributeError, SyntaxError):
|
||||||
return [ImplementActionTask(self.target_component, reimplement=True), CodeDumpTask(self.target_component)]
|
return [ImplementActionTask(self.target_component, reimplement=True), CodeDumpTask(self.target_component)]
|
||||||
return []
|
return []
|
||||||
@@ -1054,8 +1177,9 @@ class SummarizeTask(Task):
|
|||||||
user_prompt=prompt_workflow_selection, system_prompt=self.system.render()
|
user_prompt=prompt_workflow_selection, system_prompt=self.system.render()
|
||||||
)
|
)
|
||||||
|
|
||||||
KnowledgeBase().practice_knowledge.add([{"user_intention": user_prompt,
|
KnowledgeBase().practice_knowledge.add(
|
||||||
"experiment_metrics": metrics_response}])
|
[{"user_intention": user_prompt, "experiment_metrics": metrics_response}]
|
||||||
|
)
|
||||||
|
|
||||||
# notes: summarize after all experiment added to KnowledgeBase
|
# notes: summarize after all experiment added to KnowledgeBase
|
||||||
topic = Topic(name="rollingModel", describe=Template("What conclusion can you draw"))
|
topic = Topic(name="rollingModel", describe=Template("What conclusion can you draw"))
|
||||||
@@ -1092,8 +1216,11 @@ class SummarizeTask(Task):
|
|||||||
# in case of too large file
|
# in case of too large file
|
||||||
# TODO: Perhaps summarization method instead of truncation would be a better approach
|
# TODO: Perhaps summarization method instead of truncation would be a better approach
|
||||||
result.append(
|
result.append(
|
||||||
{"file": file.name, "content": content[: self.__MAX_LENGTH_OF_FILE],
|
{
|
||||||
"additional": self._context_manager.retrieve(file.name)}
|
"file": file.name,
|
||||||
|
"content": content[: self.__MAX_LENGTH_OF_FILE],
|
||||||
|
"additional": self._context_manager.retrieve(file.name),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -1134,8 +1261,9 @@ class SummarizeTask(Task):
|
|||||||
postfix = filename.split(".")[-1]
|
postfix = filename.split(".")[-1]
|
||||||
if postfix in ["jpeg"]:
|
if postfix in ["jpeg"]:
|
||||||
description = self._context_manager.retrieve(filename)
|
description = self._context_manager.retrieve(filename)
|
||||||
file_list.append({"file_name": filename, "description": description,
|
file_list.append(
|
||||||
"path": str(Path(path).joinpath(filename))})
|
{"file_name": filename, "description": description, "path": str(Path(path).joinpath(filename))}
|
||||||
|
)
|
||||||
return file_list
|
return file_list
|
||||||
|
|
||||||
def save_markdown(self, content: str, path):
|
def save_markdown(self, content: str, path):
|
||||||
|
|||||||
@@ -10,13 +10,25 @@ data_handler_config: &data_handler_config
|
|||||||
fit_start_time: 2008-01-01
|
fit_start_time: 2008-01-01
|
||||||
fit_end_time: 2014-12-31
|
fit_end_time: 2014-12-31
|
||||||
instruments: *market
|
instruments: *market
|
||||||
|
infer_processors:
|
||||||
|
- class: RobustZScoreNorm
|
||||||
|
kwargs:
|
||||||
|
fields_group: feature
|
||||||
|
clip_outlier: true
|
||||||
|
- class: Fillna
|
||||||
|
kwargs:
|
||||||
|
fields_group: feature
|
||||||
|
learn_processors:
|
||||||
|
- class: DropnaLabel
|
||||||
|
- class: CSRankNorm
|
||||||
|
kwargs:
|
||||||
|
fields_group: label
|
||||||
port_analysis_config: &port_analysis_config
|
port_analysis_config: &port_analysis_config
|
||||||
strategy:
|
strategy:
|
||||||
class: TopkDropoutStrategy
|
class: TopkDropoutStrategy
|
||||||
module_path: qlib.contrib.strategy
|
module_path: qlib.contrib.strategy
|
||||||
kwargs:
|
kwargs:
|
||||||
model: <MODEL>
|
signal: <PRED>
|
||||||
dataset: <DATASET>
|
|
||||||
topk: 50
|
topk: 50
|
||||||
n_drop: 5
|
n_drop: 5
|
||||||
backtest:
|
backtest:
|
||||||
@@ -32,18 +44,11 @@ port_analysis_config: &port_analysis_config
|
|||||||
min_cost: 5
|
min_cost: 5
|
||||||
task:
|
task:
|
||||||
model:
|
model:
|
||||||
class: LGBModel
|
class: LinearModel
|
||||||
module_path: qlib.contrib.model.gbdt
|
module_path: qlib.contrib.model.linear
|
||||||
kwargs:
|
kwargs:
|
||||||
loss: mse
|
estimator: ridge
|
||||||
colsample_bytree: 0.8879
|
alpha: 0.05
|
||||||
learning_rate: 0.2
|
|
||||||
subsample: 0.8789
|
|
||||||
lambda_l1: 205.6999
|
|
||||||
lambda_l2: 580.9768
|
|
||||||
max_depth: 8
|
|
||||||
num_leaves: 210
|
|
||||||
num_threads: 20
|
|
||||||
dataset:
|
dataset:
|
||||||
class: DatasetH
|
class: DatasetH
|
||||||
module_path: qlib.data.dataset
|
module_path: qlib.data.dataset
|
||||||
@@ -65,7 +70,7 @@ task:
|
|||||||
- class: SigAnaRecord
|
- class: SigAnaRecord
|
||||||
module_path: qlib.workflow.record_temp
|
module_path: qlib.workflow.record_temp
|
||||||
kwargs:
|
kwargs:
|
||||||
ana_long_short: False
|
ana_long_short: True
|
||||||
ann_scaler: 252
|
ann_scaler: 252
|
||||||
- class: PortAnaRecord
|
- class: PortAnaRecord
|
||||||
module_path: qlib.workflow.record_temp
|
module_path: qlib.workflow.record_temp
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class SingletonBaseClass(metaclass=SingletonMeta):
|
|||||||
This class becomes necessary
|
This class becomes necessary
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO: Add move this class to Qlib's general utils.
|
# TODO: Add move this class to Qlib's general utils.
|
||||||
|
|
||||||
|
|
||||||
@@ -42,4 +43,4 @@ def similarity(text1, text2):
|
|||||||
|
|
||||||
def random_string(length=10):
|
def random_string(length=10):
|
||||||
letters = string.ascii_letters + string.digits
|
letters = string.ascii_letters + string.digits
|
||||||
return ''.join(random.choice(letters) for i in range(length))
|
return "".join(random.choice(letters) for i in range(length))
|
||||||
|
|||||||
@@ -1,67 +1,40 @@
|
|||||||
import sys
|
import sys
|
||||||
import copy
|
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from pathlib import Path
|
||||||
from qlib.finco.task import HighLevelPlanTask, SummarizeTask, TrainTask
|
from qlib.finco.task import HighLevelPlanTask, SummarizeTask
|
||||||
from qlib.finco.prompt_template import PromptTemplate, Template
|
from qlib.finco.prompt_template import PromptTemplate, Template
|
||||||
from qlib.finco.log import FinCoLog, LogColors
|
from qlib.finco.log import FinCoLog, LogColors
|
||||||
from qlib.finco.utils import similarity
|
|
||||||
from qlib.finco.llm import APIBackend
|
from qlib.finco.llm import APIBackend
|
||||||
from qlib.finco.conf import Config
|
from qlib.finco.conf import Config
|
||||||
from qlib.finco.knowledge import KnowledgeBase, Topic
|
from qlib.finco.knowledge import KnowledgeBase, Topic
|
||||||
|
from qlib.finco.context import WorkflowContextManager
|
||||||
|
|
||||||
|
|
||||||
class WorkflowContextManager:
|
# TODO: it is not necessary in current phase
|
||||||
"""Context Manager stores the context of the workflow"""
|
# class TaskDAG:
|
||||||
|
# """
|
||||||
"""All context are key value pairs which saves the input, output and status of the whole workflow"""
|
# This is a Task manager. it maintains a graph and a stack stucture to manager the task
|
||||||
|
# The reason why the DGA relationship is maintained outside instead of inside the task is that
|
||||||
def __init__(self) -> None:
|
# - To make the creating of task simpler(user don't have to care about the relation-ship)
|
||||||
self.context = {}
|
# - To manage the relation ship when poping and executing the tasks is relatively easier instead of scattering them everywhere
|
||||||
self.logger = FinCoLog()
|
# """
|
||||||
|
# def __init__(self) -> None:
|
||||||
def set_context(self, key, value):
|
# self._finished = []
|
||||||
if key in self.context:
|
# self._stack = []
|
||||||
self.logger.warning("The key already exists in the context, the value will be overwritten")
|
# self._dag = defaultdict(list) # from id(object) -> list of id(object)
|
||||||
self.context[key] = value
|
#
|
||||||
|
# def pop(self):
|
||||||
def get_context(self, key):
|
# return self._stack.pop(0)
|
||||||
# NOTE: if the key doesn't exist, return None. In the future, we may raise an error to detect abnormal behavior
|
#
|
||||||
if key not in self.context:
|
# def push(self, task: Union[Task, List[Task]], parent: Optional[Task] = None):
|
||||||
self.logger.warning("The key doesn't exist in the context")
|
# if isinstance(task, Task):
|
||||||
return None
|
# task = [task]
|
||||||
return self.context[key]
|
# if parent is not None:
|
||||||
|
# self._dag
|
||||||
def update_context(self, key, new_value):
|
#
|
||||||
# NOTE: if the key doesn't exist, return None. In the future, we may raise an error to detect abnormal behavior
|
# def done(self) -> bool:
|
||||||
if key not in self.context:
|
# return len(self._stack) == 0
|
||||||
self.logger.warning("The key doesn't exist in the context")
|
|
||||||
self.context.update({key: new_value})
|
|
||||||
|
|
||||||
def get_all_context(self):
|
|
||||||
"""return a deep copy of the context"""
|
|
||||||
"""TODO: do we need to return a deep copy?"""
|
|
||||||
return copy.deepcopy(self.context)
|
|
||||||
|
|
||||||
def retrieve(self, query: str) -> dict:
|
|
||||||
if query in self.context.keys():
|
|
||||||
return {query: self.context.get(query)}
|
|
||||||
|
|
||||||
# Note: retrieve information from context by string similarity maybe abandon in future
|
|
||||||
scores = {}
|
|
||||||
for k, v in self.context.items():
|
|
||||||
scores.update({k: max(similarity(query, k), similarity(query, v))})
|
|
||||||
max_score_key = max(scores, key=scores.get)
|
|
||||||
return {max_score_key: self.context.get(max_score_key)}
|
|
||||||
|
|
||||||
def clear(self, reserve: list = None):
|
|
||||||
if reserve is None:
|
|
||||||
reserve = []
|
|
||||||
|
|
||||||
_context = {k: self.get_context(k) for k in reserve}
|
|
||||||
self.context = _context
|
|
||||||
|
|
||||||
|
|
||||||
class WorkflowManager:
|
class WorkflowManager:
|
||||||
@@ -78,8 +51,7 @@ class WorkflowManager:
|
|||||||
self._confirm_and_rm()
|
self._confirm_and_rm()
|
||||||
|
|
||||||
self.prompt_template = PromptTemplate()
|
self.prompt_template = PromptTemplate()
|
||||||
self.context = WorkflowContextManager()
|
self.context = WorkflowContextManager(workspace=self._workspace)
|
||||||
self.context.set_context("workspace", self._workspace)
|
|
||||||
self.default_user_prompt = "Please help me build a low turnover strategy that focus more on longterm return in China A csi300. Please help to use lightgbm model."
|
self.default_user_prompt = "Please help me build a low turnover strategy that focus more on longterm return in China A csi300. Please help to use lightgbm model."
|
||||||
|
|
||||||
def _confirm_and_rm(self):
|
def _confirm_and_rm(self):
|
||||||
@@ -92,7 +64,8 @@ class WorkflowManager:
|
|||||||
f"If you do not need to delete {self._workspace},"
|
f"If you do not need to delete {self._workspace},"
|
||||||
f" please change the workspace dir or rename existing files\n"
|
f" please change the workspace dir or rename existing files\n"
|
||||||
f"Are you sure you want to delete, yes(Y/y), no (N/n):",
|
f"Are you sure you want to delete, yes(Y/y), no (N/n):",
|
||||||
color=LogColors.WHITE)
|
color=LogColors.WHITE,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if str(flag) not in ["Y", "y"]:
|
if str(flag) not in ["Y", "y"]:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
@@ -150,10 +123,12 @@ class WorkflowManager:
|
|||||||
# TODO: sort the task list based on the priority of the task
|
# TODO: sort the task list based on the priority of the task
|
||||||
# task_list = sorted(task_list, key=lambda x: x.task_type)
|
# task_list = sorted(task_list, key=lambda x: x.task_type)
|
||||||
t = task_list.pop(0)
|
t = task_list.pop(0)
|
||||||
self.logger.info(f"Task finished: {[str(task) for task in task_finished]}",
|
self.logger.info(
|
||||||
f"Task in queue: {task_list_info}",
|
f"Task finished: {[str(task) for task in task_finished]}",
|
||||||
f"Executing task: {str(t)}",
|
f"Task in queue: {task_list_info}",
|
||||||
title="Task")
|
f"Executing task: {str(t)}",
|
||||||
|
title="Task",
|
||||||
|
)
|
||||||
|
|
||||||
t.assign_context_manager(self.context)
|
t.assign_context_manager(self.context)
|
||||||
res = t.execute()
|
res = t.execute()
|
||||||
@@ -174,9 +149,10 @@ class LearnManager:
|
|||||||
self.epoch = 0
|
self.epoch = 0
|
||||||
self.wm = WorkflowManager()
|
self.wm = WorkflowManager()
|
||||||
|
|
||||||
self.topics = [Topic(name=topic, describe=self.wm.prompt_template.get(f"Topic_{topic}")) for topic in
|
self.topics = [
|
||||||
self.__DEFAULT_TOPICS]
|
Topic(name=topic, describe=self.wm.prompt_template.get(f"Topic_{topic}")) for topic in self.__DEFAULT_TOPICS
|
||||||
self.knowledge_base = KnowledgeBase(workdir=Path.cwd().joinpath('knowledge'))
|
]
|
||||||
|
self.knowledge_base = KnowledgeBase(workdir=Path.cwd().joinpath("knowledge"))
|
||||||
self.knowledge_base.execute_knowledge.add([])
|
self.knowledge_base.execute_knowledge.add([])
|
||||||
self.knowledge_base.query(knowledge_type="infrastructure", content="resolve_path")
|
self.knowledge_base.query(knowledge_type="infrastructure", content="resolve_path")
|
||||||
|
|
||||||
@@ -209,14 +185,17 @@ class LearnManager:
|
|||||||
|
|
||||||
for task in task_finished:
|
for task in task_finished:
|
||||||
prompt_workflow_selection = self.wm.prompt_template.get(f"{self.__class__.__name__}_user").render(
|
prompt_workflow_selection = self.wm.prompt_template.get(f"{self.__class__.__name__}_user").render(
|
||||||
summary=summary, brief=knowledge_of_topics,
|
summary=summary,
|
||||||
|
brief=knowledge_of_topics,
|
||||||
task_finished=[str(t) for t in task_finished],
|
task_finished=[str(t) for t in task_finished],
|
||||||
task=task.__class__.__name__, system=task.system.render(), user_prompt=user_prompt
|
task=task.__class__.__name__,
|
||||||
|
system=task.system.render(),
|
||||||
|
user_prompt=user_prompt,
|
||||||
)
|
)
|
||||||
|
|
||||||
response = APIBackend().build_messages_and_create_chat_completion(
|
response = APIBackend().build_messages_and_create_chat_completion(
|
||||||
user_prompt=prompt_workflow_selection,
|
user_prompt=prompt_workflow_selection,
|
||||||
system_prompt=self.wm.prompt_template.get(f"{self.__class__.__name__}_system").render()
|
system_prompt=self.wm.prompt_template.get(f"{self.__class__.__name__}_system").render(),
|
||||||
)
|
)
|
||||||
|
|
||||||
# todo: response assertion
|
# todo: response assertion
|
||||||
|
|||||||
@@ -12,4 +12,4 @@ if [ -e $DIR/cridential.sh ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# run the command
|
# run the command
|
||||||
python -m qlib.finco.cli "please help me build a low turnover strategy that focus more on longterm return"
|
python -m qlib.finco.cli "build an A-share stock market daily portfolio in quantitative investment and minimize the maximum drawdown."
|
||||||
|
|||||||
5
setup.py
5
setup.py
@@ -176,11 +176,12 @@ setup(
|
|||||||
],
|
],
|
||||||
"finco": [
|
"finco": [
|
||||||
# finco is not necessary for all Qlib users; So a single require section is used for it.
|
# finco is not necessary for all Qlib users; So a single require section is used for it.
|
||||||
"openapi",
|
"openai",
|
||||||
"pydantic", # Please add it to basic requirements after the design of pydantic is state.
|
"pydantic", # Please add it to basic requirements after the design of pydantic is state.
|
||||||
|
"pydantic-settings",
|
||||||
"python-dotenv", # I don't think this is necessary if we use pydantic.
|
"python-dotenv", # I don't think this is necessary if we use pydantic.
|
||||||
"fuzzywuzzy",
|
"fuzzywuzzy",
|
||||||
"python-Levenshtein" # not necessary but accelerate fuzzywuzzy calculation
|
"python-Levenshtein", # not necessary but accelerate fuzzywuzzy calculation
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
|
|||||||
Reference in New Issue
Block a user