当前位置: 首页 > news >正文

量化交易 - 聚宽joinquant - 多因子入门研究 - 源码开源

先看一下我们的收益: JoinQuant直达这里看看

 下面讲解原理和代码。

目录

一、是否为st

二、是否停牌

三、市值小、roe大

四、编写回测代码


今天来研究一下多因子回测模型,这里以‘市值’、‘roe’作为例子。

几个标准:沪深300里选股,市值小、roe大、排出st 停牌、定期调仓

一、是否为st

# 是否st
is_st_info = get_extras('is_st', ['000001.XSHE', '000018.XSHE'], start_date='2025-04-01', end_date='2025-04-10', df=True)
print(is_st_info)
            000001.XSHE  000018.XSHE
2025-04-01        False         True
2025-04-02        False         True
2025-04-03        False         True
2025-04-07        False         True
2025-04-08        False         True
2025-04-09        False         True
2025-04-10        False         True

然后我们改装成为函数

# 获取st股票
def get_st_stocks(stock_list, end_date, count):
    is_st_info = get_extras('is_st', stock_list, end_date=end_date, count=count, df=True)
    st_stocks = []
    for stock in stock_list:
        if is_st_info[stock].any():  # 检查该股票是否在任何一天为True
            st_stocks.append(stock)
    return st_stocks

x = get_st_stocks(['300956.XSHE', '300542.XSHE', '000018.XSHE'], end_date='2025-04-12', count=5)
print(x)

二、是否停牌

# 是否停牌
x = get_price(['300956.XSHE', '300542.XSHE'], count=5, end_date='2025-04-12', 
              fields='paused',
              frequency='daily', skip_paused=False, panel=False)
print(x)
        time         code  paused
0 2025-04-07  300956.XSHE     0.0
1 2025-04-08  300956.XSHE     0.0
2 2025-04-09  300956.XSHE     0.0
3 2025-04-10  300956.XSHE     1.0
4 2025-04-11  300956.XSHE     1.0
5 2025-04-07  300542.XSHE     0.0
6 2025-04-08  300542.XSHE     1.0
7 2025-04-09  300542.XSHE     1.0
8 2025-04-10  300542.XSHE     1.0
9 2025-04-11  300542.XSHE     1.0

同样,我们改装成函数

# 是否停牌
def get_paused_stocks(stock_list, end_date, count):
    paused_info = get_price(stock_list, count=count, end_date=end_date, 
                            fields='paused', frequency='daily', 
                            skip_paused=False, panel=False)
    paused_stocks = []
    for stock in stock_list:
        if (paused_info[paused_info['code'] == stock]['paused'] == 1.0).any():  # 检查该股票是否在任何一天为1.0
            paused_stocks.append(stock)
    return paused_stocks

x = get_paused_stocks(['000001.XSHE', '300956.XSHE', '300542.XSHE'], end_date='2025-04-12', count=5)
print(x)

 

三、市值小、roe大

获取某一天沪深300股票中yb天不停牌、不st,且满足市值和ROE排名前N的股票

from jqdata import *

def get_mystocks(date, yb=63, N=20):
    """
    获取某一天沪深300股票中yb天不停牌、不st,且满足市值和ROE排名前N的股票

    :param date: 指定日期
    :param yb: 多少天不停牌
    :param N: 排名前多少
    :return: 满足条件的股票代码列表
    """
    # 获取沪深300的成分股
    hs300_stocks = get_index_stocks('000300.XSHG', date=date)

    # 获取股票的基本面数据
    q = query(
        valuation.code,
        valuation.market_cap,
        indicator.roe
    ).filter(
        valuation.code.in_(hs300_stocks)
    )
    df = get_fundamentals(q, date=date)
    #print(df.head())

    # 获取停牌股票
    paused_stocks = get_paused_stocks(hs300_stocks, end_date=date, count=yb)
    #print(paused_stocks)
    
    # 获取st股票
    st_stocks = get_st_stocks(hs300_stocks, end_date=date, count=yb)
    #print(st_stocks)

    # 获取剔除停牌和ST的股票代码列表
    all_paused_stocks = set(paused_stocks + st_stocks)

    # 从df中筛选出剔除停牌和ST的股票
    df = df[~df['code'].isin(all_paused_stocks)]

    # 对市值进行升序排名,对ROE进行降序排名
    df['market_cap_rank'] = df['market_cap'].rank(method='first', ascending=True)
    df['roe_rank'] = df['roe'].rank(method='first', ascending=False)
    df['composite_rank'] = df['market_cap_rank'] + df['roe_rank']

    # 按综合排名升序排序并选取前N
    df_sorted = df.sort_values(by='composite_rank', ascending=True).head(N)

    # 提取股票代码并返回
    mystocks = df_sorted['code'].tolist()
    return mystocks

mystocks = get_mystocks('2024-12-31')
print(mystocks)
['300628.XSHE', '002555.XSHE', '000876.XSHE', '300394.XSHE', '603833.XSHG', '605117.XSHG', '300316.XSHE', '000975.XSHE', '000661.XSHE', '002007.XSHE', '601021.XSHG', '601799.XSHG', '300896.XSHE', '688082.XSHG', '000807.XSHE', '000408.XSHE', '300832.XSHE', '600570.XSHG', '002920.XSHE', '600066.XSHG']

 

四、编写回测代码

# 导入函数库
from jqdata import *
import datetime
import numpy as np

