第 03 篇
使用 JoinQuant 编写一个策略
第 1 章之后的平台展开篇:把聚宽写策略要用的数据获取、股票池、下单、止盈止损 API 逐个过一遍,最后用「双均线」与「多股票追涨」两个官方示例演示完整策略怎么写。素材见 raw/collections/jq-getting-started/03-21-使用joinquant编写一个策略md.md。
这是什么
JQ 策略 API 手册式的实操页。它回答四个问题:行情/财务数据怎么取、股票池怎么设、买卖怎么下、止盈止损怎么写,并给出 API 文档里的两个示例策略。原素材标注为个人整理,多处代码有笔误(见「不确定 / 待验证」),适合当概念地图,不适合照抄运行。
核心要点
数据:行情与财务
- 行情数据存在
SecurityUnitData里,基本属性:open/close/low/high(开收低高)、volume/money(成交量/额)、factor(前复权因子)、high_limit/low_limit(涨停价/跌停价)、price(段内均价)、pre_close(前收)、paused(是否停牌)。 - 行情获取五种方式(对比表):
- 回测里从
data直接读:只能在策略中用,只能取当期数据,只能在handle_data里用;好处是能拿额外属性(security、returns、isnan、vwap 等)。 attribute_history(证券, n, 单位, 字段, ...):只能在策略中用;取当前时间前 n 个单位;一次一只股票、可多字段;可选跳过停牌日。history(n, 单位, 字段, 股票列表):只能在策略中用;取前 n 个单位;可多只股票但只能同一个字段;不能跳停牌日。get_current_data:只能取当期数据;只能拿涨跌停价、是否停牌、开盘价等;可多股票多字段;只能在策略中用。get_price:使用较难;策略与研究中都能用;可取任意时间段;可多股票多字段;不能跳停牌日。原素材说会单独讲,但本篇未展开 → 见「待验证」。
- 回测里从
- 财务数据用
get_fundamentals:返回基本面数据(含股票财务数据、市值数据)。链式写法:query(查询字段).filter(筛选条件).order_by(排序).limit(数量),最后带date='2015-10-15'指定取哪一天。 示例字段:valuation.code(市值表.代码)、valuation.market_cap(市值)、valuation.pe_ratio(动态市盈率)、income.total_operation_revenue(利润表.营业总收入);示例条件:市值 > 1000、PE < 10、营业总收入 > 2e10、按市值降序、limit 100。
股票池
- 三种设池方式,都要
set_universe(stocks)落地:- 按指数:
get_index_stocks('000300.XSHG')取沪深 300 成分(raw 示例代码里误写成 0003000,见待验证)。 - 按行业:
get_industry_stocks('I64')(I64 = 计算机/互联网行业)。 - 自定义:
stocks = ['000009.XSHE', '002222.XSHE', '000005.XSHE', '000002.XSHE']。
- 按指数:
- 注意:raw 说 API 文档里
set_universe被称为「老函数」,用法是否仍正确需要验证。
下单与订单
- 四种下单:
order(按股数)、order_target(调整到目标股数)、order_value(按金额)、order_target_value(调整到目标金额)。 - 订单相关操作:
cancel_order(撤单)、get_open_orders(当天未完成订单)、get_orders(当天所有订单)、get_trades(当天成交记录)。 - 下单成功返回 Order 对象(属性:add_time 添加时间、is_buy 买/卖、amount 下单数量恒正、filled 已成交数量、security 代码、order_id、price 平均成交价、status 状态——未成交/部分成交/已撤销/交易所拒绝/全部成交)。
- 成交返回 Trade 对象(time、amount、price、trade_id)。
止盈止损写法
- 持仓信息在
context.portfolio.positions[股票]:avg_cost= 持仓成本、price= 现价。 - 用「现价/成本」比例判断:
price/avg_cost >= 1.5→ 收益 50% 止盈,order_target(stock, 0)清仓;price/avg_cost <= 0.9→ 亏损 10% 止损清仓。
示例一:双均线策略(最基础的策略)
- 规则:5 日均线高于 10 日均线买入;低于且有空头可卖则卖出。
- 初始化:
g.security='000001.XSHE'(平安银行);set_benchmark('000300.XSHG')(以沪深 300 为基准);set_option('use_real_price', True)(开启动态复权,即用真实价格)。 - 每周期:
attribute_history(security, 10, 'ld', ['close'], df=False)取过去 10 天收盘;ma5 = ...[-5].mean()、ma10 = 全部.mean();cash = context.portfolio.cash。 - 买卖:
ma5 > ma10→order_value(security, cash),log.info记录;ma5 < ma10且positions[security].closeable_amount > 0→order_target(security, 0)。 - 用
record(ma5=ma5)把均线画到回测图上。 - initialize 还能配置:
set_commission(PerTrade(buy_cost=0.0001, sell_cost=0.001, min_cost=5))设手续费;set_slippage(PriceRelatedSlippage(0.002))设滑点。
示例二:多股票追涨策略(按分钟回测)
- 规则:当日收盘前 30 分钟内涨幅到 9.5%–9.9% 的股票买入(最多 5 只),第二天开盘全卖。逻辑是追接近涨停但没封板的强势股。
- initialize:
set_option('use_real_price', True);g.daily_buy_count = 5;g.stocks = get_industry_stocks('I64') + get_industry_stocks('I65')(计算机 + 信息技术板块)后set(...)去重;run_daily(morning_sell_all, 'open')注册开盘卖光函数。 morning_sell_all(自定义函数):开盘遍历positions,每只order_target(security, 0)全卖。before_trading_start:每个交易日开盘前被调用一次。这里重置g.today_bought_stocks = set(),并取g.last_df = history(1, 'ld', 'close', g.stocks)(全部股票昨日收盘价,每天只取一次)。handle_data:context.current_dt.hour < 13直接 return(只做最后两小时);已买满daily_buy_count也 return;对未买股票判断price/last_close > 1.095 且 < 1.099且high_limit - last_close >= 1.0(涨停区间大于 1 元,还有上冲空间)→ 现金分成need_count份,order_value(security, buy_cash)买入并记入g.today_bought_stocks。
机制 / 论证
- 五个入口的职责划分(本篇对框架的增量):
initialize:整个回测/模拟盘开始时执行一次——设基准、手续费、滑点、股票池、调run_daily。before_trading_start:每个交易日开盘前执行一次,开盘后当天不再执行;适合放「每天取一次的昨日数据」。handle_data:每个周期执行(按天=每天 9:30 后,按分钟=每分钟);实时决策都在这。run_daily(函数, 'open'):把自定义函数挂到指定时点(如开盘)循环执行——追涨示例用它做「开盘卖光」,卖出动作不必挤在 handle_data 里。
- 为什么追涨只做最后两小时:只有接近收盘仍接近涨停的才算强势确认,避免盘中假信号;为什么每天只买 5 只:控制当日敞口。这是把「规则」翻译成「代码里能表达的约束」(时间窗口、数量上限、涨幅区间)。
- 双均线示例用
closeable_amount > 0判断「有没有可卖头寸」,避免空仓时也触发卖出——是「卖出前先查持仓」的标准姿势。
可操作
- 取数选型口诀:策略内要「当期单股多字段」用 data;「历史单股多字段」用 attribute_history;「历史多股单字段」用 history;「只看停牌/涨跌停」用 get_current_data;「研究环境或任意区间」用 get_price。
- get_fundamentals 模板(提炼参数与逻辑):
query(valuation.code, valuation.market_cap, valuation.pe_ratio, income.total_operation_revenue).filter(市值>1000, PE<10, 营收>2e10).order_by(市值降序).limit(100)+date='2015-10-15'。 - 股票池:指数
get_index_stocks('000300.XSHG')、行业get_industry_stocks('I64')、自定义 list,最后set_universe(stocks);行业板块编号(I64、I65)从平台取。 - 止盈止损一行式:
price/avg_cost >= 1.5清仓(止盈 50%);<= 0.9清仓(止损 10%);比例可改。 - 追涨策略可复用参数:涨幅区间 9.5%–9.9%;涨停空间
high_limit - last_close >= 1.0元;每天 5 只;下午 13:00 后才进 handle_data;按分钟回测;run_daily(morning_sell_all, 'open')开盘卖光。 - 可配置项:基准
set_benchmark、手续费set_commission(PerTrade(buy_cost=…, sell_cost=…, min_cost=…))、滑点set_slippage(PriceRelatedSlippage(0.002))、动态复权set_option('use_real_price', True)。
术语
- SecurityUnitData:行情数据对象;前复权因子 factor。
- attribute_history / history / get_current_data / get_price:四种行情取数。
- get_fundamentals:财务/基本面取数;valuation / income 表(市值表 / 利润表)。
- set_universe / get_index_stocks / get_industry_stocks:股票池。
- order 系列、Order / Trade 对象、closeable_amount(可卖数量)。
- avg_cost、set_benchmark、set_commission、set_slippage、run_daily、before_trading_start。
不确定 / 待验证
- 大量代码笔误:沪深 300 写成
'0003000.XSHG'(多一个 0);Python 判断里出现AND(应为and);示例一用close_data但前面变量名是close_price(会 NameError);context.protfolio拼错。→ 全部按语义修正后理解,落地前必须对照官方 API。 set_universe在 API 文档被称「老函数」,raw 自问用法是否正确 → [需要验证] 现行推荐写法(可能已改为在 initialize 里直接 get_index_stocks 等,无需 set_universe)。- raw 承诺「get_price 会单独列出来介绍」但本篇没有展开 → [需要验证] 具体用法,见后续股票数据篇。
- 示例一里
record(ma5=ma5)之后画图用的是 close_data——同上为笔误。 - 手续费示例 buy_cost 0.0001 / sell_cost 0.001 / min_cost 5 是文档示例值,不是推荐值。
相关
- 十行代码带你量化交易入门 — 前篇最小框架
- 聚宽行情与财务数据获取 API — 行情与财务取数方式汇总
- 股票池(交易对象范围与过滤) — 股票池设置
- 聚宽下单 API 与订单对象 — 下单与订单对象
- 聚宽新手入门教程(JoinQuant 平台实操) — 本教程总览
更新 2026-09-06