借 AI 之力,将各类量化金融论文自动转成可执行交易算法

作者:老余捞鱼

原创不易,转载请标明出处及原作者。

写在前面的话:
       本文介绍了一种通过 AI 技术驱动的自动化工作流,用于将各类量化金融论文自动转化为可执行的交易算法及附带回溯测试代码,本文最后还说明了该工具的使用方法和初步测试结果。

       量化金融在很大程度上依赖于从大量研究中得出的数据驱动策略。然而,将密集的学术论文中的见解转化为可执行的交易算法既耗时又容易出错。虽然创建一个可立即投入生产的解决方案可能无法立即实现,但我们的目标是在几秒钟内为 QuantConnect 生成模板代码。这些初始代码可以帮助我们确定这篇论文是否提出了有前途的策略,是值得进一步研究,还是应该放弃。

       我们会详细说明如何运行这段代码(可在我的 GitHub 上获取),并介绍我们的初步测试结果。值得注意的是,我们的方法采用了线性工作流程,暂时搁置了双代理架构。该工具不仅能从 PDF 文章中提取和总结关键交易策略和风险管理技术,还能生成语法正确的 QuantConnect Python 代码,并带有语法高亮显示,便于查看。

       初步测试显示,生成的代码无误,但尚未在所有测试中达成 100%的准确率。该工具的 NLP 部分运用了基础技术,仍有提升空间。即便如此,我仍决定迅速发布代码,作为这一快速发展领域的概念验证。该项目仍在推进,且会随时间推移持续完善。

       所以今天我在这里介绍的是它的测试版。这个自动化工具的核心在于 ArticleProcessor 类,它协调了从 PDF 提取到代码生成的整个工作流程。下面是它的功能分解:

一、使用 pdfplumber 提取 PDF 文本

       我们从使用 pdfplumber 库加载和提取量化金融论文 PDF 格式文件中的文本开始。与其他 PDF 解析工具不同,pdfplumber 具有超高的准确性,尤其是在处理学术文章中常见的复杂布局时。

def load_pdf(self, pdf_path: str) -> str:
    try:
        text = ""
        with pdfplumber.open(pdf_path) as pdf:
            for page in pdf.pages:
                page_text = page.extract_text()
                if page_text:
                    text += page_text + "\n"
        logging.info("PDF loaded successfully.")
        return text
    except Exception as e:
        logging.error(f"Failed to load PDF: {e}")
        return ""

二、文本预处理

       提取文本后,必须对其进行清洗和预处理。这包括使用正则表达式删除 URL、页眉、页脚、独立数字(如页码)和其他无关内容。

def preprocess_text(self, text: str) -> str:
    try:
        text = re.sub(r'https?://\S+', '', text)
        text = re.sub(r'Electronic copy available at: .*', '', text)
        text = re.sub(r'^\d+\s*$', '', text, flags=re.MULTILINE)
        text = re.sub(r'\n+', '\n', text)
        text = re.sub(r'^\s*(Author|Title|Abstract)\s*$', '', text, flags=re.MULTILINE | re.IGNORECASE)
        text = text.strip()
        logging.info("Text preprocessed successfully.")
        return text
    except Exception as e:
        logging.error(f"Failed to preprocess text: {e}")
        return ""

三、利用 SpaCy 进行分析

       了解文章结构至关重要。利用 SpaCy 的 NLP 功能,该工具可根据句子长度和标题格式等启发式方法识别章节标题。这种细分有助于组织内容,进行更有效的分析。

def detect_headings(self, text: str) -> List[str]:
    try:
        doc = self.nlp(text)
        headings = []
        for sent in doc.sents:
            sent_text = sent.text.strip()
            if len(sent_text.split()) < 10 and sent_text.istitle():
                headings.append(sent_text)
        logging.info(f"Detected {len(headings)} headings.")
        return headings
    except Exception as e:
        logging.error(f"Failed to detect headings: {e}")
        return []

四、分段和关键词分析

       确定标题后,文本将被分成相应的部分。然后,该工具会执行关键字分析,将句子归类为trading_signal(交易信号)和 risk_management(风险管理)。这种分类以预定义的相关关键词列表为基础,确保只对相关信息进行进一步处理。

