【聚宽】JoinQuant 资金流数据分析:get_money_flow 函数应用指南

如何使用 JoinQuant API 获取和分析股票资金流向数据?

掌握 JoinQuant 的 get_money_flow 函数,获取主力、超大单、大单的资金净流入数据。本指南将教您如何利用资金流数据辅助您的量化选股和择时策略。

本文由 EasyQuant 提供技术支持。

Q: 怎样获取股票的主力资金净流入数据并用于分析?

在聚宽(JoinQuant)平台中,获取股票的主力资金净流入数据主要使用 get_money_flow 函数。该函数提供了基于逐笔成交数据计算的资金流向信息。

以下是关于如何获取该数据、字段说明以及一个完整的基于主力资金流入的交易策略示例。

1. 核心API说明

函数名get_money_flow

关键字段

  • net_amount_main: 主力净额(万)。这是最核心的指标,计算公式为:超大单净额 + 大单净额。
    • 正值表示主力资金净流入。
    • 负值表示主力资金净流出。
  • net_pct_main: 主力净占比(%)。计算公式为:主力净额 / 成交额。用于衡量主力资金流入的强度。

2. 在研究环境中获取数据

如果你需要在“研究”模块中分析某只股票的历史主力资金情况,可以使用以下代码:

# -*- coding: utf-8 -*-
from jqdata import *
import pandas as pd

# 设置股票代码和时间范围
security = '000001.XSHE'
start_date = '2023-01-01'
end_date = '2023-02-01'

# 获取资金流数据
# fields参数指定我们需要的主力净额(net_amount_main)和主力净占比(net_pct_main)
df = get_money_flow(
    [security], 
    start_date=start_date, 
    end_date=end_date, 
    fields=['date', 'sec_code', 'close', 'net_amount_main', 'net_pct_main']
)

# 打印前5行
print(df.head())

# 简单分析:计算这段时间内主力资金净流入的天数
positive_days = df[df['net_amount_main'] > 0].shape[0]
print("主力资金净流入天数: %d" % positive_days)

3. 策略示例:主力资金跟随策略

下面是一个完整的策略代码,可以直接在聚宽的回测环境中运行。

策略逻辑

  1. 股票池:沪深300成分股。
  2. 买入条件:选取昨日“主力资金净占比”最高的前10只股票,且主力资金必须为净流入(大于0)。
  3. 卖出条件:如果持仓股票不在今日的买入列表中,则卖出。
  4. 调仓频率:每日调仓。
# -*- coding: utf-8 -*-
from jqdata import *

def initialize(context):
    # 设定基准为沪深300
    set_benchmark('000300.XSHG')
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    
    # 设定每日交易费用
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 每天开盘前运行
    run_daily(before_market_open, time='before_open')
    # 每天开盘时运行
    run_daily(market_open, time='open')

def before_market_open(context):
    # 获取沪深300成分股
    g.security_list = get_index_stocks('000300.XSHG')

def market_open(context):
    # 1. 获取资金流数据
    # 注意:在回测中,get_money_flow 如果不指定 end_date,默认获取的是截止到昨天的历史数据
    # 我们获取过去1天的数据
    q = get_money_flow(
        g.security_list, 
        end_date=context.previous_date, 
        count=1, 
        fields=['sec_code', 'net_amount_main', 'net_pct_main']
    )
    
    # 2. 数据清洗与筛选
    if len(q) > 0:
        # 筛选出主力净额大于0的股票
        df_positive = q[q['net_amount_main'] > 0]
        
        # 按照主力净占比(net_pct_main)从大到小排序
        df_sorted = df_positive.sort_values(by='net_pct_main', ascending=False)
        
        # 取前10只股票作为目标买入列表
        target_list = df_sorted['sec_code'].head(10).tolist()
    else:
        target_list = []
    
    # 3. 交易操作
    # 获取当前持仓
    current_holdings = list(context.portfolio.positions.keys())
    
    # 卖出:如果持仓股票不在目标列表中,则卖出
    for stock in current_holdings:
        if stock not in target_list:
            order_target_value(stock, 0)
    
    # 买入:对目标列表中的股票进行等权重买入
    if len(target_list) > 0:
        # 将资金平分成几份
        value_per_stock = context.portfolio.available_cash / len(target_list)
        
        for stock in target_list:
            order_target_value(stock, value_per_stock)
            
    # 打印日志方便观察
    # log.info("今日目标持仓: %s" % target_list)

