人工智能驱动与人工管理股票基金在不同市场趋势下的比较

登录后收藏

学术论文

人工智能驱动与人工管理股票基金在不同市场趋势下的比较分析

作者Amirul Ammar Anuar; Ahmad Azam Bin Sulaiman; Mohammad Taqiuddin Mohamad

机构
  • University of Malaya

导读

投资标的:XLB、XLE、XLF、XLI、XLK、XLP、XLU、XLV、XLY、XLC、XLRE。再平衡:每月。Universe: XLB, XLE, XLF, XLI, XLK, XLP, XLU, XLV, XLY, XLC, XLRE. Parameters: rebalance=monthly.

策略概要

该策略投资于 XLB、XLE、XLF、XLI、XLK、XLP、XLU、XLV、XLY、XLC、XLRE,按 126 日过去收益排序并等权持有前 3 名,按月再平衡。由于基金分类主信号不可用,回测用行业动量规则保留主题。论文比较 2022 年下跌与 2023–2024 年回升中 AI 基金与人工基金的夏普、Treynor 与 Jensen Alpha。

经济逻辑

相关证据来自“人工智能驱动与人工管理股票基金在不同市场趋势下的比较分析”:不同周期需要不同技能;AI 规则在某些趋势中更稳,人工判断在另一些转折中更优,行业动量则提供不依赖基金分类的可交易代理。公开数据里没有论文的原始信号,实验室改为在XLB、XLE、XLF、XLI、XLK、XLP、XLU、XLV、XLY、XLC、XLRE.上运行同一再平衡规则。绩效来自这次可交易替代回测,不是论文原表。

入场与出场

入场

每月按 126 日收益排序 XLB, XLE, XLF, XLI, XLK, XLP, XLU, XLV, XLY, XLC (+1 more),等权持有最强的 3 只。

出场

跌出前 3 即卖出,换入新的领涨标的。

所需行情数据

需要 XLB, XLE, XLF, XLI, XLK, XLP (+5 more) 至少 126 根复权收盘价,才能形成第一个有效信号。ETF 仅交易日有行情。 实验室通过 backtest.data.load_daily_prices 读取 Yahoo Finance。

标的

XLB, XLE, XLF, XLI, XLK, XLP, XLU, XLV, XLY, XLC, XLRE

频率

每月 · 日频复权收盘价;信号按月再平衡(用当月最后一个有效交易日)

所需历史

每个标的至少 126 根交易日 K 线(约 6 个交易月),外加数根缓冲才能发出第一个信号。

字段

  • 复权收盘价(Adj Close / auto_adjust Close)
  • 交易日日期索引

凭证

  • 回测与实验室:无需密钥
  • 实盘下单才需要券商 / 交易所 API

API 与数据源

Quant Buffet 数据 API

实验室用 load_daily_prices 拉取 ASSETS 的复权收盘价面板,磁盘缓存后回测可复现。

提供方
backtest.data.load_daily_prices
鉴权
回测无需密钥
接口
load_daily_prices(ASSETS, start="2000-01-01")
API / docs → /zh/docs/data-api

Yahoo Finance(yfinance)

美股 / ETF 复权 OHLCV,免费研究接口,有速率限制。

提供方
yfinance → Yahoo Finance
鉴权
公开历史无需密钥
接口
Ticker.history / yf.download(auto_adjust=True)
API / docs → github.com/ranaroussi/yfinance

Onsite backtest IDE

Quant Buffet 原生回测 IDE

Edit and run Quant Buffet Python for 人工智能驱动与人工管理股票基金在不同市场趋势下的比较 in the browser. Results update live with equity, drawdown, and metrics charts. Allowed: backtest.data, backtest.engine, backtest.metrics, numpy, pandas. Define ASSETS and make_on_day(prices). Shortcut: Ctrl+Enter. API docs →

Ready — edit code, then Run backtest.
IDE · 42 lines
Quant Buffet syntax cheat sheet (copy / insert)

Paste these fragments into the editor. The sandbox rejects QuantConnect, os, and network libraries.

Required imports
Only these libraries are allowed in the sandbox.
from __future__ import annotations

import numpy as np
import pandas as pd

