from jqdata import *
from jqfactor import get_factor_values
import datetime
import math
from scipy.optimize import minimize
import pandas as pd
# 初始化函数,设定基准等等
def initialize(context):
# 设定沪深300作为基准
set_benchmark("399303.XSHE")
# 打开防未来函数
set_option("avoid_future_data", True)
# 开启动态复权模式(真实价格)
set_option("use_real_price", True)
# 输出内容到日志 log.info()
log.info("初始函数开始运行")
# 过滤掉order系列API产生的比error级别低的log
log.set_level("order", "error")
# 固定滑点设置ETF 0.001(即交易对手方一档价)
set_slippage(FixedSlippage(0.002), type="fund")
# 股票交易总成本0.3%(含固定滑点0.02)
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",
)
g.hold_list = [] # 记录策略的持仓股票
g.positions = {} # 记录策略的持仓股票
# 持仓股票数
g.stock_sum = 6
# 判断买卖点的行业数量
g.num = 1
# 空仓的月份
g.pass_months = []
# 策略执行计划
run_weekly(adjust, 1, "9:31")
run_daily(check, "14:50")
# 获取昨日涨停票并卖出
def check(context):
# 获取已持有列表
g.hold_list = list(g.positions.keys())
banner_stocks = []
# 获取昨日涨停列表
if g.hold_list != []:
df = get_price(
g.hold_list,
end_date=context.previous_date,
frequency="daily",
fields=["close", "high_limit"],
count=1,
panel=False,
fill_paused=False,
)
df = df[df["close"] == df["high_limit"]]
banner_stocks = list(df.code)
for stock in banner_stocks:
order_target_value_(context, stock, 0)
# 获取昨日跌停列表
if g.hold_list != []:
df = get_price(
g.hold_list,
end_date=context.previous_date,
frequency="daily",
fields=["close", "low_limit"],
count=1,
panel=False,
fill_paused=False,
)
df = df[df["close"] == df["low_limit"]]
banner_stocks = list(df.code)
for stock in banner_stocks:
order_target_value_(context, stock, 0)
# 获取策略当前持仓市值
def get_total_value(context):
return sum(context.portfolio.positions[key].price * value for key, value in g.positions.items())
# 调仓
def adjust(context):
target = select(context)
# 获取前stock_sum个标的
target = target[:min(len(target), g.stock_sum)]
# 获取已持有列表
g.hold_list = list(g.positions.keys())
portfolio = context.portfolio
# 调仓卖出
for stock in g.hold_list:
if stock not in target:
order_target_value_(context, stock, 0)
# 调仓买入
count = len(set(target) - set(g.hold_list))
if count == 0:
return
# 目标市值
target_value = portfolio.total_value
# 当前市值
position_value = get_total_value(context)
# 可用现金:当前现金
available_cash = portfolio.available_cash
# 买入股票的总市值
value = max(0, min(target_value - position_value, available_cash))
# 等价值买入每一个未买入的标的
for security in target:
if security not in g.hold_list:
order_target_value_(context, security, value / count)
# 择时
def select(context):
I = get_market_breadth(context)
industries = {"银行I", "煤炭I", "采掘I", "钢铁I"}
if not industries.intersection(I) and not is_empty_month(context):
return filter(context)
return []
# 获取市场
def get_market_breadth(context):
# 指定日期防止未来数据
yesterday = context.previous_date
# 获取初始列表 中证全指(000985.XSHG)
stocks = get_index_stocks("000985.XSHG")
count = 1
h = get_price(
stocks,
end_date=yesterday,
frequency="1d",
fields=["close"],
count=count + 20,
panel=False,
)
h["date"] = pd.DatetimeIndex(h.time).date
# 将长表格转换为宽表格,方便按日期分析股票价格。
df_close = h.pivot(index="code", columns="date", values="close").dropna(axis=0)
# 计算20日均线
df_ma20 = df_close.rolling(window=20, axis=1).mean().iloc[:, -count:]
# 计算偏离程度
df_bias = df_close.iloc[:, -count:] > df_ma20
df_bias["industry_name"] = getStockIndustry(stocks)
# 计算行业偏离比例
df_ratio = ((df_bias.groupby("industry_name").sum() * 100.0) / df_bias.groupby("industry_name").count()).round()
# 获取偏离程度最高的行业
top_values = df_ratio.loc[:, yesterday].nlargest(g.num)
I = top_values.index.tolist()
return I
# 基础过滤(过滤科创北交、ST、停牌、次新股)
def filter_basic_stock(context, stock_list):
# 30开头的是深交所的创业板,
# 68开头的是上交所的科创板,
# 8开头的股票可能指的是北交所的,
# 新三板北交所的股票代码通常以43、83、87等开头
# 4开头的股票可能属于退市板块
current_data = get_current_data()
return [
stock
for stock in stock_list
if not current_data[stock].paused
and not current_data[stock].is_st
and "ST" not in current_data[stock].name
and "*" not in current_data[stock].name
and "退" not in current_data[stock].name
and not (stock[0] == "4" or stock[0] == "8" or stock[:2] == "68")
and not context.previous_date - get_security_info(stock).start_date < datetime.timedelta(375)
]
# 过滤当前时间涨跌停的股票
def filter_limitup_limitdown_stock(stock_list):
current_data = get_current_data()
return [
stock
for stock in stock_list
if current_data[stock].last_price < current_data[stock].high_limit and current_data[stock].last_price > current_data[stock].low_limit
]
# 判断今天是在空仓月
def is_empty_month(context):
month = context.current_dt.month
return month in g.pass_months
def getStockIndustry(stocks):
# 第一步:获取原始行业数据(假设stocks是股票代码列表)
industry = get_industry(stocks)
# 第二步:提取申万一级行业名称
return pd.Series({stock: info["sw_l1"]["industry_name"] for stock, info in industry.items() if "sw_l1" in info})
# 过滤股票
def filter(context):
stocks = get_index_stocks("399303.XSHE") # 这里的有问题,需要由399303.XSHE代替
stocks = filter_basic_stock(context, stocks)
stocks = (
get_fundamentals(
query(
valuation.code,
)
.filter(
valuation.code.in_(stocks), # 从现有股票池中筛选
indicator.adjusted_profit > 0, # 要求调整后净利润>0
)
.order_by(valuation.market_cap.asc()) # 按市值升序排列(从小市值开始)
)
.head(20) # 取前20只股票
.code # 提取股票代码
)
stocks = filter_limitup_limitdown_stock(stocks)
return stocks
# 自定义下单(涨跌停不交易)
def order_target_value_(context, security, value):
current_data = get_current_data()
# 检查标的是否停牌、涨停、跌停
if current_data[security].paused:
log.info(f"{security}: 今日停牌")
return False
# 检查是否涨停
if current_data[security].last_price == current_data[security].high_limit:
log.info(f"{security}: 当前涨停")
return False
# 检查是否跌停
if current_data[security].last_price == current_data[security].low_limit:
log.info(f"{security}: 当前跌停")
return False
# 获取当前标的的价格
price = current_data[security].last_price
# 获取当前策略的持仓数量
current_position = g.positions.get(security, 0)
# 计算目标持仓数量
target_position = (int(value / price) // 100) * 100 if price != 0 else 0
# 计算需要调整的数量
adjustment = target_position - current_position
# 检查是否当天买入卖出
closeable_amount = context.portfolio.positions[security].closeable_amount if security in context.portfolio.positions else 0
if adjustment < 0 and closeable_amount == 0:
log.info(f"{security}: 当天买入不可卖出")
return False
# 下单并更新持仓
if adjustment != 0:
o = order(security, adjustment)
if o:
# 更新持仓数量
amount = o.amount if o.is_buy else -o.amount
g.positions[security] = amount + current_position
# 如果目标持仓为零,移除该证券
if target_position == 0:
g.positions.pop(security, None)
# 更新持有列表
g.hold_list = list(g.positions.keys())
return True
return False
(把这个聚宽的代码迁移到backtrade