4. 注意事项

  1. 数据滞后性get_money_flow 获取的是日级别数据。在回测中,如果在 handle_datarun_daily 中调用且不指定日期,通常获取的是前一交易日的数据(因为当天的资金流数据在收盘后才能结算完成)。
  2. 未来函数:如果在回测中使用了 datetime.date.today()context.current_dt 作为 end_date 来获取当日的资金流数据并用于当日交易,会包含未来函数(因为实盘中盘中无法获取全天结算后的资金流),这会导致回测结果虚高。上述策略示例中使用了 context.previous_date 来确保数据的安全性。
  3. 数据范围:该数据提供2010年至今的数据。

Q&A

Q: 主力资金净流入(net_amount_main)是如何定义的?
A: 在聚宽的数据定义中,主力净额 = 超大单净额 + 大单净额。其中,超大单通常指大于等于50万股或者100万元的成交单,大单指大于等于10万股或者20万元且小于超大单标准的成交单。

Q: get_money_flow 函数能否获取实时的盘中资金流数据?
A: 不能。get_money_flow 提供的是日频数据,通常在收盘后结算更新。如果需要盘中实时数据,通常需要处理 Tick 数据或分钟数据自行计算,或者使用 Level-2 相关的高频接口(如果平台支持)。

Q: 为什么有时候查询到的数据为空?
A: 可能的原因包括:1. 查询的日期是非交易日;2. 股票在该日期停牌;3. 股票在查询日期尚未上市;4. 数据尚未更新(如在当日收盘后立即查询当日数据,可能需要等待系统结算完成)。


更多量化相关的问题,欢迎来 EasyQuant 体验 AI 编写量化策略。

