mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-29 00:17:44 +00:00
@@ -0,0 +1,42 @@
|
||||
from pathlib import Path
|
||||
|
||||
from finco.conf import FincoSettings
|
||||
|
||||
|
||||
class FactorImplementSettings(FincoSettings):
|
||||
file_based_execution_data_folder: str = str(
|
||||
(Path().cwd() / "git_ignore_folder" / "factor_implementation_source_data").absolute(),
|
||||
)
|
||||
file_based_execution_workspace: str = str(
|
||||
(Path().cwd() / "git_ignore_folder" / "factor_implementation_workspace").absolute(),
|
||||
)
|
||||
implementation_execution_cache_location: str = str(
|
||||
(Path().cwd() / "git_ignore_folder" / "factor_implementation_execution_cache.pkl").absolute(),
|
||||
)
|
||||
enable_execution_cache: bool = True # whether to enable the execution cache
|
||||
|
||||
# TODO: the factor implement specific settings should not appear in this settings
|
||||
# Evolving should have a method specific settings
|
||||
# evolving related config
|
||||
fail_task_trial_limit: int = 20
|
||||
|
||||
v1_query_former_trace_limit: int = 5
|
||||
v1_query_similar_success_limit: int = 5
|
||||
|
||||
v2_query_component_limit: int = 1
|
||||
v2_query_error_limit: int = 1
|
||||
v2_query_former_trace_limit: int = 1
|
||||
v2_error_summary: bool = False
|
||||
v2_knowledge_sampler: float = 1.0
|
||||
|
||||
chat_token_limit: int = (
|
||||
100000 # 100000 is the maximum limit of gpt4, which might increase in the future version of gpt
|
||||
)
|
||||
|
||||
implementation_factors_per_round: int = 100 # how many factors to choose for each round of evolving
|
||||
evo_multi_proc_n: int = 16 # how many processes to use for evolving (including eval & generation)
|
||||
|
||||
file_based_execution_timeout: int = 120 # seconds for each factor implementation execution
|
||||
|
||||
|
||||
FIS = FactorImplementSettings()
|
||||
@@ -0,0 +1,535 @@
|
||||
import json
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Tuple
|
||||
|
||||
import pandas as pd
|
||||
from jinja2 import Template
|
||||
|
||||
from oai.llm_utils import APIBackend
|
||||
from finco.log import FinCoLog
|
||||
from factor_implementation.share_modules.conf import FactorImplementSettings
|
||||
from factor_implementation.share_modules.factor import (
|
||||
FactorImplementation,
|
||||
FactorImplementationTask,
|
||||
)
|
||||
from factor_implementation.share_modules.prompt import (
|
||||
FactorImplementationPrompts,
|
||||
)
|
||||
|
||||
|
||||
class Evaluator(ABC):
|
||||
@abstractmethod
|
||||
def evaluate(
|
||||
self,
|
||||
target_task: FactorImplementationTask,
|
||||
implementation: FactorImplementation,
|
||||
gt_implementation: FactorImplementation,
|
||||
**kwargs,
|
||||
):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class FactorImplementationCodeEvaluator(Evaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
target_task: FactorImplementationTask,
|
||||
implementation: FactorImplementation,
|
||||
execution_feedback: str,
|
||||
factor_value_feedback: str = "",
|
||||
gt_implementation: FactorImplementation = None,
|
||||
**kwargs,
|
||||
):
|
||||
factor_information = target_task.get_factor_information()
|
||||
code = implementation.code
|
||||
|
||||
system_prompt = FactorImplementationPrompts()["evaluator_code_feedback_v1_system"]
|
||||
|
||||
execution_feedback_to_render = execution_feedback
|
||||
user_prompt = Template(
|
||||
FactorImplementationPrompts()["evaluator_code_feedback_v1_user"],
|
||||
).render(
|
||||
factor_information=factor_information,
|
||||
code=code,
|
||||
execution_feedback=execution_feedback_to_render,
|
||||
factor_value_feedback=factor_value_feedback,
|
||||
gt_code=gt_implementation.code if gt_implementation else None,
|
||||
)
|
||||
while (
|
||||
APIBackend().build_messages_and_calculate_token(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
former_messages=[],
|
||||
)
|
||||
> FactorImplementSettings().chat_token_limit
|
||||
):
|
||||
execution_feedback_to_render = execution_feedback_to_render[len(execution_feedback_to_render) // 2 :]
|
||||
user_prompt = Template(
|
||||
FactorImplementationPrompts()["evaluator_code_feedback_v1_user"],
|
||||
).render(
|
||||
factor_information=factor_information,
|
||||
code=code,
|
||||
execution_feedback=execution_feedback_to_render,
|
||||
factor_value_feedback=factor_value_feedback,
|
||||
gt_code=gt_implementation.code if gt_implementation else None,
|
||||
)
|
||||
critic_response = APIBackend().build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
json_mode=False,
|
||||
)
|
||||
|
||||
# critic_response = json.loads(critic_response)
|
||||
return critic_response
|
||||
|
||||
|
||||
class FactorImplementationEvaluator(Evaluator):
|
||||
# TODO:
|
||||
# I think we should have unified interface for all evaluates, for examples.
|
||||
# So we should adjust the interface of other factors
|
||||
@abstractmethod
|
||||
def evaluate(
|
||||
self,
|
||||
gt: FactorImplementation,
|
||||
gen: FactorImplementation,
|
||||
) -> Tuple[str, object]:
|
||||
"""You can get the dataframe by
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
_, gt_df = gt.execute()
|
||||
_, gen_df = gen.execute()
|
||||
|
||||
Returns
|
||||
-------
|
||||
Tuple[str, object]
|
||||
- str: the text-based description of the evaluation result
|
||||
- object: a comparable metric (bool, integer, float ...)
|
||||
|
||||
"""
|
||||
raise NotImplementedError("Please implement the `evaluator` method")
|
||||
|
||||
def _get_df(self, gt: FactorImplementation, gen: FactorImplementation):
|
||||
_, gt_df = gt.execute()
|
||||
_, gen_df = gen.execute()
|
||||
if isinstance(gen_df, pd.Series):
|
||||
gen_df = gen_df.to_frame("source_factor")
|
||||
if isinstance(gt_df, pd.Series):
|
||||
gt_df = gt_df.to_frame("gt_factor")
|
||||
return gt_df, gen_df
|
||||
|
||||
|
||||
# NOTE: the following evaluators are splited from FactorImplementationValueEvaluator
|
||||
|
||||
|
||||
class FactorImplementationSingleColumnEvaluator(FactorImplementationEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
gt: FactorImplementation,
|
||||
gen: FactorImplementation,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt, gen)
|
||||
|
||||
if len(gen_df.columns) == 1 and len(gt_df.columns) == 1:
|
||||
return "Both dataframes have only one column.", True
|
||||
elif len(gen_df.columns) != 1:
|
||||
gen_df = gen_df.iloc(axis=1)[
|
||||
[
|
||||
0,
|
||||
]
|
||||
]
|
||||
return (
|
||||
"The source dataframe has more than one column. Please check the implementation. We only evaluate the first column.",
|
||||
False,
|
||||
)
|
||||
return "", False
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__class__.__name__
|
||||
|
||||
|
||||
class FactorImplementationIndexFormatEvaluator(FactorImplementationEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
gt: FactorImplementation,
|
||||
gen: FactorImplementation,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt, gen)
|
||||
idx_name_right = gen_df.index.names == ("datetime", "instrument")
|
||||
if idx_name_right:
|
||||
return (
|
||||
'The index of the dataframe is ("datetime", "instrument") and align with the predefined format.',
|
||||
True,
|
||||
)
|
||||
else:
|
||||
return (
|
||||
'The index of the dataframe is not ("datetime", "instrument"). Please check the implementation.',
|
||||
False,
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__class__.__name__
|
||||
|
||||
|
||||
class FactorImplementationRowCountEvaluator(FactorImplementationEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
gt: FactorImplementation,
|
||||
gen: FactorImplementation,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt, gen)
|
||||
|
||||
if gen_df.shape[0] == gt_df.shape[0]:
|
||||
return "Both dataframes have the same rows count.", True
|
||||
else:
|
||||
return (
|
||||
f"The source dataframe and the ground truth dataframe have different rows count. The source dataframe has {gen_df.shape[0]} rows, while the ground truth dataframe has {gt_df.shape[0]} rows. Please check the implementation.",
|
||||
False,
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__class__.__name__
|
||||
|
||||
|
||||
class FactorImplementationIndexEvaluator(FactorImplementationEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
gt: FactorImplementation,
|
||||
gen: FactorImplementation,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt, gen)
|
||||
|
||||
if gen_df.index.equals(gt_df.index):
|
||||
return "Both dataframes have the same index.", True
|
||||
else:
|
||||
return (
|
||||
"The source dataframe and the ground truth dataframe have different index. Please check the implementation.",
|
||||
False,
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__class__.__name__
|
||||
|
||||
|
||||
class FactorImplementationMissingValuesEvaluator(FactorImplementationEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
gt: FactorImplementation,
|
||||
gen: FactorImplementation,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt, gen)
|
||||
|
||||
if gen_df.isna().sum().sum() == gt_df.isna().sum().sum():
|
||||
return "Both dataframes have the same missing values.", True
|
||||
else:
|
||||
return (
|
||||
f"The dataframes do not have the same missing values. The source dataframe has {gen_df.isna().sum().sum()} missing values, while the ground truth dataframe has {gt_df.isna().sum().sum()} missing values. Please check the implementation.",
|
||||
False,
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__class__.__name__
|
||||
|
||||
|
||||
class FactorImplementationValuesEvaluator(FactorImplementationEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
gt: FactorImplementation,
|
||||
gen: FactorImplementation,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt, gen)
|
||||
|
||||
try:
|
||||
close_values = gen_df.sub(gt_df).abs().lt(1e-6)
|
||||
result_int = close_values.astype(int)
|
||||
pos_num = result_int.sum().sum()
|
||||
acc_rate = pos_num / close_values.size
|
||||
except:
|
||||
close_values = gen_df
|
||||
if close_values.all().iloc[0]:
|
||||
return (
|
||||
"All values in the dataframes are equal within the tolerance of 1e-6.",
|
||||
acc_rate,
|
||||
)
|
||||
else:
|
||||
return (
|
||||
"Some values differ by more than the tolerance of 1e-6. Check for rounding errors or differences in the calculation methods.",
|
||||
acc_rate,
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__class__.__name__
|
||||
|
||||
|
||||
class FactorImplementationCorrelationEvaluator(FactorImplementationEvaluator):
|
||||
def __init__(self, hard_check: bool) -> None:
|
||||
self.hard_check = hard_check
|
||||
|
||||
def evaluate(
|
||||
self,
|
||||
gt: FactorImplementation,
|
||||
gen: FactorImplementation,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt, gen)
|
||||
|
||||
concat_df = pd.concat([gen_df, gt_df], axis=1)
|
||||
concat_df.columns = ["source", "gt"]
|
||||
ic = concat_df.groupby("datetime").apply(lambda df: df["source"].corr(df["gt"])).dropna().mean()
|
||||
ric = (
|
||||
concat_df.groupby("datetime")
|
||||
.apply(lambda df: df["source"].corr(df["gt"], method="spearman"))
|
||||
.dropna()
|
||||
.mean()
|
||||
)
|
||||
|
||||
if self.hard_check:
|
||||
if ic > 0.99 and ric > 0.99:
|
||||
return (
|
||||
f"The dataframes are highly correlated. The ic is {ic:.6f} and the rankic is {ric:.6f}.",
|
||||
True,
|
||||
)
|
||||
else:
|
||||
return (
|
||||
f"The dataframes are not sufficiently high correlated. The ic is {ic:.6f} and the rankic is {ric:.6f}. Investigate the factors that might be causing the discrepancies and ensure that the logic of the factor calculation is consistent.",
|
||||
False,
|
||||
)
|
||||
else:
|
||||
return f"The ic is ({ic:.6f}) and the rankic is ({ric:.6f}).", ic
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__class__.__name__
|
||||
|
||||
|
||||
class FactorImplementationValEvaluator(FactorImplementationEvaluator):
|
||||
def evaluate(self, gt: FactorImplementation, gen: FactorImplementation):
|
||||
_, gt_df = gt.execute()
|
||||
_, gen_df = gen.execute()
|
||||
# FIXME: refactor the two classes
|
||||
fiv = FactorImplementationValueEvaluator()
|
||||
return fiv.evaluate(source_df=gen_df, gt_df=gt_df)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__class__.__name__
|
||||
|
||||
|
||||
class FactorImplementationValueEvaluator(Evaluator):
|
||||
# TODO: let's discuss the about the interface of the evaluator
|
||||
def evaluate(
|
||||
self,
|
||||
source_df: pd.DataFrame,
|
||||
gt_df: pd.DataFrame,
|
||||
**kwargs,
|
||||
) -> Tuple:
|
||||
conclusions = []
|
||||
|
||||
if isinstance(source_df, pd.Series):
|
||||
source_df = source_df.to_frame("source_factor")
|
||||
conclusions.append(
|
||||
"The source dataframe is a series, better convert it to a dataframe.",
|
||||
)
|
||||
if gt_df is not None and isinstance(gt_df, pd.Series):
|
||||
gt_df = gt_df.to_frame("gt_factor")
|
||||
conclusions.append(
|
||||
"The ground truth dataframe is a series, convert it to a dataframe.",
|
||||
)
|
||||
|
||||
# Check if both dataframe has only one columns
|
||||
if len(source_df.columns) == 1:
|
||||
conclusions.append("The source dataframe has only one column which is correct.")
|
||||
else:
|
||||
conclusions.append(
|
||||
"The source dataframe has more than one column. Please check the implementation. We only evaluate the first column.",
|
||||
)
|
||||
source_df = source_df.iloc(axis=1)[
|
||||
[
|
||||
0,
|
||||
]
|
||||
]
|
||||
|
||||
if list(source_df.index.names) != ["datetime", "instrument"]:
|
||||
conclusions.append(
|
||||
rf"The index of the dataframe is not (\"datetime\", \"instrument\"), instead is {source_df.index.names}. Please check the implementation.",
|
||||
)
|
||||
else:
|
||||
conclusions.append(
|
||||
'The index of the dataframe is ("datetime", "instrument") and align with the predefined format.',
|
||||
)
|
||||
|
||||
# Check if both dataframe have the same rows count
|
||||
if gt_df is not None:
|
||||
if source_df.shape[0] == gt_df.shape[0]:
|
||||
conclusions.append("Both dataframes have the same rows count.")
|
||||
same_row_count_result = True
|
||||
else:
|
||||
conclusions.append(
|
||||
f"The source dataframe and the ground truth dataframe have different rows count. The source dataframe has {source_df.shape[0]} rows, while the ground truth dataframe has {gt_df.shape[0]} rows. Please check the implementation.",
|
||||
)
|
||||
same_row_count_result = False
|
||||
|
||||
# Check whether both dataframe has the same index
|
||||
if source_df.index.equals(gt_df.index):
|
||||
conclusions.append("Both dataframes have the same index.")
|
||||
same_index_result = True
|
||||
else:
|
||||
conclusions.append(
|
||||
"The source dataframe and the ground truth dataframe have different index. Please check the implementation.",
|
||||
)
|
||||
same_index_result = False
|
||||
|
||||
# Check for the same missing values (NaN)
|
||||
if source_df.isna().sum().sum() == gt_df.isna().sum().sum():
|
||||
conclusions.append("Both dataframes have the same missing values.")
|
||||
same_missing_values_result = True
|
||||
else:
|
||||
conclusions.append(
|
||||
f"The dataframes do not have the same missing values. The source dataframe has {source_df.isna().sum().sum()} missing values, while the ground truth dataframe has {gt_df.isna().sum().sum()} missing values. Please check the implementation.",
|
||||
)
|
||||
same_missing_values_result = False
|
||||
|
||||
# Check if the values are the same within a small tolerance
|
||||
if not same_index_result:
|
||||
conclusions.append(
|
||||
"The source dataframe and the ground truth dataframe have different index. Give up comparing the values and correlation because it's useless",
|
||||
)
|
||||
same_values_result = False
|
||||
high_correlation_result = False
|
||||
else:
|
||||
close_values = source_df.sub(gt_df).abs().lt(1e-6)
|
||||
if close_values.all().iloc[0]:
|
||||
conclusions.append(
|
||||
"All values in the dataframes are equal within the tolerance of 1e-6.",
|
||||
)
|
||||
same_values_result = True
|
||||
else:
|
||||
conclusions.append(
|
||||
"Some values differ by more than the tolerance of 1e-6. Check for rounding errors or differences in the calculation methods.",
|
||||
)
|
||||
same_values_result = False
|
||||
|
||||
# Check the ic and rankic between the two dataframes
|
||||
concat_df = pd.concat([source_df, gt_df], axis=1)
|
||||
concat_df.columns = ["source", "gt"]
|
||||
try:
|
||||
ic = concat_df.groupby("datetime").apply(lambda df: df["source"].corr(df["gt"])).dropna().mean()
|
||||
ric = (
|
||||
concat_df.groupby("datetime")
|
||||
.apply(lambda df: df["source"].corr(df["gt"], method="spearman"))
|
||||
.dropna()
|
||||
.mean()
|
||||
)
|
||||
|
||||
if ic > 0.99 and ric > 0.99:
|
||||
conclusions.append(
|
||||
f"The dataframes are highly correlated. The ic is {ic:.6f} and the rankic is {ric:.6f}.",
|
||||
)
|
||||
high_correlation_result = True
|
||||
else:
|
||||
conclusions.append(
|
||||
f"The dataframes are not sufficiently high correlated. The ic is {ic:.6f} and the rankic is {ric:.6f}. Investigate the factors that might be causing the discrepancies and ensure that the logic of the factor calculation is consistent.",
|
||||
)
|
||||
high_correlation_result = False
|
||||
|
||||
# Check for shifted alignments only in the "datetime" index
|
||||
max_shift_days = 2
|
||||
for shift in range(-max_shift_days, max_shift_days + 1):
|
||||
if shift == 0:
|
||||
continue # Skip the case where there is no shift
|
||||
|
||||
shifted_source_df = source_df.groupby(level="instrument").shift(shift)
|
||||
concat_df = pd.concat([shifted_source_df, gt_df], axis=1)
|
||||
concat_df.columns = ["source", "gt"]
|
||||
shifted_ric = (
|
||||
concat_df.groupby("datetime")
|
||||
.apply(lambda df: df["source"].corr(df["gt"], method="spearman"))
|
||||
.dropna()
|
||||
.mean()
|
||||
)
|
||||
if shifted_ric > 0.99:
|
||||
conclusions.append(
|
||||
f"The dataframes are highly correlated with a shift of {max_shift_days} days in the 'date' index. Shifted rankic: {shifted_ric:.6f}.",
|
||||
)
|
||||
break
|
||||
else:
|
||||
conclusions.append(
|
||||
f"No sufficient correlation found when shifting up to {max_shift_days} days in the 'date' index. Investigate the factors that might be causing discrepancies.",
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
FinCoLog().warning(f"Error occurred when calculating the correlation: {str(e)}")
|
||||
conclusions.append(
|
||||
f"Some error occurred when calculating the correlation. Investigate the factors that might be causing the discrepancies and ensure that the logic of the factor calculation is consistent. Error: {e}",
|
||||
)
|
||||
high_correlation_result = False
|
||||
|
||||
# Combine all conclusions into a single string
|
||||
conclusion_str = "\n".join(conclusions)
|
||||
|
||||
final_result = (same_values_result or high_correlation_result) if gt_df is not None else False
|
||||
return conclusion_str, final_result
|
||||
|
||||
|
||||
# TODO:
|
||||
def shorten_prompt(tpl: str, render_kwargs: dict, shorten_key: str, max_trail: int = 10) -> str:
|
||||
"""When the prompt is too long. We have to shorten it.
|
||||
But we should not truncate the prompt directly, so we should find the key we want to shorten and then shorten it.
|
||||
"""
|
||||
# TODO: this should replace most of code in
|
||||
# - FactorImplementationFinalDecisionEvaluator.evaluate
|
||||
# - FactorImplementationCodeEvaluator.evaluate
|
||||
|
||||
|
||||
class FactorImplementationFinalDecisionEvaluator(Evaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
target_task: FactorImplementationTask,
|
||||
execution_feedback: str,
|
||||
value_feedback: str,
|
||||
code_feedback: str,
|
||||
**kwargs,
|
||||
) -> Tuple:
|
||||
system_prompt = FactorImplementationPrompts()["evaluator_final_decision_v1_system"]
|
||||
execution_feedback_to_render = execution_feedback
|
||||
user_prompt = Template(
|
||||
FactorImplementationPrompts()["evaluator_final_decision_v1_user"],
|
||||
).render(
|
||||
factor_information=target_task.get_factor_information(),
|
||||
execution_feedback=execution_feedback_to_render,
|
||||
code_feedback=code_feedback,
|
||||
factor_value_feedback=(
|
||||
value_feedback
|
||||
if value_feedback is not None
|
||||
else "No Ground Truth Value provided, so no evaluation on value is performed."
|
||||
),
|
||||
)
|
||||
while (
|
||||
APIBackend().build_messages_and_calculate_token(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
former_messages=[],
|
||||
)
|
||||
> FactorImplementSettings().chat_token_limit
|
||||
):
|
||||
execution_feedback_to_render = execution_feedback_to_render[len(execution_feedback_to_render) // 2 :]
|
||||
user_prompt = Template(
|
||||
FactorImplementationPrompts()["evaluator_final_decision_v1_user"],
|
||||
).render(
|
||||
factor_information=target_task.get_factor_information(),
|
||||
execution_feedback=execution_feedback_to_render,
|
||||
code_feedback=code_feedback,
|
||||
factor_value_feedback=(
|
||||
value_feedback
|
||||
if value_feedback is not None
|
||||
else "No Ground Truth Value provided, so no evaluation on value is performed."
|
||||
),
|
||||
)
|
||||
|
||||
final_evaluation_dict = json.loads(
|
||||
APIBackend().build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
json_mode=True,
|
||||
),
|
||||
)
|
||||
return (
|
||||
final_evaluation_dict["final_decision"],
|
||||
final_evaluation_dict["final_feedback"],
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
class ImplementRunException(Exception):
|
||||
"""
|
||||
Exceptions raised when Implementing and running code.
|
||||
- start: FactorImplementationTask => FactorGenerator
|
||||
- end: Get dataframe after execution
|
||||
|
||||
The more detailed evaluation in dataframe values are managed by the evaluator.
|
||||
"""
|
||||
|
||||
|
||||
class CodeFormatException(ImplementRunException):
|
||||
"""
|
||||
The generated code is not found due format error.
|
||||
"""
|
||||
|
||||
|
||||
class RuntimeErrorException(ImplementRunException):
|
||||
"""
|
||||
The generated code fail to execute the script.
|
||||
"""
|
||||
|
||||
|
||||
class NoOutputException(ImplementRunException):
|
||||
"""
|
||||
The code fail to generate output file.
|
||||
"""
|
||||
@@ -0,0 +1,221 @@
|
||||
import pickle
|
||||
import subprocess
|
||||
import uuid
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import Tuple, Union
|
||||
|
||||
import pandas as pd
|
||||
from filelock import FileLock
|
||||
|
||||
from oai.llm_utils import md5_hash
|
||||
from finco.log import FinCoLog
|
||||
from factor_implementation.share_modules.conf import FactorImplementSettings
|
||||
from factor_implementation.share_modules.exception import (
|
||||
CodeFormatException,
|
||||
NoOutputException,
|
||||
RuntimeErrorException,
|
||||
)
|
||||
|
||||
|
||||
class FactorImplementationTask:
|
||||
# TODO: remove the factor_ prefix may be better
|
||||
def __init__(
|
||||
self,
|
||||
factor_name,
|
||||
factor_description,
|
||||
factor_formulation,
|
||||
factor_formulation_description,
|
||||
variables: dict = {},
|
||||
) -> None:
|
||||
self.factor_name = factor_name
|
||||
self.factor_description = factor_description
|
||||
self.factor_formulation = factor_formulation
|
||||
self.factor_formulation_description = factor_formulation_description
|
||||
# TODO: check variables a good candidate
|
||||
self.variables = variables
|
||||
|
||||
def get_factor_information(self):
|
||||
return f"""factor_name: {self.factor_name}
|
||||
factor_description: {self.factor_description}
|
||||
factor_formulation: {self.factor_formulation}
|
||||
factor_formulation_description: {self.factor_formulation_description}"""
|
||||
|
||||
@staticmethod
|
||||
def from_dict(dict):
|
||||
return FactorImplementationTask(**dict)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self.__class__.__name__}[{self.factor_name}]>"
|
||||
|
||||
|
||||
class FactorImplementation(ABC):
|
||||
def __init__(self, target_task: FactorImplementationTask) -> None:
|
||||
self.target_task = target_task
|
||||
|
||||
@abstractmethod
|
||||
def execute(self, *args, **kwargs) -> Tuple[str, pd.DataFrame]:
|
||||
raise NotImplementedError("__call__ method is not implemented.")
|
||||
|
||||
|
||||
class FileBasedFactorImplementation(FactorImplementation):
|
||||
"""
|
||||
This class is used to implement a factor by writing the code to a file.
|
||||
Input data and output factor value are also written to files.
|
||||
"""
|
||||
|
||||
# TODO: (Xiao) think raising errors may get better information for processing
|
||||
FB_FROM_CACHE = "The factor value has been executed and stored in the instance variable."
|
||||
FB_EXEC_SUCCESS = "Execution succeeded without error."
|
||||
FB_CODE_NOT_SET = "code is not set."
|
||||
FB_EXECUTION_SUCCEEDED = "Execution succeeded without error."
|
||||
FB_OUTPUT_FILE_NOT_FOUND = "\nExpected output file not found."
|
||||
FB_OUTPUT_FILE_FOUND = "\nExpected output file found."
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
target_task: FactorImplementationTask,
|
||||
code,
|
||||
executed_factor_value_dataframe=None,
|
||||
raise_exception=False,
|
||||
) -> None:
|
||||
super().__init__(target_task)
|
||||
self.code = code
|
||||
self.executed_factor_value_dataframe = executed_factor_value_dataframe
|
||||
self.logger = FinCoLog()
|
||||
self.raise_exception = raise_exception
|
||||
self.workspace_path = Path(
|
||||
FactorImplementSettings().file_based_execution_workspace,
|
||||
) / str(uuid.uuid4())
|
||||
|
||||
@staticmethod
|
||||
def link_data_to_workspace(data_path: Path, workspace_path: Path):
|
||||
data_path = Path(data_path)
|
||||
workspace_path = Path(workspace_path)
|
||||
for data_file_path in data_path.iterdir():
|
||||
workspace_data_file_path = workspace_path / data_file_path.name
|
||||
if workspace_data_file_path.exists():
|
||||
workspace_data_file_path.unlink()
|
||||
subprocess.run(
|
||||
["ln", "-s", data_file_path, workspace_data_file_path],
|
||||
check=False,
|
||||
)
|
||||
|
||||
def execute(self, store_result: bool = False) -> Tuple[str, pd.DataFrame]:
|
||||
"""
|
||||
execute the implementation and get the factor value by the following steps:
|
||||
1. make the directory in workspace path
|
||||
2. write the code to the file in the workspace path
|
||||
3. link all the source data to the workspace path folder
|
||||
4. execute the code
|
||||
5. read the factor value from the output file in the workspace path folder
|
||||
returns the execution feedback as a string and the factor value as a pandas dataframe
|
||||
|
||||
parameters:
|
||||
store_result: if True, store the factor value in the instance variable, this feature is to be used in the gt implementation to avoid multiple execution on the same gt implementation
|
||||
"""
|
||||
if self.code is None:
|
||||
if self.raise_exception:
|
||||
raise CodeFormatException(self.FB_CODE_NOT_SET)
|
||||
else:
|
||||
# TODO: to make the interface compatible with previous code. I kept the original behavior.
|
||||
raise ValueError(self.FB_CODE_NOT_SET)
|
||||
with FileLock(self.workspace_path / "execution.lock"):
|
||||
(Path.cwd() / "git_ignore_folder" / "factor_implementation_execution_cache").mkdir(
|
||||
exist_ok=True, parents=True
|
||||
)
|
||||
if FactorImplementSettings().enable_execution_cache:
|
||||
# NOTE: cache the result for the same code
|
||||
target_file_name = md5_hash(self.code)
|
||||
cache_file_path = (
|
||||
Path.cwd()
|
||||
/ "git_ignore_folder"
|
||||
/ "factor_implementation_execution_cache"
|
||||
/ f"{target_file_name}.pkl"
|
||||
)
|
||||
if cache_file_path.exists() and not self.raise_exception:
|
||||
cached_res = pickle.load(open(cache_file_path, "rb"))
|
||||
if store_result and cached_res[1] is not None:
|
||||
self.executed_factor_value_dataframe = cached_res[1]
|
||||
return cached_res
|
||||
|
||||
if self.executed_factor_value_dataframe is not None:
|
||||
return self.FB_FROM_CACHE, self.executed_factor_value_dataframe
|
||||
|
||||
source_data_path = Path(
|
||||
FactorImplementSettings().file_based_execution_data_folder,
|
||||
)
|
||||
self.workspace_path.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
code_path = self.workspace_path / f"{self.target_task.factor_name}.py"
|
||||
code_path.write_text(self.code)
|
||||
|
||||
self.link_data_to_workspace(source_data_path, self.workspace_path)
|
||||
|
||||
execution_feedback = self.FB_EXECUTION_SUCCEEDED
|
||||
try:
|
||||
subprocess.check_output(
|
||||
f"python {code_path}",
|
||||
shell=True,
|
||||
cwd=self.workspace_path,
|
||||
stderr=subprocess.STDOUT,
|
||||
timeout=FactorImplementSettings().file_based_execution_timeout,
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
import site
|
||||
|
||||
execution_feedback = (
|
||||
e.output.decode()
|
||||
.replace(str(code_path.parent.absolute()), r"/path/to")
|
||||
.replace(str(site.getsitepackages()[0]), r"/path/to/site-packages")
|
||||
)
|
||||
if len(execution_feedback) > 2000:
|
||||
execution_feedback = (
|
||||
execution_feedback[:1000] + "....hidden long error message...." + execution_feedback[-1000:]
|
||||
)
|
||||
if self.raise_exception:
|
||||
raise RuntimeErrorException(execution_feedback)
|
||||
except subprocess.TimeoutExpired:
|
||||
execution_feedback += f"Execution timeout error and the timeout is set to {FactorImplementSettings().file_based_execution_timeout} seconds."
|
||||
if self.raise_exception:
|
||||
raise RuntimeErrorException(execution_feedback)
|
||||
|
||||
workspace_output_file_path = self.workspace_path / "result.h5"
|
||||
if not workspace_output_file_path.exists():
|
||||
execution_feedback += self.FB_OUTPUT_FILE_NOT_FOUND
|
||||
executed_factor_value_dataframe = None
|
||||
if self.raise_exception:
|
||||
raise NoOutputException(execution_feedback)
|
||||
else:
|
||||
try:
|
||||
executed_factor_value_dataframe = pd.read_hdf(workspace_output_file_path)
|
||||
execution_feedback += self.FB_OUTPUT_FILE_FOUND
|
||||
except Exception as e:
|
||||
execution_feedback += f"Error found when reading hdf file: {e}"[:1000]
|
||||
executed_factor_value_dataframe = None
|
||||
|
||||
if store_result and executed_factor_value_dataframe is not None:
|
||||
self.executed_factor_value_dataframe = executed_factor_value_dataframe
|
||||
|
||||
if FactorImplementSettings().enable_execution_cache:
|
||||
pickle.dump(
|
||||
(execution_feedback, executed_factor_value_dataframe),
|
||||
open(cache_file_path, "wb"),
|
||||
)
|
||||
return execution_feedback, executed_factor_value_dataframe
|
||||
|
||||
def __str__(self) -> str:
|
||||
# NOTE:
|
||||
# If the code cache works, the workspace will be None.
|
||||
return f"File Factor[{self.target_task.factor_name}]: {self.workspace_path}"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.__str__()
|
||||
|
||||
@staticmethod
|
||||
def from_folder(task: FactorImplementationTask, path: Union[str, Path], **kwargs):
|
||||
path = Path(path)
|
||||
factor_path = (path / task.factor_name).with_suffix(".py")
|
||||
with factor_path.open("r") as f:
|
||||
code = f.read()
|
||||
return FileBasedFactorImplementation(task, code=code, **kwargs)
|
||||
@@ -0,0 +1,31 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List
|
||||
|
||||
from factor_implementation.share_modules.factor import (
|
||||
FactorImplementation,
|
||||
FactorImplementationTask,
|
||||
)
|
||||
|
||||
|
||||
class FactorGenerator(ABC):
|
||||
"""
|
||||
Because implementing factors will help each other in the process of implementation, we use the interface `List[FactorImplementationTask] -> List[FactorImplementation]` instead of single factor .
|
||||
"""
|
||||
|
||||
def __init__(self, target_task_l: List[FactorImplementationTask]) -> None:
|
||||
self.target_task_l = target_task_l
|
||||
|
||||
@abstractmethod
|
||||
def generate(self, *args, **kwargs) -> List[FactorImplementation]:
|
||||
raise NotImplementedError("generate method is not implemented.")
|
||||
|
||||
def collect_feedback(self, feedback_obj_l: List[object]):
|
||||
"""
|
||||
When online evaluation.
|
||||
The preivous feedbacks will be collected to support advanced factor generator
|
||||
|
||||
Parameters
|
||||
----------
|
||||
feedback_obj_l : List[object]
|
||||
|
||||
"""
|
||||
@@ -0,0 +1,23 @@
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
import yaml
|
||||
|
||||
from finco.utils import SingletonBaseClass
|
||||
|
||||
|
||||
class FactorImplementationPrompts(Dict, SingletonBaseClass):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
prompt_yaml_path = Path(__file__).parent / "prompts.yaml"
|
||||
|
||||
prompt_yaml_dict = yaml.load(
|
||||
open(
|
||||
prompt_yaml_path,
|
||||
encoding="utf8",
|
||||
),
|
||||
Loader=yaml.FullLoader,
|
||||
)
|
||||
|
||||
for key, value in prompt_yaml_dict.items():
|
||||
self[key] = value
|
||||
@@ -0,0 +1,196 @@
|
||||
evaluator_code_feedback_v1_system: |-
|
||||
Your job is to give critic to user's code. User's code is expected to implement some factors in quant investment. The code contains reading data from a HDF5(H5) file, calculate the factor to each instrument on each datetime, and save the result pandas dataframe to a HDF5(H5) file.
|
||||
|
||||
User will firstly provide you the information of the factor, which includes the name of the factor, description of the factor, the formulation of the factor and the description of the formulation. You can check whether user's code is align with the factor.
|
||||
|
||||
The user will provide the source python code and the execution error message if execution failed.
|
||||
The user might provide you the ground truth code for you to provide the critic. You should not leak the ground truth code to the user in any form but you can use it to provide the critic.
|
||||
|
||||
User has also compared the factor values calculated by the user's code and the ground truth code. The user will provide you some analyze result comparing two output. You may find some error in the code which caused the difference between the two output.
|
||||
|
||||
If the ground truth code is provided, your critic should only consider checking whether the user's code is align with the ground truth code since the ground truth is definitely correct.
|
||||
If the ground truth code is not provided, your critic should consider checking whether the user's code is reasonable and correct.
|
||||
|
||||
You should provide the suggestion to each of your critic to help the user improve the code. Please response the critic in the following format. Here is an example structure for the output:
|
||||
critic 1: The critic message to critic 1
|
||||
critic 2: The critic message to critic 2
|
||||
evaluator_code_feedback_v1_user: |-
|
||||
--------------Factor information:---------------
|
||||
{{ factor_information }}
|
||||
--------------Python code:---------------
|
||||
{{ code }}
|
||||
--------------Execution feedback:---------------
|
||||
{{ execution_feedback }}
|
||||
{% if factor_value_feedback is not none %}
|
||||
--------------Factor value feedback:---------------
|
||||
{{ factor_value_feedback }}
|
||||
{% endif %}
|
||||
{% if gt_code is not none %}
|
||||
--------------Ground truth Python code:---------------
|
||||
{{ gt_code }}
|
||||
{% endif %}
|
||||
evolving_strategy_factor_implementation_v1_system: |-
|
||||
The user is trying to implement some factors in quant investment, and you are the one to help write the python code.
|
||||
|
||||
{{ data_info }}
|
||||
|
||||
The user will provide you a formulation of the factor, which contains some function calls and some operators. You need to implement the function calls and operators in python. Your code is expected to align the formulation in any form which means The user needs to get the exact factor values with your code as expected.
|
||||
|
||||
Your code should contain the following part: the import part, the function part, and the main part. You should write a main function name: "calculate_{function_name}" and call this function in "if __name__ == __main__" part. Don't write any try-except block in your code. The user will catch the exception message and provide the feedback to you.
|
||||
|
||||
User will write your code into a python file and execute the file directly with "python {your_file_name}.py". You should calculate the factor values and save the result into a HDF5(H5) file named "result.h5" in the same directory as your python file. The result file is a HDF5(H5) file containing a pandas dataframe. The index of the dataframe is the "datetime" and "instrument", and the single column name is the factor name,and the value is the factor value. The result file should be saved in the same directory as your python file.
|
||||
|
||||
To help you write the correct code, the user might provide multiple information that helps you write the correct code:
|
||||
1. The user might provide you the correct code to similar factors. Your should learn from these code to write the correct code.
|
||||
2. The user might provide you the failed former code and the corresponding feedback to the code. The feedback contains to the execution, the code and the factor value. You should analyze the feedback and try to correct the latest code.
|
||||
3. The user might provide you the suggestion to the latest fail code and some similar fail to correct pairs. Each pair contains the fail code with similar error and the corresponding corrected version code. You should learn from these suggestion to write the correct code.
|
||||
|
||||
Your must write your code based on your former lastest attempt below which consists of your former code and code feedback, you should read the former attempt carefully and must not modify the right part of your former code.
|
||||
{% if queried_former_failed_knowledge|length != 0 %}
|
||||
--------------Your former latest attempt:---------------
|
||||
{% for former_failed_knowledge in queried_former_failed_knowledge %}
|
||||
=====Code to implementation {{ loop.index }}=====
|
||||
{{ former_failed_knowledge.implementation.code }}
|
||||
=====Feedback to implementation {{ loop.index }}=====
|
||||
{{ former_failed_knowledge.feedback }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
A typical format of `result.h5` may be like following:
|
||||
datetime instrument
|
||||
2020-01-02 SZ000001 -0.001796
|
||||
SZ000166 0.005780
|
||||
SZ000686 0.004228
|
||||
SZ000712 0.001298
|
||||
SZ000728 0.005330
|
||||
...
|
||||
2021-12-31 SZ000750 0.000000
|
||||
SZ000776 0.002459
|
||||
|
||||
Please response the code in the following json format. Here is an example structure for the JSON output:
|
||||
{
|
||||
"code": "The Python code as a string."
|
||||
}
|
||||
|
||||
evolving_strategy_factor_implementation_v1_user: |-
|
||||
--------------Target factor information:---------------
|
||||
{{ factor_information_str }}
|
||||
|
||||
{% if queried_similar_successful_knowledge|length != 0 %}
|
||||
--------------Correct code to similar factors:---------------
|
||||
{% for similar_successful_knowledge in queried_similar_successful_knowledge %}
|
||||
=====Factor {{loop.index}}:=====
|
||||
{{ similar_successful_knowledge.target_task.get_factor_information() }}
|
||||
=====Code:=====
|
||||
{{ similar_successful_knowledge.implementation.code }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if queried_former_failed_knowledge|length != 0 %}
|
||||
--------------Former failed code:---------------
|
||||
{% for former_failed_knowledge in queried_former_failed_knowledge %}
|
||||
=====Code to implementation {{ loop.index }}=====
|
||||
{{ former_failed_knowledge.implementation.code }}
|
||||
=====Feedback to implementation {{ loop.index }}=====
|
||||
{{ former_failed_knowledge.feedback }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
evaluator_final_decision_v1_system: |-
|
||||
User is trying to implement some factor in quant investment and has finished a version of implementation. User has finished evaluation and got some feedback from the evaluator.
|
||||
The evaluator run the code and get the factor value dataframe and provide several feedback regarding user's code and code output. You should analyze the feedback and considering the factor description to give a final decision about the evaluation result. The final decision concludes whether the factor is implemented correctly and if not, detail feedback containing reason and suggestion if the final decision is False.
|
||||
|
||||
The implementation final decision is considered in the following logic:
|
||||
1. If the value and the ground truth value are exactly the same under a small tolerance, the implementation is considered correct.
|
||||
2. If the value and the ground truth value have a high correlation on ic or rank ic, the implementation is considered correct.
|
||||
3. If no ground truth value is not provided, the implementation is considered correct if the code execution is successful and the code feedback is reasonable.
|
||||
|
||||
Please response the critic in the json format. Here is an example structure for the JSON output, please strictly follow the format:
|
||||
{
|
||||
"final_decision": True,
|
||||
"final_feedback": "The final feedback message",
|
||||
}
|
||||
|
||||
evaluator_final_decision_v1_user: |-
|
||||
--------------Factor information:---------------
|
||||
{{ factor_information }}
|
||||
--------------Execution feedback:---------------
|
||||
{{ execution_feedback }}
|
||||
--------------Code feedback:---------------
|
||||
{{ code_feedback }}
|
||||
--------------Factor value feedback:---------------
|
||||
{{ factor_value_feedback }}
|
||||
|
||||
|
||||
analyze_component_prompt_v1_system: |-
|
||||
User is getting a new task that might consist of the components below (given in component_index: component_description):
|
||||
{{all_component_content}}
|
||||
|
||||
You should find out what components does the new task have, and put their indices in a list.
|
||||
Please response the critic in the json format. Here is an example structure for the JSON output, please strictly follow the format:
|
||||
{
|
||||
"component_no_list": the list containing indices of components.
|
||||
}
|
||||
|
||||
|
||||
|
||||
evolving_strategy_factor_implementation_v2_user: |-
|
||||
--------------Target factor information:---------------
|
||||
{{ factor_information_str }}
|
||||
|
||||
{% if queried_similar_error_knowledge|length != 0 %}
|
||||
{% if not error_summary %}
|
||||
Recall your last failure, your implementation met some errors.
|
||||
When doing other tasks, you met some similar errors but you finally solve them. Here are some examples:
|
||||
{% for error_content, similar_error_knowledge in queried_similar_error_knowledge %}
|
||||
--------------Factor information to similar error ({{error_content}}):---------------
|
||||
{{ similar_error_knowledge[0].target_task.get_factor_information() }}
|
||||
=====Code with similar error ({{error_content}}):=====
|
||||
{{ similar_error_knowledge[0].implementation.code }}
|
||||
=====Success code to former code with similar error ({{error_content}}):=====
|
||||
{{ similar_error_knowledge[1].implementation.code }}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
Recall your last failure, your implementation met some errors.
|
||||
After reviewing some similar errors and their solutions, here are some suggestions for you to correct your code:
|
||||
{{error_summary_critics}}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if queried_similar_component_knowledge|length != 0 %}
|
||||
Here are some success implements of similar component tasks, take them as references:
|
||||
--------------Correct code to similar factors:---------------
|
||||
{% for similar_component_knowledge in queried_similar_component_knowledge %}
|
||||
=====Factor {{loop.index}}:=====
|
||||
{{ similar_component_knowledge.target_task.get_factor_information() }}
|
||||
=====Code:=====
|
||||
{{ similar_component_knowledge.implementation.code }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
evolving_strategy_error_summary_v2_system: |-
|
||||
You are doing the following task:
|
||||
{{factor_information_str}}
|
||||
|
||||
You have written some code but it meets errors like the following:
|
||||
{{code_and_feedback}}
|
||||
|
||||
The user has found some tasks that met similar errors, and their final correct solutions.
|
||||
Please refer to these similar errors and their solutions, provide some clear, short and accurate critics that might help you solve the issues in your code.
|
||||
|
||||
Please response the critic in the following format. Here is an example structure for the output:
|
||||
critic 1: The critic message to critic 1
|
||||
critic 2: The critic message to critic 2
|
||||
|
||||
evolving_strategy_error_summary_v2_user: |-
|
||||
{% if queried_similar_error_knowledge|length != 0 %}
|
||||
{% for error_content, similar_error_knowledge in queried_similar_error_knowledge %}
|
||||
--------------Factor information to similar error ({{error_content}}):---------------
|
||||
{{ similar_error_knowledge[0].target_task.get_factor_information() }}
|
||||
=====Code with similar error ({{error_content}}):=====
|
||||
{{ similar_error_knowledge[0].implementation.code }}
|
||||
=====Success code to former code with similar error ({{error_content}}):=====
|
||||
{{ similar_error_knowledge[1].implementation.code }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
|
||||
# render it with jinja
|
||||
from jinja2 import Template
|
||||
|
||||
from factor_implementation.share_modules.conf import FIS
|
||||
|
||||
TPL = """
|
||||
{{file_name}}
|
||||
```{{type_desc}}
|
||||
{{content}}
|
||||
````
|
||||
"""
|
||||
# Create a Jinja template from the string
|
||||
JJ_TPL = Template(TPL)
|
||||
|
||||
|
||||
def get_data_folder_intro():
|
||||
"""Direclty get the info of the data folder.
|
||||
It is for preparing prompting message.
|
||||
"""
|
||||
content_l = []
|
||||
for p in Path(FIS.file_based_execution_data_folder).iterdir():
|
||||
if p.name.endswith(".h5"):
|
||||
df = pd.read_hdf(p)
|
||||
# get df.head() as string with full width
|
||||
pd.set_option("display.max_columns", None) # or 1000
|
||||
pd.set_option("display.max_rows", None) # or 1000
|
||||
pd.set_option("display.max_colwidth", None) # or 199
|
||||
rendered = JJ_TPL.render(
|
||||
file_name=p.name,
|
||||
type_desc="generated by `pd.read_hdf(filename).head()`",
|
||||
content=df.head().to_string(),
|
||||
)
|
||||
content_l.append(rendered)
|
||||
elif p.name.endswith(".md"):
|
||||
with open(p) as f:
|
||||
content = f.read()
|
||||
rendered = JJ_TPL.render(
|
||||
file_name=p.name,
|
||||
type_desc="markdown",
|
||||
content=content,
|
||||
)
|
||||
content_l.append(rendered)
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f"file type {p.name} is not supported. Please implement its description function.",
|
||||
)
|
||||
return "\n ----------------- file spliter -------------\n".join(content_l)
|
||||
Reference in New Issue
Block a user