def keyword_analysis(self, sections: Dict[str, str]) -> Dict[str, List[str]]:
    keyword_map = defaultdict(list)
    
    risk_management_keywords = [
        "drawdown", "volatility", "reduce", "limit", "risk", "risk-adjusted", 
        "maximal drawdown", "market volatility", "bear markets", "stability", 
        "sidestep", "reduce drawdown", "stop-loss", "position sizing", "hedging"
    ]
    trading_signal_keywords = [
        "buy", "sell", "signal", "indicator", "trend", "SMA", "moving average", 
        "momentum", "RSI", "MACD", "bollinger bands", "Rachev ratio", "stay long", 
        "exit", "market timing", "yield curve", "recession", "unemployment", 
        "housing starts", "Treasuries", "economic indicator"
    ]
    
    irrelevant_patterns = [
        r'figure \d+',  
        r'\[\d+\]',     
        r'\(.*?\)',     
        r'chart',       
        r'\bfigure\b',  
        r'performance chart',  
        r'\d{4}-\d{4}',  
        r'^\s*$'        
    ]
    
    processed_sentences = set()
    
    for section, content in sections.items():
        doc = self.nlp(content)
        for sent in doc.sents:
            sent_text = sent.text.lower().strip()
            
            if any(re.search(pattern, sent_text) for pattern in irrelevant_patterns):
                continue
            if sent_text in processed_sentences:
                continue
            processed_sentences.add(sent_text)
            
            if any(kw in sent_text for kw in trading_signal_keywords):
                keyword_map['trading_signal'].append(sent.text.strip())
            elif any(kw in sent_text for kw in risk_management_keywords):
                keyword_map['risk_management'].append(sent.text.strip())
    
    for category, sentences in keyword_map.items():
        unique_sentences = list(set(sentences))
        keyword_map[category] = sorted(unique_sentences, key=len)
    
    logging.info("Keyword analysis completed.")
    return keyword_map

五、使用 OpenAI 的 GPT-4o 生成摘要和 QuantConnect 代码

       利用 OpenAI 的 GPT-4o 可生成提取策略和风险管理技术的简明摘要。它能将这些见解转化为功能完备的 QuantConnect Python 算法,确保这些算法符合最佳实践和语法正确性。

def generate_qc_code(self, extracted_data: Dict[str, List[str]]) -> str:
    trading_signals = '\n'.join(extracted_data.get('trading_signal', []))
    risk_management = '\n'.join(extracted_data.get('risk_management', []))

    prompt = f"""
    You are an expert QuantConnect algorithm developer. Convert the following trading strategy and risk management descriptions into a complete, error-free QuantConnect Python algorithm.

    ### Trading Strategy:
    {trading_signals}

    ### Risk Management:
    {risk_management}

    ### Requirements:
    1. **Initialize Method**:
        - Set the start and end dates.
        - Set the initial cash.
        - Define the universe selection logic.
        - Initialize required indicators.
    2. **OnData Method**:
        - Implement buy/sell logic based on indicators.
        - Ensure indicators are updated correctly.
    3. **Risk Management**:
        - Implement drawdown limit of 15%.
        - Apply position sizing or stop-loss mechanisms as described.
    4. **Ensure Compliance**:
        - Use only QuantConnect's supported indicators and methods.
        - The code must be syntactically correct and free of errors.

    ### Example Structure:
    ```python
    from AlgorithmImports import *

    class MyAlgorithm(QCAlgorithm):
        def Initialize(self):
            self.SetStartDate(2020, 1, 1)
            self.SetEndDate(2023, 1, 1)
            self.SetCash(100000)
            # Define universe, indicators, etc.

        def OnData(self, data):
            # Trading logic

        def OnEndOfDay(self):
            # Risk management
    ```

    ### Generated Code:
    ```
    # The LLM will generate the code after this line
    ```
    """

    try:
        response = openai.ChatCompletion.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": "You are a helpful assistant specialized in generating QuantConnect algorithms in Python."},
                {"role": "user", "content": prompt}
            ],
            max_tokens=2500,
            temperature=0.3,
            n=1
        )
        generated_code = response['choices'][0]['message']['content'].strip()
        code_match = re.search(r'```python(.*?)```', generated_code, re.DOTALL)
        if code_match:
            generated_code = code_match.group(1).strip()
        logging.info("Code generated by LLM.")
        return generated_code
    except Exception as e:
        logging.error(f"Failed to generate code: {e}")
        return ""