这是一段选股的代码,希望能够更准确的抓取市场热点 import utils import logging import work_flow import settings import schedule import time import pandas as pd import tushare as ts from datetime import datetime, timedelta import numpy as np # 初始化交易引擎 class TradingEngine: def __init__(self): self.position = {} self.cash = 1000000 # 初始资金 self.pro = ts.pro_api('your_token') def buy_stock(self, stock_code, price, quantity): cost = price * quantity if self.cash >= cost: if stock_code in self.position: self.position[stock_code] += quantity else: self.position[stock_code] = quantity self.cash -= cost logging.info(f"Bought {quantity} shares of {stock_code} at {price}") else: logging.warning("Insufficient cash to buy stocks") def sell_stock(self, stock_code, price, quantity): if stock_code in self.position and self.position[stock_code] >= quantity: income = price * quantity self.position[stock_code] -= quantity if self.position[stock_code] == 0: del self.position[stock_code] self.cash += income logging.info(f"Sold {quantity} shares of {stock_code} at {price}") else: logging.warning("Insufficient shares to sell") def get_current_position(self): return self.position def get_cash(self): return self.cash # 获取全市股票 def get_all_stocks(): data = TradingEngine().pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,area,industry,list_date') return data # 剔除停牌股、st股、退市股和B股 def filter_stocks(stocks): filtered_stocks = stocks[~stocks['name'].str.contains('ST')] return filtered_stocks # 获取股票的市净率数据 def get_pb_data(stocks): trade_date = (datetime.now() - timedelta(days=1)).strftime('%Y%m%d') data = TradingEngine().pro.daily_basic(ts_code='', trade_date=trade_date, fields='ts_code,pb') return data # 筛选市净率小于2的股票 def filter_by_pb(stocks): pb_data = get_pb_data(stocks) stocks_with_pb = pd.merge(stocks, pb_data, on='ts_code') filtered_stocks = stocks_with_pb[stocks_with_pb['pb'] < 2] return filtered_stocks # 按市净率降序排序取前20只 def sort_and_select(stocks): sorted_stocks = stocks.sort_values(by='pb', ascending=False).head(20) return sorted_stocks # 获取股票的流动资产、流动负债和负债率数据 def get_financial_data(stocks): quarter = (datetime.now().month - 1) // 3 + 1 year = datetime.now().year data = [] for stock in stocks['ts_code']: try: df = TradingEngine().pro.balancesheet(ts_code=stock, period=f'{year}{quarter:02}01', fields='ts_code,total_cur_assets,total_cur_liab,total_liab,total_assets') if not df.empty: data.append(df.iloc[0]) except Exception as e: logging.error(f"Error getting financial data for {stock}: {e}") financial_data = pd.DataFrame(data) financial_data['debt_ratio'] = financial_data['total_liab'] / financial_data['total_assets'] return financial_data # 筛选流动资产至少是流动负债的1.2倍,企业负债率低于市场平均值的股票 def filter_by_financials(stocks): financial_data = get_financial_data(stocks) market_avg_debt_ratio = financial_data['debt_ratio'].mean() filtered_stocks = financial_data[(financial_data['total_cur_assets'] / financial_data['total_cur_liab'] >= 1.2) & (financial_data['debt_ratio'] < market_avg_debt_ratio)] return filtered_stocks # 每日数据处理函数 def daily_data_processing(trading_engine): all_stocks = get_all_stocks() filtered_stocks = filter_stocks(all_stocks) pb_filtered_stocks = filter_by_pb(filtered_stocks) sorted_stocks = sort_and_select(pb_filtered_stocks) final_stocks = filter_by_financials(sorted_stocks) # 获取当前持仓 current_position = trading_engine.get_current_position() # 卖出不在股票池的仓位 for stock in list(current_position.keys()): if stock not in final_stocks['ts_code'].values: price = get_stock_price(stock) trading_engine.sell_stock(stock, price, current_position[stock]) # 买入股票池中的股票 for stock in final_stocks['ts_code']: price = get_stock_price(stock) quantity = 100 # 简单假设买入100股 trading_engine.buy_stock(stock, price, quantity) # 获取股票当前价格 def get_stock_price(stock_code): trade_date = (datetime.now() - timedelta(days=1)).strftime('%Y%m%d') data = TradingEngine().pro.daily(ts_code=stock_code, trade_date=trade_date, fields='close') if not data.empty: return data['close'].values[0] return 0 # 回测模块 def backtest(start_date, end_date): trading_engine = TradingEngine() current_date = start_date while current_date <= end_date: if utils.is_weekday(current_date): daily_data_processing(trading_engine) current_date += timedelta(days=1) final_value = trading_engine.get_cash() for stock, quantity in trading_engine.get_current_position().items(): price = get_stock_price(stock) final_value += price * quantity return final_value # 主函数 def job(): trading_engine = TradingEngine() daily_data_processing(trading_engine) logging.basicConfig(format='%(asctime)s %(message)s', filename='sequoia.log') logging.getLogger().setLevel(logging.INFO) settings.init() if settings.config['cron']: EXEC_TIME = "09:40" def get_first_trading_day_of_month(year, month): first_day = datetime(year, month, 1) while not utils.is_weekday(first_day): first_day += timedelta(days=1) return first_day now = datetime.now() first_trading_day = get_first_trading_day_of_month(now.year, now.month) exec_datetime = datetime.combine(first_trading_day, datetime.strptime(EXEC_TIME, '%H:%M').time()) if datetime.now() < exec_datetime: schedule.every().day.at(EXEC_TIME).do(job) while True: schedule.run_pending() time.sleep(1) else: job()
09-02
复制代码 # -*- coding: utf-8 -*- # BigQuant 平台适配版:热点狙击选股策略 import pandas as pd import numpy as np import logging from datetime import datetime, timedelta from bigquant.data_source import DataSource from bigquant.trade.strategy import TradeStrategy from bigquant.trade.context import Context from bigquant.utils import date_to_str # 日志设置 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # 策略参数配置 class StrategyParams: pb_threshold = 2 # 市净率阈值 min_current_ratio = 1.2 # 最低流动比率 top_n = 2 # 选择前2只股票 hot_keywords = ['人工智能', '新能源', '光伏', '半导体', '数字经济', '5G', 'AI芯片'] # 获取所有交易中的股票 def get_all_stocks(context): instruments = M.instruments(context=context) df = pd.DataFrame(instruments, columns=['order_book_id', 'symbol', 'name', 'status', 'list_date']) return df[df['status'] == 'trading'] # 剔除 ST、B股、停牌股 def filter_stocks(df): return df[~df['name'].str.contains('ST|退|B股', na=True, regex=True)] # 获取市净率(PB)数据 def get_pb_data(context): trade_date = date_to_str(context.current_date, fmt='%Y%m%d') ds = DataSource('daily_basic') df = ds.read(context=context, trade_date=trade_date, fields='ts_code,pb') df = df.dropna(subset=['pb']) return df # 获取财务数据(流动资产、流动负债、负债率) def get_financial_data(stock_list, context): year = context.current_date.year quarter = (context.current_date.month - 1) // 3 + 1 period = f"{year}{quarter:02}01" ds = DataSource('balancesheet') # 兼容空股票池 if not stock_list: return pd.DataFrame() df = ds.read(context=context, ts_code=','.join(stock_list), period=period, fields='ts_code,total_cur_assets,total_cur_liab,total_liab,total_assets') df['debt_ratio'] = df['total_liab'] / df['total_assets'] df = df.replace([np.inf, -np.inf], np.nan).dropna() return df # 获取换手率 def get_turnover_rate(context): trade_date = date_to_str(context.current_date, fmt='%Y%m%d') ds = DataSource('daily_basic') df = ds.read(context=context, trade_date=trade_date, fields='ts_code,turnover_rate') df = df.dropna(subset=['turnover_rate']) return df # 计算量比 def calculate_volume_ratio(stock_code, context): end_date = context.current_date start_date = end_date - timedelta(days=10) df = M.history_data([stock_code], context=context, period=f"{date_to_str(start_date)}:{date_to_str(end_date)}", fields=['volume']) if len(df) < 6: return 0 today_vol = df.iloc[-1]['volume'] avg_vol_5 = df.iloc[-6:-1]['volume'].mean() return today_vol / avg_vol_5 if avg_vol_5 > 0 else 0 # 获取资金流入数据 def get_money_flow(stock_list, context): trade_date = date_to_str(context.current_date, fmt='%Y%m%d') ds = DataSource('moneyflow') if not stock_list: return pd.DataFrame() df = ds.read(context=context, ts_code=','.join(stock_list), fields='ts_code,net_mf') df = df.dropna(subset=['net_mf']) return df # 获取股票所属概念板块 def get_stock_concepts(stock_code, context): try: ds = DataSource('concept_detail') df = ds.read(context=context, ts_code=stock_code) if not df.empty and 'concept_name' in df.columns: return df['concept_name'].unique().tolist() return [] except Exception as e: logger.error(f"Error getting concepts for {stock_code}: {e}") return [] # 板块热度评分 def get_concept_hot_score(concepts, context): if not concepts: return ds = DataSource('concept_detail') scores = [] for concept in concepts: df = ds.read(context=context, concept_name=concept) if df.empty or 'ts_code' not in df.columns: continue stocks = df['ts_code'].unique().tolist() pct_chg_list = [] for stock in stocks: try: # 取近5日涨跌幅 df_hist = M.history_data([stock], context=context,=f"{date_to_str(context.current_date - timedelta(days=5))}:{date_to_str(context.current_date)}", fields=['pct_chg']) if not df_hist.empty and 'pct_chg' in df_hist.columns: pct_chg_list.append(df_hist['pct_chg'].mean()) except: continue if pct_chg_list: scores.append(np.mean(pct_chg_list)) return np.mean(scores) if scores else 0 # 新闻关键词匹配得分 def match_news_keywords(stock_code, context): concepts = get_stock_concepts(stock_code, context) matched = set(concepts) & set(StrategyParams.hot_keywords) return len(matched) * 10 # 热点评分函数 def get_hot_score(stocks, context): scores = [] turnover_data = get_turnover_rate(context) money_flow_data = get_money_flow(stocks['ts_code'].tolist(), context) stocks_with_turnover = pd.merge(stocks, turnover_data, on='_code', how='left') for _, row in stocks_with_turnover.iterrows(): stock = row['ts_code'] try: # 近5日涨跌幅均值 df_hist = M.history_data([stock], context=context, period=f"{date_to_str(context.current_date - timedelta(days=5))}:{date_to_str(context.current_date)}", fields=['close']) price_score = df_hist['close'].pct_change().mean() * 100 if not df_hist.empty else 0 # 资金流money_row = money_flow_data[money_flow_data['ts_code'] == stock] money_score = money_row['net_mf'].iloc[0] / 1e6 if not money_row.empty else 0 # 换手率 turnover_score = row.get('turnover_rate', 0) # 量比 volume_score = calculate_volume_ratio(stock, context) * 10 # 板块热度 concepts = get_stock_concepts(stock, context) concept_score = get_concept_hot_score(concepts, context) # 新闻关键词匹配 keyword_score = match_news_keywords(stock, context) # 总评分 total_score = ( price_score * 0.2 + money_score * 0.15 + turnover_score * 0.15 + volume_score * 0.1 + concept_score * 0.2 + keyword_score * 0.2 ) scores.append({'ts_code': stock, 'hot_score': total_score}) except Exception as e: logger.error(f"Error calculating hot score for {stock}: {e}") return pd.DataFrame(scores) # 选股逻辑 def select_stocks(context): all_stocks = get_all_stocks(context) if all_stocks.empty: return [] filtered_stocks = filter_stocks(all_stocks) pb_data = get_pb_data(context) pb_filtered = pd.merge(filtered_stocks, pb_data, left_on='order_book_id', right_on='ts_code', how='inner') pb_filtered = pb_filtered[pb_filtered['pb'] < StrategyParams.pb_threshold] stock_list = pb_filtered['ts_code'].tolist() if not stock_list: return [] financial_data = get_financial_data(stock_list, context) if financial_data.empty: return [] final_data = pd.merge(pb_filtered, financial_data, on='ts_code', how='inner') # 流动比率和负债率过滤 final_data = final_data[ (final_data['total_cur_liab'] > 0) & # 防止除0 (final_data['total_cur_assets'] / final_data['total_cur_liab'] >= StrategyParams.min_current_ratio) & (final_data['debt_ratio'] < final_data['debt_ratio'].mean()) ] if final_data.empty: return [] hot_scores = get_hot_score(final_data, context) if hot_scores.empty: return [] scored_stocks = pd.merge(final_data, hot_scores, on='ts_code', how='inner') top_stocks = scored_stocks.sort_values(by='hot_score', ascending=False).head(StrategyParams.top_n) return top_stocks['ts_code'].tolist() # 交易逻辑 class HotStockStrategy(TradeStrategy): def __init__(self): super().__init__() self.params = StrategyParams def on_day_open(self, context: Context): logger.info(f"Start selecting stocks on {context.current_date}") top_stocks = select_stocks(context) logger.info(f"Selected stocks: {top_stocks}") # 卖出不在池子中的股票 for stock in list(context.portfolio.positions): if stock not in top_stocks: context.order_target_percent(stock, 0) # 买入池子中的股票 if top_stocks: cash_per_stock = context.portfolio.available_cash / len(top_stocks) for stock in top_stocks: context.order_target_value(stock, cash_per_stock) # 注册策略 def bigquant_run(context): strategy = HotStockStrategy() context.register_strategy(strategy)
09-02
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值