#coding:gbk
"""
高胜率沪深300多因子策略 - 国信iQuant平台优化版
提高信号量版本:放宽买入条件、增加备选信号、优化因子计算
"""
import pandas as pd
import numpy as np
import time
import datetime
def init(ContextInfo):
# 获取沪深300成分股
ContextInfo.s = ContextInfo.get_sector('000300.SH')
ContextInfo.set_universe(ContextInfo.s)
ContextInfo.day = 0
ContextInfo.holdings = {i:0 for i in ContextInfo.s}
ContextInfo.buypoint = {}
ContextInfo.money = ContextInfo.capital
ContextInfo.profit = 0
ContextInfo.accountID = 'testS'
# 添加止损止盈机制
ContextInfo.stop_loss = {} # 止损位记录
ContextInfo.take_profit = {} # 止盈位记录
ContextInfo.max_price = {} # 最高价记录
def handlebar(ContextInfo):
d = ContextInfo.barpos
current_date = timetag_to_datetime(ContextInfo.get_bar_timetag(d), '%Y%m%d')
# 获取当前价格数据
price = ContextInfo.get_history_data(1, '1d', 'close', 1)
# 每日更新最高价并检查止损止盈条件
for stock in list(ContextInfo.holdings.keys()):
if stock in price and ContextInfo.holdings.get(stock, 0) > 0:
current_price = price[stock][0]
# 更新最高价
if stock not in ContextInfo.max_price or current_price > ContextInfo.max_price[stock]:
ContextInfo.max_price[stock] = current_price
# 更新止损位:最高价回撤10%
if stock not in ContextInfo.stop_loss:
ContextInfo.stop_loss[stock] = ContextInfo.buypoint[stock] * 0.90
else:
ContextInfo.stop_loss[stock] = max(ContextInfo.stop_loss[stock], ContextInfo.max_price[stock] * 0.90)
# 更新止盈位:动态调整
if stock not in ContextInfo.take_profit:
ContextInfo.take_profit[stock] = ContextInfo.buypoint[stock] * 1.20 # 20%止盈
elif current_price > ContextInfo.take_profit[stock]:
ContextInfo.take_profit[stock] = current_price * 0.95 # 回撤5%止盈
# 触发止损
if current_price <= ContextInfo.stop_loss[stock]:
print(f'止损卖出: {stock}, 价格: {current_price}')
order_shares(stock, -ContextInfo.holdings[stock], 'fix', current_price, ContextInfo, ContextInfo.accountID)
ContextInfo.money += current_price * ContextInfo.holdings[stock] - 0.0003 * ContextInfo.holdings[stock] * current_price
ContextInfo.holdings[stock] = 0
ContextInfo.stop_loss.pop(stock, None)
ContextInfo.take_profit.pop(stock, None)
ContextInfo.max_price.pop(stock, None)
# 触发止盈
elif current_price >= ContextInfo.take_profit[stock]:
print(f'止盈卖出: {stock}, 价格: {current_price}')
order_shares(stock, -ContextInfo.holdings[stock], 'fix', current_price, ContextInfo, ContextInfo.accountID)
ContextInfo.money += current_price * ContextInfo.holdings[stock] - 0.0003 * ContextInfo.holdings[stock] * current_price
ContextInfo.holdings[stock] = 0
ContextInfo.stop_loss.pop(stock, None)
ContextInfo.take_profit.pop(stock, None)
ContextInfo.max_price.pop(stock, None)
# 每月调仓
if d > 60 and d % 20 == 0:
print(f"调仓日期: {current_date}")
# 生成买卖信号
buys, sells = signal(ContextInfo)
# 多因子评分:动量 + 价值 + 波动性
factor_scores = {}
for stock in ContextInfo.s:
if buys.get(stock, 0) == 1:
try:
# 获取历史数据
close_data = ContextInfo.get_history_data(120, '1d', 'close', 3).get(stock, [])
high_data = ContextInfo.get_history_data(60, '1d', 'high', 3).get(stock, [])
low_data = ContextInfo.get_history_data(60, '1d', 'low', 3).get(stock, [])
if len(close_data) < 120 or len(high_data) < 60 or len(low_data) < 60:
continue
# 1. 动量因子 (过去3个月收益率)
momentum = (close_data[-1] / close_data[-60] - 1) * 100
# 2. 价值因子 (价格相对位置)
min_low = np.min(low_data)
max_high = np.max(high_data)
value_factor = (close_data[-1] - min_low) / (max_high - min_low) if (max_high - min_low) > 0 else 0
# 3. 波动性因子 (使用标准差替代ATR)
volatility = np.std(close_data[-60:]) # 60日波动率
volatility_factor = 1 / (volatility + 1e-5) # 波动率越低越好
# 综合得分 (动量40% + 价值30% + 波动性30%)
factor_scores[stock] = 0.4 * momentum + 0.3 * value_factor + 0.3 * volatility_factor
except Exception as e:
print(f"因子计算错误 {stock}: {str(e)}")
factor_scores[stock] = -9999 # 无效值
# 选取得分最高的10只股票
buy_stocks = []
if factor_scores:
sorted_stocks = sorted(factor_scores.items(), key=lambda x: x[1], reverse=True)
# 选择排名前20%的股票
num_stocks = max(1, int(len(sorted_stocks) * 0.2)) # 至少选择1只
buy_stocks = [s[0] for s in sorted_stocks[:num_stocks]]
print(f"买入股票数量: {len(buy_stocks)}")
# 卖出不在买入名单中的股票
for stock in ContextInfo.s:
if ContextInfo.holdings.get(stock, 0) > 0 and stock not in buy_stocks:
if stock in price:
current_price = price[stock][0]
print(f'卖出: {stock}, 数量: {ContextInfo.holdings[stock]}')
order_shares(stock, -ContextInfo.holdings[stock], 'fix', current_price, ContextInfo, ContextInfo.accountID)
ContextInfo.money += current_price * ContextInfo.holdings[stock] - 0.0003 * ContextInfo.holdings[stock] * current_price
ContextInfo.holdings[stock] = 0
if stock in ContextInfo.stop_loss:
ContextInfo.stop_loss.pop(stock)
if stock in ContextInfo.take_profit:
ContextInfo.take_profit.pop(stock)
if stock in ContextInfo.max_price:
ContextInfo.max_price.pop(stock)
# 买入新股票
if buy_stocks:
print('买入股票池:', buy_stocks)
# 确保不会除以零
num_stocks = len(buy_stocks)
if num_stocks > 0:
per_stock_money = ContextInfo.money / num_stocks
for stock in buy_stocks:
if stock in price and ContextInfo.holdings.get(stock, 0) == 0:
current_price = price[stock][0]
buy_quantity = int(per_stock_money / current_price)
if buy_quantity > 0:
print(f'买入: {stock}, 数量: {buy_quantity}, 价格: {current_price}')
order_shares(stock, buy_quantity, 'fix', current_price, ContextInfo, ContextInfo.accountID)
ContextInfo.holdings[stock] = buy_quantity
ContextInfo.money -= current_price * buy_quantity + 0.0003 * buy_quantity * current_price
ContextInfo.buypoint[stock] = current_price
# 设置初始止损止盈
ContextInfo.stop_loss[stock] = current_price * 0.90 # 10%止损
ContextInfo.take_profit[stock] = current_price * 1.20 # 20%止盈
ContextInfo.max_price[stock] = current_price
else:
print("没有符合条件的股票,保持空仓")
# 更新收益曲线
total_value = ContextInfo.money
for stock, quantity in ContextInfo.holdings.items():
if stock in price:
total_value += price[stock][0] * quantity
profit_ratio = (total_value - ContextInfo.capital) / ContextInfo.capital
ContextInfo.profit = total_value - ContextInfo.capital
if not ContextInfo.do_back_test:
ContextInfo.paint('profit_ratio', profit_ratio, -1, 0)
def signal(ContextInfo):
"""优化的买卖信号生成函数 - 提高信号数量"""
buy = {i:0 for i in ContextInfo.s}
sell = {i:0 for i in ContextInfo.s}
# 获取历史数据
data_high = ContextInfo.get_history_data(22, '1d', 'high', 3)
data_low = ContextInfo.get_history_data(22, '1d', 'low', 3)
data_close = ContextInfo.get_history_data(60, '1d', 'close', 3)
data_volume = ContextInfo.get_history_data(20, '1d', 'volume', 3)
# 获取沪深300指数数据判断市场趋势
hs300_data = ContextInfo.get_history_data(60, '1d', 'close', 3).get('000300.SH', [])
market_up_trend = False
if hs300_data and len(hs300_data) >= 60:
ma60 = np.mean(hs300_data)
market_up_trend = hs300_data[-1] > ma60
buy_count = 0
for stock in ContextInfo.s:
# 检查数据是否存在
if stock not in data_high or stock not in data_low or stock not in data_close or stock not in data_volume:
continue
high_data = data_high[stock]
low_data = data_low[stock]
close_data = data_close[stock]
volume_data = data_volume[stock]
# 确保有足够的历史数据
if len(high_data) < 22 or len(low_data) < 22 or len(close_data) < 60 or len(volume_data) < 20:
continue
try:
# 计算技术指标
ma20 = np.mean(close_data[-20:]) # 20日均价
ma60 = np.mean(close_data[-60:]) # 60日均价
current_close = close_data[-1] # 当前收盘价
# 条件1:突破20日高点
condition_breakout = high_data[-1] > max(high_data[-22:-1])
# 条件2:接近20日高点(95%以上)
condition_near_high = high_data[-1] > 0.95 * max(high_data[-22:-1])
# 条件3:均线多头排列
condition_ma = (current_close > ma20 > ma60)
# 条件4:成交量确认
avg_volume = np.mean(volume_data[-20:])
condition_volume = volume_data[-1] > avg_volume * 1.0 # 不低于平均成交量
# 条件5:短期趋势向上(5日线上穿10日线)
ma5 = np.mean(close_data[-5:])
ma10 = np.mean(close_data[-10:])
condition_short_trend = ma5 > ma10
# 买入信号:主要条件 + 辅助条件
# 主要条件:突破20日高点 + 多头排列
main_condition = condition_breakout and condition_ma
# 备选条件:接近高点 + 多头排列 + 短期趋势向上
alt_condition = condition_near_high and condition_ma and condition_short_trend
# 买入信号:满足主要条件或备选条件,且有成交量支持
if (main_condition or alt_condition) and condition_volume:
buy[stock] = 1
buy_count += 1
# 卖出信号条件
sell_condition1 = current_close < ma60 * 0.95 # 跌破60日均线5%
sell_condition2 = current_close < ma20 * 0.93 # 跌破20日均线7%
sell[stock] = 1 if sell_condition1 or sell_condition2 else 0
except Exception as e:
print(f"信号生成错误 {stock}: {str(e)}")
# 出错时默认不交易
buy[stock] = 0
sell[stock] = 0
# 调试输出:统计买入信号数量
print(f"生成买入信号数量: {buy_count}")
return buy, sell
优化一下以上策略,因为要求比较严格,至买入了一直股票
最新发布