Skip to content

Ollama 出马,为你创建实时 AI 股票顾问

作者:老余捞鱼

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

写在前面的话:本文会介绍一个利用大模型 LLM(Ollama Llama 3)和 Streamlit 开发的实时人工智能股票顾问项目,该顾问目前能够做到每分钟获取股票数据,分析趋势,并为您提供易于理解的实时解释。

前阵子为一个智慧金融项目制作股票数据可视化部署 AI能力时,我突然冒出个想法:要是数据能自己用通俗易懂的话来解释,那该多好啊!于是,我就开始尝试做这个。这个脚本利用 LLM 驱动(Llama 3)的洞察力,每分钟获取股票数据,分析趋势,然后给出简单易懂的解释。这样,我们就再也不用眯着眼睛看图表啦 —— 就像有个私人股票专家在身边,随时为你分析正在发生的事情。闲话不多扯了,我们直接进入正题。


步骤 1:设置环境

在开始编写代码之前,你需要安装几个关键库,它们将使本项目成为可能。我们将使用 Streamlit 创建用户界面,使用 yfinance 获取股票数据,使用 Ollama 生成自然语言见解。

import streamlit as st
import yfinance as yf
import pandas as pd
import schedule
import time
import ollama
from datetime import datetime, timedelta

如果还有同学不知道 Streamlit 是啥东东,我简单的解释下:

Streamlit是一个基于Python的Web应用程序框架,专为数据科学和机器学习领域的快速应用开发而设计。它简化了数据科学和机器学习应用的开发过程,使得用户能够更快地构建和部署交互式、可视化的Web应用程序。无论是对于数据科学家、机器学习工程师还是教育工作者来说,Streamlit都是一个值得尝试的工具。
Streamlit的安装非常简单,与安装其他Python包类似。用户可以通过pip命令轻松安装。使用Streamlit时,用户只需编写Python脚本,并在脚本中调用Streamlit的API来创建界面元素和逻辑。完成编写后,可以使用Streamlit的命令行工具来运行应用程序,并在浏览器中查看和交互。

步骤 2:获取库存数据进行分析

接下来,我们获取苹果公司 (AAPL) 和道琼斯指数 (DJI) 的历史股票数据。我们将使用 yfinance 以 1 分钟为间隔获取前一天的股票数据。

# Fetching historical data for Apple (AAPL) and Dow Jones (DJI)
stock = yf.Ticker("AAPL")
dow_jones = yf.Ticker("^DJI")
data = stock.history(period="1d", interval="1m")
dow_data = dow_jones.history(period="1d", interval="1m")

在这个阶段,我们已经掌握了股票数据,并将利用这些数据进行进一步分析。为了便于演示,我使用了上一个交易日的数据。你也可以自己来调整,直接从应用程序接口获取实时股票数据,处理实时市场更新。

步骤 3:处理实时库存更新

现在让我们通过每分钟处理一个股票数据点来模拟实时更新。我们将计算滚动平均线和动量等指标,以了解市场趋势。

# Global variables to store rolling data
rolling_window = pd.DataFrame()
dow_rolling_window = pd.DataFrame()
daily_high = float('-inf')
daily_low = float('inf')
buying_momentum = 0
selling_momentum = 0

# Function to process a new stock update every minute
def process_stock_update():
    global rolling_window, data, dow_rolling_window, dow_data
    global daily_high, daily_low, buying_momentum, selling_momentum

    if not data.empty and not dow_data.empty:
        # Simulate receiving a new data point for AAPL and Dow Jones
        update = data.iloc[0].to_frame().T
        dow_update = dow_data.iloc[0].to_frame().T
        data = data.iloc[1:]  # Remove the processed row
        dow_data = dow_data.iloc[1:]

        # Append the new data points to the rolling windows
        rolling_window = pd.concat([rolling_window, update], ignore_index=False)
        dow_rolling_window = pd.concat([dow_rolling_window, dow_update], ignore_index=False)

        # Update daily high and low
        daily_high = max(daily_high, update['Close'].values[0])
        daily_low = min(daily_low, update['Close'].values[0])

        # Calculate momentum
        if len(rolling_window) >= 2:
            price_change = update['Close'].values[0] - rolling_window['Close'].iloc[-2]
            if price_change > 0:
                buying_momentum += price_change
            else:
                selling_momentum += abs(price_change)

每个新数据点都会添加到 5 分钟分析的滚动窗口中。每日最高/最低值和价格动量会根据连续数据点之间的变化进行更新。

步骤 4:分析股票走势

为了理解实时数据,我们会计算移动平均线、价格变化、成交量变化以及指数移动平均线 (EMA)、布林带和 RSI(相对强弱指数)等技术指标。以下是这些指标的工作原理:

  • 指数移动平均线 (EMA):更看重近期价格,以识别短期趋势。
  • 相对强弱指数(RSI):衡量价格变动速度,在 0 和 100 之间震荡,以确定超买/超卖情况。
  • 布林线:利用移动平均线附近的上下限带帮助评估市场波动。