六、用 Tkinter 和 Pygments 显示结果

       为了提供友好的用户体验,该工具使用 Tkinter 在单独的窗口中显示文章摘要和生成的代码。Pygments 库通过为 Python 代码添加语法高亮来提高可读性。

def display_summary_and_code(self, summary: str, code: str):
    # Create the main Tkinter root
    root = tk.Tk()
    root.withdraw()  # Hide the root window

    # Summary Window
    summary_window = tk.Toplevel()
    summary_window.title("Article Summary")
    summary_window.geometry("800x600")

    summary_text = scrolledtext.ScrolledText(summary_window, wrap=tk.WORD, font=("Arial", 12))
    summary_text.pack(expand=True, fill='both')
    summary_text.insert(tk.END, summary)
    summary_text.configure(state='disabled')  # Make it read-only

    # Code Window
    code_window = tk.Toplevel()
    code_window.title("Generated QuantConnect Code")
    code_window.geometry("1000x800")

    code_text = scrolledtext.ScrolledText(code_window, wrap=tk.NONE, font=("Consolas", 12), bg="#2B2B2B", fg="#F8F8F2")
    code_text.pack(expand=True, fill='both')

    # Apply syntax highlighting
    self.apply_syntax_highlighting(code, code_text)

    code_text.configure(state='disabled')  # Make it read-only. 

    # Start the Tkinter event loop
    root.mainloop()

七、回溯测试

       我们使用 Quantpedia 中 L. Durian 和 R. Vojtko 的文章 "利用市场时机策略避开熊市"进行测试,输出结果如下:

       第一次运行时生成的代码会生成以下回溯测试 :

       我们可以看到,该算法在 2020 年处于闲置状态,这正是规避科维德熊市的策略所期望的。这篇文章似乎确实在设法避免熊市,绝对值得进一步研究。

八、观点回顾

       将定量金融研究自动转化为可执行的交易算法可以显著提高效率。通过集成 pdfplumber、SpaCy、OpenAI 的 GPT-4o、Tkinter 和 Pygments 等强大的库,这款基于 Python 的工具提供了一个无缝的解决方案,在研究和执行之间架起了一座桥梁。

  • 自动化工作流程的重要性:将金融论文中的见解自动转化为交易算法能显著提高效率,减少人为错误。
  • 技术实现的详细步骤:本文详细描述了从PDF提取到代码生成的整个技术流程,包括使用的库和工具,如pdfplumber、SpaCy、GPT-4o、Tkinter和Pygments。
  • 关键词分析的作用:通过关键词分析,自动化工具能够有效地从文章中提取交易策略和风险管理技术,确保生成的代码仅包含相关信息。
  • GPT-4o 在代码生成中的应用:GPT-4o 被用于将提取的策略和风险管理技术转化为完整、无误的QuantConnect Python算法。
  • 用户体验的优化:使用Tkinter创建用户友好的界面,并通过Pygments对代码进行语法高亮,以提高代码的可读性。
  • 初步测试的展示:通过对一篇具体文章的测试,展示了自动化工具的实际应用和效果,以及如何通过QuantConnect平台进行回溯测试。
  • 结论和未来展望:强调自动化工具在量化金融研究和实践中的潜力,并鼓励读者进一步探索和改进该工具。

参考资料

  1. Ďurian, Ladislav and Vojtko, Radovan, Avoid Equity Bear Markets with a Market Timing Strategy (March 23, 2023). Available at SSRN: https://ssrn.com/abstract=4397638 or http://dx.doi.org/10.2139/ssrn.4397638
  2. 我的 GitHub 上有该项目的代码,欢迎下载并参与研究
    GitHub - alexyu2013/Article_to_Code: Algorithms Generation from Quantitative Finance Articles to QuantConnect
  3. 你们会用到的QuantConnect 平台地址
    Open Source Algorithmic Trading Platform. - QuantConnect.com

感谢您阅读到最后。如果对文中的内容有任何疑问,请给我留言,必复。


文内容仅仅是技术探讨和学习,并不构成任何投资建议。

转发请注明原作者和出处。

上一篇:IntelliJ IDEA 查看类class的结构Structure轮廓outline窗口, 快捷键是Alt+7


下一篇:设计模式——装饰器模式