1 什么是KDJ策略
KDJ 是⼀种技术分析指标,常⽤于股票市场和其他⾦融市场中,⽤来辅助判断市场的超买和超
卖状态,以及趋势的强弱。KDJ 指标基于随机指标(Stochastic Oscillator)进⾏改进,它包括三
条线,分别是 K 线、D 线和 J 线。
2 知识储备
set_order_cost() 设置佣⾦/印花税
- set_order_cost(cost, type, ref=None)
- 指定每笔交易要收取的⼿续费, 系统会根据⽤户指定的费率计算每笔交易的⼿续费
参数
-
cost: OrderCost 对象
-
open_tax,买⼊时印花税 (只股票类标的收取,基⾦与期货不收)
-
close_tax,卖出时印花税 (只股票类标的收取,基⾦与期货不收)
-
open_commission,买⼊时佣⾦
-
close_commission, 卖出时佣⾦
-
close_today_commission, 平今仓佣⾦
-
min_commission, 最低佣⾦,不包含印花税
-
type: 股票、场内基⾦、场内交易的货币基⾦、分级A基⾦、分级B基⾦、分级⺟基⾦、⾦融期货、期货、债券基⾦、股票基⾦、QDII 基⾦、混合基⾦,'stock'/ 'fund' / 'mmf' /'fja'/'fjb'/ 'fjm'/'index_futures' / 'futures' / 'bond_fund' / 'stock_fund' / 'QDII_fund' / / 'mixture_fund' /
-
ref: 参考代码,⽀持股票代码/基⾦代码/期货合约代码,以及期货的品种,如'000001.XSHE'/'510180.XSHG'/'IF1709'/'IF'/'000300.OF'
注意:针对特定的交易品种类别设置⼿续费时,必须将ref设为None;若针对特定 的交易品种或者标的,需要将type设置为对应的交易品种类别,将ref设置为对应 的交易品种或者标的
python
import jqdata
def initialize(context):
# 股票类每笔交易时的⼿续费是:买⼊时佣⾦万分之三,卖出时佣⾦万分之三加千分之⼀印花
税, 每笔交易佣⾦最低扣5块钱
set_order_cost(OrderCost(open_tax=0, close_tax=0.001, open_commission=
0.0003, close_commission=0.0003, close_today_commission=0, min_commission=5
), type='stock')
Context 策略信息总览,包含账户、时间等信息
-
subportfolios: 当前单个操作仓位的资⾦、标的信息,是⼀个SubPortfolio 的数组
-
portfolio: 账户信息,即subportfolios 的汇总信息, Portfolio对象,单个操作仓位时,portfolio 指向 subportfolios[0]
-
current_dt: 当前单位时间的开始时间, [datetime.datetime]对象
-
previous_date: 前⼀个交易⽇, [datetime.date]对象, 注意, 这是⼀个⽇期, 是 date, ⽽不是 datetime
-
run_params: 表示此次运⾏的参数, 有如下属性
- start_date: 回测/模拟开始⽇期, [datetime.date]对象
- end_date: 回测/模拟结束⽇期, [datetime.date]对象
- type: 运⾏⽅式, 如下四个字符串之⼀
- 'simple_backtest': 回测, 通过点击'编译运⾏'运⾏
- 'full_backtest': 回测, 通过点击'运⾏回测'运⾏
- 'sim_trade': 模拟交易
- 'live_trade': 实盘交易
- frequency: 运⾏频率, 如下三个字符串之⼀
'day'
'minute'
'tick'
- 为了让从其他平台迁移过来的同学更顺⼿的使⽤系统, 我们对此对象也做了和 [g] ⼀样的处理:
- 可以添加⾃⼰的变量, 每次进程关闭时持久保存, 进程重启时恢复.
- 以 '__' 开头的变量不会被持久保存
- 如果添加的变量与系统的冲突, 将覆盖掉系统变量, 如果想恢复系统变量, 请删除⾃⼰的变量.
KD函数
在因⼦库------ 超买超卖型⾥
python
KD(security_list, check_date, N = 9, M1 = 3, M2 = 3, unit = '1d', include_n
ow = True, fq_ref_date = None)
参数:
- security_list:股票列表
- check_date:要查询数据的⽇期
- N:统计的天数 N,计算RSV的参数
- M1:统计的天数 M1,计算K的M1⽇移动平均值
- M2:统计的天数 M2,计算D的M1⽇移动平均值
- unit:统计周期,默认为 '1d', ⽀持如下周期: '1m', '5m', '15m', '30m', '60m',
- '120m', '1d', '1w', '1M'. '1w' 表示⼀周, '1M' 表示⼀⽉
- include_now:是否包含当前周期,默认为 True
- fq_ref_date:复权基准⽇,默认为 None
返回:
- K和D 的值。
返回结果类型:
-
字典(dict):键(key)为股票代码,值(value)为数据。
-
如:({'000001.XSHE': 76.634909558440839, '603177.XSHG': nan, '000002.XSHE':31.205826728484286, '601211.XSHG': 65.666306018342183}, {'000001.XSHE': 77.290976650878633, '603177.XSHG': nan, '000002.XSHE': 32.552242430383409,'601211.XSHG': 64.593395785918702})
⽤法注释: -
KD的计算公式与SKDJ的不太⼤,⽽常⻅的炒股软件中均没有找到KD的⽤法注释,所以该处的⽤法注释使⽤的是公式SKDJ的;
-
指标>80 时,回档机率⼤;指标<20 时,反弹机率⼤;
-
K在20左右向上交叉D时,视为买进信号;
-
K在80左右向下交叉D时,视为卖出信号;
-
SKDJ波动于50左右的任何讯号,其作⽤不⼤。
order_value 按价值下单
python
order_value(security, value, style=None, side='long', pindex=0, close_today
=False)
买卖价值为value的标的。
参数:
- security: 股票名字
- value: 股票价值,value = 最新价 * ⼿数 * 保证⾦率(股票为1) * 乘数(股
- 票为100)
- style: 参⻅OrderStyle, None代表MarketOrder
- side: 'long'/'short',操作多单还是空单。默认为多单。默认为多单,股票、基 ⾦暂不⽀持开空单。 pindex: 在使⽤set_subportfolios创建了多个仓位时,指定subportfolio 的序号, 从 0 开始, ⽐如 0为 指定第⼀个 subportfolio, 1 为指定第⼆个subportfolio,默认为0。
- close_today: 平今字段,close_today: 平今字段,仅对上海国际能源中⼼,上海期货交易所,中⾦所⽣效,其他交易所将返回 Order对象或者None, 如果创建委托成功, 则返回Order对象, 失败则返回None
Position 持仓标的信息
持有的某个标的的信息,注意区分多仓和空仓 - security: 标的代码
- price: 最新⾏情价格
- acc_avg_cost 是累计的持仓成本,在清仓/减仓时也会更新,该持仓累积的收 益都会⽤于计算成本(⼀开始acc_avg_cost为0,amount也为0),加仓: new_acc_avg_cost = (acc_avg_cost * amount + trade_value + commission) / (amount + trade_amount);减仓:new_acc_avg_cost = (acc_avg_cost * amount - trade_value + commission) / (amount - trade_amount) 说明:commission是本次买⼊或者卖出的⼿续费 avg_cost 是当前持仓成本,
- hold_cost: 当⽇持仓成本,计算⽅法:当⽇⽆收益:hold_cost = 前收价 (清算后)
- init_time: 建仓时间,格式为 datetime.datetime
- transact_time: 最后交易时间,格式为 datetime.datetime
- locked_amount: 挂单冻结仓位
- total_amount: 总仓位, 但不包括挂单冻结仓位( 如果要获取当前持仓的仓位,需 要将locked_amount和total_amount相加)
- closeable_amount: 可卖出的仓位
- today_amount: 今天开的仓位
- value: 标的价值,计算⽅法是: price * total_amount * multiplier, 其中股票、
- 基⾦的multiplier为1,期货为相应的合约乘数
- side: 多/空,'long' or 'short'
- pindex: 仓位索引,subportfolio index
order_target 期货⽬标⼿数下单
python
order_target(security, amount, style=None, side='long', pindex=0, close_today=False)
买卖标的, 使最终标的的数量达到指定的amount
参数:
- security: 标的代码
- amount: 期望的最终数量
- style: 参⻅OrderStyle, None代表MarketOrder
- side: 'long'/'short',操作多单还是空单,默认为多单。
- pindex: 在使⽤set_subportfolios创建了多个仓位时,指定subportfolio 的序 号, 从 0 开始, ⽐如 0为 指定第⼀个 subportfolio, 1 为指定第⼆个
- subportfolio,默认为0。
- close_today: 平今字段返回 Order对象或者None, 如果创建委托成功, 则返回Order对象, 失败则返回None
3 获取KD
python
from jqlib.technical_analysis import *
# 定义股票池列表
security_list1 = '000001.XSHE'
security_list2 = ['000001.XSHE','000002.XSHE','601211.XSHG']
# 计算并输出 security_list1 的 KD值
K1, D1 = KD(security_list1, check_date = '2017-01-04', N = 9, M1 = 3, M2 =3)
print('股票1的K1',K1[security_list1]) # K1是⼀个字典
print('股票1的D1',D1[security_list1])
# 输出 security_list2 的 KD 值
K2, D2 = KD(security_list2, check_date = '2017-01-04', N = 9, M1 = 3, M2 =3)
for stock in security_list2:
print('股票池2的K2',K2[stock])
print('股票池2的D2',D2[stock])
4 获取KDJ
python
from jqlib.technical_analysis import *
security = '000001.XSHE'
check_dates=['2022-09-05','2022-09-06','2022-09-07']
for check_date in check_dates:
_K,_D,_J = KDJ(security, check_date=check_date, N=9,M1=3,M2=3)
print(check_date,'K值为:',_K[security])
print(check_date,'D值为:',_D[security])
print(check_date,'J值为:',_J[security])
代码
python
# 有仓位就卖,没有就买
def initialize(context):
# 设定一个基准
set_benchmark('000300.XSHG')
g.security = '000001.XSHE'
# 每日执行
run_daily(market_open, time='9:30')
def market_open(context):
# 如果没有持仓
if g.security not in context.portfolio.positions:
order_target(g.security,100) # 买100股
else:
order_target(g.security, 0) # 全部卖出
运行回测
双均线策略
python
# 双均线策略
# 金叉:快线上穿慢线 买
# 死叉:快线下穿慢线 卖
def initialize(context):
g.security = '000333.XSHE'
g.short_count = 15 # 快线15日
g.long_count = 30 # 慢线30日
g.unit = '1d'
run_daily(market_open, time='every_bar')
# 获取均线价格
def get_ma(security,count, unit):
df = attribute_history(security, count+1, unit, ['close'])
# 计算当前的ma
now_ma = df[:count+1]['close'].rolling(count).mean()[-1]
# 前一天的ma
pre_ma = df[:count]['close'].rolling(count).mean()[-1]
return [pre_ma,now_ma]
# 要执行的策略函数
def market_open(context):
# 金叉:快线上穿慢线 买
# 15日均线
short_ma = get_ma(g.security,g.short_count,g.unit)
# 30日均线
long_ma = get_ma(g.security,g.long_count,g.unit)
# 死叉:快线下穿慢线 卖
if (long_ma[0]>short_ma[0] and (long_ma[1]<=short_ma[1])):
print(f'金叉买入,MA{g.short_count}={short_ma},MA(g.long_count)={long_ma}')
order_target(g.security, 100)
elif (long_ma[0]<short_ma[0] and (long_ma[1]>short_ma[1])):
print(f'死叉卖出,MA{g.short_count}={short_ma},MA(g.long_count)={long_ma}')
order_target(g.security, 0)
运行结果