def calculate_insights(window, dow_window):
    if len(window) >= 5:
        # 5-minute rolling average
        rolling_avg = window['Close'].rolling(window=5).mean().iloc[-1]
        
        # Exponential Moving Average (EMA)
        ema = window['Close'].ewm(span=5, adjust=False).mean().iloc[-1]
        
        # Bollinger Bands (using a 5-period window)
        std = window['Close'].rolling(window=5).std().iloc[-1]
        bollinger_upper = rolling_avg + (2 * std)
        bollinger_lower = rolling_avg - (2 * std)

        # RSI calculation
        delta = window['Close'].diff()
        gain = delta.where(delta > 0, 0)
        loss = -delta.where(delta < 0, 0)
        avg_gain = gain.rolling(window=14, min_periods=1).mean().iloc[-1]
        avg_loss = loss.rolling(window=14, min_periods=1).mean().iloc[-1]
        rs = avg_gain / avg_loss if avg_loss != 0 else float('nan')
        rsi = 100 - (100 / (1 + rs))

        # Calculate Relative Strength Index (RSI) if there are enough periods (14 is typical)
        delta = window['Close'].diff()
        gain = delta.where(delta > 0, 0)
        loss = -delta.where(delta < 0, 0)
        avg_gain = gain.rolling(window=14, min_periods=1).mean().iloc[-1]
        avg_loss = loss.rolling(window=14, min_periods=1).mean().iloc[-1]
        rs = avg_gain / avg_loss if avg_loss != 0 else float('nan')
        rsi = 100 - (100 / (1 + rs))

        # Calculate Dow Jones index rolling average
        dow_rolling_avg = dow_window['Close'].rolling(window=5).mean().iloc[-1]
        
        market_open_duration = get_market_open_duration(window)
        if int(market_open_duration) % 5 == 0:  # Trigger LLM every 5 minutes
            get_natural_language_insights(
                rolling_avg, ema, rsi, bollinger_upper, bollinger_lower,
                price_change, volume_change, dow_rolling_avg, market_open_duration, 
                dow_price_change, dow_volume_change, daily_high, daily_low, 
                buying_momentum, selling_momentum
            )


步骤 5:使用 Ollama 获取自然语言解释

为了使该项目更具互动性,我们可以整合了免费的 Llama 3 模型,每 5 分钟生成一次自然语言见解。

def get_natural_language_insights(
    rolling_avg, ema, rsi, bollinger_upper, bollinger_lower,
    price_change, volume_change, dow_rolling_avg, market_open_duration, dow_price_change, dow_volume_change, 
    daily_high, daily_low, buying_momentum, selling_momentum, timestamp
):
    prompt = f"""
    You are a professional stock broker. Apple's stock has a 5-minute rolling average of {rolling_avg:.2f}.
    The Exponential Moving Average (EMA) is {ema:.2f}, and the Relative Strength Index (RSI) is {rsi:.2f}.
    The Bollinger Bands are set with an upper band of {bollinger_upper:.2f} and a lower band of {bollinger_lower:.2f}.
    The price has changed by {price_change:.2f}, and the volume has shifted by {volume_change}.
    The DOW price has changed by {dow_price_change:.2f}, and the volume has shifted by {dow_volume_change}.
    Meanwhile, the Dow Jones index has a 5-minute rolling average of {dow_rolling_avg:.2f}.
    The market has been open for {market_open_duration:.2f} minutes.
    Today's high was {daily_high:.2f} and low was {daily_low:.2f}.
    The buying momentum is {buying_momentum:.2f} and selling momentum is {selling_momentum:.2f}.
    Based on this data, provide insights into the current stock trend and the general market sentiment.
    The insights should not be longer than 100 words and should not have an introduction.
    """
    response = ollama.chat(
        model="llama3",
        messages=[{"role": "user", "content": prompt}]
    )
    response_text = response['message']['content'].strip()
    print("Natural Language Insight:", response_text)

该功能使用前 5 分钟的技术指标和趋势,以简单的英语总结股票表现。

步骤 6:设置 Streamlit 用户界面

现在,让我们用 Streamlit 设置一个基本的用户界面,实时显示股票更新和见解。

message = st.chat_message("assistant")
message.write(timestamp)
message.write(response_text)
streamlit run app.py

到这里,你就拥有自己的实时股票顾问了!如果需要中文,你可以在前面定义输出语言。

改进和提升空间

该项目只是一个原型,重点关注上一个交易日的趋势。您可以通过纳入历史数据、新闻整合或时间序列神经网络等高级模型来改进它。以下是一些升级构想供您参考(自己去折腾):

  1. 历史数据集成:目前,该应用程序只处理前一个交易日的数据。您可以通过整合数周或数月的滚动历史数据窗口来增强其功能。
  2. 新闻整合:股票走势经常受到新闻事件的影响。添加实时新闻源可提供更好的背景信息。
  3. 高级时间序列预测:您可以通过时间序列神经网络进一步提高洞察力,从而获得更准确的趋势预测。
  4. 自定义警报:根据重要的股票走势或 RSI 等指标实施自定义警报,可使这款应用程序对日间交易者更加实用。

完整的源代码和 Jupyter Notebook 可在我的 GitHub 中获取。如果您有优化提示或其他改进意见,请随时联系我。

https://github.com/alexyu2013/llama3/tree/main/stock-trader

感谢您阅读到最后,希望本文能给您带来新的收获。如果对文中的内容有任何疑问,请给我留言,必复。


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

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

Published inAI&Invest专栏

Be First to Comment

    发表回复