仿alphaarena项目增加A股ai自动量化

This commit is contained in:
oficcejo
2025-10-26 10:02:55 +08:00
parent b9f0368d25
commit 16071f81e8
21 changed files with 6218 additions and 16 deletions
+526
View File
@@ -0,0 +1,526 @@
"""
智能盯盘 - DeepSeek AI 决策引擎
适配A股T+1交易规则的AI决策系统
"""
import logging
from typing import Dict, List, Optional
from datetime import datetime, time
import pytz
class SmartMonitorDeepSeek:
"""A股智能盯盘 - DeepSeek AI决策引擎"""
def __init__(self, api_key: str):
"""
初始化DeepSeek客户端
Args:
api_key: DeepSeek API密钥
"""
self.api_key = api_key
self.base_url = "https://api.deepseek.com/v1"
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
self.logger = logging.getLogger(__name__)
def is_trading_time(self) -> bool:
"""
判断当前是否在A股交易时间内
Returns:
bool: 是否可以交易
"""
beijing_tz = pytz.timezone('Asia/Shanghai')
now = datetime.now(beijing_tz)
current_time = now.time()
# 排除周末
if now.weekday() >= 5:
return False
# 上午:9:30-11:30
morning_start = time(9, 30)
morning_end = time(11, 30)
# 下午:13:00-15:00
afternoon_start = time(13, 0)
afternoon_end = time(15, 0)
is_trading = (
(morning_start <= current_time <= morning_end) or
(afternoon_start <= current_time <= afternoon_end)
)
return is_trading
def get_trading_session(self) -> Dict:
"""
获取当前交易时段信息(A股版本)
Returns:
Dict: 时段信息
"""
beijing_tz = pytz.timezone('Asia/Shanghai')
now = datetime.now(beijing_tz)
current_time = now.time()
# 判断是否交易日
if now.weekday() >= 5:
return {
'session': '休市',
'volatility': 'none',
'recommendation': '周末不可交易',
'beijing_hour': now.hour,
'can_trade': False
}
# 开盘前(9:00-9:30):集合竞价时段
if time(9, 0) <= current_time < time(9, 30):
return {
'session': '集合竞价',
'volatility': 'high',
'recommendation': '可观察盘面情绪,准备开盘交易',
'beijing_hour': now.hour,
'can_trade': False
}
# 上午盘(9:30-11:30
elif time(9, 30) <= current_time <= time(11, 30):
return {
'session': '上午盘',
'volatility': 'high',
'recommendation': '交易活跃,波动较大',
'beijing_hour': now.hour,
'can_trade': True
}
# 午间休市(11:30-13:00
elif time(11, 30) < current_time < time(13, 0):
return {
'session': '午间休市',
'volatility': 'none',
'recommendation': '不可交易,可分析上午盘面',
'beijing_hour': now.hour,
'can_trade': False
}
# 下午盘(13:00-15:00
elif time(13, 0) <= current_time <= time(15, 0):
# 尾盘最后半小时(14:30-15:00
if current_time >= time(14, 30):
return {
'session': '尾盘',
'volatility': 'high',
'recommendation': '尾盘波动大,谨慎操作',
'beijing_hour': now.hour,
'can_trade': True
}
else:
return {
'session': '下午盘',
'volatility': 'medium',
'recommendation': '波动趋缓,适合布局',
'beijing_hour': now.hour,
'can_trade': True
}
# 盘后(15:00之后)
else:
return {
'session': '盘后',
'volatility': 'none',
'recommendation': '收盘后,可复盘分析',
'beijing_hour': now.hour,
'can_trade': False
}
def chat_completion(self, messages: List[Dict], model: str = "deepseek-chat",
temperature: float = 0.7, max_tokens: int = 2000) -> Dict:
"""
调用DeepSeek API
Args:
messages: 对话消息列表
model: 模型名称
temperature: 温度参数
max_tokens: 最大token数
Returns:
API响应
"""
import requests
payload = {
"model": model,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens
}
try:
response = requests.post(
f"{self.base_url}/chat/completions",
headers=self.headers,
json=payload,
timeout=60
)
response.raise_for_status()
return response.json()
except Exception as e:
self.logger.error(f"DeepSeek API调用失败: {e}")
raise
def analyze_stock_and_decide(self, stock_code: str, market_data: Dict,
account_info: Dict, has_position: bool = False,
position_cost: float = 0, position_quantity: int = 0) -> Dict:
"""
分析股票并做出交易决策(A股T+1规则)
Args:
stock_code: 股票代码(如:600519
market_data: 市场数据
account_info: 账户信息
has_position: 是否已持有该股票
position_cost: 持仓成本价格
position_quantity: 持仓数量
Returns:
交易决策
"""
# 获取交易时段
session_info = self.get_trading_session()
# 构建Prompt
prompt = self._build_a_stock_prompt(
stock_code, market_data, account_info,
has_position, session_info, position_cost, position_quantity
)
system_prompt = """你是一位资深的A股量化交易专家,拥有15年实战经验。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠️ A股交易规则(与币圈完全不同!)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[CRITICAL] T+1规则:
- 今天买入的股票,**今天不能卖出**,必须等到下一个交易日
- 这意味着:一旦买入,至少要持有到明天才能卖出
- 因此买入决策必须**极其谨慎**,不能像币圈那样快进快出
[CRITICAL] 涨跌停限制:
- 主板/中小板:±10%涨跌停
- 创业板/科创板:±20%涨跌停
- ST股票:±5%涨跌停
- 一旦涨停,很难买入;一旦跌停,很难卖出
[CRITICAL] 交易时间:
- 上午:9:30-11:30
- 下午:13:00-15:00
- 其他时间不能交易
[CRITICAL] 只能做多:
- A股不能做空(融券门槛高,散户基本不用)
- 只有买入和卖出两个动作
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎯 你的交易哲学(适配T+1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
**因为T+1限制,你的策略必须更加稳健!**
1. **买入前三思**
- 买入后至少持有1天,所以必须确保趋势向上
- 不能像币圈那样"试探性开仓",一旦买入就是承诺
- 最好在尾盘或第二天开盘前决策,避免盲目追高
2. **止损更困难**
- 如果今天买入后下跌,今天无法止损(T+1)
- 只能等明天再卖,可能面临更大亏损
- 因此:**宁可错过,不可做错**
3. **技术分析更重要**
- 日线级别趋势确认
- 支撑位/阻力位
- 成交量配合
- 量价关系判断
4. **风险控制严格**
- 单只股票仓位 ≤ 30%T+1风险大)
- 止损位:-5%(明天开盘立即执行)
- 止盈位:+8-15%(分批止盈)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 可选的交易动作
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
**如果当前无持仓**
- action = "BUY"(买入)- 必须确保技术面强势,趋势向上
- action = "HOLD"(观望)- 信号不明确时选择观望
**如果当前有持仓**
- action = "SELL"(卖出)- 达到止盈/止损条件,或技术面转弱
- action = "HOLD"(持有)- 趋势未改变,继续持有
- ⚠️ 注意:如果股票是今天买入的,受T+1限制无法卖出,只能选择HOLD
**绝对禁止**
- 不要在开盘前5分钟(9:30-9:35)买入,容易追高
- 不要在尾盘最后5分钟(14:55-15:00)买入,可能被套
- 不要逆趋势交易(趋势向下时买入)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📈 买入信号(必须满足至少3个条件)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. ✅ 趋势向上:价格 > MA5 > MA20 > MA60(多头排列)
2. ✅ 量价配合:成交量 > 5日均量的120%(放量上涨)
3. ✅ MACD金叉:MACD > 0 且DIF上穿DEA
4. ✅ RSI健康:RSI在50-70区间(不超买不超卖)
5. ✅ 突破关键位:突破前期高点或重要阻力位
6. ✅ 布林带位置:价格接近布林中轨上方,有上行空间
**加分项**
- 行业板块同步上涨
- 有重大利好消息
- 机构调研增加
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📉 卖出信号(满足任一条件立即卖出)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 🔴 止损触发:亏损 ≥ -5%(明天开盘立即卖出)
2. 🟢 止盈触发:盈利 ≥ +10%(分批止盈,先卖一半)
3. 🔴 趋势转弱:跌破MA20或MA60,且MACD死叉
4. 🔴 放量下跌:成交量放大但价格下跌(主力出货)
5. 🔴 技术破位:跌破重要支撑位
6. 🔴 重大利空:公司公告重大利空消息
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
💬 返回格式(必须严格JSON
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{
"action": "BUY" | "SELL" | "HOLD",
"confidence": 0-100,
"reasoning": "详细的决策理由,包括技术分析、风险评估等,200-300字",
"position_size_pct": 10-30, // 建议仓位百分比(因为T+1,建议≤30%
"stop_loss_pct": 5.0, // 止损百分比(建议5%
"take_profit_pct": 10.0, // 止盈百分比(建议10-15%
"risk_level": "low" | "medium" | "high",
"key_price_levels": {
"support": 支撑位价格,
"resistance": 阻力位价格,
"stop_loss": 止损位价格
}
}
**reasoning 示例**
"茅台当前价格1650元,日线级别呈多头排列(MA5 1645 > MA20 1620 > MA60 1580),
MACD金叉且柱状图持续放大,RSI 62处于健康区间。今日成交量较5日均量放大135%
显示有增量资金入场。技术面支撑位在1630元附近,阻力位在1680元。综合判断短期
趋势向上,但考虑T+1规则,建议仓位控制在20%,止损位设在1568元(-5%),
止盈目标1815元(+10%)。风险提示:如明日低开需谨慎..."
"""
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt}
]
try:
response = self.chat_completion(messages, temperature=0.3)
ai_response = response['choices'][0]['message']['content']
# 解析JSON决策
decision = self._parse_decision(ai_response)
return {
'success': True,
'decision': decision,
'raw_response': ai_response
}
except Exception as e:
self.logger.error(f"AI决策失败: {e}")
return {
'success': False,
'error': str(e)
}
def _build_a_stock_prompt(self, stock_code: str, market_data: Dict,
account_info: Dict, has_position: bool,
session_info: Dict, position_cost: float = 0,
position_quantity: int = 0) -> str:
"""构建A股分析提示词"""
prompt = f"""
[TIMER] 当前交易时段
═══════════════════════════════════════════════════════════
当前时段: {session_info['session']} (北京时间{session_info['beijing_hour']}:00)
市场状态: {session_info['volatility'].upper()}
时段建议: {session_info['recommendation']}
可交易: {'' if session_info['can_trade'] else ''}
[STOCK] 股票基本信息
═══════════════════════════════════════════════════════════
股票代码: {stock_code}
股票名称: {market_data.get('name', 'N/A')}
当前价格: ¥{market_data.get('current_price', 0):.2f}
今日涨跌: {market_data.get('change_pct', 0):+.2f}%
今日涨跌额: ¥{market_data.get('change_amount', 0):+.2f}
最高价: ¥{market_data.get('high', 0):.2f}
最低价: ¥{market_data.get('low', 0):.2f}
开盘价: ¥{market_data.get('open', 0):.2f}
昨收价: ¥{market_data.get('pre_close', 0):.2f}
成交量: {market_data.get('volume', 0):,.0f}
成交额: ¥{market_data.get('amount', 0):,.2f}
[TECHNICAL] 技术指标
═══════════════════════════════════════════════════════════
MA5: ¥{market_data.get('ma5', 0):.2f}
MA20: ¥{market_data.get('ma20', 0):.2f}
MA60: ¥{market_data.get('ma60', 0):.2f}
趋势判断: {'多头排列' if market_data.get('trend') == 'up' else '空头排列' if market_data.get('trend') == 'down' else '震荡'}
MACD:
DIF: {market_data.get('macd_dif', 0):.4f}
DEA: {market_data.get('macd_dea', 0):.4f}
MACD: {market_data.get('macd', 0):.4f} ({'金叉' if market_data.get('macd', 0) > 0 else '死叉'})
RSI(6): {market_data.get('rsi6', 50):.2f} {'[超买]' if market_data.get('rsi6', 50) > 80 else '[超卖]' if market_data.get('rsi6', 50) < 20 else '[正常]'}
RSI(12): {market_data.get('rsi12', 50):.2f}
RSI(24): {market_data.get('rsi24', 50):.2f}
KDJ:
K: {market_data.get('kdj_k', 50):.2f}
D: {market_data.get('kdj_d', 50):.2f}
J: {market_data.get('kdj_j', 50):.2f}
布林带:
上轨: ¥{market_data.get('boll_upper', 0):.2f}
中轨: ¥{market_data.get('boll_mid', 0):.2f}
下轨: ¥{market_data.get('boll_lower', 0):.2f}
位置: {market_data.get('boll_position', 'N/A')}
[VOLUME] 量能分析
═══════════════════════════════════════════════════════════
今日成交量: {market_data.get('volume', 0):,.0f}
5日均量: {market_data.get('vol_ma5', 0):,.0f}
量比: {market_data.get('volume_ratio', 0):.2f} ({'放量' if market_data.get('volume_ratio', 0) > 1.2 else '缩量' if market_data.get('volume_ratio', 0) < 0.8 else '正常'})
换手率: {market_data.get('turnover_rate', 0):.2f}%
[ACCOUNT] 账户状态
═══════════════════════════════════════════════════════════
可用资金: ¥{account_info.get('available_cash', 0):,.2f}
总资产: ¥{account_info.get('total_value', 0):,.2f}
持仓数量: {account_info.get('positions_count', 0)}
"""
# 如果已持有该股票
if has_position and position_cost > 0 and position_quantity > 0:
current_price = market_data.get('current_price', 0)
cost_total = position_cost * position_quantity
current_total = current_price * position_quantity
profit_loss = current_total - cost_total
profit_loss_pct = (profit_loss / cost_total * 100) if cost_total > 0 else 0
prompt += f"""
[POSITION] 当前持仓({stock_code} ⭐ 重要
═══════════════════════════════════════════════════════════
持仓数量: {position_quantity}
成本价: ¥{position_cost:.2f}
当前价: ¥{current_price:.2f}
持仓市值: ¥{current_total:,.2f}
浮动盈亏: ¥{profit_loss:,.2f} ({profit_loss_pct:+.2f}%)
⚠️ T+1限制: 该股票可以卖出(不受T+1限制)
💡 决策建议:
- 如果盈利且技术指标转弱 → 建议止盈卖出
- 如果亏损超过止损线(通常-5%)→ 建议止损卖出
- 如果技术指标强势且未到止盈位 → 建议继续持有
- 如果盈利且看好后市 → 可考虑加仓(但注意仓位控制)
"""
else:
prompt += """
[POSITION] 当前无持仓
═══════════════════════════════════════════════════════════
可考虑买入,但必须确保:
1. 技术面强势(满足至少3个买入信号)
2. 有足够的安全边际
3. 考虑T+1规则,买入后至少持有1天
4. 控制仓位,建议单只股票仓位≤30%
"""
# 主力资金数据(已禁用 - 接口不稳定)
# if 'main_force' in market_data:
# mf = market_data['main_force']
# prompt += f"""
# [MONEY] 主力资金流向
# ═══════════════════════════════════════════════════════════
# 主力净额: ¥{mf.get('main_net', 0):,.2f}万 ({mf.get('main_net_pct', 0):+.2f}%)
# 超大单: ¥{mf.get('super_net', 0):,.2f}万
# 大单: ¥{mf.get('big_net', 0):,.2f}万
# 中单: ¥{mf.get('mid_net', 0):,.2f}万
# 小单: ¥{mf.get('small_net', 0):,.2f}万
# 主力动向: {mf.get('trend', '观望')}
# """
prompt += "\n请基于以上数据,给出交易决策(JSON格式)。"
return prompt
def _parse_decision(self, ai_response: str) -> Dict:
"""解析AI决策响应"""
import json
try:
# 尝试多种提取方式
if "```json" in ai_response.lower():
json_start = ai_response.lower().find("```json") + 7
json_end = ai_response.find("```", json_start)
json_str = ai_response[json_start:json_end].strip()
elif "```" in ai_response:
first_tick = ai_response.find("```")
json_start = ai_response.find("\n", first_tick) + 1
json_end = ai_response.find("```", json_start)
json_str = ai_response[json_start:json_end].strip()
elif "{" in ai_response and "}" in ai_response:
start_idx = ai_response.find('{')
end_idx = ai_response.rfind('}') + 1
json_str = ai_response[start_idx:end_idx]
else:
json_str = ai_response
decision = json.loads(json_str)
# 验证必需字段
required_fields = ['action', 'confidence', 'reasoning']
for field in required_fields:
if field not in decision:
raise ValueError(f"缺少必需字段: {field}")
# 设置默认值
decision.setdefault('position_size_pct', 20)
decision.setdefault('stop_loss_pct', 5.0)
decision.setdefault('take_profit_pct', 10.0)
decision.setdefault('risk_level', 'medium')
return decision
except Exception as e:
self.logger.error(f"解析AI决策失败: {e}")
# 返回保守决策
return {
'action': 'HOLD',
'confidence': 0,
'reasoning': f'AI响应解析失败: {str(e)}',
'position_size_pct': 0,
'stop_loss_pct': 5.0,
'take_profit_pct': 10.0,
'risk_level': 'high'
}