from backtest.data import load_daily_prices
from backtest.engine import EngineConfig, PortfolioEngine
from backtest.metrics import compute_metrics
ASSETS list (whitelisted ETFs)
Module-level list. Tickers must be in the Quant Buffet whitelist.
ASSETS = ["SPY", "QQQ", "TLT", "GLD", "BIL"]
make_on_day contract
Must return (on_day, ready). on_day calls engine.set_target_weights.
def make_on_day(prices: pd.DataFrame):
    cols = [c for c in ASSETS if c in prices.columns]
    sma = prices[cols].rolling(200, min_periods=200).mean()
    state = {"last": None}

    def on_day(engine: PortfolioEngine, dt: pd.Timestamp) -> None:
        if sma.loc[dt].isna().all():
            return
        key = (dt.year, dt.month)
        if state["last"] == key:
            return
        state["last"] = key
        long = [
            s for s in cols
            if pd.notna(prices.at[dt, s]) and pd.notna(sma.at[dt, s])
            and prices.at[dt, s] > sma.at[dt, s]
        ]
        weights = {} if not long else {s: 1.0 / len(long) for s in long}
        engine.set_target_weights(dt, weights)

    ready = sma.dropna(how="all").index.min() if sma.notna().any().any() else None
    return on_day, ready
Set target weights
Weights should sum to about 1.0. Empty dict = 100% cash.
engine.set_target_weights(dt, {"SPY": 0.60, "BIL": 0.40})

Live backtest performance

CAGR
6.75%
Sharpe
0.45
Max DD
-53.11%
Vol
18.22%
Sortino
0.69
Beta
0.78

Stored preview only. Run backtest to simulate every daily session from Yahoo Finance adjusted closes.

Run the backtest to load orders, then mark them on the equity chart.
Equity curve (indexed = 100)

Accent = strategy · dashed grey = buy-and-hold benchmark. Hover or tap any point for its date and values.

593135678222000-072011-012019-062026-08DateIndex (start = 100)
Drawdown
-42%-28%-14%0%2000-072011-012019-062026-08DateDrawdown (%)
Metrics bar chart
Grey = baseline · Accent = live run0.0183553CAGRSharpeSortinoVol|DD|MetricReported value
Monthly returns
-1.0%0.0%+1.0%2020-062022-082024-072026-08MonthReturn (%)

Order records

Run the backtest to load every order fill alongside performance.

Export to your platform

Transform Quant Buffet lab code (ASSETS + make_on_day / PortfolioEngine) into native classes for a third-party IDE — then copy and paste.

Target platform

Run in: QuantConnect Cloud or LEAN CLI · QCAlgorithm with Equity securities and monthly rebalance.

Detected pattern: Momentum rotationAssets: XLB, XLE, XLF, XLI, XLK, XLP, XLU, XLV, XLY, XLC, XLRE
# Generated from Quant Buffet → QuantConnect LEAN
# Strategy: 人工智能驱动与人工管理股票基金在不同市场趋势下的比较
# Detected pattern: Momentum rotation
# Source uses Quant Buffet lab APIs (ASSETS + make_on_day / PortfolioEngine).
# Review fees, data, and risk before live trading — educational export only.

from AlgorithmImports import *


class QuantBuffetExport(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2010, 1, 1)
        self.SetCash(100000)
        tickers = ["XLB", "XLE", "XLF", "XLI", "XLK", "XLP", "XLU", "XLV", "XLY", "XLC", "XLRE"]
        self.symbols = []
        for t in tickers:
            if "-" in t:  # crypto proxy e.g. BTC-USD
                self.symbols.append(self.AddCrypto(t.replace("-USD", ""), Resolution.Daily).Symbol)
            else:
                self.symbols.append(self.AddEquity(t, Resolution.Daily).Symbol)
        self.Schedule.On(
            self.DateRules.MonthStart(self.symbols[0]),
            self.TimeRules.AfterMarketOpen(self.symbols[0], 30),
            self.Rebalance,
        )
        # Logic: Hold top 3 by 126-day return; monthly.

    def Rebalance(self):
        scores = {}
        for symbol in self.symbols:
            hist = self.History(symbol, 126 + 5, Resolution.Daily)
            if hist.empty: continue
            close = hist["close"]
            if hasattr(close, "unstack"):
                close = close.unstack(level=0).iloc[:, 0]
            if len(close) < 126 + 1: continue
            scores[symbol] = float(close.iloc[-1] / close.iloc[-126 - 1] - 1)
        ranked = sorted(scores.items(), key=lambda kv: kv[1], reverse=True)[:3]
        for symbol in self.symbols:
            self.SetHoldings(symbol, 0)
        if ranked:
            w = 1.0 / len(ranked)
            for symbol, _ in ranked:
                self.SetHoldings(symbol, w)

导出代码使用目标平台的原生类与库。请在第三方 IDE 中安装依赖后运行;实盘前请自行验证。