Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FInancial COpilot to Qlib #1531

Draft
wants to merge 67 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
7c4f3b8
Initial interface for discussion
you-n-g May 24, 2023
f24253e
add openai interface support
peteryangms May 30, 2023
55611aa
Merge pull request #1527 from microsoft/xuyang1/add_openai_api_support
peteryang1 May 30, 2023
f376435
first round
peteryangms May 30, 2023
2af35d9
second commit
peteryangms May 30, 2023
ce39b4b
add qlib auto init so logger can display info
peteryangms May 30, 2023
74a5d7c
add parse method for summarization;
Cadenza-Li May 30, 2023
94102fb
remove tasktype variable
peteryangms May 31, 2023
421b140
Merge pull request #1528 from microsoft/xuyang1/refine_task_and_imple…
peteryang1 May 31, 2023
3919678
split task into workflow and task to make the strcture more clear
peteryangms May 31, 2023
e7cd93a
add base method for summarization; (#1530)
Fivele-Li May 31, 2023
08d9dbc
update v1 code containing SLplan and config action
peteryangms May 31, 2023
e2332a0
imporove some words in prompt
peteryangms May 31, 2023
cda32d5
Merge pull request #1532 from microsoft/xuyang1/add-plan-and-config-t…
peteryang1 Jun 1, 2023
0515524
add code implementation code
peteryangms Jun 1, 2023
d46b4c1
Merge pull request #1534 from microsoft/xuyang1/add_code_implementati…
peteryang1 Jun 1, 2023
5f37f32
update code
peteryangms Jun 1, 2023
e376648
Merge pull request #1536 from microsoft/xuyang1/add_debug_mode_to_sav…
peteryang1 Jun 1, 2023
40e0c32
Add configurable dataset (#1535)
you-n-g Jun 1, 2023
3b56b8e
Optimize summarize task prompt and others (#1533)
Fivele-Li Jun 1, 2023
73d51f0
Init workspace and CMDTask (#1537)
you-n-g Jun 1, 2023
ad7498e
Edit yaml task (#1538)
you-n-g Jun 1, 2023
1d88830
Add recorder task and visualize (#1542)
Fivele-Li Jun 12, 2023
01accec
update code
peteryangms Jun 12, 2023
80fbc00
move prompt templates to yaml file to make code clean
peteryangms Jun 13, 2023
429c9a7
format
peteryangms Jun 13, 2023
fa7ef29
Merge pull request #1548 from microsoft/xuyang1/add_dump_to_file_task
peteryang1 Jun 13, 2023
7762c5a
add datahandler and design action task according to component
peteryangms Jun 13, 2023
f9cc8a5
remove useless prompt
peteryangms Jun 14, 2023
1a523df
Optimize log and interact of FinCo (#1549)
Fivele-Li Jun 14, 2023
74619ed
fix using defaut in record strategy and backtest
peteryangms Jun 14, 2023
a70386a
Merge pull request #1550 from microsoft/xuyang1/refine_task_prompts
peteryang1 Jun 14, 2023
f12184c
Add analyser task and optimize interact (#1552)
Fivele-Li Jun 16, 2023
1326ac6
Add docs to context and retrieve (#1566)
Fivele-Li Jun 24, 2023
7e84f3a
Add backtest and backforward task (#1568)
Fivele-Li Jun 30, 2023
73bd79c
merge into one commit
peteryangms Jun 30, 2023
4fccf81
fix one workflow
peteryangms Jun 30, 2023
9119bcd
Merge pull request #1576 from microsoft/xuyang1/add_config_and_code_d…
peteryang1 Jun 30, 2023
6cb87ec
refine code to use qrun
peteryangms Jul 3, 2023
ee5e5cf
remove useless code
peteryangms Jul 3, 2023
b7757d5
Merge pull request #1580 from microsoft/xuyang1/refine_workflow_to_in…
peteryang1 Jul 3, 2023
9a36f8d
fix singleton bug
peteryangms Jul 4, 2023
8b0fdf1
Merge pull request #1581 from microsoft/xuyang1/fix_singleton_bug
peteryang1 Jul 4, 2023
aef1153
rename & test
you-n-g Jul 4, 2023
86ffd17
Add knowledge module and tune summarizeTask (#1582)
Fivele-Li Jul 6, 2023
effed38
Optimize prompt for entire learn loop (#1589)
Fivele-Li Jul 11, 2023
d7ab693
update knowledge module;
Cadenza-Li Jul 12, 2023
37d83fd
update knowledge module;
Cadenza-Li Jul 13, 2023
51a9403
Merge remote-tracking branch 'origin/main' into finco
you-n-g Jul 14, 2023
b9b6938
Merge branch 'finco' into update_knowledge_module
Fivele-Li Jul 14, 2023
e5f685c
merge all commit (#1593)
peteryang1 Jul 14, 2023
025859a
Merge branch 'finco' into update_knowledge_module
Fivele-Li Jul 14, 2023
a19e616
Update test_utils.py
you-n-g Jul 14, 2023
8a56cf6
add KnowledgeBase to workflow;
Fivele-Li Jul 14, 2023
5e0873c
Merge pull request #1592 from Fivele-Li/update_knowledge_module
peteryang1 Jul 16, 2023
1c9841b
Connect TrainTask & Rolling & DDG-DA (#1599)
you-n-g Jul 17, 2023
8c1905d
Optimize KnowledgeBase to complete workflow (#1598)
Fivele-Li Jul 17, 2023
b21e044
Fix find class bug (#1601)
you-n-g Jul 17, 2023
13c63ee
merge into one commit
peteryangms Jul 17, 2023
d909d54
Merge pull request #1603 from microsoft/xuyang1/add_idea_task
peteryang1 Jul 17, 2023
1c5a73a
small refinement in finance knowledge
peteryangms Jul 17, 2023
ce8cb51
hot fix one small bug in template
peteryangms Jul 18, 2023
8eb1293
Add prompt logger
you-n-g Jul 18, 2023
561086d
commit
peteryangms Jul 19, 2023
f93f331
Merge pull request #1609 from microsoft/xuyang1/finetune_prompts
peteryang1 Jul 19, 2023
70a066b
optimize workflow and output format
Fivele-Li Jul 20, 2023
5af99e1
optimize log (#1612)
Fivele-Li Aug 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -22,6 +22,7 @@ dist/
qlib/VERSION.txt
qlib/data/_libs/expanding.cpp
qlib/data/_libs/rolling.cpp
qlib/finco/prompt_cache.json
examples/estimator/estimator_example/
examples/rl/data/
examples/rl/checkpoints/
Expand Down
16 changes: 12 additions & 4 deletions qlib/contrib/data/handler.py
@@ -1,6 +1,8 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

from typing import Optional
from qlib.utils.data import update_config
from ...data.dataset.handler import DataHandlerLP
from ...data.dataset.processor import Processor
from ...utils import get_callable_kwargs
Expand Down Expand Up @@ -57,12 +59,13 @@ def __init__(
fit_end_time=None,
filter_pipe=None,
inst_processors=None,
data_loader: Optional[dict] = None,
**kwargs
):
infer_processors = check_transform_proc(infer_processors, fit_start_time, fit_end_time)
learn_processors = check_transform_proc(learn_processors, fit_start_time, fit_end_time)

data_loader = {
_data_loader = {
"class": "QlibDataLoader",
"kwargs": {
"config": {
Expand All @@ -74,12 +77,14 @@ def __init__(
"inst_processors": inst_processors,
},
}
if data_loader is not None:
update_config(_data_loader, data_loader)

super().__init__(
instruments=instruments,
start_time=start_time,
end_time=end_time,
data_loader=data_loader,
data_loader=_data_loader,
learn_processors=learn_processors,
infer_processors=infer_processors,
**kwargs
Expand Down Expand Up @@ -153,12 +158,13 @@ def __init__(
process_type=DataHandlerLP.PTYPE_A,
filter_pipe=None,
inst_processors=None,
data_loader: Optional[dict] = None,
**kwargs
):
infer_processors = check_transform_proc(infer_processors, fit_start_time, fit_end_time)
learn_processors = check_transform_proc(learn_processors, fit_start_time, fit_end_time)

data_loader = {
_data_loader = {
"class": "QlibDataLoader",
"kwargs": {
"config": {
Expand All @@ -170,11 +176,13 @@ def __init__(
"inst_processors": inst_processors,
},
}
if data_loader is not None:
update_config(_data_loader, data_loader)
super().__init__(
instruments=instruments,
start_time=start_time,
end_time=end_time,
data_loader=data_loader,
data_loader=_data_loader,
infer_processors=infer_processors,
learn_processors=learn_processors,
process_type=process_type,
Expand Down
18 changes: 18 additions & 0 deletions qlib/finco/.env.example
@@ -0,0 +1,18 @@

OPENAI_API_KEY=your_api_key

# USE_AZURE=True
# AZURE_API_BASE=your_api_base
# AZURE_API_VERSION=your_api_version

# use gpt-4 means more token but more wait time
# MODEL=gpt-4
# MAX_TOKENS=1600
# MAX_RETRY=1000


MAX_TOKENS=1600
MAX_RETRY=120

CONTINOUS_MODE=True
DEBUG_MODE=True
Empty file added qlib/finco/__init__.py
Empty file.
15 changes: 15 additions & 0 deletions qlib/finco/cli.py
@@ -0,0 +1,15 @@
import fire
from qlib.finco.workflow import WorkflowManager
from dotenv import load_dotenv
from qlib import auto_init


def main(prompt=None):
load_dotenv(verbose=True, override=True)
wm = WorkflowManager()
wm.run(prompt)


if __name__ == "__main__":
auto_init()
fire.Fire(main)
31 changes: 31 additions & 0 deletions qlib/finco/conf.py
@@ -0,0 +1,31 @@
# TODO: use pydantic for other modules in Qlib
from pydantic import BaseSettings
from qlib.finco.utils import Singleton

import os


class Config(Singleton):
"""
This config is for fast demo purpose.
Please use BaseSettings insetead in the future
"""

def __init__(self):
self.use_azure = os.getenv("USE_AZURE") == "True"
self.temperature = 0.5 if os.getenv("TEMPERATURE") is None else float(os.getenv("TEMPERATURE"))
self.max_tokens = 800 if os.getenv("MAX_TOKENS") is None else int(os.getenv("MAX_TOKENS"))

self.openai_api_key = os.getenv("OPENAI_API_KEY")
self.use_azure = os.getenv("USE_AZURE") == "True"
self.azure_api_base = os.getenv("AZURE_API_BASE")
self.azure_api_version = os.getenv("AZURE_API_VERSION")
self.model = os.getenv("MODEL") or ("gpt-35-turbo" if self.use_azure else "gpt-3.5-turbo")

self.max_retry = int(os.getenv("MAX_RETRY")) if os.getenv("MAX_RETRY") is not None else None

self.continous_mode = (
os.getenv("CONTINOUS_MODE") == "True" if os.getenv("CONTINOUS_MODE") is not None else False
)
self.debug_mode = os.getenv("DEBUG_MODE") == "True" if os.getenv("DEBUG_MODE") is not None else False
self.workspace = os.getenv("WORKSPACE") if os.getenv("WORKSPACE") is not None else "./finco_workspace"
98 changes: 98 additions & 0 deletions qlib/finco/llm.py
@@ -0,0 +1,98 @@
import os
import time
import openai
import json
from typing import Optional
from qlib.log import get_module_logger
from qlib.finco.conf import Config
from qlib.finco.utils import Singleton


class APIBackend(Singleton):
def __init__(self):
self.cfg = Config()
openai.api_key = self.cfg.openai_api_key
if self.cfg.use_azure:
openai.api_type = "azure"
openai.api_base = self.cfg.azure_api_base
openai.api_version = self.cfg.azure_api_version
self.use_azure = self.cfg.use_azure

self.debug_mode = False
if self.cfg.debug_mode:
self.debug_mode = True
cwd = os.getcwd()
self.cache_file_location = os.path.join(cwd, "prompt_cache.json")
self.cache = (
json.load(open(self.cache_file_location, "r")) if os.path.exists(self.cache_file_location) else {}
)

def build_messages_and_create_chat_completion(self, user_prompt, system_prompt=None):
"""build the messages to avoid implementing several redundant lines of code"""
cfg = Config()
# TODO: system prompt should always be provided. In development stage we can use default value
if system_prompt is None:
try:
system_prompt = cfg.system_prompt
except AttributeError:
get_module_logger("finco").warning("system_prompt is not set, using default value.")
system_prompt = "You are an AI assistant who helps to answer user's questions about finance."
messages = [
{
"role": "system",
"content": system_prompt,
},
{
"role": "user",
"content": user_prompt,
},
]
response = self.try_create_chat_completion(messages=messages)
return response

def try_create_chat_completion(self, max_retry=10, **kwargs):
max_retry = self.cfg.max_retry if self.cfg.max_retry is not None else max_retry
for i in range(max_retry):
try:
response = self.create_chat_completion(**kwargs)
return response
except openai.error.RateLimitError as e:
print(e)
print(f"Retrying {i+1}th time...")
time.sleep(1)
continue
raise Exception(f"Failed to create chat completion after {max_retry} retries.")

def create_chat_completion(
self,
messages,
model=None,
temperature: float = None,
max_tokens: Optional[int] = None,
) -> str:

if self.debug_mode:
if messages[1]["content"] in self.cache:
return self.cache[messages[1]["content"]]

if temperature is None:
temperature = self.cfg.temperature
if max_tokens is None:
max_tokens = self.cfg.max_tokens

if self.cfg.use_azure:
response = openai.ChatCompletion.create(
engine=self.cfg.model,
messages=messages,
max_tokens=self.cfg.max_tokens,
)
else:
response = openai.ChatCompletion.create(
model=self.cfg.model,
messages=messages,
)
resp = response.choices[0].message["content"]
if self.debug_mode:
self.cache[messages[1]["content"]] = resp
json.dump(self.cache, open(self.cache_file_location, "w"))
return resp