import pandas as pd
import datetime
from jqdata import * # 请确保jqdata库可用
DIVIDEND_QUERY_INTERVAL = 1000 # 用于分批查询分红信息的区间长度
def initialize(context):
# 初始化设置
log.set_level('order', 'error')
set_option('use_real_price', True)
set_benchmark('000905.XSHG')
# 设置交易成本
set_order_cost(OrderCost(open_tax=0, close_tax=0.0001, open_commission=0.0003, close_commission=0.0003, close_today_commission=0, min_commission=5), type='fund')
# 策略全局变量
g.stock_num = 3
g.buylist = []
g.new_high_value = context.portfolio.starting_cash
g.maxdown = 0
# 安排日常任务
run_daily(get_high_limit_stocks, time='9:05', reference_security='000300.XSHG')
run_monthly(select_stocks_and_buy, 1, time='9:30')
run_daily(sell_stocks_opened_from_up_limit, time='13:57')
run_daily(sell_hi_vol_stocks_at_dayend_and_buy_again, time='14:27')
run_monthly(analyze_stocks_held, 1, time='15:01')
def get_dividend_ratio_filter_list(context, stock_list, sort=True, p1=0.0, p2=1.0):
# 计算股息率并筛选
time1 = context.previous_date
time0 = time1 - datetime.timedelta(days=365)
# 分批查询分红数据
dividend_list = []
for i in range(0, len(stock_list), DIVIDEND_QUERY_INTERVAL):
q = query(finance.STK_XR_XD.code,
finance.STK_XR_XD.a_registration_date,
finance.STK_XR_XD.bonus_amount_rmb
).filter(finance.STK_XR_XD.a_registration_date >= time0,
finance.STK_XR_XD.a_registration_date <= time1,
finance.STK_XR_XD.code.in_(stock_list[i:i+DIVIDEND_QUERY_INTERVAL]))
dividend_list.append(finance.run_query(q))
dividend = pd.concat(dividend_list).fillna(0).groupby('code').sum()
# 获取市值数据
q = query(valuation.code, valuation.market_cap).filter(valuation.code.in_(stock_list))
market_cap_data = get_fundamentals(q, date=time1).set_index('code')
# 计算股息率并进行排序筛选
DR = pd.concat([dividend, market_cap_data], axis=1)
DR['dividend_ratio'] = (DR['bonus_amount_rmb']/10000) / DR['market_cap']
DR.sort_values(by='dividend_ratio', ascending=sort, inplace=True)
final_list = DR.index[int(p1*len(DR)):int(p2*len(DR))].tolist()
return final_list
def sell_stocks(context, sell_list):
current_data=get_current_data()
if len(sell_list)>0:
for security in sell_list:
cprice = current_data[security].last_price
boughtcost = context.portfolio.positions[security].avg_cost
if context.portfolio.positions[security].avg_cost==0:
log.error("Sell %s " % (current_data[security].name), "avg_cost is 0")
profit = 0
else:
profit = (cprice - boughtcost)/boughtcost *100
log.info("Sell %s " % (current_data[security].name), "profit: %.1f%%" % profit, "init time %s" % context.portfolio.positions[security].init_time)
limit_price = max(cprice*0.95,current_data[security].low_limit)
ordert = order_target_value(security,0, LimitOrderStyle(limit_price))
if (None == ordert):
log.info("Sell failed %s" % (current_data[security].name))
#else:
# log.info("no one to sell")
return
################End sell_stocks
# 准备股票池
def get_high_limit_stocks(context):
#获取已持有列表
g.high_limit_list = []
hold_list = list(context.portfolio.positions)
if hold_list:
for stock in hold_list:
df=get_price(stock, count = 1, end_date=context.previous_date, frequency='daily', fields=['high_limit', 'close'])
if df['high_limit'][-1]==df['close'][-1]:
g.high_limit_list.append(stock)
# 调整昨日涨停股票
def sell_stocks_opened_from_up_limit(context):
cdata = get_current_data()
sell_list = [stock for stock in g.high_limit_list if cdata[stock].last_price < cdata[stock].high_limit]
for stock in sell_list:
log.info("[%s]涨停打开,卖出" % cdata[stock].name)
if sell_list:
sell_stocks(context, sell_list)
# 尾盘买卖股;放量未涨停的卖出;低位的买进
def sell_hi_vol_stocks_at_dayend_and_buy_again(context):
btlist = context.portfolio.positions
cdata = get_current_data()
stock_vol_info = {
stock: (now_vol(context, stock), ma_vol(context, stock, 10))
for stock in btlist
if cdata[stock].last_price != cdata[stock].high_limit
}
sell_list = [stock for stock, vol in stock_vol_info.items() if vol[0] > vol[1] * 3]
for stock in sell_list:
log.info("[%s]放量未涨停,卖出" % cdata[stock].name)
if sell_list:
sell_stocks(context, sell_list)
buy_stocks(context, g.buylist)
def analyze_stocks_held(context):
current_data = get_current_data()
hold_stocks = list(context.portfolio.positions.keys())
for s in hold_stocks:
q = query(valuation.code, valuation.market_cap, valuation.pe_ratio, indicator.inc_net_profit_year_on_year).filter(valuation.code == s)
df = get_fundamentals(q)
stock_name = current_data[s].name
if not df.empty: # 确保df不是空
pe_ratio = df['pe_ratio'].iloc[0] if not df['pe_ratio'].isnull().all() else 'N/A'
inc_net_profit_yoy = df['inc_net_profit_year_on_year'].iloc[0] if not df['inc_net_profit_year_on_year'].isnull().all() else 'N/A'
log.info(f"{s} {stock_name} 市盈率: {pe_ratio}")
log.info(f"{s} {stock_name} 净利润同比增长率: {inc_net_profit_yoy}")
else:
log.info(f"{s} {stock_name} 数据不足,无法提供市盈率或净利润同比增长率。")
log.info('一天结束')
log.info('#' * 60)
def after_trading_end(context):
g.total_value = context.portfolio.total_value
if g.total_value > g.new_high_value:
g.new_high_value = g.total_value
g.maxdown = 0
else:
g.maxdown = max(g.maxdown, (g.new_high_value - g.total_value) / g.new_high_value * 100)
record(maxdown=g.maxdown)
def select_stocks_and_buy(context):
select_stocks(context)
buy_stocks(context, g.buylist)
def select_stocks(context):
dt_last = context.previous_date
# 假设get_all_securities返回DataFrame
all_stocks_df = get_all_securities('stock', dt_last)
stocks = all_stocks_df.index.tolist()
stocks = filter_unwanted_stocks(context, stocks)
# 获取基本面数据,并按市值排序选择小市值股票
df = get_fundamentals(query(valuation.code, valuation.market_cap)
.filter(valuation.code.in_(stocks),
valuation.market_cap <= 100)
.order_by(valuation.market_cap.asc()))
# Update buylist
g.buylist = df['code'].tolist()[:g.stock_num * 2]
def now_vol(context, stock):
dt_zero_clock_today = context.current_dt - datetime.timedelta(hours=context.current_dt.hour, minutes=context.current_dt.minute, seconds=context.current_dt.second, microseconds=context.current_dt.microsecond)
#print ('dt_zero_clock_today', dt_zero_clock_today, type(dt_zero_clock_today))
dt_trading_start_today = dt_zero_clock_today + datetime.timedelta(hours=9, minutes=15, seconds=00)
#print ('dt_trading_start_today', dt_trading_start_today, type(dt_trading_start_today))
df_vol = get_price(stock, start_date=dt_trading_start_today, end_date=context.current_dt, frequency='minute', fields=['volume'])
ft_now_vol = df_vol['volume'].sum()
#print (stock, 'ft_now_vol', ft_now_vol)
return ft_now_vol
def ma_vol(context, stock, number_of_days):
df_vol = get_price(stock, end_date=context.previous_date, frequency='daily', count=number_of_days, fields=['volume'])
ft_ma_vol = df_vol['volume'].mean()
#print (stock, number_of_days, 'volume mean', ft_ma_vol)
return ft_ma_vol
# 用于过滤股票的函数 - 一次过滤所有不需要的股票
def filter_unwanted_stocks(context, stocks):
current_data = get_current_data()
last_prices = history(1, unit='1m', field='close', security_list=stocks)
def is_unwanted_stock(stock):
data = current_data[stock]
last_price = last_prices[stock][-1]
# 过滤逻辑
return any([
stock[0] == '4' or stock[0] == '8' or stock[:2] == '68', # 科创北交股票
data.paused, # 停牌股票
data.is_st or 'ST' in data.name or '*' in data.name or '退' in data.name, # ST股票
last_price >= data.high_limit or last_price <= data.low_limit, # 涨跌停股票
last_price >= 9, # 股价高于9元的股票
])
return [stock for stock in stocks if not is_unwanted_stock(stock)]
def buy_stocks(context, choice):
position_count = len(context.portfolio.positions)
if g.stock_num <= position_count:
return
psize = context.portfolio.available_cash / (g.stock_num - position_count)
current_data = get_current_data()
for stock in choice:
if stock not in context.portfolio.positions:
log.info('buy', stock, current_data[stock].name)
order_value(stock, psize)
if len(context.portfolio.positions) == g.stock_num:
break
=买买===========
import pandas as pd
import datetime
from jqdata import * # 请确保jqdata库可用
DIVIDEND_QUERY_INTERVAL = 1000 # 用于分批查询分红信息的区间长度
def initialize(context):
# 初始化设置
log.set_level('order', 'error')
set_option('use_real_price', True)
set_benchmark('000905.XSHG')
# 设置交易成本
set_order_cost(OrderCost(open_tax=0, close_tax=0.0001, open_commission=0.0003, close_commission=0.0003, close_today_commission=0, min_commission=5), type='fund')
# 策略全局变量
g.stock_num = 3
g.buylist = []
# 初始化一个空集合用来跟踪已卖出的股票
context.stocks_sold_today = set()
g.new_high_value = context.portfolio.starting_cash
g.maxdown = 0
# 安排日常任务
run_daily(get_high_limit_stocks, time='9:05', reference_security='000300.XSHG')
run_monthly(select_stocks_and_buy, 1, time='9:30')
run_daily(sell_stocks_opened_from_up_limit, time='14:00')
run_daily(sell_hi_vol_stocks_at_dayend_and_buy_again, time='14:30')
run_monthly(analyze_stocks_held, 1, time='15:01')
def get_dividend_ratio_filter_list(context, stock_list, sort=True, p1=0.0, p2=1.0):
# 计算股息率并筛选
time1 = context.previous_date
time0 = time1 - datetime.timedelta(days=365)
# 分批查询分红数据
dividend_list = []
for i in range(0, len(stock_list), DIVIDEND_QUERY_INTERVAL):
q = query(finance.STK_XR_XD.code,
finance.STK_XR_XD.a_registration_date,
finance.STK_XR_XD.bonus_amount_rmb
).filter(finance.STK_XR_XD.a_registration_date >= time0,
finance.STK_XR_XD.a_registration_date <= time1,
finance.STK_XR_XD.code.in_(stock_list[i:i+DIVIDEND_QUERY_INTERVAL]))
dividend_list.append(finance.run_query(q))
dividend = pd.concat(dividend_list).fillna(0).groupby('code').sum()
# 获取市值数据
q = query(valuation.code, valuation.market_cap).filter(valuation.code.in_(stock_list))
market_cap_data = get_fundamentals(q, date=time1).set_index('code')
# 计算股息率并进行排序筛选
DR = pd.concat([dividend, market_cap_data], axis=1)
DR['dividend_ratio'] = (DR['bonus_amount_rmb']/10000) / DR['market_cap']
DR.sort_values(by='dividend_ratio', ascending=sort, inplace=True)
final_list = DR.index[int(p1*len(DR)):int(p2*len(DR))].tolist()
return final_list
def sell_stocks(context, sell_list):
current_data = get_current_data()
if len(sell_list) > 0:
for security in sell_list:
cprice = current_data[security].last_price
boughtcost = context.portfolio.positions[security].avg_cost
if boughtcost == 0:
log.error("Sell %s " % (current_data[security].name), "avg_cost is 0")
profit = 0
else:
profit = (cprice - boughtcost) / boughtcost * 100
log.info(
"Sell %s " % (current_data[security].name),
"profit: %.1f%%" % profit,
"init time %s" % context.portfolio.positions[security].init_time,
)
limit_price = max(cprice * 0.95, current_data[security].low_limit)
order_id = order_target_value(security, 0, style=LimitOrderStyle(limit_price))
if order_id is not None:
# 记录成功卖出的股票
context.stocks_sold_today.add(security)
else:
log.info("Sell failed %s" % (current_data[security].name))
# else:
# log.info("no one to sell")
return
################End sell_stocks
# 准备股票池
def get_high_limit_stocks(context):
#获取已持有列表
g.high_limit_list = []
hold_list = list(context.portfolio.positions)
if hold_list:
for stock in hold_list:
df=get_price(stock, count = 1, end_date=context.previous_date, frequency='daily', fields=['high_limit', 'close'])
if df['high_limit'][-1]==df['close'][-1]:
g.high_limit_list.append(stock)
# 调整昨日涨停股票
def sell_stocks_opened_from_up_limit(context):
cdata = get_current_data()
sell_list = [stock for stock in g.high_limit_list if cdata[stock].last_price < cdata[stock].high_limit]
for stock in sell_list:
log.info("[%s]涨停打开,卖出" % cdata[stock].name)
if sell_list:
sell_stocks(context, sell_list)
# 尾盘买卖股;放量未涨停的卖出;低位的买进
def sell_hi_vol_stocks_at_dayend_and_buy_again(context):
btlist = context.portfolio.positions
cdata = get_current_data()
stock_vol_info = {
stock: (now_vol(context, stock), ma_vol(context, stock, 10))
for stock in btlist
if cdata[stock].last_price != cdata[stock].high_limit
}
sell_list = [stock for stock, vol in stock_vol_info.items() if vol[0] > vol[1] * 3]
for stock in sell_list:
log.info("[%s]放量未涨停,卖出" % cdata[stock].name)
if sell_list:
sell_stocks(context, sell_list)
buy_stocks(context, g.buylist)
def analyze_stocks_held(context):
current_data = get_current_data()
hold_stocks = list(context.portfolio.positions.keys())
for s in hold_stocks:
q = query(valuation.code, valuation.market_cap, valuation.pe_ratio, indicator.inc_net_profit_year_on_year).filter(valuation.code == s)
df = get_fundamentals(q)
stock_name = current_data[s].name
if not df.empty: # 确保df不是空
pe_ratio = df['pe_ratio'].iloc[0] if not df['pe_ratio'].isnull().all() else 'N/A'
inc_net_profit_yoy = df['inc_net_profit_year_on_year'].iloc[0] if not df['inc_net_profit_year_on_year'].isnull().all() else 'N/A'
log.info(f"{s} {stock_name} 市盈率: {pe_ratio}")
log.info(f"{s} {stock_name} 净利润同比增长率: {inc_net_profit_yoy}")
else:
log.info(f"{s} {stock_name} 数据不足,无法提供市盈率或净利润同比增长率。")
log.info('一天结束')
log.info('#' * 60)
def after_trading_end(context):
g.total_value = context.portfolio.total_value
if g.total_value > g.new_high_value:
g.new_high_value = g.total_value
g.maxdown = 0
else:
g.maxdown = max(g.maxdown, (g.new_high_value - g.total_value) / g.new_high_value * 100)
record(maxdown=g.maxdown)
def select_stocks_and_buy(context):
select_stocks(context)
buy_stocks(context, g.buylist)
def select_stocks(context):
dt_last = context.previous_date
# 假设get_all_securities返回DataFrame
all_stocks_df = get_all_securities('stock', dt_last)
stocks = all_stocks_df.index.tolist()
stocks = filter_unwanted_stocks(context, stocks)
# 获取基本面数据,并按市值排序选择小市值股票
df = get_fundamentals(query(valuation.code, valuation.market_cap)
.filter(valuation.code.in_(stocks),
valuation.market_cap <= 100)
.order_by(valuation.market_cap.asc()))
# Update buylist
g.buylist = df['code'].tolist()[:g.stock_num * 2]
def now_vol(context, stock):
dt_zero_clock_today = context.current_dt - datetime.timedelta(hours=context.current_dt.hour, minutes=context.current_dt.minute, seconds=context.current_dt.second, microseconds=context.current_dt.microsecond)
#print ('dt_zero_clock_today', dt_zero_clock_today, type(dt_zero_clock_today))
dt_trading_start_today = dt_zero_clock_today + datetime.timedelta(hours=9, minutes=15, seconds=00)
#print ('dt_trading_start_today', dt_trading_start_today, type(dt_trading_start_today))
df_vol = get_price(stock, start_date=dt_trading_start_today, end_date=context.current_dt, frequency='minute', fields=['volume'])
ft_now_vol = df_vol['volume'].sum()
#print (stock, 'ft_now_vol', ft_now_vol)
return ft_now_vol
def ma_vol(context, stock, number_of_days):
df_vol = get_price(stock, end_date=context.previous_date, frequency='daily', count=number_of_days, fields=['volume'])
ft_ma_vol = df_vol['volume'].mean()
#print (stock, number_of_days, 'volume mean', ft_ma_vol)
return ft_ma_vol
# 用于过滤股票的函数 - 一次过滤所有不需要的股票
def filter_unwanted_stocks(context, stocks):
current_data = get_current_data()
last_prices = history(1, unit='1m', field='close', security_list=stocks)
def is_unwanted_stock(stock):
data = current_data[stock]
last_price = last_prices[stock][-1]
# 过滤逻辑
return any([
stock[0] == '4' or stock[0] == '8' or stock[:2] == '68', # 科创北交股票
data.paused, # 停牌股票
data.is_st or 'ST' in data.name or '*' in data.name or '退' in data.name, # ST股票
last_price >= data.high_limit or last_price <= data.low_limit, # 涨跌停股票
last_price >= 9, # 股价高于9元的股票
])
return [stock for stock in stocks if not is_unwanted_stock(stock)]
def buy_stocks(context, choice):
position_count = len(context.portfolio.positions)
if g.stock_num <= position_count:
return
psize = context.portfolio.available_cash / (g.stock_num - position_count)
current_data = get_current_data()
for stock in choice:
# 检查是否持有股票,并且当天没有卖出过
if stock not in context.portfolio.positions and stock not in context.stocks_sold_today:
log.info('buy', stock, current_data[stock].name)
order_value(stock, psize)
# 断言:确保函数在持仓达到期望数量后停止买入
if len(context.portfolio.positions) == g.stock_num:
break