# 策略初始化
def initialize(context):
    set_benchmark('000300.XSHG')
    set_option('use_real_price', True)
    log.set_level('order', 'error')
    set_order_cost(OrderCost(close_tax=0, open_commission=0, close_commission=0, min_commission=0), type='stock')
    
    g.t = 0
    g.tc = 15  # 调仓频率
    g.yb = 63  # 样本长度
    g.N = 20   # 持仓数量
    run_daily(market_open, time='open', reference_security='000300.XSHG')


def market_open(context):
    now = context.current_dt
    before_1d_time = now - timedelta(days=1)
    end_date = before_1d_time.strftime("%Y-%m-%d")
    
    if g.t % g.tc == 0:
        mystocks = get_mystocks(end_date, g.yb, g.N)
        rebalance_position(context, mystocks)
    
    g.t += 1


# 获取st股票
def get_st_stocks(stock_list, end_date, count):
    is_st_info = get_extras('is_st', stock_list, end_date=end_date, count=count, df=True)
    st_stocks = []
    for stock in stock_list:
        if is_st_info[stock].any():  # 检查该股票是否在任何一天为True
            st_stocks.append(stock)
    return st_stocks
    
# 是否停牌
def get_paused_stocks(stock_list, end_date, count):
    paused_info = get_price(stock_list, count=count, end_date=end_date, 
                            fields='paused', frequency='daily', 
                            skip_paused=False, panel=False)
    paused_stocks = []
    for stock in stock_list:
        if (paused_info[paused_info['code'] == stock]['paused'] == 1.0).any():  # 检查该股票是否在任何一天为1.0
            paused_stocks.append(stock)
    return paused_stocks
    

def get_mystocks(date, yb=63, N=20):
    """
    获取某一天沪深300股票中yb天不停牌,且满足市值和ROE排名前N的股票

    :param date: 指定日期
    :param yb: 多少天不停牌
    :param N: 排名前多少
    :return: 满足条件的股票代码列表
    """
    # 获取沪深300的成分股
    hs300_stocks = get_index_stocks('000300.XSHG', date=date)

    # 获取股票的基本面数据
    q = query(
        valuation.code,
        valuation.market_cap,
        indicator.roe
    ).filter(
        valuation.code.in_(hs300_stocks)
    )
    df = get_fundamentals(q, date=date)
    #print(df.head())

    # 获取停牌股票
    paused_stocks = get_paused_stocks(hs300_stocks, end_date=date, count=yb)
    #print(paused_stocks)
    
    # 获取st股票
    st_stocks = get_st_stocks(hs300_stocks, end_date=date, count=yb)
    #print(st_stocks)

    # 获取剔除停牌和ST的股票代码列表
    all_paused_stocks = set(paused_stocks + st_stocks)

    # 从df中筛选出剔除停牌和ST的股票
    df = df[~df['code'].isin(all_paused_stocks)]

    # 对市值进行升序排名,对ROE进行降序排名
    df['market_cap_rank'] = df['market_cap'].rank(method='first', ascending=True)
    df['roe_rank'] = df['roe'].rank(method='first', ascending=False)
    df['composite_rank'] = df['market_cap_rank'] + df['roe_rank']

    # 按综合排名升序排序并选取前N
    df_sorted = df.sort_values(by='composite_rank', ascending=True).head(N)

    # 提取股票代码并返回
    mystocks = df_sorted['code'].tolist()
    return mystocks

"""
###################### 工具 ######################

调仓:
先卖出持仓中不在 stock_list 中的股票
再等价值买入 stock_list 中的股票
"""
def rebalance_position(context, stock_list):
    current_holding = context.portfolio.positions.keys()
    stocks_to_sell = list(set(current_holding) - set(stock_list))
    # 卖出
    bulk_orders(stocks_to_sell, 0)
    total_value = context.portfolio.total_value

    # 买入
    bulk_orders(stock_list, total_value/len(stock_list))

# 批量买卖股票
def bulk_orders(stock_list,target_value):
    for i in stock_list:
        order_target_value(i, target_value)

上面就是所有的源码了。

参考:这里

原文太老了,代码也比较繁琐,故简化很多。

相关文章:

  • 高效数据拷贝方法总结
  • 第16届蓝桥杯c++省赛c组个人题解
  • 基于spring boot的交通旅游订票系统
  • 输入输出系统(I/O系统)
  • 记一次项目上线404--Nginx配置文件
  • 【mllm】——qnn后端解读
  • Linux多线程同步与互斥:从互斥锁原理到死锁防范的深度实践
  • Tkinter事件与绑定
  • 计算机组成原理笔记(十五)——3.5指令系统的发展
  • 使用FormData格式上传图片
  • zk(Zookeeper)实现分布式锁
  • Java基本数据类型与包装类的区别
  • Linux安装开源版MQTT Broker——EMQX服务器环境从零到一的详细搭建教程
  • Linux驱动开发-网络设备驱动
  • 游戏引擎学习第216天
  • Python 的安装与快速入门
  • 联想电脑开机出现Defalut Boot Device Missing or Boot Failed怎么办
  • nfs共享目录主配置文件权限参数
  • 从“被动跳闸”到“主动预警”:智慧用电系统守护老旧小区安全
  • 为什么我们需要if __name__ == __main__:
  • 海航回应“男团粉丝为追星堵住机舱通道”:已紧急阻止
  • 高龄老人骨折后,生死可能就在家属一念之间
  • 秦洪看盘|交易型资金收缩,释放短线压力
  • 上海交大:关注到对教师邵某的网络举报,已成立专班开展调查
  • 复旦设立新文科发展基金,校友曹国伟、王长田联合捐赠1亿助力人文学科与社会科学创新
  • 央行将增加3000亿元科技创新和技术改造再贷款额度